github.com/pritambaral/docker@v1.4.2-0.20150120174542-b2fe1b3dd952/pkg/networkfs/resolvconf/resolvconf.go (about) 1 package resolvconf 2 3 import ( 4 "bytes" 5 "io/ioutil" 6 "regexp" 7 "strings" 8 "sync" 9 10 log "github.com/Sirupsen/logrus" 11 "github.com/docker/docker/utils" 12 ) 13 14 var ( 15 defaultDns = []string{"8.8.8.8", "8.8.4.4"} 16 localHostRegexp = regexp.MustCompile(`(?m)^nameserver 127[^\n]+\n*`) 17 nsRegexp = regexp.MustCompile(`^\s*nameserver\s*(([0-9]+\.){3}([0-9]+))\s*$`) 18 searchRegexp = regexp.MustCompile(`^\s*search\s*(([^\s]+\s*)*)$`) 19 ) 20 21 var lastModified struct { 22 sync.Mutex 23 sha256 string 24 contents []byte 25 } 26 27 func Get() ([]byte, error) { 28 resolv, err := ioutil.ReadFile("/etc/resolv.conf") 29 if err != nil { 30 return nil, err 31 } 32 return resolv, nil 33 } 34 35 // Retrieves the host /etc/resolv.conf file, checks against the last hash 36 // and, if modified since last check, returns the bytes and new hash. 37 // This feature is used by the resolv.conf updater for containers 38 func GetIfChanged() ([]byte, string, error) { 39 lastModified.Lock() 40 defer lastModified.Unlock() 41 42 resolv, err := ioutil.ReadFile("/etc/resolv.conf") 43 if err != nil { 44 return nil, "", err 45 } 46 newHash, err := utils.HashData(bytes.NewReader(resolv)) 47 if err != nil { 48 return nil, "", err 49 } 50 if lastModified.sha256 != newHash { 51 lastModified.sha256 = newHash 52 lastModified.contents = resolv 53 return resolv, newHash, nil 54 } 55 // nothing changed, so return no data 56 return nil, "", nil 57 } 58 59 // retrieve the last used contents and hash of the host resolv.conf 60 // Used by containers updating on restart 61 func GetLastModified() ([]byte, string) { 62 lastModified.Lock() 63 defer lastModified.Unlock() 64 65 return lastModified.contents, lastModified.sha256 66 } 67 68 // RemoveReplaceLocalDns looks for localhost (127.*) entries in the provided 69 // resolv.conf, removing local nameserver entries, and, if the resulting 70 // cleaned config has no defined nameservers left, adds default DNS entries 71 // It also returns a boolean to notify the caller if changes were made at all 72 func RemoveReplaceLocalDns(resolvConf []byte) ([]byte, bool) { 73 changed := false 74 cleanedResolvConf := localHostRegexp.ReplaceAll(resolvConf, []byte{}) 75 // if the resulting resolvConf is empty, use defaultDns 76 if !bytes.Contains(cleanedResolvConf, []byte("nameserver")) { 77 log.Infof("No non-localhost DNS nameservers are left in resolv.conf. Using default external servers : %v", defaultDns) 78 cleanedResolvConf = append(cleanedResolvConf, []byte("\nnameserver "+strings.Join(defaultDns, "\nnameserver "))...) 79 } 80 if !bytes.Equal(resolvConf, cleanedResolvConf) { 81 changed = true 82 } 83 return cleanedResolvConf, changed 84 } 85 86 // getLines parses input into lines and strips away comments. 87 func getLines(input []byte, commentMarker []byte) [][]byte { 88 lines := bytes.Split(input, []byte("\n")) 89 var output [][]byte 90 for _, currentLine := range lines { 91 var commentIndex = bytes.Index(currentLine, commentMarker) 92 if commentIndex == -1 { 93 output = append(output, currentLine) 94 } else { 95 output = append(output, currentLine[:commentIndex]) 96 } 97 } 98 return output 99 } 100 101 // GetNameservers returns nameservers (if any) listed in /etc/resolv.conf 102 func GetNameservers(resolvConf []byte) []string { 103 nameservers := []string{} 104 for _, line := range getLines(resolvConf, []byte("#")) { 105 var ns = nsRegexp.FindSubmatch(line) 106 if len(ns) > 0 { 107 nameservers = append(nameservers, string(ns[1])) 108 } 109 } 110 return nameservers 111 } 112 113 // GetNameserversAsCIDR returns nameservers (if any) listed in 114 // /etc/resolv.conf as CIDR blocks (e.g., "1.2.3.4/32") 115 // This function's output is intended for net.ParseCIDR 116 func GetNameserversAsCIDR(resolvConf []byte) []string { 117 nameservers := []string{} 118 for _, nameserver := range GetNameservers(resolvConf) { 119 nameservers = append(nameservers, nameserver+"/32") 120 } 121 return nameservers 122 } 123 124 // GetSearchDomains returns search domains (if any) listed in /etc/resolv.conf 125 // If more than one search line is encountered, only the contents of the last 126 // one is returned. 127 func GetSearchDomains(resolvConf []byte) []string { 128 domains := []string{} 129 for _, line := range getLines(resolvConf, []byte("#")) { 130 match := searchRegexp.FindSubmatch(line) 131 if match == nil { 132 continue 133 } 134 domains = strings.Fields(string(match[1])) 135 } 136 return domains 137 } 138 139 func Build(path string, dns, dnsSearch []string) error { 140 content := bytes.NewBuffer(nil) 141 for _, dns := range dns { 142 if _, err := content.WriteString("nameserver " + dns + "\n"); err != nil { 143 return err 144 } 145 } 146 if len(dnsSearch) > 0 { 147 if searchString := strings.Join(dnsSearch, " "); strings.Trim(searchString, " ") != "." { 148 if _, err := content.WriteString("search " + searchString + "\n"); err != nil { 149 return err 150 } 151 } 152 } 153 154 return ioutil.WriteFile(path, content.Bytes(), 0644) 155 }