Skip to content

Commit 258ae90

Browse files
author
Santhosh Manohar
committed
Source external DNS queries from container namespace
Signed-off-by: Santhosh Manohar <santhosh@docker.com>
1 parent 9994ce1 commit 258ae90

2 files changed

Lines changed: 83 additions & 26 deletions

File tree

resolver.go

Lines changed: 79 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"fmt"
55
"net"
66
"strings"
7+
"sync"
8+
"time"
79

810
log "github.com/Sirupsen/logrus"
911
"github.com/docker/libnetwork/iptables"
@@ -37,17 +39,24 @@ const (
3739
ptrIPv6domain = ".ip6.arpa."
3840
respTTL = 600
3941
maxExtDNS = 3 //max number of external servers to try
42+
extIOTimeout = 3 * time.Second
4043
)
4144

45+
type extDNSEntry struct {
46+
ipStr string
47+
extConn net.Conn
48+
extOnce sync.Once
49+
}
50+
4251
// resolver implements the Resolver interface
4352
type resolver struct {
44-
sb *sandbox
45-
extDNS []string
46-
server *dns.Server
47-
conn *net.UDPConn
48-
tcpServer *dns.Server
49-
tcpListen *net.TCPListener
50-
err error
53+
sb *sandbox
54+
extDNSList [maxExtDNS]extDNSEntry
55+
server *dns.Server
56+
conn *net.UDPConn
57+
tcpServer *dns.Server
58+
tcpListen *net.TCPListener
59+
err error
5160
}
5261

5362
// NewResolver creates a new instance of the Resolver
@@ -136,7 +145,13 @@ func (r *resolver) Stop() {
136145
}
137146

138147
func (r *resolver) SetExtServers(dns []string) {
139-
r.extDNS = dns
148+
l := len(dns)
149+
if l > maxExtDNS {
150+
l = maxExtDNS
151+
}
152+
for i := 0; i < l; i++ {
153+
r.extDNSList[i].ipStr = dns[i]
154+
}
140155
}
141156

142157
func (r *resolver) NameServer() string {
@@ -202,8 +217,9 @@ func (r *resolver) handlePTRQuery(ptr string, query *dns.Msg) (*dns.Msg, error)
202217

203218
func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
204219
var (
205-
resp *dns.Msg
206-
err error
220+
extConn net.Conn
221+
resp *dns.Msg
222+
err error
207223
)
208224

209225
if query == nil || len(query.Question) == 0 {
@@ -221,28 +237,65 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
221237
return
222238
}
223239

240+
proto := w.LocalAddr().Network()
224241
if resp == nil {
225-
if len(r.extDNS) == 0 {
226-
return
227-
}
242+
for i := 0; i < maxExtDNS; i++ {
243+
extDNS := &r.extDNSList[i]
244+
if extDNS.ipStr == "" {
245+
break
246+
}
247+
log.Debugf("Querying ext dns %s:%s for %s[%d]", proto, extDNS.ipStr, name, query.Question[0].Qtype)
228248

229-
num := maxExtDNS
230-
if len(r.extDNS) < maxExtDNS {
231-
num = len(r.extDNS)
232-
}
233-
for i := 0; i < num; i++ {
234-
log.Debugf("Querying ext dns %s:%s for %s[%d]", w.LocalAddr().Network(), r.extDNS[i], name, query.Question[0].Qtype)
249+
extConnect := func() {
250+
addr := fmt.Sprintf("%s:%d", extDNS.ipStr, 53)
251+
extConn, err = net.DialTimeout(proto, addr, extIOTimeout)
252+
}
235253

236-
c := &dns.Client{Net: w.LocalAddr().Network()}
237-
addr := fmt.Sprintf("%s:%d", r.extDNS[i], 53)
254+
// For udp clients connection is persisted to reuse for further queries.
255+
// Accessing extDNS.extConn be a race here between go rouines. Hence the
256+
// connection setup is done in a Once block and fetch the extConn again
257+
extConn = extDNS.extConn
258+
if extConn == nil || proto == "tcp" {
259+
if proto == "udp" {
260+
extDNS.extOnce.Do(func() {
261+
r.sb.execFunc(extConnect)
262+
extDNS.extConn = extConn
263+
})
264+
extConn = extDNS.extConn
265+
} else {
266+
r.sb.execFunc(extConnect)
267+
}
268+
if err != nil {
269+
log.Debugf("Connect failed, %s", err)
270+
continue
271+
}
272+
}
238273

239-
resp, _, err = c.Exchange(query, addr)
240-
if err == nil {
241-
resp.Compress = true
242-
break
274+
// Timeout has to be set for every IO operation.
275+
extConn.SetDeadline(time.Now().Add(extIOTimeout))
276+
co := &dns.Conn{Conn: extConn}
277+
278+
defer func() {
279+
if proto == "tcp" {
280+
co.Close()
281+
}
282+
}()
283+
err = co.WriteMsg(query)
284+
if err != nil {
285+
log.Debugf("Send to DNS server failed, %s", err)
286+
continue
243287
}
244-
log.Errorf("external resolution failed, %s", err)
288+
289+
resp, err = co.ReadMsg()
290+
if err != nil {
291+
log.Debugf("Read from DNS server failed, %s", err)
292+
continue
293+
}
294+
295+
resp.Compress = true
296+
break
245297
}
298+
246299
if resp == nil {
247300
return
248301
}

sandbox.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,10 @@ func (sb *sandbox) ResolveIP(ip string) string {
391391
return svc
392392
}
393393

394+
func (sb *sandbox) execFunc(f func()) {
395+
sb.osSbox.InvokeFunc(f)
396+
}
397+
394398
func (sb *sandbox) ResolveName(name string) net.IP {
395399
var ip net.IP
396400

0 commit comments

Comments
 (0)