Ver código fonte

Implement DNS RR in the Docker embedded DNS server

Signed-off-by: Santhosh Manohar <santhosh@docker.com>
Santhosh Manohar 9 anos atrás
pai
commit
51058eecec
3 arquivos alterados com 68 adições e 19 exclusões
  1. 1 1
      libnetwork/libnetwork_test.go
  2. 62 13
      libnetwork/resolver.go
  3. 5 5
      libnetwork/sandbox.go

+ 1 - 1
libnetwork/libnetwork_test.go

@@ -1213,7 +1213,7 @@ func (f *fakeSandbox) SetKey(key string) error {
 	return nil
 }
 
-func (f *fakeSandbox) ResolveName(name string) net.IP {
+func (f *fakeSandbox) ResolveName(name string) []net.IP {
 	return nil
 }
 

+ 62 - 13
libnetwork/resolver.go

@@ -2,6 +2,7 @@ package libnetwork
 
 import (
 	"fmt"
+	"math/rand"
 	"net"
 	"strings"
 	"sync"
@@ -33,13 +34,14 @@ type Resolver interface {
 }
 
 const (
-	resolverIP    = "127.0.0.11"
-	dnsPort       = "53"
-	ptrIPv4domain = ".in-addr.arpa."
-	ptrIPv6domain = ".ip6.arpa."
-	respTTL       = 600
-	maxExtDNS     = 3 //max number of external servers to try
-	extIOTimeout  = 3 * time.Second
+	resolverIP      = "127.0.0.11"
+	dnsPort         = "53"
+	ptrIPv4domain   = ".in-addr.arpa."
+	ptrIPv6domain   = ".ip6.arpa."
+	respTTL         = 600
+	maxExtDNS       = 3 //max number of external servers to try
+	extIOTimeout    = 3 * time.Second
+	defaultRespSize = 512
 )
 
 type extDNSEntry struct {
@@ -59,6 +61,10 @@ type resolver struct {
 	err        error
 }
 
+func init() {
+	rand.Seed(time.Now().Unix())
+}
+
 // NewResolver creates a new instance of the Resolver
 func NewResolver(sb *sandbox) Resolver {
 	return &resolver{
@@ -166,22 +172,36 @@ func setCommonFlags(msg *dns.Msg) {
 	msg.RecursionAvailable = true
 }
 
+func shuffleAddr(addr []net.IP) []net.IP {
+	for i := len(addr) - 1; i > 0; i-- {
+		r := rand.Intn(i + 1)
+		addr[i], addr[r] = addr[r], addr[i]
+	}
+	return addr
+}
+
 func (r *resolver) handleIPv4Query(name string, query *dns.Msg) (*dns.Msg, error) {
 	addr := r.sb.ResolveName(name)
 	if addr == nil {
 		return nil, nil
 	}
 
-	log.Debugf("Lookup for %s: IP %s", name, addr.String())
+	log.Debugf("Lookup for %s: IP %v", name, addr)
 
 	resp := new(dns.Msg)
 	resp.SetReply(query)
 	setCommonFlags(resp)
 
-	rr := new(dns.A)
-	rr.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: respTTL}
-	rr.A = addr
-	resp.Answer = append(resp.Answer, rr)
+	if len(addr) > 1 {
+		addr = shuffleAddr(addr)
+	}
+
+	for _, ip := range addr {
+		rr := new(dns.A)
+		rr.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: respTTL}
+		rr.A = ip
+		resp.Answer = append(resp.Answer, rr)
+	}
 	return resp, nil
 }
 
@@ -215,6 +235,18 @@ func (r *resolver) handlePTRQuery(ptr string, query *dns.Msg) (*dns.Msg, error)
 	return resp, nil
 }
 
+func truncateResp(resp *dns.Msg, maxSize int, isTCP bool) {
+	if !isTCP {
+		resp.Truncated = true
+	}
+
+	// trim the Answer RRs one by one till the whole message fits
+	// within the reply size
+	for resp.Len() > maxSize {
+		resp.Answer = resp.Answer[:len(resp.Answer)-1]
+	}
+}
+
 func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
 	var (
 		extConn net.Conn
@@ -238,7 +270,24 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
 	}
 
 	proto := w.LocalAddr().Network()
-	if resp == nil {
+	maxSize := 0
+	if proto == "tcp" {
+		maxSize = dns.MaxMsgSize - 1
+	} else if proto == "udp" {
+		optRR := query.IsEdns0()
+		if optRR != nil {
+			maxSize = int(optRR.UDPSize())
+		}
+		if maxSize < defaultRespSize {
+			maxSize = defaultRespSize
+		}
+	}
+
+	if resp != nil {
+		if resp.Len() > maxSize {
+			truncateResp(resp, maxSize, proto == "tcp")
+		}
+	} else {
 		for i := 0; i < maxExtDNS; i++ {
 			extDNS := &r.extDNSList[i]
 			if extDNS.ipStr == "" {

+ 5 - 5
libnetwork/sandbox.go

@@ -37,7 +37,7 @@ type Sandbox interface {
 	Delete() error
 	// ResolveName searches for the service name in the networks to which the sandbox
 	// is connected to.
-	ResolveName(name string) net.IP
+	ResolveName(name string) []net.IP
 	// 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
@@ -398,8 +398,8 @@ func (sb *sandbox) execFunc(f func()) {
 	sb.osSbox.InvokeFunc(f)
 }
 
-func (sb *sandbox) ResolveName(name string) net.IP {
-	var ip net.IP
+func (sb *sandbox) ResolveName(name string) []net.IP {
+	var ip []net.IP
 
 	// Embedded server owns the docker network domain. Resolution should work
 	// for both container_name and container_name.network_name
@@ -447,7 +447,7 @@ func (sb *sandbox) ResolveName(name string) net.IP {
 	return nil
 }
 
-func (sb *sandbox) resolveName(req string, networkName string, epList []*endpoint, alias bool) net.IP {
+func (sb *sandbox) resolveName(req string, networkName string, epList []*endpoint, alias bool) []net.IP {
 	for _, ep := range epList {
 		name := req
 		n := ep.getNetwork()
@@ -488,7 +488,7 @@ func (sb *sandbox) resolveName(req string, networkName string, epList []*endpoin
 		ip, ok := sr.svcMap[name]
 		n.Unlock()
 		if ok {
-			return ip[0]
+			return ip
 		}
 	}
 	return nil