1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
|
package main
import (
"bufio"
"bytes"
"fmt"
"io/ioutil"
"net"
"os"
"strings"
"text/template"
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend/disk"
"github.com/coreos/go-iptables/iptables"
"github.com/sirupsen/logrus"
)
// dnsNameLock embeds the CNI disk lock so we can hang methods from it
type dnsNameLock struct {
lock *disk.FileLock
}
// release unlocks and closes the disk lock.
func (m *dnsNameLock) release() error {
if err := m.lock.Unlock(); err != nil {
return err
}
return m.lock.Close()
}
// acquire locks the disk lock.
func (m *dnsNameLock) acquire() error {
return m.lock.Lock()
}
// getLock returns a dnsNameLock synchronizing the configuration directory for
// the domain.
func getLock(path string) (*dnsNameLock, error) {
l, err := disk.NewFileLock(path)
if err != nil {
return nil, err
}
return &dnsNameLock{l}, nil
}
// checkFromDNSMasqConfFile ensures that the dnsmasq conf file for
// the network interface exists or it creates it
func checkForDNSMasqConfFile(conf dnsNameFile) error {
if _, err := os.Stat(conf.ConfigFile); err == nil {
// the file already exists, we can proceed
return err
}
newConfig, err := generateDNSMasqConfig(conf)
if err != nil {
return err
}
ip, err := iptables.New()
if err != nil {
return err
}
args := []string{"-i", conf.NetworkInterface, "-p", "udp", "-m", "udp", "--dport", "53", "-j", "ACCEPT"}
exists, err := ip.Exists("filter", "INPUT", args...)
if err != nil {
return err
}
if !exists {
if err := ip.Insert("filter", "INPUT", 1, args...); err != nil {
return err
}
}
// Generate the template and compile it.
return ioutil.WriteFile(conf.ConfigFile, newConfig, 0700)
}
// generateDNSMasqConfig fills out the configuration file template for the dnsmasq service
func generateDNSMasqConfig(config dnsNameFile) ([]byte, error) {
var buf bytes.Buffer
templ, err := template.New("dnsmasq-conf-file").Parse(dnsMasqTemplate)
if err != nil {
return nil, err
}
if err := templ.Execute(&buf, config); err != nil {
return nil, err
}
buf.WriteByte('\n')
return buf.Bytes(), nil
}
// appendToFile appends a new entry to the dnsmasqs hosts file
func appendToFile(path, podname string, aliases []string, ips []*net.IPNet) error {
f, err := openFile(path)
if err != nil {
return err
}
defer func() {
if err := f.Close(); err != nil {
logrus.Errorf("failed to close file %q: %v", path, err)
}
}()
for _, ip := range ips {
entry := fmt.Sprintf("%s\t%s", ip.IP.String(), podname)
for _, alias := range aliases {
entry += fmt.Sprintf("\t%s", alias)
}
entry += "\n"
if _, err = f.WriteString(entry); err != nil {
return err
}
logrus.Debugf("appended %s: %s", path, entry)
}
return nil
}
// removeLineFromFile removes a given entry from the dnsmasq host file
func removeFromFile(path, podname string) (bool, error) {
var (
keepers []string
found bool
)
shouldHUP := false
backup := fmt.Sprintf("%s.old", path)
if err := os.Rename(path, backup); err != nil {
return shouldHUP, err
}
f, err := os.Open(backup)
if err != nil {
// if the open fails here, we need to revert things
renameFile(backup, path)
return shouldHUP, err
}
defer func() {
if err := f.Close(); err != nil {
logrus.Errorf("unable to close %q: %v", backup, err)
}
}()
oldFile := bufio.NewScanner(f)
// Iterate the old file
for oldFile.Scan() {
fields := strings.Fields(oldFile.Text())
// if the IP of the entry and the given IP dont match, it should
// go into the new file
if len(fields) > 1 && fields[1] != podname {
keepers = append(keepers, fmt.Sprintf("%s\n", oldFile.Text()))
continue
}
found = true
}
if !found {
// We never found a matching record; non-fatal
logrus.Debugf("a record for %s was never found in %s", podname, path)
}
fileLength, err := writeFile(path, keepers)
if err != nil {
renameFile(backup, path)
return shouldHUP, err
}
if fileLength > 0 {
shouldHUP = true
}
if err := os.Remove(backup); err != nil {
logrus.Errorf("unable to delete '%s': %q", backup, err)
}
return shouldHUP, nil
}
// renameFile renames a file to backup
func renameFile(oldpath, newpath string) {
if renameError := os.Rename(oldpath, newpath); renameError != nil {
logrus.Errorf("unable to restore %q to %q: %v", oldpath, newpath, renameError)
}
}
// writeFile writes a []string to the given path and returns the number
// of lines in the file
func writeFile(path string, content []string) (int, error) {
var counter int
f, err := openFile(path)
if err != nil {
return 0, err
}
defer func() {
if err := f.Close(); err != nil {
logrus.Errorf("unable to close %q: %v", path, err)
}
}()
for _, line := range content {
if _, err := f.WriteString(line); err != nil {
return 0, err
}
counter++
}
return counter, nil
}
// openFile opens a file for reading
func openFile(path string) (*os.File, error) {
return os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
}
|