14 "github.com/Sirupsen/logrus"
15 "github.com/docker/libnetwork/etchosts"
16 "github.com/docker/libnetwork/resolvconf"
17 "github.com/docker/libnetwork/resolvconf/dns"
18 "github.com/docker/libnetwork/types"
22 defaultPrefix = "/var/lib/docker/network/files"
27 func (sb *sandbox) startResolver(restore bool) {
28 sb.resolverOnce.Do(func() {
30 sb.resolver = NewResolver(resolverIPSandbox, true, sb.Key(), sb)
37 // In the case of live restore container is already running with
38 // right resolv.conf contents created before. Just update the
39 // external DNS servers from the restored sandbox for embedded
44 logrus.Errorf("Updating resolv.conf failed for container %s, %q", sb.ContainerID(), err)
48 sb.resolver.SetExtServers(sb.extDNS)
50 if err = sb.osSbox.InvokeFunc(sb.resolver.SetupFunc(0)); err != nil {
51 logrus.Errorf("Resolver Setup function failed for container %s, %q", sb.ContainerID(), err)
55 if err = sb.resolver.Start(); err != nil {
56 logrus.Errorf("Resolver Start failed for container %s, %q", sb.ContainerID(), err)
61 func (sb *sandbox) setupResolutionFiles() error {
62 if err := sb.buildHostsFile(); err != nil {
66 if err := sb.updateParentHosts(); err != nil {
70 if err := sb.setupDNS(); err != nil {
77 func (sb *sandbox) buildHostsFile() error {
78 if sb.config.hostsPath == "" {
79 sb.config.hostsPath = defaultPrefix + "/" + sb.id + "/hosts"
82 dir, _ := filepath.Split(sb.config.hostsPath)
83 if err := createBasePath(dir); err != nil {
87 // This is for the host mode networking
88 if sb.config.originHostsPath != "" {
89 if err := copyFile(sb.config.originHostsPath, sb.config.hostsPath); err != nil && !os.IsNotExist(err) {
90 return types.InternalErrorf("could not copy source hosts file %s to %s: %v", sb.config.originHostsPath, sb.config.hostsPath, err)
95 extraContent := make([]etchosts.Record, 0, len(sb.config.extraHosts))
96 for _, extraHost := range sb.config.extraHosts {
97 extraContent = append(extraContent, etchosts.Record{Hosts: extraHost.name, IP: extraHost.IP})
100 return etchosts.Build(sb.config.hostsPath, "", sb.config.hostName, sb.config.domainName, extraContent)
103 func (sb *sandbox) updateHostsFile(ifaceIP string) error {
108 if sb.config.originHostsPath != "" {
112 // User might have provided a FQDN in hostname or split it across hostname
113 // and domainname. We want the FQDN and the bare hostname.
114 fqdn := sb.config.hostName
115 mhost := sb.config.hostName
116 if sb.config.domainName != "" {
117 fqdn = fmt.Sprintf("%s.%s", fqdn, sb.config.domainName)
120 parts := strings.SplitN(fqdn, ".", 2)
122 mhost = fmt.Sprintf("%s %s", fqdn, parts[0])
125 extraContent := []etchosts.Record{{Hosts: mhost, IP: ifaceIP}}
127 sb.addHostsEntries(extraContent)
131 func (sb *sandbox) addHostsEntries(recs []etchosts.Record) {
132 if err := etchosts.Add(sb.config.hostsPath, recs); err != nil {
133 logrus.Warnf("Failed adding service host entries to the running container: %v", err)
137 func (sb *sandbox) deleteHostsEntries(recs []etchosts.Record) {
138 if err := etchosts.Delete(sb.config.hostsPath, recs); err != nil {
139 logrus.Warnf("Failed deleting service host entries to the running container: %v", err)
143 func (sb *sandbox) updateParentHosts() error {
146 for _, update := range sb.config.parentUpdates {
147 sb.controller.WalkSandboxes(SandboxContainerWalker(&pSb, update.cid))
151 if err := etchosts.Update(pSb.(*sandbox).config.hostsPath, update.ip, update.name); err != nil {
159 func (sb *sandbox) restorePath() {
160 if sb.config.resolvConfPath == "" {
161 sb.config.resolvConfPath = defaultPrefix + "/" + sb.id + "/resolv.conf"
163 sb.config.resolvConfHashFile = sb.config.resolvConfPath + ".hash"
164 if sb.config.hostsPath == "" {
165 sb.config.hostsPath = defaultPrefix + "/" + sb.id + "/hosts"
169 func (sb *sandbox) setExternalResolvers(content []byte, addrType int, checkLoopback bool) {
170 servers := resolvconf.GetNameservers(content, addrType)
171 for _, ip := range servers {
172 hostLoopback := false
174 hostLoopback = dns.IsIPv4Localhost(ip)
176 sb.extDNS = append(sb.extDNS, extDNSEntry{
178 HostLoopback: hostLoopback,
183 func (sb *sandbox) setupDNS() error {
184 var newRC *resolvconf.File
186 if sb.config.resolvConfPath == "" {
187 sb.config.resolvConfPath = defaultPrefix + "/" + sb.id + "/resolv.conf"
190 sb.config.resolvConfHashFile = sb.config.resolvConfPath + ".hash"
192 dir, _ := filepath.Split(sb.config.resolvConfPath)
193 if err := createBasePath(dir); err != nil {
197 // This is for the host mode networking
198 if sb.config.originResolvConfPath != "" {
199 if err := copyFile(sb.config.originResolvConfPath, sb.config.resolvConfPath); err != nil {
200 return fmt.Errorf("could not copy source resolv.conf file %s to %s: %v", sb.config.originResolvConfPath, sb.config.resolvConfPath, err)
205 currRC, err := resolvconf.Get()
210 if len(sb.config.dnsList) > 0 || len(sb.config.dnsSearchList) > 0 || len(sb.config.dnsOptionsList) > 0 {
213 dnsList = resolvconf.GetNameservers(currRC.Content, types.IP)
214 dnsSearchList = resolvconf.GetSearchDomains(currRC.Content)
215 dnsOptionsList = resolvconf.GetOptions(currRC.Content)
217 if len(sb.config.dnsList) > 0 {
218 dnsList = sb.config.dnsList
220 if len(sb.config.dnsSearchList) > 0 {
221 dnsSearchList = sb.config.dnsSearchList
223 if len(sb.config.dnsOptionsList) > 0 {
224 dnsOptionsList = sb.config.dnsOptionsList
226 newRC, err = resolvconf.Build(sb.config.resolvConfPath, dnsList, dnsSearchList, dnsOptionsList)
230 // After building the resolv.conf from the user config save the
231 // external resolvers in the sandbox. Note that --dns 127.0.0.x
232 // config refers to the loopback in the container namespace
233 sb.setExternalResolvers(newRC.Content, types.IPv4, false)
235 // If the host resolv.conf file has 127.0.0.x container should
236 // use the host restolver for queries. This is supported by the
237 // docker embedded DNS server. Hence save the external resolvers
238 // before filtering it out.
239 sb.setExternalResolvers(currRC.Content, types.IPv4, true)
241 // Replace any localhost/127.* (at this point we have no info about ipv6, pass it as true)
242 if newRC, err = resolvconf.FilterResolvDNS(currRC.Content, true); err != nil {
245 // No contention on container resolv.conf file at sandbox creation
246 if err := ioutil.WriteFile(sb.config.resolvConfPath, newRC.Content, filePerm); err != nil {
247 return types.InternalErrorf("failed to write unhaltered resolv.conf file content when setting up dns for sandbox %s: %v", sb.ID(), err)
252 if err := ioutil.WriteFile(sb.config.resolvConfHashFile, []byte(newRC.Hash), filePerm); err != nil {
253 return types.InternalErrorf("failed to write resolv.conf hash file when setting up dns for sandbox %s: %v", sb.ID(), err)
259 func (sb *sandbox) updateDNS(ipv6Enabled bool) error {
262 hashFile = sb.config.resolvConfHashFile
265 // This is for the host mode networking
266 if sb.config.originResolvConfPath != "" {
270 if len(sb.config.dnsList) > 0 || len(sb.config.dnsSearchList) > 0 || len(sb.config.dnsOptionsList) > 0 {
274 currRC, err := resolvconf.GetSpecific(sb.config.resolvConfPath)
276 if !os.IsNotExist(err) {
280 h, err := ioutil.ReadFile(hashFile)
282 if !os.IsNotExist(err) {
290 if currHash != "" && currHash != currRC.Hash {
291 // Seems the user has changed the container resolv.conf since the last time
292 // we checked so return without doing anything.
293 //logrus.Infof("Skipping update of resolv.conf file with ipv6Enabled: %t because file was touched by user", ipv6Enabled)
297 // replace any localhost/127.* and remove IPv6 nameservers if IPv6 disabled.
298 newRC, err := resolvconf.FilterResolvDNS(currRC.Content, ipv6Enabled)
302 err = ioutil.WriteFile(sb.config.resolvConfPath, newRC.Content, 0644)
307 // write the new hash in a temp file and rename it to make the update atomic
308 dir := path.Dir(sb.config.resolvConfPath)
309 tmpHashFile, err := ioutil.TempFile(dir, "hash")
313 if err = tmpHashFile.Chmod(filePerm); err != nil {
317 _, err = tmpHashFile.Write([]byte(newRC.Hash))
318 if err1 := tmpHashFile.Close(); err == nil {
324 return os.Rename(tmpHashFile.Name(), hashFile)
327 // Embedded DNS server has to be enabled for this sandbox. Rebuild the container's
328 // resolv.conf by doing the following
329 // - Add only the embedded server's IP to container's resolv.conf
330 // - If the embedded server needs any resolv.conf options add it to the current list
331 func (sb *sandbox) rebuildDNS() error {
332 currRC, err := resolvconf.GetSpecific(sb.config.resolvConfPath)
337 if len(sb.extDNS) == 0 {
338 sb.setExternalResolvers(currRC.Content, types.IPv4, false)
341 dnsList = []string{sb.resolver.NameServer()}
342 dnsOptionsList = resolvconf.GetOptions(currRC.Content)
343 dnsSearchList = resolvconf.GetSearchDomains(currRC.Content)
346 // external v6 DNS servers has to be listed in resolv.conf
347 dnsList = append(dnsList, resolvconf.GetNameservers(currRC.Content, types.IPv6)...)
349 // If the user config and embedded DNS server both have ndots option set,
350 // remember the user's config so that unqualified names not in the docker
351 // domain can be dropped.
352 resOptions := sb.resolver.ResolverOptions()
355 for _, resOpt := range resOptions {
356 if strings.Contains(resOpt, "ndots") {
357 for _, option := range dnsOptionsList {
358 if strings.Contains(option, "ndots") {
359 parts := strings.Split(option, ":")
361 return fmt.Errorf("invalid ndots option %v", option)
363 if num, err := strconv.Atoi(parts[1]); err != nil {
364 return fmt.Errorf("invalid number for ndots option %v", option)
374 dnsOptionsList = append(dnsOptionsList, resOptions...)
376 _, err = resolvconf.Build(sb.config.resolvConfPath, dnsList, dnsSearchList, dnsOptionsList)
380 func createBasePath(dir string) error {
381 return os.MkdirAll(dir, dirPerm)
384 func createFile(path string) error {
387 dir, _ := filepath.Split(path)
388 err := createBasePath(dir)
393 f, err = os.Create(path)
401 func copyFile(src, dst string) error {
402 sBytes, err := ioutil.ReadFile(src)
406 return ioutil.WriteFile(dst, sBytes, filePerm)