Tizen_4.0 base
[platform/upstream/docker-engine.git] / vendor / github.com / docker / libnetwork / sandbox_dns_unix.go
1 // +build !windows
2
3 package libnetwork
4
5 import (
6         "fmt"
7         "io/ioutil"
8         "os"
9         "path"
10         "path/filepath"
11         "strconv"
12         "strings"
13
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"
19 )
20
21 const (
22         defaultPrefix = "/var/lib/docker/network/files"
23         dirPerm       = 0755
24         filePerm      = 0644
25 )
26
27 func (sb *sandbox) startResolver(restore bool) {
28         sb.resolverOnce.Do(func() {
29                 var err error
30                 sb.resolver = NewResolver(resolverIPSandbox, true, sb.Key(), sb)
31                 defer func() {
32                         if err != nil {
33                                 sb.resolver = nil
34                         }
35                 }()
36
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
40                 // server to use.
41                 if !restore {
42                         err = sb.rebuildDNS()
43                         if err != nil {
44                                 logrus.Errorf("Updating resolv.conf failed for container %s, %q", sb.ContainerID(), err)
45                                 return
46                         }
47                 }
48                 sb.resolver.SetExtServers(sb.extDNS)
49
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)
52                         return
53                 }
54
55                 if err = sb.resolver.Start(); err != nil {
56                         logrus.Errorf("Resolver Start failed for container %s, %q", sb.ContainerID(), err)
57                 }
58         })
59 }
60
61 func (sb *sandbox) setupResolutionFiles() error {
62         if err := sb.buildHostsFile(); err != nil {
63                 return err
64         }
65
66         if err := sb.updateParentHosts(); err != nil {
67                 return err
68         }
69
70         if err := sb.setupDNS(); err != nil {
71                 return err
72         }
73
74         return nil
75 }
76
77 func (sb *sandbox) buildHostsFile() error {
78         if sb.config.hostsPath == "" {
79                 sb.config.hostsPath = defaultPrefix + "/" + sb.id + "/hosts"
80         }
81
82         dir, _ := filepath.Split(sb.config.hostsPath)
83         if err := createBasePath(dir); err != nil {
84                 return err
85         }
86
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)
91                 }
92                 return nil
93         }
94
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})
98         }
99
100         return etchosts.Build(sb.config.hostsPath, "", sb.config.hostName, sb.config.domainName, extraContent)
101 }
102
103 func (sb *sandbox) updateHostsFile(ifaceIP string) error {
104         if ifaceIP == "" {
105                 return nil
106         }
107
108         if sb.config.originHostsPath != "" {
109                 return nil
110         }
111
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)
118         }
119
120         parts := strings.SplitN(fqdn, ".", 2)
121         if len(parts) == 2 {
122                 mhost = fmt.Sprintf("%s %s", fqdn, parts[0])
123         }
124
125         extraContent := []etchosts.Record{{Hosts: mhost, IP: ifaceIP}}
126
127         sb.addHostsEntries(extraContent)
128         return nil
129 }
130
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)
134         }
135 }
136
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)
140         }
141 }
142
143 func (sb *sandbox) updateParentHosts() error {
144         var pSb Sandbox
145
146         for _, update := range sb.config.parentUpdates {
147                 sb.controller.WalkSandboxes(SandboxContainerWalker(&pSb, update.cid))
148                 if pSb == nil {
149                         continue
150                 }
151                 if err := etchosts.Update(pSb.(*sandbox).config.hostsPath, update.ip, update.name); err != nil {
152                         return err
153                 }
154         }
155
156         return nil
157 }
158
159 func (sb *sandbox) restorePath() {
160         if sb.config.resolvConfPath == "" {
161                 sb.config.resolvConfPath = defaultPrefix + "/" + sb.id + "/resolv.conf"
162         }
163         sb.config.resolvConfHashFile = sb.config.resolvConfPath + ".hash"
164         if sb.config.hostsPath == "" {
165                 sb.config.hostsPath = defaultPrefix + "/" + sb.id + "/hosts"
166         }
167 }
168
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
173                 if checkLoopback {
174                         hostLoopback = dns.IsIPv4Localhost(ip)
175                 }
176                 sb.extDNS = append(sb.extDNS, extDNSEntry{
177                         IPStr:        ip,
178                         HostLoopback: hostLoopback,
179                 })
180         }
181 }
182
183 func (sb *sandbox) setupDNS() error {
184         var newRC *resolvconf.File
185
186         if sb.config.resolvConfPath == "" {
187                 sb.config.resolvConfPath = defaultPrefix + "/" + sb.id + "/resolv.conf"
188         }
189
190         sb.config.resolvConfHashFile = sb.config.resolvConfPath + ".hash"
191
192         dir, _ := filepath.Split(sb.config.resolvConfPath)
193         if err := createBasePath(dir); err != nil {
194                 return err
195         }
196
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)
201                 }
202                 return nil
203         }
204
205         currRC, err := resolvconf.Get()
206         if err != nil {
207                 return err
208         }
209
210         if len(sb.config.dnsList) > 0 || len(sb.config.dnsSearchList) > 0 || len(sb.config.dnsOptionsList) > 0 {
211                 var (
212                         err            error
213                         dnsList        = resolvconf.GetNameservers(currRC.Content, types.IP)
214                         dnsSearchList  = resolvconf.GetSearchDomains(currRC.Content)
215                         dnsOptionsList = resolvconf.GetOptions(currRC.Content)
216                 )
217                 if len(sb.config.dnsList) > 0 {
218                         dnsList = sb.config.dnsList
219                 }
220                 if len(sb.config.dnsSearchList) > 0 {
221                         dnsSearchList = sb.config.dnsSearchList
222                 }
223                 if len(sb.config.dnsOptionsList) > 0 {
224                         dnsOptionsList = sb.config.dnsOptionsList
225                 }
226                 newRC, err = resolvconf.Build(sb.config.resolvConfPath, dnsList, dnsSearchList, dnsOptionsList)
227                 if err != nil {
228                         return err
229                 }
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)
234         } else {
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)
240
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 {
243                         return err
244                 }
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)
248                 }
249         }
250
251         // Write hash
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)
254         }
255
256         return nil
257 }
258
259 func (sb *sandbox) updateDNS(ipv6Enabled bool) error {
260         var (
261                 currHash string
262                 hashFile = sb.config.resolvConfHashFile
263         )
264
265         // This is for the host mode networking
266         if sb.config.originResolvConfPath != "" {
267                 return nil
268         }
269
270         if len(sb.config.dnsList) > 0 || len(sb.config.dnsSearchList) > 0 || len(sb.config.dnsOptionsList) > 0 {
271                 return nil
272         }
273
274         currRC, err := resolvconf.GetSpecific(sb.config.resolvConfPath)
275         if err != nil {
276                 if !os.IsNotExist(err) {
277                         return err
278                 }
279         } else {
280                 h, err := ioutil.ReadFile(hashFile)
281                 if err != nil {
282                         if !os.IsNotExist(err) {
283                                 return err
284                         }
285                 } else {
286                         currHash = string(h)
287                 }
288         }
289
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)
294                 return nil
295         }
296
297         // replace any localhost/127.* and remove IPv6 nameservers if IPv6 disabled.
298         newRC, err := resolvconf.FilterResolvDNS(currRC.Content, ipv6Enabled)
299         if err != nil {
300                 return err
301         }
302         err = ioutil.WriteFile(sb.config.resolvConfPath, newRC.Content, 0644)
303         if err != nil {
304                 return err
305         }
306
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")
310         if err != nil {
311                 return err
312         }
313         if err = tmpHashFile.Chmod(filePerm); err != nil {
314                 tmpHashFile.Close()
315                 return err
316         }
317         _, err = tmpHashFile.Write([]byte(newRC.Hash))
318         if err1 := tmpHashFile.Close(); err == nil {
319                 err = err1
320         }
321         if err != nil {
322                 return err
323         }
324         return os.Rename(tmpHashFile.Name(), hashFile)
325 }
326
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)
333         if err != nil {
334                 return err
335         }
336
337         if len(sb.extDNS) == 0 {
338                 sb.setExternalResolvers(currRC.Content, types.IPv4, false)
339         }
340         var (
341                 dnsList        = []string{sb.resolver.NameServer()}
342                 dnsOptionsList = resolvconf.GetOptions(currRC.Content)
343                 dnsSearchList  = resolvconf.GetSearchDomains(currRC.Content)
344         )
345
346         // external v6 DNS servers has to be listed in resolv.conf
347         dnsList = append(dnsList, resolvconf.GetNameservers(currRC.Content, types.IPv6)...)
348
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()
353
354 dnsOpt:
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, ":")
360                                         if len(parts) != 2 {
361                                                 return fmt.Errorf("invalid ndots option %v", option)
362                                         }
363                                         if num, err := strconv.Atoi(parts[1]); err != nil {
364                                                 return fmt.Errorf("invalid number for ndots option %v", option)
365                                         } else if num > 0 {
366                                                 sb.ndotsSet = true
367                                                 break dnsOpt
368                                         }
369                                 }
370                         }
371                 }
372         }
373
374         dnsOptionsList = append(dnsOptionsList, resOptions...)
375
376         _, err = resolvconf.Build(sb.config.resolvConfPath, dnsList, dnsSearchList, dnsOptionsList)
377         return err
378 }
379
380 func createBasePath(dir string) error {
381         return os.MkdirAll(dir, dirPerm)
382 }
383
384 func createFile(path string) error {
385         var f *os.File
386
387         dir, _ := filepath.Split(path)
388         err := createBasePath(dir)
389         if err != nil {
390                 return err
391         }
392
393         f, err = os.Create(path)
394         if err == nil {
395                 f.Close()
396         }
397
398         return err
399 }
400
401 func copyFile(src, dst string) error {
402         sBytes, err := ioutil.ReadFile(src)
403         if err != nil {
404                 return err
405         }
406         return ioutil.WriteFile(dst, sBytes, filePerm)
407 }