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
4352type 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
138147func (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
142157func (r * resolver ) NameServer () string {
@@ -202,8 +217,9 @@ func (r *resolver) handlePTRQuery(ptr string, query *dns.Msg) (*dns.Msg, error)
202217
203218func (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 }
0 commit comments