Skip to content

Commit 6d1a65c

Browse files
committed
Merge pull request #974 from sanimej/RR
Embedded DNS server to return multiple A records for enabling DNS Round Robin
2 parents 3702cb6 + fdd6e8a commit 6d1a65c

3 files changed

Lines changed: 68 additions & 19 deletions

File tree

libnetwork_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1213,7 +1213,7 @@ func (f *fakeSandbox) SetKey(key string) error {
12131213
return nil
12141214
}
12151215

1216-
func (f *fakeSandbox) ResolveName(name string) net.IP {
1216+
func (f *fakeSandbox) ResolveName(name string) []net.IP {
12171217
return nil
12181218
}
12191219

resolver.go

Lines changed: 62 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package libnetwork
22

33
import (
44
"fmt"
5+
"math/rand"
56
"net"
67
"strings"
78
"sync"
@@ -33,13 +34,14 @@ type Resolver interface {
3334
}
3435

3536
const (
36-
resolverIP = "127.0.0.11"
37-
dnsPort = "53"
38-
ptrIPv4domain = ".in-addr.arpa."
39-
ptrIPv6domain = ".ip6.arpa."
40-
respTTL = 600
41-
maxExtDNS = 3 //max number of external servers to try
42-
extIOTimeout = 3 * time.Second
37+
resolverIP = "127.0.0.11"
38+
dnsPort = "53"
39+
ptrIPv4domain = ".in-addr.arpa."
40+
ptrIPv6domain = ".ip6.arpa."
41+
respTTL = 600
42+
maxExtDNS = 3 //max number of external servers to try
43+
extIOTimeout = 3 * time.Second
44+
defaultRespSize = 512
4345
)
4446

4547
type extDNSEntry struct {
@@ -59,6 +61,10 @@ type resolver struct {
5961
err error
6062
}
6163

64+
func init() {
65+
rand.Seed(time.Now().Unix())
66+
}
67+
6268
// NewResolver creates a new instance of the Resolver
6369
func NewResolver(sb *sandbox) Resolver {
6470
return &resolver{
@@ -166,22 +172,36 @@ func setCommonFlags(msg *dns.Msg) {
166172
msg.RecursionAvailable = true
167173
}
168174

175+
func shuffleAddr(addr []net.IP) []net.IP {
176+
for i := len(addr) - 1; i > 0; i-- {
177+
r := rand.Intn(i + 1)
178+
addr[i], addr[r] = addr[r], addr[i]
179+
}
180+
return addr
181+
}
182+
169183
func (r *resolver) handleIPv4Query(name string, query *dns.Msg) (*dns.Msg, error) {
170184
addr := r.sb.ResolveName(name)
171185
if addr == nil {
172186
return nil, nil
173187
}
174188

175-
log.Debugf("Lookup for %s: IP %s", name, addr.String())
189+
log.Debugf("Lookup for %s: IP %v", name, addr)
176190

177191
resp := new(dns.Msg)
178192
resp.SetReply(query)
179193
setCommonFlags(resp)
180194

181-
rr := new(dns.A)
182-
rr.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: respTTL}
183-
rr.A = addr
184-
resp.Answer = append(resp.Answer, rr)
195+
if len(addr) > 1 {
196+
addr = shuffleAddr(addr)
197+
}
198+
199+
for _, ip := range addr {
200+
rr := new(dns.A)
201+
rr.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: respTTL}
202+
rr.A = ip
203+
resp.Answer = append(resp.Answer, rr)
204+
}
185205
return resp, nil
186206
}
187207

@@ -215,6 +235,18 @@ func (r *resolver) handlePTRQuery(ptr string, query *dns.Msg) (*dns.Msg, error)
215235
return resp, nil
216236
}
217237

238+
func truncateResp(resp *dns.Msg, maxSize int, isTCP bool) {
239+
if !isTCP {
240+
resp.Truncated = true
241+
}
242+
243+
// trim the Answer RRs one by one till the whole message fits
244+
// within the reply size
245+
for resp.Len() > maxSize {
246+
resp.Answer = resp.Answer[:len(resp.Answer)-1]
247+
}
248+
}
249+
218250
func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
219251
var (
220252
extConn net.Conn
@@ -238,7 +270,24 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
238270
}
239271

240272
proto := w.LocalAddr().Network()
241-
if resp == nil {
273+
maxSize := 0
274+
if proto == "tcp" {
275+
maxSize = dns.MaxMsgSize - 1
276+
} else if proto == "udp" {
277+
optRR := query.IsEdns0()
278+
if optRR != nil {
279+
maxSize = int(optRR.UDPSize())
280+
}
281+
if maxSize < defaultRespSize {
282+
maxSize = defaultRespSize
283+
}
284+
}
285+
286+
if resp != nil {
287+
if resp.Len() > maxSize {
288+
truncateResp(resp, maxSize, proto == "tcp")
289+
}
290+
} else {
242291
for i := 0; i < maxExtDNS; i++ {
243292
extDNS := &r.extDNSList[i]
244293
if extDNS.ipStr == "" {

sandbox.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ type Sandbox interface {
3737
Delete() error
3838
// ResolveName searches for the service name in the networks to which the sandbox
3939
// is connected to.
40-
ResolveName(name string) net.IP
40+
ResolveName(name string) []net.IP
4141
// ResolveIP returns the service name for the passed in IP. IP is in reverse dotted
4242
// notation; the format used for DNS PTR records
4343
ResolveIP(name string) string
@@ -398,8 +398,8 @@ func (sb *sandbox) execFunc(f func()) {
398398
sb.osSbox.InvokeFunc(f)
399399
}
400400

401-
func (sb *sandbox) ResolveName(name string) net.IP {
402-
var ip net.IP
401+
func (sb *sandbox) ResolveName(name string) []net.IP {
402+
var ip []net.IP
403403

404404
// Embedded server owns the docker network domain. Resolution should work
405405
// for both container_name and container_name.network_name
@@ -447,7 +447,7 @@ func (sb *sandbox) ResolveName(name string) net.IP {
447447
return nil
448448
}
449449

450-
func (sb *sandbox) resolveName(req string, networkName string, epList []*endpoint, alias bool) net.IP {
450+
func (sb *sandbox) resolveName(req string, networkName string, epList []*endpoint, alias bool) []net.IP {
451451
for _, ep := range epList {
452452
name := req
453453
n := ep.getNetwork()
@@ -488,7 +488,7 @@ func (sb *sandbox) resolveName(req string, networkName string, epList []*endpoin
488488
ip, ok := sr.svcMap[name]
489489
n.Unlock()
490490
if ok {
491-
return ip[0]
491+
return ip
492492
}
493493
}
494494
return nil

0 commit comments

Comments
 (0)