Source external DNS queries from container namespace
Signed-off-by: Santhosh Manohar <santhosh@docker.com>
This commit is contained in:
parent
2ffd4a9011
commit
43e3da2f3a
2 changed files with 85 additions and 28 deletions
|
@ -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
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
c := &dns.Client{Net: w.LocalAddr().Network()}
|
||||
addr := fmt.Sprintf("%s:%d", r.extDNS[i], 53)
|
||||
|
||||
resp, _, err = c.Exchange(query, addr)
|
||||
if err == nil {
|
||||
resp.Compress = true
|
||||
for i := 0; i < maxExtDNS; i++ {
|
||||
extDNS := &r.extDNSList[i]
|
||||
if extDNS.ipStr == "" {
|
||||
break
|
||||
}
|
||||
log.Errorf("external resolution failed, %s", err)
|
||||
log.Debugf("Querying ext dns %s:%s for %s[%d]", proto, extDNS.ipStr, name, query.Question[0].Qtype)
|
||||
|
||||
extConnect := func() {
|
||||
addr := fmt.Sprintf("%s:%d", extDNS.ipStr, 53)
|
||||
extConn, err = net.DialTimeout(proto, addr, extIOTimeout)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -391,6 +391,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
|
||||
|
||||
|
|
Loading…
Reference in a new issue