浏览代码

Merge pull request #895 from sanimej/loop

Source external DNS queries from container namespace
Madhu Venugopal 9 年之前
父节点
当前提交
eef3a6da75
共有 2 个文件被更改,包括 83 次插入26 次删除
  1. 79 26
      libnetwork/resolver.go
  2. 4 0
      libnetwork/sandbox.go

+ 79 - 26
libnetwork/resolver.go

@@ -4,6 +4,8 @@ import (
 	"fmt"
 	"net"
 	"strings"
+	"sync"
+	"time"
 
 	log "github.com/Sirupsen/logrus"
 	"github.com/docker/libnetwork/iptables"
@@ -37,17 +39,24 @@ const (
 	ptrIPv6domain = ".ip6.arpa."
 	respTTL       = 600
 	maxExtDNS     = 3 //max number of external servers to try
+	extIOTimeout  = 3 * time.Second
 )
 
+type extDNSEntry struct {
+	ipStr   string
+	extConn net.Conn
+	extOnce sync.Once
+}
+
 // resolver implements the Resolver interface
 type resolver struct {
-	sb        *sandbox
-	extDNS    []string
-	server    *dns.Server
-	conn      *net.UDPConn
-	tcpServer *dns.Server
-	tcpListen *net.TCPListener
-	err       error
+	sb         *sandbox
+	extDNSList [maxExtDNS]extDNSEntry
+	server     *dns.Server
+	conn       *net.UDPConn
+	tcpServer  *dns.Server
+	tcpListen  *net.TCPListener
+	err        error
 }
 
 // NewResolver creates a new instance of the Resolver
@@ -136,7 +145,13 @@ func (r *resolver) Stop() {
 }
 
 func (r *resolver) SetExtServers(dns []string) {
-	r.extDNS = dns
+	l := len(dns)
+	if l > maxExtDNS {
+		l = maxExtDNS
+	}
+	for i := 0; i < l; i++ {
+		r.extDNSList[i].ipStr = dns[i]
+	}
 }
 
 func (r *resolver) NameServer() string {
@@ -202,8 +217,9 @@ func (r *resolver) handlePTRQuery(ptr string, query *dns.Msg) (*dns.Msg, error)
 
 func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
 	var (
-		resp *dns.Msg
-		err  error
+		extConn net.Conn
+		resp    *dns.Msg
+		err     error
 	)
 
 	if query == nil || len(query.Question) == 0 {
@@ -221,28 +237,65 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
 		return
 	}
 
+	proto := w.LocalAddr().Network()
 	if resp == nil {
-		if len(r.extDNS) == 0 {
-			return
-		}
+		for i := 0; i < maxExtDNS; i++ {
+			extDNS := &r.extDNSList[i]
+			if extDNS.ipStr == "" {
+				break
+			}
+			log.Debugf("Querying ext dns %s:%s for %s[%d]", proto, extDNS.ipStr, name, query.Question[0].Qtype)
 
-		num := maxExtDNS
-		if len(r.extDNS) < maxExtDNS {
-			num = len(r.extDNS)
-		}
-		for i := 0; i < num; i++ {
-			log.Debugf("Querying ext dns %s:%s for %s[%d]", w.LocalAddr().Network(), r.extDNS[i], name, query.Question[0].Qtype)
+			extConnect := func() {
+				addr := fmt.Sprintf("%s:%d", extDNS.ipStr, 53)
+				extConn, err = net.DialTimeout(proto, addr, extIOTimeout)
+			}
 
-			c := &dns.Client{Net: w.LocalAddr().Network()}
-			addr := fmt.Sprintf("%s:%d", r.extDNS[i], 53)
+			// For udp clients connection is persisted to reuse for further queries.
+			// Accessing extDNS.extConn be a race here between go rouines. Hence the
+			// connection setup is done in a Once block and fetch the extConn again
+			extConn = extDNS.extConn
+			if extConn == nil || proto == "tcp" {
+				if proto == "udp" {
+					extDNS.extOnce.Do(func() {
+						r.sb.execFunc(extConnect)
+						extDNS.extConn = extConn
+					})
+					extConn = extDNS.extConn
+				} else {
+					r.sb.execFunc(extConnect)
+				}
+				if err != nil {
+					log.Debugf("Connect failed, %s", err)
+					continue
+				}
+			}
 
-			resp, _, err = c.Exchange(query, addr)
-			if err == nil {
-				resp.Compress = true
-				break
+			// Timeout has to be set for every IO operation.
+			extConn.SetDeadline(time.Now().Add(extIOTimeout))
+			co := &dns.Conn{Conn: extConn}
+
+			defer func() {
+				if proto == "tcp" {
+					co.Close()
+				}
+			}()
+			err = co.WriteMsg(query)
+			if err != nil {
+				log.Debugf("Send to DNS server failed, %s", err)
+				continue
 			}
-			log.Errorf("external resolution failed, %s", err)
+
+			resp, err = co.ReadMsg()
+			if err != nil {
+				log.Debugf("Read from DNS server failed, %s", err)
+				continue
+			}
+
+			resp.Compress = true
+			break
 		}
+
 		if resp == nil {
 			return
 		}

+ 4 - 0
libnetwork/sandbox.go

@@ -394,6 +394,10 @@ func (sb *sandbox) ResolveIP(ip string) string {
 	return svc
 }
 
+func (sb *sandbox) execFunc(f func()) {
+	sb.osSbox.InvokeFunc(f)
+}
+
 func (sb *sandbox) ResolveName(name string) net.IP {
 	var ip net.IP