From bf832ec2a7b4c47fc06d6ee16728cb95f5718d39 Mon Sep 17 00:00:00 2001 From: Santhosh Manohar Date: Sun, 18 Dec 2016 18:27:13 -0800 Subject: [PATCH] Add embedded DNS server support for host loopback resolver Signed-off-by: Santhosh Manohar --- libnetwork/controller.go | 1 + libnetwork/resolvconf/dns/resolvconf.go | 11 ++++++++- libnetwork/resolver.go | 23 ++++++++++------- libnetwork/sandbox.go | 2 +- libnetwork/sandbox_dns_unix.go | 33 +++++++++++++++++++++---- libnetwork/sandbox_store.go | 2 +- 6 files changed, 55 insertions(+), 17 deletions(-) diff --git a/libnetwork/controller.go b/libnetwork/controller.go index 154b337a94..3fad7c4f61 100644 --- a/libnetwork/controller.go +++ b/libnetwork/controller.go @@ -918,6 +918,7 @@ func (c *controller) NewSandbox(containerID string, options ...SandboxOption) (s populatedEndpoints: map[string]struct{}{}, config: containerConfig{}, controller: c, + extDNS: []extDNSEntry{}, } } sBox = sb diff --git a/libnetwork/resolvconf/dns/resolvconf.go b/libnetwork/resolvconf/dns/resolvconf.go index 6c6dac589f..e348bc57f5 100644 --- a/libnetwork/resolvconf/dns/resolvconf.go +++ b/libnetwork/resolvconf/dns/resolvconf.go @@ -4,10 +4,14 @@ import ( "regexp" ) -// IPLocalhost is a regex patter for localhost IP address range. +// IPLocalhost is a regex pattern for IPv4 or IPv6 loopback range. const IPLocalhost = `((127\.([0-9]{1,3}\.){2}[0-9]{1,3})|(::1)$)` +// IPv4Localhost is a regex pattern for IPv4 localhost address range. +const IPv4Localhost = `(127\.([0-9]{1,3}\.){2}[0-9]{1,3})` + var localhostIPRegexp = regexp.MustCompile(IPLocalhost) +var localhostIPv4Regexp = regexp.MustCompile(IPv4Localhost) // IsLocalhost returns true if ip matches the localhost IP regular expression. // Used for determining if nameserver settings are being passed which are @@ -15,3 +19,8 @@ var localhostIPRegexp = regexp.MustCompile(IPLocalhost) func IsLocalhost(ip string) bool { return localhostIPRegexp.MatchString(ip) } + +// IsIPv4Localhost returns true if ip matches the IPv4 localhost regular expression. +func IsIPv4Localhost(ip string) bool { + return localhostIPv4Regexp.MatchString(ip) +} diff --git a/libnetwork/resolver.go b/libnetwork/resolver.go index ca3850c58e..fd8a9a909d 100644 --- a/libnetwork/resolver.go +++ b/libnetwork/resolver.go @@ -29,7 +29,7 @@ type Resolver interface { NameServer() string // SetExtServers configures the external nameservers the resolver // should use to forward queries - SetExtServers([]string) + SetExtServers([]extDNSEntry) // ResolverOptions returns resolv.conf options that should be set ResolverOptions() []string } @@ -69,7 +69,8 @@ const ( ) type extDNSEntry struct { - ipStr string + ipStr string + hostLoopback bool } // resolver implements the Resolver interface @@ -182,13 +183,13 @@ func (r *resolver) Stop() { r.queryLock = sync.Mutex{} } -func (r *resolver) SetExtServers(dns []string) { - l := len(dns) +func (r *resolver) SetExtServers(extDNS []extDNSEntry) { + l := len(extDNS) if l > maxExtDNS { l = maxExtDNS } for i := 0; i < l; i++ { - r.extDNSList[i].ipStr = dns[i] + r.extDNSList[i] = extDNS[i] } } @@ -417,10 +418,14 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) { extConn, err = net.DialTimeout(proto, addr, extIOTimeout) } - execErr := r.backend.ExecFunc(extConnect) - if execErr != nil { - logrus.Warn(execErr) - continue + if extDNS.hostLoopback { + extConnect() + } else { + execErr := r.backend.ExecFunc(extConnect) + if execErr != nil { + logrus.Warn(execErr) + continue + } } if err != nil { logrus.Warnf("Connect failed: %s", err) diff --git a/libnetwork/sandbox.go b/libnetwork/sandbox.go index 1fd585cd28..9067ddc9e0 100644 --- a/libnetwork/sandbox.go +++ b/libnetwork/sandbox.go @@ -69,7 +69,7 @@ type sandbox struct { id string containerID string config containerConfig - extDNS []string + extDNS []extDNSEntry osSbox osl.Sandbox controller *controller resolver Resolver diff --git a/libnetwork/sandbox_dns_unix.go b/libnetwork/sandbox_dns_unix.go index f4d1fad605..4ea0c23771 100644 --- a/libnetwork/sandbox_dns_unix.go +++ b/libnetwork/sandbox_dns_unix.go @@ -14,6 +14,7 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/libnetwork/etchosts" "github.com/docker/libnetwork/resolvconf" + "github.com/docker/libnetwork/resolvconf/dns" "github.com/docker/libnetwork/types" ) @@ -161,6 +162,20 @@ func (sb *sandbox) restorePath() { } } +func (sb *sandbox) setExternalResolvers(content []byte, addrType int, checkLoopback bool) { + servers := resolvconf.GetNameservers(content, addrType) + for _, ip := range servers { + hostLoopback := false + if checkLoopback { + hostLoopback = dns.IsIPv4Localhost(ip) + } + sb.extDNS = append(sb.extDNS, extDNSEntry{ + ipStr: ip, + hostLoopback: hostLoopback, + }) + } +} + func (sb *sandbox) setupDNS() error { var newRC *resolvconf.File @@ -208,7 +223,17 @@ func (sb *sandbox) setupDNS() error { if err != nil { return err } + // After building the resolv.conf from the user config save the + // external resolvers in the sandbox. Note that --dns 127.0.0.x + // config refers to the loopback in the container namespace + sb.setExternalResolvers(newRC.Content, types.IPv4, false) } else { + // If the host resolv.conf file has 127.0.0.x container should + // use the host restolver for queries. This is supported by the + // docker embedded DNS server. Hence save the external resolvers + // before filtering it out. + sb.setExternalResolvers(currRC.Content, types.IPv4, true) + // Replace any localhost/127.* (at this point we have no info about ipv6, pass it as true) if newRC, err = resolvconf.FilterResolvDNS(currRC.Content, true); err != nil { return err @@ -297,7 +322,6 @@ func (sb *sandbox) updateDNS(ipv6Enabled bool) error { // Embedded DNS server has to be enabled for this sandbox. Rebuild the container's // resolv.conf by doing the following -// - Save the external name servers in resolv.conf in the sandbox // - Add only the embedded server's IP to container's resolv.conf // - If the embedded server needs any resolv.conf options add it to the current list func (sb *sandbox) rebuildDNS() error { @@ -306,10 +330,9 @@ func (sb *sandbox) rebuildDNS() error { return err } - // localhost entries have already been filtered out from the list - // retain only the v4 servers in sb for forwarding the DNS queries - sb.extDNS = resolvconf.GetNameservers(currRC.Content, types.IPv4) - + if len(sb.extDNS) == 0 { + sb.setExternalResolvers(currRC.Content, types.IPv4, false) + } var ( dnsList = []string{sb.resolver.NameServer()} dnsOptionsList = resolvconf.GetOptions(currRC.Content) diff --git a/libnetwork/sandbox_store.go b/libnetwork/sandbox_store.go index 8b36d688a1..d50163ded5 100644 --- a/libnetwork/sandbox_store.go +++ b/libnetwork/sandbox_store.go @@ -27,7 +27,7 @@ type sbState struct { dbExists bool Eps []epState EpPriority map[string]int - ExtDNS []string + ExtDNS []extDNSEntry } func (sbs *sbState) Key() []string {