diff --git a/libnetwork/libnetwork_test.go b/libnetwork/libnetwork_test.go index 5f305e0239..1c588ee2a9 100644 --- a/libnetwork/libnetwork_test.go +++ b/libnetwork/libnetwork_test.go @@ -1706,7 +1706,7 @@ func TestEnableIPv6(t *testing.T) { } tmpResolvConf := []byte("search pommesfrites.fr\nnameserver 12.34.56.78\nnameserver 2001:4860:4860::8888\n") - expectedResolvConf := []byte("search pommesfrites.fr\nnameserver 127.0.0.11\noptions ndots:0\n") + expectedResolvConf := []byte("search pommesfrites.fr\nnameserver 127.0.0.11\nnameserver 2001:4860:4860::8888\noptions ndots:0\n") //take a copy of resolv.conf for restoring after test completes resolvConfSystem, err := ioutil.ReadFile("/etc/resolv.conf") if err != nil { diff --git a/libnetwork/netutils/utils.go b/libnetwork/netutils/utils.go index 62287efcc9..482e4f038f 100644 --- a/libnetwork/netutils/utils.go +++ b/libnetwork/netutils/utils.go @@ -14,6 +14,13 @@ import ( "github.com/docker/libnetwork/types" ) +// constants for the IP address type +const ( + IP = iota // IPv4 and IPv6 + IPv4 + IPv6 +) + var ( // ErrNetworkOverlapsWithNameservers preformatted error ErrNetworkOverlapsWithNameservers = errors.New("requested network overlaps with nameserver") diff --git a/libnetwork/resolvconf/resolvconf.go b/libnetwork/resolvconf/resolvconf.go index d5169ada35..507d9ef50d 100644 --- a/libnetwork/resolvconf/resolvconf.go +++ b/libnetwork/resolvconf/resolvconf.go @@ -10,6 +10,7 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/docker/pkg/ioutils" + "github.com/docker/libnetwork/netutils" "github.com/docker/libnetwork/resolvconf/dns" ) @@ -29,6 +30,8 @@ var ( localhostNSRegexp = regexp.MustCompile(`(?m)^nameserver\s+` + dns.IPLocalhost + `\s*\n*`) nsIPv6Regexp = regexp.MustCompile(`(?m)^nameserver\s+` + ipv6Address + `\s*\n*`) nsRegexp = regexp.MustCompile(`^\s*nameserver\s*((` + ipv4Address + `)|(` + ipv6Address + `))\s*$`) + nsIPv6Regexpmatch = regexp.MustCompile(`^\s*nameserver\s*((` + ipv6Address + `))\s*$`) + nsIPv4Regexpmatch = regexp.MustCompile(`^\s*nameserver\s*((` + ipv4Address + `))\s*$`) searchRegexp = regexp.MustCompile(`^\s*search\s*(([^\s]+\s*)*)$`) optionsRegexp = regexp.MustCompile(`^\s*options\s*(([^\s]+\s*)*)$`) ) @@ -119,7 +122,7 @@ func FilterResolvDNS(resolvConf []byte, ipv6Enabled bool) (*File, error) { } // if the resulting resolvConf has no more nameservers defined, add appropriate // default DNS servers for IPv4 and (optionally) IPv6 - if len(GetNameservers(cleanedResolvConf)) == 0 { + if len(GetNameservers(cleanedResolvConf, netutils.IP)) == 0 { logrus.Infof("No non-localhost DNS nameservers are left in resolv.conf. Using default external servers : %v", defaultIPv4Dns) dns := defaultIPv4Dns if ipv6Enabled { @@ -151,10 +154,17 @@ func getLines(input []byte, commentMarker []byte) [][]byte { } // GetNameservers returns nameservers (if any) listed in /etc/resolv.conf -func GetNameservers(resolvConf []byte) []string { +func GetNameservers(resolvConf []byte, kind int) []string { nameservers := []string{} for _, line := range getLines(resolvConf, []byte("#")) { - var ns = nsRegexp.FindSubmatch(line) + var ns [][]byte + if kind == netutils.IP { + ns = nsRegexp.FindSubmatch(line) + } else if kind == netutils.IPv4 { + ns = nsIPv4Regexpmatch.FindSubmatch(line) + } else if kind == netutils.IPv6 { + ns = nsIPv6Regexpmatch.FindSubmatch(line) + } if len(ns) > 0 { nameservers = append(nameservers, string(ns[1])) } @@ -167,7 +177,7 @@ func GetNameservers(resolvConf []byte) []string { // This function's output is intended for net.ParseCIDR func GetNameserversAsCIDR(resolvConf []byte) []string { nameservers := []string{} - for _, nameserver := range GetNameservers(resolvConf) { + for _, nameserver := range GetNameservers(resolvConf, netutils.IP) { nameservers = append(nameservers, nameserver+"/32") } return nameservers diff --git a/libnetwork/resolvconf/resolvconf_test.go b/libnetwork/resolvconf/resolvconf_test.go index 878d248489..f0c2080cef 100644 --- a/libnetwork/resolvconf/resolvconf_test.go +++ b/libnetwork/resolvconf/resolvconf_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/docker/docker/pkg/ioutils" + "github.com/docker/libnetwork/netutils" _ "github.com/docker/libnetwork/testutils" ) @@ -48,7 +49,7 @@ nameserver 1.2.3.4 `search example.com nameserver 1.2.3.4 # not 4.3.2.1`: {"1.2.3.4"}, } { - test := GetNameservers([]byte(resolv)) + test := GetNameservers([]byte(resolv), netutils.IP) if !strSlicesEqual(test, result) { t.Fatalf("Wrong nameserver string {%s} should be %v. Input: %s", test, result, resolv) } diff --git a/libnetwork/resolver.go b/libnetwork/resolver.go index d395ab46b3..00302f668c 100644 --- a/libnetwork/resolver.go +++ b/libnetwork/resolver.go @@ -36,6 +36,7 @@ const ( ptrIPv4domain = ".in-addr.arpa." ptrIPv6domain = ".ip6.arpa." respTTL = 1800 + maxExtDNS = 3 //max number of external servers to try ) // resolver implements the Resolver interface @@ -188,15 +189,24 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) { if len(r.extDNS) == 0 { return } - log.Debugf("Querying ext dns %s for %s[%d]", r.extDNS[0], name, query.Question[0].Qtype) - c := &dns.Client{Net: "udp"} - addr := fmt.Sprintf("%s:%d", r.extDNS[0], 53) + num := maxExtDNS + if len(r.extDNS) < maxExtDNS { + num = len(r.extDNS) + } + for i := 0; i < num; i++ { + log.Debugf("Querying ext dns %s for %s[%d]", r.extDNS[i], name, query.Question[0].Qtype) - // TODO: iterate over avilable servers in case of error - resp, _, err = c.Exchange(query, addr) - if err != nil { + c := &dns.Client{Net: "udp"} + addr := fmt.Sprintf("%s:%d", r.extDNS[i], 53) + + resp, _, err = c.Exchange(query, addr) + if err == nil { + break + } log.Errorf("external resolution failed, %s", err) + } + if resp == nil { return } } diff --git a/libnetwork/sandbox.go b/libnetwork/sandbox.go index c8cf250863..b8d308e04e 100644 --- a/libnetwork/sandbox.go +++ b/libnetwork/sandbox.go @@ -14,6 +14,7 @@ import ( log "github.com/Sirupsen/logrus" "github.com/docker/libnetwork/etchosts" + "github.com/docker/libnetwork/netutils" "github.com/docker/libnetwork/osl" "github.com/docker/libnetwork/resolvconf" "github.com/docker/libnetwork/types" @@ -827,7 +828,7 @@ func (sb *sandbox) setupDNS() error { if len(sb.config.dnsList) > 0 || len(sb.config.dnsSearchList) > 0 || len(sb.config.dnsOptionsList) > 0 { var ( err error - dnsList = resolvconf.GetNameservers(currRC.Content) + dnsList = resolvconf.GetNameservers(currRC.Content, netutils.IP) dnsSearchList = resolvconf.GetSearchDomains(currRC.Content) dnsOptionsList = resolvconf.GetOptions(currRC.Content) ) @@ -935,7 +936,8 @@ func (sb *sandbox) rebuildDNS() error { } // localhost entries have already been filtered out from the list - sb.extDNS = resolvconf.GetNameservers(currRC.Content) + // retain only the v4 servers in sb for forwarding the DNS queries + sb.extDNS = resolvconf.GetNameservers(currRC.Content, netutils.IPv4) var ( dnsList = []string{sb.resolver.NameServer()} @@ -943,6 +945,9 @@ func (sb *sandbox) rebuildDNS() error { dnsSearchList = resolvconf.GetSearchDomains(currRC.Content) ) + // external v6 DNS servers has to be listed in resolv.conf + dnsList = append(dnsList, resolvconf.GetNameservers(currRC.Content, netutils.IPv6)...) + // Resolver returns the options in the format resolv.conf expects dnsOptionsList = append(dnsOptionsList, sb.resolver.ResolverOptions()...)