Ver código fonte

DNS support

Signed-off-by: msabansal <sabansal@microsoft.com>
msabansal 8 anos atrás
pai
commit
7f43fd30f3

+ 2 - 0
libnetwork/controller.go

@@ -804,6 +804,8 @@ func (c *controller) addNetwork(n *network) error {
 		return err
 	}
 
+	n.startResolver()
+
 	return nil
 }
 

+ 15 - 0
libnetwork/drivers/windows/labels.go

@@ -15,4 +15,19 @@ const (
 
 	// QosPolicies of the endpoint
 	QosPolicies = "com.docker.endpoint.windowsshim.qospolicies"
+
+	// VLAN of the network
+	VLAN = "com.docker.network.windowsshim.vlanid"
+
+	// VSID of the network
+	VSID = "com.docker.network.windowsshim.vsid"
+
+	// DNSSuffix of the network
+	DNSSuffix = "com.docker.network.windowsshim.dnssuffix"
+
+	// DNSServers of the network
+	DNSServers = "com.docker.network.windowsshim.dnsservers"
+
+	// SourceMac of the network
+	SourceMac = "com.docker.network.windowsshim.sourcemac"
 )

+ 69 - 1
libnetwork/drivers/windows/windows.go

@@ -15,6 +15,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"net"
+	"strconv"
 	"strings"
 	"sync"
 
@@ -34,6 +35,11 @@ type networkConfiguration struct {
 	Name               string
 	HnsID              string
 	RDID               string
+	VLAN               uint
+	VSID               uint
+	DNSServers         string
+	DNSSuffix          string
+	SourceMac          string
 	NetworkAdapterName string
 }
 
@@ -43,6 +49,7 @@ type endpointConfiguration struct {
 	PortBindings []types.PortBinding
 	ExposedPorts []types.TransportPort
 	QosPolicies  []types.QosPolicy
+	DNSServers   []string
 }
 
 type hnsEndpoint struct {
@@ -69,7 +76,7 @@ type driver struct {
 }
 
 func isValidNetworkType(networkType string) bool {
-	if "l2bridge" == networkType || "l2tunnel" == networkType || "nat" == networkType || "transparent" == networkType {
+	if "l2bridge" == networkType || "l2tunnel" == networkType || "nat" == networkType || "ics" == networkType || "transparent" == networkType {
 		return true
 	}
 
@@ -129,6 +136,22 @@ func (d *driver) parseNetworkOptions(id string, genericOptions map[string]string
 			config.RDID = value
 		case Interface:
 			config.NetworkAdapterName = value
+		case DNSSuffix:
+			config.DNSSuffix = value
+		case DNSServers:
+			config.DNSServers = value
+		case VLAN:
+			vlan, err := strconv.ParseUint(value, 10, 32)
+			if err != nil {
+				return nil, err
+			}
+			config.VLAN = uint(vlan)
+		case VSID:
+			vsid, err := strconv.ParseUint(value, 10, 32)
+			if err != nil {
+				return nil, err
+			}
+			config.VSID = uint(vsid)
 		}
 	}
 
@@ -207,9 +230,36 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo d
 			Name:               config.Name,
 			Type:               d.name,
 			Subnets:            subnets,
+			DNSServerList:      config.DNSServers,
+			DNSSuffix:          config.DNSSuffix,
+			SourceMac:          config.SourceMac,
 			NetworkAdapterName: config.NetworkAdapterName,
 		}
 
+		if config.VLAN != 0 {
+			vlanPolicy, err := json.Marshal(hcsshim.VlanPolicy{
+				Type: "VLAN",
+				VLAN: config.VLAN,
+			})
+
+			if err != nil {
+				return err
+			}
+			network.Policies = append(network.Policies, vlanPolicy)
+		}
+
+		if config.VSID != 0 {
+			vsidPolicy, err := json.Marshal(hcsshim.VsidPolicy{
+				Type: "VSID",
+				VSID: config.VSID,
+			})
+
+			if err != nil {
+				return err
+			}
+			network.Policies = append(network.Policies, vsidPolicy)
+		}
+
 		if network.Name == "" {
 			network.Name = id
 		}
@@ -379,6 +429,14 @@ func parseEndpointOptions(epOptions map[string]interface{}) (*endpointConfigurat
 		}
 	}
 
+	if opt, ok := epOptions[netlabel.DNSServers]; ok {
+		if dns, ok := opt.([]string); ok {
+			ec.DNSServers = dns
+		} else {
+			return nil, fmt.Errorf("Invalid endpoint configuration")
+		}
+	}
+
 	return ec, nil
 }
 
@@ -421,6 +479,12 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo,
 		endpointStruct.IPAddress = ifInfo.Address().IP
 	}
 
+	endpointStruct.DNSServerList = strings.Join(ec.DNSServers, ",")
+
+	if n.driver.name == "nat" {
+		endpointStruct.EnableInternalDNS = true
+	}
+
 	configurationb, err := json.Marshal(endpointStruct)
 	if err != nil {
 		return err
@@ -502,6 +566,10 @@ func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, erro
 	}
 
 	data := make(map[string]interface{}, 1)
+	if network.driver.name == "nat" {
+		data["AllowUnqualifiedDNSQuery"] = true
+	}
+
 	data["hnsid"] = ep.profileID
 	if ep.config.ExposedPorts != nil {
 		// Return a copy of the config data

+ 1 - 1
libnetwork/drivers/windows/windows_test.go

@@ -29,7 +29,7 @@ func testNetwork(networkType string, t *testing.T) {
 		},
 	}
 
-	err := d.CreateNetwork("dummy", netOption, ipdList, nil)
+	err := d.CreateNetwork("dummy", netOption, nil, ipdList, nil)
 	if err != nil {
 		t.Fatalf("Failed to create bridge: %v", err)
 	}

+ 8 - 0
libnetwork/endpoint.go

@@ -883,6 +883,14 @@ func CreateOptionPortMapping(portBindings []types.PortBinding) EndpointOption {
 	}
 }
 
+// CreateOptionDNS function returns an option setter for dns entry option to
+// be passed to container Create method.
+func CreateOptionDNS(dns []string) EndpointOption {
+	return func(ep *endpoint) {
+		ep.generic[netlabel.DNSServers] = dns
+	}
+}
+
 // CreateOptionAnonymous function returns an option setter for setting
 // this endpoint as anonymous
 func CreateOptionAnonymous() EndpointOption {

+ 5 - 9
libnetwork/libnetwork_internal_test.go

@@ -389,10 +389,8 @@ func TestSRVServiceQuery(t *testing.T) {
 
 	c.(*controller).svcRecords[n.ID()] = sr
 
-	_, ip, err := ep.Info().Sandbox().ResolveService("_http._tcp.web.swarm")
-	if err != nil {
-		t.Fatal(err)
-	}
+	_, ip := ep.Info().Sandbox().ResolveService("_http._tcp.web.swarm")
+
 	if len(ip) == 0 {
 		t.Fatal(err)
 	}
@@ -400,10 +398,8 @@ func TestSRVServiceQuery(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	_, ip, err = ep.Info().Sandbox().ResolveService("_host_http._tcp.web.swarm")
-	if err != nil {
-		t.Fatal(err)
-	}
+	_, ip = ep.Info().Sandbox().ResolveService("_host_http._tcp.web.swarm")
+
 	if len(ip) == 0 {
 		t.Fatal(err)
 	}
@@ -412,7 +408,7 @@ func TestSRVServiceQuery(t *testing.T) {
 	}
 
 	// Service name with invalid protocol name. Should fail without error
-	_, ip, err = ep.Info().Sandbox().ResolveService("_http._icmp.web.swarm")
+	_, ip = ep.Info().Sandbox().ResolveService("_http._icmp.web.swarm")
 	if len(ip) != 0 {
 		t.Fatal("Valid response for invalid service name")
 	}

+ 2 - 2
libnetwork/libnetwork_test.go

@@ -1208,8 +1208,8 @@ func (f *fakeSandbox) ResolveIP(ip string) string {
 	return ""
 }
 
-func (f *fakeSandbox) ResolveService(name string) ([]*net.SRV, []net.IP, error) {
-	return nil, nil, nil
+func (f *fakeSandbox) ResolveService(name string) ([]*net.SRV, []net.IP) {
+	return nil, nil
 }
 
 func (f *fakeSandbox) Endpoints() []libnetwork.Endpoint {

+ 3 - 0
libnetwork/netlabel/labels.go

@@ -27,6 +27,9 @@ const (
 	// ExposedPorts constant represents the container's Exposed Ports
 	ExposedPorts = Prefix + ".endpoint.exposedports"
 
+	// DNSServers A list of DNS servers associated with the endpoint
+	DNSServers = Prefix + ".endpoint.dnsservers"
+
 	//EnableIPv6 constant represents enabling IPV6 at network level
 	EnableIPv6 = Prefix + ".enable_ipv6"
 

+ 128 - 0
libnetwork/network.go

@@ -184,6 +184,8 @@ type network struct {
 	persist      bool
 	stopWatchCh  chan struct{}
 	drvOnce      *sync.Once
+	resolverOnce sync.Once
+	resolver     []Resolver
 	internal     bool
 	inDelete     bool
 	ingress      bool
@@ -803,6 +805,9 @@ func (n *network) deleteNetwork() error {
 		}
 	}
 
+	for _, resolver := range n.resolver {
+		resolver.Stop()
+	}
 	return nil
 }
 
@@ -1528,3 +1533,126 @@ func (n *network) TableEventRegister(tableName string) error {
 func (n *network) hasSpecialDriver() bool {
 	return n.Type() == "host" || n.Type() == "null"
 }
+
+func (n *network) ResolveName(req string, ipType int) ([]net.IP, bool) {
+	var ipv6Miss bool
+
+	c := n.getController()
+	c.Lock()
+	sr, ok := c.svcRecords[n.ID()]
+	c.Unlock()
+
+	if !ok {
+		return nil, false
+	}
+
+	req = strings.TrimSuffix(req, ".")
+	var ip []net.IP
+	n.Lock()
+	ip, ok = sr.svcMap[req]
+
+	if ipType == types.IPv6 {
+		// 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[req]
+	}
+	n.Unlock()
+
+	if ip != nil {
+		return ip, false
+	}
+
+	return nil, ipv6Miss
+}
+
+func (n *network) ResolveIP(ip string) string {
+	var svc string
+
+	c := n.getController()
+	c.Lock()
+	sr, ok := c.svcRecords[n.ID()]
+	c.Unlock()
+
+	if !ok {
+		return ""
+	}
+
+	nwName := n.Name()
+
+	n.Lock()
+	defer n.Unlock()
+	svc, ok = sr.ipMap[ip]
+
+	if ok {
+		return svc + "." + nwName
+	}
+
+	return svc
+}
+
+func (n *network) ResolveService(name string) ([]*net.SRV, []net.IP) {
+	c := n.getController()
+
+	srv := []*net.SRV{}
+	ip := []net.IP{}
+
+	log.Debugf("Service name To resolve: %v", name)
+
+	// There are DNS implementaions that allow SRV queries for names not in
+	// the format defined by RFC 2782. Hence specific validations checks are
+	// not done
+	parts := strings.Split(name, ".")
+	if len(parts) < 3 {
+		return nil, nil
+	}
+
+	portName := parts[0]
+	proto := parts[1]
+	svcName := strings.Join(parts[2:], ".")
+
+	c.Lock()
+	sr, ok := c.svcRecords[n.ID()]
+	c.Unlock()
+
+	if !ok {
+		return nil, nil
+	}
+
+	svcs, ok := sr.service[svcName]
+	if !ok {
+		return nil, nil
+	}
+
+	for _, svc := range svcs {
+		if svc.portName != portName {
+			continue
+		}
+		if svc.proto != proto {
+			continue
+		}
+		for _, t := range svc.target {
+			srv = append(srv,
+				&net.SRV{
+					Target: t.name,
+					Port:   t.port,
+				})
+
+			ip = append(ip, t.ip)
+		}
+	}
+
+	return srv, ip
+}
+
+func (n *network) ExecFunc(f func()) error {
+	return types.NotImplementedErrorf("ExecFunc not supported by network")
+}
+
+func (n *network) NdotsSet() bool {
+	return false
+}

+ 8 - 0
libnetwork/network_unix.go

@@ -0,0 +1,8 @@
+// +build !windows
+
+package libnetwork
+
+// Stub implementations for DNS related functions
+
+func (n *network) startResolver() {
+}

+ 52 - 0
libnetwork/network_windows.go

@@ -0,0 +1,52 @@
+// +build windows
+
+package libnetwork
+
+import (
+	"runtime"
+
+	"github.com/Microsoft/hcsshim"
+	log "github.com/Sirupsen/logrus"
+	"github.com/docker/libnetwork/drivers/windows"
+)
+
+func executeInCompartment(compartmentID uint32, x func()) {
+	runtime.LockOSThread()
+
+	if err := hcsshim.SetCurrentThreadCompartmentId(compartmentID); err != nil {
+		log.Error(err)
+	}
+	defer func() {
+		hcsshim.SetCurrentThreadCompartmentId(0)
+		runtime.UnlockOSThread()
+	}()
+
+	x()
+}
+
+func (n *network) startResolver() {
+	n.resolverOnce.Do(func() {
+		log.Debugf("Launching DNS server for network", n.Name())
+		options := n.Info().DriverOptions()
+		hnsid := options[windows.HNSID]
+
+		hnsresponse, err := hcsshim.HNSNetworkRequest("GET", hnsid, "")
+		if err != nil {
+			log.Errorf("Resolver Setup/Start failed for container %s, %q", n.Name(), err)
+			return
+		}
+
+		for _, subnet := range hnsresponse.Subnets {
+			if subnet.GatewayAddress != "" {
+				resolver := NewResolver(subnet.GatewayAddress, false, "", n)
+				log.Debugf("Binding a resolver on network %s gateway %s", n.Name(), subnet.GatewayAddress)
+				executeInCompartment(hnsresponse.DNSServerCompartment, resolver.SetupFunc(53))
+				if err = resolver.Start(); err != nil {
+					log.Errorf("Resolver Setup/Start failed for container %s, %q", n.Name(), err)
+				} else {
+					n.resolver = append(n.resolver, resolver)
+				}
+			}
+		}
+	})
+}

+ 72 - 32
libnetwork/resolver.go

@@ -23,7 +23,7 @@ type Resolver interface {
 	Stop()
 	// SetupFunc() provides the setup function that should be run
 	// in the container's network namespace.
-	SetupFunc() func()
+	SetupFunc(int) func()
 	// NameServer() returns the IP of the DNS resolver for the
 	// containers.
 	NameServer() string
@@ -34,8 +34,29 @@ type Resolver interface {
 	ResolverOptions() []string
 }
 
+// DNSBackend represents a backend DNS resolver used for DNS name
+// resolution. All the queries to the resolver are forwared to the
+// backend resolver.
+type DNSBackend interface {
+	// 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
+	// ResolveService returns all the backend details about the containers or hosts
+	// backing a service. Its purpose is to satisfy an SRV query
+	ResolveService(name string) ([]*net.SRV, []net.IP)
+	// ExecFunc allows a function to be executed in the context of the backend
+	// on behalf of the resolver.
+	ExecFunc(f func()) error
+	//NdotsSet queries the backends ndots dns option settings
+	NdotsSet() bool
+}
+
 const (
-	resolverIP      = "127.0.0.11"
 	dnsPort         = "53"
 	ptrIPv4domain   = ".in-addr.arpa."
 	ptrIPv6domain   = ".ip6.arpa."
@@ -53,16 +74,19 @@ type extDNSEntry struct {
 
 // resolver implements the Resolver interface
 type resolver struct {
-	sb         *sandbox
-	extDNSList [maxExtDNS]extDNSEntry
-	server     *dns.Server
-	conn       *net.UDPConn
-	tcpServer  *dns.Server
-	tcpListen  *net.TCPListener
-	err        error
-	count      int32
-	tStamp     time.Time
-	queryLock  sync.Mutex
+	backend       DNSBackend
+	extDNSList    [maxExtDNS]extDNSEntry
+	server        *dns.Server
+	conn          *net.UDPConn
+	tcpServer     *dns.Server
+	tcpListen     *net.TCPListener
+	err           error
+	count         int32
+	tStamp        time.Time
+	queryLock     sync.Mutex
+	listenAddress string
+	proxyDNS      bool
+	resolverKey   string
 }
 
 func init() {
@@ -70,20 +94,24 @@ func init() {
 }
 
 // NewResolver creates a new instance of the Resolver
-func NewResolver(sb *sandbox) Resolver {
+func NewResolver(address string, proxyDNS bool, resolverKey string, backend DNSBackend) Resolver {
 	return &resolver{
-		sb:  sb,
-		err: fmt.Errorf("setup not done yet"),
+		backend:       backend,
+		proxyDNS:      proxyDNS,
+		listenAddress: address,
+		resolverKey:   resolverKey,
+		err:           fmt.Errorf("setup not done yet"),
 	}
 }
 
-func (r *resolver) SetupFunc() func() {
+func (r *resolver) SetupFunc(port int) func() {
 	return (func() {
 		var err error
 
 		// DNS operates primarily on UDP
 		addr := &net.UDPAddr{
-			IP: net.ParseIP(resolverIP),
+			IP:   net.ParseIP(r.listenAddress),
+			Port: port,
 		}
 
 		r.conn, err = net.ListenUDP("udp", addr)
@@ -94,7 +122,8 @@ func (r *resolver) SetupFunc() func() {
 
 		// Listen on a TCP as well
 		tcpaddr := &net.TCPAddr{
-			IP: net.ParseIP(resolverIP),
+			IP:   net.ParseIP(r.listenAddress),
+			Port: port,
 		}
 
 		r.tcpListen, err = net.ListenTCP("tcp", tcpaddr)
@@ -156,7 +185,7 @@ func (r *resolver) SetExtServers(dns []string) {
 }
 
 func (r *resolver) NameServer() string {
-	return resolverIP
+	return r.listenAddress
 }
 
 func (r *resolver) ResolverOptions() []string {
@@ -184,7 +213,10 @@ func createRespMsg(query *dns.Msg) *dns.Msg {
 }
 
 func (r *resolver) handleIPQuery(name string, query *dns.Msg, ipType int) (*dns.Msg, error) {
-	addr, ipv6Miss := r.sb.ResolveName(name, ipType)
+	var addr []net.IP
+	var ipv6Miss bool
+	addr, ipv6Miss = r.backend.ResolveName(name, ipType)
+
 	if addr == nil && ipv6Miss {
 		// Send a reply without any Answer sections
 		log.Debugf("Lookup name %s present without IPv6 address", name)
@@ -230,7 +262,8 @@ func (r *resolver) handlePTRQuery(ptr string, query *dns.Msg) (*dns.Msg, error)
 		return nil, fmt.Errorf("invalid PTR query, %v", ptr)
 	}
 
-	host := r.sb.ResolveIP(parts[0])
+	host := r.backend.ResolveIP(parts[0])
+
 	if len(host) == 0 {
 		return nil, nil
 	}
@@ -250,11 +283,9 @@ func (r *resolver) handlePTRQuery(ptr string, query *dns.Msg) (*dns.Msg, error)
 }
 
 func (r *resolver) handleSRVQuery(svc string, query *dns.Msg) (*dns.Msg, error) {
-	srv, ip, err := r.sb.ResolveService(svc)
 
-	if err != nil {
-		return nil, err
-	}
+	srv, ip := r.backend.ResolveService(svc)
+
 	if len(srv) == 0 {
 		return nil, nil
 	}
@@ -325,16 +356,25 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
 		return
 	}
 
-	// If the user sets ndots > 0 explicitly and the query is
-	// in the root domain don't forward it out. We will return
-	// failure and let the client retry with the search domain
-	// attached
 	if resp == nil {
+		// If the backend doesn't support proxying dns request
+		// fail the response
+		if !r.proxyDNS {
+			resp = new(dns.Msg)
+			resp.SetRcode(query, dns.RcodeServerFailure)
+			w.WriteMsg(resp)
+			return
+		}
+
+		// If the user sets ndots > 0 explicitly and the query is
+		// in the root domain don't forward it out. We will return
+		// failure and let the client retry with the search domain
+		// attached
 		switch query.Question[0].Qtype {
 		case dns.TypeA:
 			fallthrough
 		case dns.TypeAAAA:
-			if r.sb.ndotsSet && !strings.Contains(strings.TrimSuffix(name, "."), ".") {
+			if r.backend.NdotsSet() && !strings.Contains(strings.TrimSuffix(name, "."), ".") {
 				resp = createRespMsg(query)
 			}
 		}
@@ -369,8 +409,8 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
 				extConn, err = net.DialTimeout(proto, addr, extIOTimeout)
 			}
 
-			r.sb.execFunc(extConnect)
-			if err != nil {
+			execErr := r.backend.ExecFunc(extConnect)
+			if execErr != nil || err != nil {
 				log.Debugf("Connect failed, %s", err)
 				continue
 			}

+ 2 - 2
libnetwork/resolver_unix.go

@@ -35,7 +35,7 @@ func reexecSetupResolver() {
 		os.Exit(1)
 	}
 
-	_, ipPort, _ := net.SplitHostPort(os.Args[2])
+	resolverIP, ipPort, _ := net.SplitHostPort(os.Args[2])
 	_, tcpPort, _ := net.SplitHostPort(os.Args[3])
 	rules := [][]string{
 		{"-t", "nat", "-I", outputChain, "-d", resolverIP, "-p", "udp", "--dport", dnsPort, "-j", "DNAT", "--to-destination", os.Args[2]},
@@ -90,7 +90,7 @@ func (r *resolver) setupIPTable() error {
 
 	cmd := &exec.Cmd{
 		Path:   reexec.Self(),
-		Args:   append([]string{"setup-resolver"}, r.sb.Key(), laddr, ltcpaddr),
+		Args:   append([]string{"setup-resolver"}, r.resolverKey, laddr, ltcpaddr),
 		Stdout: os.Stdout,
 		Stderr: os.Stderr,
 	}

+ 27 - 92
libnetwork/sandbox.go

@@ -37,19 +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. 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
-	// ResolveService returns all the backend details about the containers or hosts
-	// backing a service. Its purpose is to satisfy an SRV query
-	ResolveService(name string) ([]*net.SRV, []net.IP, error)
 	// Endpoints returns all the endpoints connected to the sandbox
 	Endpoints() []Endpoint
+	// ResolveService returns all the backend details about the containers or hosts
+	// backing a service. Its purpose is to satisfy an SRV query
+	ResolveService(name string) ([]*net.SRV, []net.IP)
 }
 
 // SandboxOption is an option setter function type used to pass various options to
@@ -131,6 +123,10 @@ type containerConfig struct {
 	exposedPorts      []types.TransportPort
 }
 
+const (
+	resolverIPSandbox = "127.0.0.11"
+)
+
 func (sb *sandbox) ID() string {
 	return sb.id
 }
@@ -415,33 +411,21 @@ func (sb *sandbox) ResolveIP(ip string) string {
 
 	for _, ep := range sb.getConnectedEndpoints() {
 		n := ep.getNetwork()
-
-		c := n.getController()
-
-		c.Lock()
-		sr, ok := c.svcRecords[n.ID()]
-		c.Unlock()
-
-		if !ok {
-			continue
-		}
-
-		nwName := n.Name()
-		n.Lock()
-		svc, ok = sr.ipMap[ip]
-		n.Unlock()
-		if ok {
-			return svc + "." + nwName
+		svc = n.ResolveIP(ip)
+		if len(svc) != 0 {
+			return svc
 		}
 	}
+
 	return svc
 }
 
-func (sb *sandbox) execFunc(f func()) {
+func (sb *sandbox) ExecFunc(f func()) error {
 	sb.osSbox.InvokeFunc(f)
+	return nil
 }
 
-func (sb *sandbox) ResolveService(name string) ([]*net.SRV, []net.IP, error) {
+func (sb *sandbox) ResolveService(name string) ([]*net.SRV, []net.IP) {
 	srv := []*net.SRV{}
 	ip := []net.IP{}
 
@@ -452,53 +436,18 @@ func (sb *sandbox) ResolveService(name string) ([]*net.SRV, []net.IP, error) {
 	// not done
 	parts := strings.Split(name, ".")
 	if len(parts) < 3 {
-		return nil, nil, nil
+		return nil, nil
 	}
 
-	portName := parts[0]
-	proto := parts[1]
-	svcName := strings.Join(parts[2:], ".")
-
 	for _, ep := range sb.getConnectedEndpoints() {
 		n := ep.getNetwork()
 
-		c := n.getController()
-
-		c.Lock()
-		sr, ok := c.svcRecords[n.ID()]
-		c.Unlock()
-
-		if !ok {
-			continue
-		}
-
-		svcs, ok := sr.service[svcName]
-		if !ok {
-			continue
-		}
-
-		for _, svc := range svcs {
-			if svc.portName != portName {
-				continue
-			}
-			if svc.proto != proto {
-				continue
-			}
-			for _, t := range svc.target {
-				srv = append(srv,
-					&net.SRV{
-						Target: t.name,
-						Port:   t.port,
-					})
-
-				ip = append(ip, t.ip)
-			}
-		}
+		srv, ip = n.ResolveService(name)
 		if len(srv) > 0 {
 			break
 		}
 	}
-	return srv, ip, nil
+	return srv, ip
 }
 
 func getDynamicNwEndpoints(epList []*endpoint) []*endpoint {
@@ -635,33 +584,15 @@ func (sb *sandbox) resolveName(req string, networkName string, epList []*endpoin
 			ep.Unlock()
 		}
 
-		c := n.getController()
-		c.Lock()
-		sr, ok := c.svcRecords[n.ID()]
-		c.Unlock()
+		ip, miss := n.ResolveName(name, ipType)
 
-		if !ok {
-			continue
-		}
-
-		var ip []net.IP
-		n.Lock()
-		ip, ok = sr.svcMap[name]
-
-		if ipType == types.IPv6 {
-			// 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 ip != nil {
 			return ip, false
 		}
+
+		if miss {
+			ipv6Miss = miss
+		}
 	}
 	return nil, ipv6Miss
 }
@@ -708,7 +639,7 @@ func (sb *sandbox) SetKey(basePath string) error {
 	if oldosSbox != nil && sb.resolver != nil {
 		sb.resolver.Stop()
 
-		sb.osSbox.InvokeFunc(sb.resolver.SetupFunc())
+		sb.osSbox.InvokeFunc(sb.resolver.SetupFunc(0))
 		if err := sb.resolver.Start(); err != nil {
 			log.Errorf("Resolver Setup/Start failed for container %s, %q", sb.ContainerID(), err)
 		}
@@ -1231,3 +1162,7 @@ func (eh *epHeap) Pop() interface{} {
 	*eh = old[0 : n-1]
 	return x
 }
+
+func (sb *sandbox) NdotsSet() bool {
+	return sb.ndotsSet
+}

+ 2 - 2
libnetwork/sandbox_dns_unix.go

@@ -26,7 +26,7 @@ const (
 func (sb *sandbox) startResolver(restore bool) {
 	sb.resolverOnce.Do(func() {
 		var err error
-		sb.resolver = NewResolver(sb)
+		sb.resolver = NewResolver(resolverIPSandbox, true, sb.Key(), sb)
 		defer func() {
 			if err != nil {
 				sb.resolver = nil
@@ -46,7 +46,7 @@ func (sb *sandbox) startResolver(restore bool) {
 		}
 		sb.resolver.SetExtServers(sb.extDNS)
 
-		sb.osSbox.InvokeFunc(sb.resolver.SetupFunc())
+		sb.osSbox.InvokeFunc(sb.resolver.SetupFunc(0))
 		if err = sb.resolver.Start(); err != nil {
 			log.Errorf("Resolver Setup/Start failed for container %s, %q", sb.ContainerID(), err)
 		}