diff --git a/libnetwork/libnetwork_internal_test.go b/libnetwork/libnetwork_internal_test.go index 9d4d562d0d..5d5ecbba20 100644 --- a/libnetwork/libnetwork_internal_test.go +++ b/libnetwork/libnetwork_internal_test.go @@ -380,7 +380,7 @@ func TestSRVServiceQuery(t *testing.T) { sr := svcInfo{ svcMap: make(map[string][]net.IP), svcIPv6Map: make(map[string][]net.IP), - ipMap: make(map[string]string), + ipMap: make(map[string]*ipInfo), service: make(map[string][]servicePorts), } // backing container for the service diff --git a/libnetwork/network.go b/libnetwork/network.go index 79e7e49a95..e966dae001 100644 --- a/libnetwork/network.go +++ b/libnetwork/network.go @@ -80,10 +80,18 @@ type NetworkInfo interface { // When the function returns true, the walk will stop. type EndpointWalker func(ep Endpoint) bool +// ipInfo is the reverse mapping from IP to service name to serve the PTR query. +// extResolver is set if an externl server resolves a service name to this IP. +// Its an indication to defer PTR queries also to that external server. +type ipInfo struct { + name string + extResolver bool +} + type svcInfo struct { svcMap map[string][]net.IP svcIPv6Map map[string][]net.IP - ipMap map[string]string + ipMap map[string]*ipInfo service map[string][]servicePorts } @@ -1070,10 +1078,12 @@ func (n *network) updateSvcRecord(ep *endpoint, localEps []*endpoint, isAdd bool } } -func addIPToName(ipMap map[string]string, name string, ip net.IP) { +func addIPToName(ipMap map[string]*ipInfo, name string, ip net.IP) { reverseIP := netutils.ReverseIP(ip.String()) if _, ok := ipMap[reverseIP]; !ok { - ipMap[reverseIP] = name + ipMap[reverseIP] = &ipInfo{ + name: name, + } } } @@ -1117,7 +1127,7 @@ func (n *network) addSvcRecords(name string, epIP net.IP, epIPv6 net.IP, ipMapUp sr = svcInfo{ svcMap: make(map[string][]net.IP), svcIPv6Map: make(map[string][]net.IP), - ipMap: make(map[string]string), + ipMap: make(map[string]*ipInfo), } c.svcRecords[n.ID()] = sr } @@ -1612,8 +1622,8 @@ func (n *network) ResolveName(req string, ipType int) ([]net.IP, bool) { c := n.getController() c.Lock() + defer c.Unlock() sr, ok := c.svcRecords[n.ID()] - c.Unlock() if !ok { return nil, false @@ -1621,7 +1631,6 @@ func (n *network) ResolveName(req string, ipType int) ([]net.IP, bool) { req = strings.TrimSuffix(req, ".") var ip []net.IP - n.Lock() ip, ok = sr.svcMap[req] if ipType == types.IPv6 { @@ -1634,7 +1643,6 @@ func (n *network) ResolveName(req string, ipType int) ([]net.IP, bool) { } ip = sr.svcIPv6Map[req] } - n.Unlock() if ip != nil { return ip, false @@ -1643,13 +1651,28 @@ func (n *network) ResolveName(req string, ipType int) ([]net.IP, bool) { return nil, ipv6Miss } -func (n *network) ResolveIP(ip string) string { - var svc string - +func (n *network) HandleQueryResp(name string, ip net.IP) { c := n.getController() c.Lock() + defer c.Unlock() + sr, ok := c.svcRecords[n.ID()] + + if !ok { + return + } + + ipStr := netutils.ReverseIP(ip.String()) + + if ipInfo, ok := sr.ipMap[ipStr]; ok { + ipInfo.extResolver = true + } +} + +func (n *network) ResolveIP(ip string) string { + c := n.getController() + c.Lock() + defer c.Unlock() sr, ok := c.svcRecords[n.ID()] - c.Unlock() if !ok { return "" @@ -1657,15 +1680,13 @@ func (n *network) ResolveIP(ip string) string { nwName := n.Name() - n.Lock() - defer n.Unlock() - svc, ok = sr.ipMap[ip] + ipInfo, ok := sr.ipMap[ip] - if ok { - return svc + "." + nwName + if !ok || ipInfo.extResolver { + return "" } - return svc + return ipInfo.name + "." + nwName } func (n *network) ResolveService(name string) ([]*net.SRV, []net.IP) { @@ -1689,8 +1710,8 @@ func (n *network) ResolveService(name string) ([]*net.SRV, []net.IP) { svcName := strings.Join(parts[2:], ".") c.Lock() + defer c.Unlock() sr, ok := c.svcRecords[n.ID()] - c.Unlock() if !ok { return nil, nil diff --git a/libnetwork/resolver.go b/libnetwork/resolver.go index ca3850c58e..1fb816c8aa 100644 --- a/libnetwork/resolver.go +++ b/libnetwork/resolver.go @@ -54,6 +54,9 @@ type DNSBackend interface { ExecFunc(f func()) error //NdotsSet queries the backends ndots dns option settings NdotsSet() bool + // HandleQueryResp passes the name & IP from a response to the backend. backend + // can use it to maintain any required state about the resolution + HandleQueryResp(name string, ip net.IP) } const ( @@ -462,9 +465,20 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) { logrus.Debugf("Read from DNS server failed, %s", err) continue } - r.forwardQueryEnd() - + if resp != nil { + for _, rr := range resp.Answer { + h := rr.Header() + switch h.Rrtype { + case dns.TypeA: + ip := rr.(*dns.A).A + r.backend.HandleQueryResp(h.Name, ip) + case dns.TypeAAAA: + ip := rr.(*dns.AAAA).AAAA + r.backend.HandleQueryResp(h.Name, ip) + } + } + } resp.Compress = true break } diff --git a/libnetwork/sandbox.go b/libnetwork/sandbox.go index 1fd585cd28..a3e890503a 100644 --- a/libnetwork/sandbox.go +++ b/libnetwork/sandbox.go @@ -411,6 +411,13 @@ func (sb *sandbox) updateGateway(ep *endpoint) error { return nil } +func (sb *sandbox) HandleQueryResp(name string, ip net.IP) { + for _, ep := range sb.getConnectedEndpoints() { + n := ep.getNetwork() + n.HandleQueryResp(name, ip) + } +} + func (sb *sandbox) ResolveIP(ip string) string { var svc string logrus.Debugf("IP To resolve %v", ip)