diff --git a/libnetwork/libnetwork_test.go b/libnetwork/libnetwork_test.go index 44b636f10d..474a3c7b8f 100644 --- a/libnetwork/libnetwork_test.go +++ b/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 } diff --git a/libnetwork/resolver.go b/libnetwork/resolver.go index f311d03d3b..a4dde792c7 100644 --- a/libnetwork/resolver.go +++ b/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 == "" { diff --git a/libnetwork/sandbox.go b/libnetwork/sandbox.go index 7b4a4b99f2..b841155b89 100644 --- a/libnetwork/sandbox.go +++ b/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