Browse Source

Avoid V6 queries in docker domain going to external nameservers

Signed-off-by: Santhosh Manohar <santhosh@docker.com>
Santhosh Manohar 9 years ago
parent
commit
0c22e1bd07
3 changed files with 51 additions and 25 deletions
  1. 2 2
      libnetwork/libnetwork_test.go
  2. 16 5
      libnetwork/resolver.go
  3. 33 18
      libnetwork/sandbox.go

+ 2 - 2
libnetwork/libnetwork_test.go

@@ -1216,8 +1216,8 @@ func (f *fakeSandbox) SetKey(key string) error {
 	return nil
 }
 
-func (f *fakeSandbox) ResolveName(name string, ipType int) []net.IP {
-	return nil
+func (f *fakeSandbox) ResolveName(name string, ipType int) ([]net.IP, bool) {
+	return nil, false
 }
 
 func (f *fakeSandbox) ResolveIP(ip string) string {

+ 16 - 5
libnetwork/resolver.go

@@ -186,18 +186,29 @@ func shuffleAddr(addr []net.IP) []net.IP {
 	return addr
 }
 
+func createRespMsg(query *dns.Msg) *dns.Msg {
+	resp := new(dns.Msg)
+	resp.SetReply(query)
+	setCommonFlags(resp)
+
+	return resp
+}
+
 func (r *resolver) handleIPQuery(name string, query *dns.Msg, ipType int) (*dns.Msg, error) {
-	addr := r.sb.ResolveName(name, ipType)
+	addr, ipv6Miss := r.sb.ResolveName(name, ipType)
+	if addr == nil && ipv6Miss {
+		// Send a reply without any Answer sections
+		log.Debugf("Lookup name %s present without IPv6 address", name)
+		resp := createRespMsg(query)
+		return resp, nil
+	}
 	if addr == nil {
 		return nil, nil
 	}
 
 	log.Debugf("Lookup for %s: IP %v", name, addr)
 
-	resp := new(dns.Msg)
-	resp.SetReply(query)
-	setCommonFlags(resp)
-
+	resp := createRespMsg(query)
 	if len(addr) > 1 {
 		addr = shuffleAddr(addr)
 	}

+ 33 - 18
libnetwork/sandbox.go

@@ -37,9 +37,11 @@ type Sandbox interface {
 	Rename(name string) error
 	// Delete destroys this container after detaching it from all connected endpoints.
 	Delete() error
-	// ResolveName resolves a service name to an IPv4 or IPv6 address by searching the
-	// networks the sandbox is connected to.
-	ResolveName(name string, iplen int) []net.IP
+	// ResolveName resolves a service name to an IPv4 or IPv6 address by searching
+	// the networks the sandbox is connected to. For IPv6 queries, second  return
+	// value will be true if the name exists in docker domain but doesn't have an
+	// IPv6 address. Such queries shouldn't be forwarded  to external nameservers.
+	ResolveName(name string, iplen int) ([]net.IP, bool)
 	// ResolveIP returns the service name for the passed in IP. IP is in reverse dotted
 	// notation; the format used for DNS PTR records
 	ResolveIP(name string) string
@@ -419,9 +421,7 @@ func (sb *sandbox) execFunc(f func()) {
 	sb.osSbox.InvokeFunc(f)
 }
 
-func (sb *sandbox) ResolveName(name string, ipType int) []net.IP {
-	var ip []net.IP
-
+func (sb *sandbox) ResolveName(name string, ipType int) ([]net.IP, bool) {
 	// Embedded server owns the docker network domain. Resolution should work
 	// for both container_name and container_name.network_name
 	// We allow '.' in service name and network name. For a name a.b.c.d the
@@ -454,21 +454,29 @@ func (sb *sandbox) ResolveName(name string, ipType int) []net.IP {
 		log.Debugf("To resolve: %v in %v", reqName[i], networkName[i])
 
 		// First check for local container alias
-		ip = sb.resolveName(reqName[i], networkName[i], epList, true, ipType)
+		ip, ipv6Miss := sb.resolveName(reqName[i], networkName[i], epList, true, ipType)
 		if ip != nil {
-			return ip
+			return ip, false
+		}
+		if ipv6Miss {
+			return ip, ipv6Miss
 		}
 
 		// Resolve the actual container name
-		ip = sb.resolveName(reqName[i], networkName[i], epList, false, ipType)
+		ip, ipv6Miss = sb.resolveName(reqName[i], networkName[i], epList, false, ipType)
 		if ip != nil {
-			return ip
+			return ip, false
+		}
+		if ipv6Miss {
+			return ip, ipv6Miss
 		}
 	}
-	return nil
+	return nil, false
 }
 
-func (sb *sandbox) resolveName(req string, networkName string, epList []*endpoint, alias bool, ipType int) []net.IP {
+func (sb *sandbox) resolveName(req string, networkName string, epList []*endpoint, alias bool, ipType int) ([]net.IP, bool) {
+	var ipv6Miss bool
+
 	for _, ep := range epList {
 		name := req
 		n := ep.getNetwork()
@@ -507,17 +515,24 @@ func (sb *sandbox) resolveName(req string, networkName string, epList []*endpoin
 
 		var ip []net.IP
 		n.Lock()
+		ip, ok = sr.svcMap[name]
+
 		if ipType == netutils.IPv6 {
-			ip, ok = sr.svcIPv6Map[name]
-		} else {
-			ip, ok = sr.svcMap[name]
+			// If the name resolved to v4 address then its a valid name in
+			// the docker network domain. If the network is not v6 enabled
+			// set ipv6Miss to filter the DNS query from going to external
+			// resolvers.
+			if ok && n.enableIPv6 == false {
+				ipv6Miss = true
+			}
+			ip = sr.svcIPv6Map[name]
 		}
 		n.Unlock()
-		if ok {
-			return ip
+		if ip != nil {
+			return ip, false
 		}
 	}
-	return nil
+	return nil, ipv6Miss
 }
 
 func (sb *sandbox) SetKey(basePath string) error {