github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/libnetwork/resolver.go (about) 1 package libnetwork 2 3 import ( 4 "fmt" 5 "math/rand" 6 "net" 7 "strings" 8 "sync" 9 "time" 10 11 "github.com/docker/docker/libnetwork/types" 12 "github.com/miekg/dns" 13 "github.com/sirupsen/logrus" 14 ) 15 16 // Resolver represents the embedded DNS server in Docker. It operates 17 // by listening on container's loopback interface for DNS queries. 18 type Resolver interface { 19 // Start starts the name server for the container 20 Start() error 21 // Stop stops the name server for the container. Stopped resolver 22 // can be reused after running the SetupFunc again. 23 Stop() 24 // SetupFunc provides the setup function that should be run 25 // in the container's network namespace. 26 SetupFunc(int) func() 27 // NameServer returns the IP of the DNS resolver for the 28 // containers. 29 NameServer() string 30 // SetExtServers configures the external nameservers the resolver 31 // should use to forward queries 32 SetExtServers([]extDNSEntry) 33 // ResolverOptions returns resolv.conf options that should be set 34 ResolverOptions() []string 35 } 36 37 // DNSBackend represents a backend DNS resolver used for DNS name 38 // resolution. All the queries to the resolver are forwarded to the 39 // backend resolver. 40 type DNSBackend interface { 41 // ResolveName resolves a service name to an IPv4 or IPv6 address by searching 42 // the networks the sandbox is connected to. For IPv6 queries, second return 43 // value will be true if the name exists in docker domain but doesn't have an 44 // IPv6 address. Such queries shouldn't be forwarded to external nameservers. 45 ResolveName(name string, iplen int) ([]net.IP, bool) 46 // ResolveIP returns the service name for the passed in IP. IP is in reverse dotted 47 // notation; the format used for DNS PTR records 48 ResolveIP(name string) string 49 // ResolveService returns all the backend details about the containers or hosts 50 // backing a service. Its purpose is to satisfy an SRV query 51 ResolveService(name string) ([]*net.SRV, []net.IP) 52 // ExecFunc allows a function to be executed in the context of the backend 53 // on behalf of the resolver. 54 ExecFunc(f func()) error 55 // NdotsSet queries the backends ndots dns option settings 56 NdotsSet() bool 57 // HandleQueryResp passes the name & IP from a response to the backend. backend 58 // can use it to maintain any required state about the resolution 59 HandleQueryResp(name string, ip net.IP) 60 } 61 62 const ( 63 dnsPort = "53" 64 ptrIPv4domain = ".in-addr.arpa." 65 ptrIPv6domain = ".ip6.arpa." 66 respTTL = 600 67 maxExtDNS = 3 // max number of external servers to try 68 extIOTimeout = 4 * time.Second 69 defaultRespSize = 512 70 maxConcurrent = 1024 71 logInterval = 2 * time.Second 72 ) 73 74 type extDNSEntry struct { 75 IPStr string 76 HostLoopback bool 77 } 78 79 // resolver implements the Resolver interface 80 type resolver struct { 81 backend DNSBackend 82 extDNSList [maxExtDNS]extDNSEntry 83 server *dns.Server 84 conn *net.UDPConn 85 tcpServer *dns.Server 86 tcpListen *net.TCPListener 87 err error 88 count int32 89 tStamp time.Time 90 queryLock sync.Mutex 91 listenAddress string 92 proxyDNS bool 93 resolverKey string 94 startCh chan struct{} 95 } 96 97 func init() { 98 rand.Seed(time.Now().Unix()) 99 } 100 101 // NewResolver creates a new instance of the Resolver 102 func NewResolver(address string, proxyDNS bool, resolverKey string, backend DNSBackend) Resolver { 103 return &resolver{ 104 backend: backend, 105 proxyDNS: proxyDNS, 106 listenAddress: address, 107 resolverKey: resolverKey, 108 err: fmt.Errorf("setup not done yet"), 109 startCh: make(chan struct{}, 1), 110 } 111 } 112 113 func (r *resolver) SetupFunc(port int) func() { 114 return func() { 115 var err error 116 117 // DNS operates primarily on UDP 118 addr := &net.UDPAddr{ 119 IP: net.ParseIP(r.listenAddress), 120 Port: port, 121 } 122 123 r.conn, err = net.ListenUDP("udp", addr) 124 if err != nil { 125 r.err = fmt.Errorf("error in opening name server socket %v", err) 126 return 127 } 128 129 // Listen on a TCP as well 130 tcpaddr := &net.TCPAddr{ 131 IP: net.ParseIP(r.listenAddress), 132 Port: port, 133 } 134 135 r.tcpListen, err = net.ListenTCP("tcp", tcpaddr) 136 if err != nil { 137 r.err = fmt.Errorf("error in opening name TCP server socket %v", err) 138 return 139 } 140 r.err = nil 141 } 142 } 143 144 func (r *resolver) Start() error { 145 r.startCh <- struct{}{} 146 defer func() { <-r.startCh }() 147 148 // make sure the resolver has been setup before starting 149 if r.err != nil { 150 return r.err 151 } 152 153 if err := r.setupIPTable(); err != nil { 154 return fmt.Errorf("setting up IP table rules failed: %v", err) 155 } 156 157 s := &dns.Server{Handler: r, PacketConn: r.conn} 158 r.server = s 159 go func() { 160 if err := s.ActivateAndServe(); err != nil { 161 logrus.WithError(err).Error("[resolver] failed to start PacketConn DNS server") 162 } 163 }() 164 165 tcpServer := &dns.Server{Handler: r, Listener: r.tcpListen} 166 r.tcpServer = tcpServer 167 go func() { 168 if err := tcpServer.ActivateAndServe(); err != nil { 169 logrus.WithError(err).Error("[resolver] failed to start TCP DNS server") 170 } 171 }() 172 return nil 173 } 174 175 func (r *resolver) Stop() { 176 r.startCh <- struct{}{} 177 defer func() { <-r.startCh }() 178 179 if r.server != nil { 180 r.server.Shutdown() //nolint:errcheck 181 } 182 if r.tcpServer != nil { 183 r.tcpServer.Shutdown() //nolint:errcheck 184 } 185 r.conn = nil 186 r.tcpServer = nil 187 r.err = fmt.Errorf("setup not done yet") 188 r.tStamp = time.Time{} 189 r.count = 0 190 r.queryLock = sync.Mutex{} 191 } 192 193 func (r *resolver) SetExtServers(extDNS []extDNSEntry) { 194 l := len(extDNS) 195 if l > maxExtDNS { 196 l = maxExtDNS 197 } 198 for i := 0; i < l; i++ { 199 r.extDNSList[i] = extDNS[i] 200 } 201 } 202 203 func (r *resolver) NameServer() string { 204 return r.listenAddress 205 } 206 207 func (r *resolver) ResolverOptions() []string { 208 return []string{"ndots:0"} 209 } 210 211 func setCommonFlags(msg *dns.Msg) { 212 msg.RecursionAvailable = true 213 } 214 215 func shuffleAddr(addr []net.IP) []net.IP { 216 for i := len(addr) - 1; i > 0; i-- { 217 r := rand.Intn(i + 1) //nolint:gosec // gosec complains about the use of rand here. It should be fine. 218 addr[i], addr[r] = addr[r], addr[i] 219 } 220 return addr 221 } 222 223 func createRespMsg(query *dns.Msg) *dns.Msg { 224 resp := new(dns.Msg) 225 resp.SetReply(query) 226 setCommonFlags(resp) 227 228 return resp 229 } 230 231 func (r *resolver) handleMXQuery(query *dns.Msg) (*dns.Msg, error) { 232 name := query.Question[0].Name 233 addrv4, _ := r.backend.ResolveName(name, types.IPv4) 234 addrv6, _ := r.backend.ResolveName(name, types.IPv6) 235 236 if addrv4 == nil && addrv6 == nil { 237 return nil, nil 238 } 239 240 // We were able to resolve the name. Respond with an empty list with 241 // RcodeSuccess/NOERROR so that email clients can treat it as "implicit MX" 242 // [RFC 5321 Section-5.1] and issue a Type A/AAAA query for the name. 243 244 resp := createRespMsg(query) 245 return resp, nil 246 } 247 248 func (r *resolver) handleIPQuery(query *dns.Msg, ipType int) (*dns.Msg, error) { 249 var ( 250 addr []net.IP 251 ipv6Miss bool 252 name = query.Question[0].Name 253 ) 254 addr, ipv6Miss = r.backend.ResolveName(name, ipType) 255 256 if addr == nil && ipv6Miss { 257 // Send a reply without any Answer sections 258 logrus.Debugf("[resolver] lookup name %s present without IPv6 address", name) 259 resp := createRespMsg(query) 260 return resp, nil 261 } 262 if addr == nil { 263 return nil, nil 264 } 265 266 logrus.Debugf("[resolver] lookup for %s: IP %v", name, addr) 267 268 resp := createRespMsg(query) 269 if len(addr) > 1 { 270 addr = shuffleAddr(addr) 271 } 272 if ipType == types.IPv4 { 273 for _, ip := range addr { 274 rr := new(dns.A) 275 rr.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: respTTL} 276 rr.A = ip 277 resp.Answer = append(resp.Answer, rr) 278 } 279 } else { 280 for _, ip := range addr { 281 rr := new(dns.AAAA) 282 rr.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: respTTL} 283 rr.AAAA = ip 284 resp.Answer = append(resp.Answer, rr) 285 } 286 } 287 return resp, nil 288 } 289 290 func (r *resolver) handlePTRQuery(query *dns.Msg) (*dns.Msg, error) { 291 var ( 292 parts []string 293 ptr = query.Question[0].Name 294 ) 295 296 if strings.HasSuffix(ptr, ptrIPv4domain) { 297 parts = strings.Split(ptr, ptrIPv4domain) 298 } else if strings.HasSuffix(ptr, ptrIPv6domain) { 299 parts = strings.Split(ptr, ptrIPv6domain) 300 } else { 301 return nil, fmt.Errorf("invalid PTR query, %v", ptr) 302 } 303 304 host := r.backend.ResolveIP(parts[0]) 305 306 if len(host) == 0 { 307 return nil, nil 308 } 309 310 logrus.Debugf("[resolver] lookup for IP %s: name %s", parts[0], host) 311 fqdn := dns.Fqdn(host) 312 313 resp := new(dns.Msg) 314 resp.SetReply(query) 315 setCommonFlags(resp) 316 317 rr := new(dns.PTR) 318 rr.Hdr = dns.RR_Header{Name: ptr, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: respTTL} 319 rr.Ptr = fqdn 320 resp.Answer = append(resp.Answer, rr) 321 return resp, nil 322 } 323 324 func (r *resolver) handleSRVQuery(query *dns.Msg) (*dns.Msg, error) { 325 svc := query.Question[0].Name 326 srv, ip := r.backend.ResolveService(svc) 327 328 if len(srv) == 0 { 329 return nil, nil 330 } 331 if len(srv) != len(ip) { 332 return nil, fmt.Errorf("invalid reply for SRV query %s", svc) 333 } 334 335 resp := createRespMsg(query) 336 337 for i, r := range srv { 338 rr := new(dns.SRV) 339 rr.Hdr = dns.RR_Header{Name: svc, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: respTTL} 340 rr.Port = r.Port 341 rr.Target = r.Target 342 resp.Answer = append(resp.Answer, rr) 343 344 rr1 := new(dns.A) 345 rr1.Hdr = dns.RR_Header{Name: r.Target, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: respTTL} 346 rr1.A = ip[i] 347 resp.Extra = append(resp.Extra, rr1) 348 } 349 return resp, nil 350 } 351 352 func truncateResp(resp *dns.Msg, maxSize int, isTCP bool) { 353 if !isTCP { 354 resp.Truncated = true 355 } 356 357 srv := resp.Question[0].Qtype == dns.TypeSRV 358 // trim the Answer RRs one by one till the whole message fits 359 // within the reply size 360 for resp.Len() > maxSize { 361 resp.Answer = resp.Answer[:len(resp.Answer)-1] 362 363 if srv && len(resp.Extra) > 0 { 364 resp.Extra = resp.Extra[:len(resp.Extra)-1] 365 } 366 } 367 } 368 369 func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) { 370 var ( 371 extConn net.Conn 372 resp *dns.Msg 373 err error 374 ) 375 376 if query == nil || len(query.Question) == 0 { 377 return 378 } 379 380 queryName := query.Question[0].Name 381 queryType := query.Question[0].Qtype 382 383 switch queryType { 384 case dns.TypeA: 385 resp, err = r.handleIPQuery(query, types.IPv4) 386 case dns.TypeAAAA: 387 resp, err = r.handleIPQuery(query, types.IPv6) 388 case dns.TypeMX: 389 resp, err = r.handleMXQuery(query) 390 case dns.TypePTR: 391 resp, err = r.handlePTRQuery(query) 392 case dns.TypeSRV: 393 resp, err = r.handleSRVQuery(query) 394 default: 395 logrus.Debugf("[resolver] query type %s is not supported by the embedded DNS and will be forwarded to external DNS", dns.TypeToString[queryType]) 396 } 397 398 if err != nil { 399 logrus.WithError(err).Errorf("[resolver] failed to handle query: %s (%s) from %s", queryName, dns.TypeToString[queryType], extConn.LocalAddr().String()) 400 return 401 } 402 403 if resp == nil { 404 // If the backend doesn't support proxying dns request 405 // fail the response 406 if !r.proxyDNS { 407 resp = new(dns.Msg) 408 resp.SetRcode(query, dns.RcodeServerFailure) 409 if err := w.WriteMsg(resp); err != nil { 410 logrus.WithError(err).Error("[resolver] error writing dns response") 411 } 412 return 413 } 414 415 // If the user sets ndots > 0 explicitly and the query is 416 // in the root domain don't forward it out. We will return 417 // failure and let the client retry with the search domain 418 // attached 419 switch queryType { 420 case dns.TypeA, dns.TypeAAAA: 421 if r.backend.NdotsSet() && !strings.Contains(strings.TrimSuffix(queryName, "."), ".") { 422 resp = createRespMsg(query) 423 } 424 } 425 } 426 427 proto := w.LocalAddr().Network() 428 maxSize := 0 429 if proto == "tcp" { 430 maxSize = dns.MaxMsgSize - 1 431 } else if proto == "udp" { 432 optRR := query.IsEdns0() 433 if optRR != nil { 434 maxSize = int(optRR.UDPSize()) 435 } 436 if maxSize < defaultRespSize { 437 maxSize = defaultRespSize 438 } 439 } 440 441 if resp != nil { 442 if resp.Len() > maxSize { 443 truncateResp(resp, maxSize, proto == "tcp") 444 } 445 } else { 446 for i := 0; i < maxExtDNS; i++ { 447 extDNS := &r.extDNSList[i] 448 if extDNS.IPStr == "" { 449 break 450 } 451 extConnect := func() { 452 addr := fmt.Sprintf("%s:%d", extDNS.IPStr, 53) 453 extConn, err = net.DialTimeout(proto, addr, extIOTimeout) 454 } 455 456 if extDNS.HostLoopback { 457 extConnect() 458 } else { 459 execErr := r.backend.ExecFunc(extConnect) 460 if execErr != nil { 461 logrus.Warn(execErr) 462 continue 463 } 464 } 465 if err != nil { 466 logrus.WithField("retries", i).Warnf("[resolver] connect failed: %s", err) 467 continue 468 } 469 logrus.Debugf("[resolver] query %s (%s) from %s, forwarding to %s:%s", queryName, dns.TypeToString[queryType], 470 extConn.LocalAddr().String(), proto, extDNS.IPStr) 471 472 // Timeout has to be set for every IO operation. 473 if err := extConn.SetDeadline(time.Now().Add(extIOTimeout)); err != nil { 474 logrus.WithError(err).Error("[resolver] error setting conn deadline") 475 } 476 co := &dns.Conn{ 477 Conn: extConn, 478 UDPSize: uint16(maxSize), 479 } 480 defer co.Close() 481 482 // limits the number of outstanding concurrent queries. 483 if !r.forwardQueryStart() { 484 old := r.tStamp 485 r.tStamp = time.Now() 486 if r.tStamp.Sub(old) > logInterval { 487 logrus.Errorf("[resolver] more than %v concurrent queries from %s", maxConcurrent, extConn.LocalAddr().String()) 488 } 489 continue 490 } 491 492 err = co.WriteMsg(query) 493 if err != nil { 494 r.forwardQueryEnd() 495 logrus.Debugf("[resolver] send to DNS server failed, %s", err) 496 continue 497 } 498 499 resp, err = co.ReadMsg() 500 // Truncated DNS replies should be sent to the client so that the 501 // client can retry over TCP 502 if err != nil && (resp == nil || !resp.Truncated) { 503 r.forwardQueryEnd() 504 logrus.WithError(err).Debugf("[resolver] failed to read from DNS server") 505 continue 506 } 507 r.forwardQueryEnd() 508 509 if resp == nil { 510 logrus.Debugf("[resolver] external DNS %s:%s returned empty response for %q", proto, extDNS.IPStr, queryName) 511 break 512 } 513 switch resp.Rcode { 514 case dns.RcodeServerFailure, dns.RcodeRefused: 515 // Server returned FAILURE: continue with the next external DNS server 516 // Server returned REFUSED: this can be a transitional status, so continue with the next external DNS server 517 logrus.Debugf("[resolver] external DNS %s:%s responded with %s for %q", proto, extDNS.IPStr, statusString(resp.Rcode), queryName) 518 continue 519 case dns.RcodeNameError: 520 // Server returned NXDOMAIN. Stop resolution if it's an authoritative answer (see RFC 8020: https://tools.ietf.org/html/rfc8020#section-2) 521 logrus.Debugf("[resolver] external DNS %s:%s responded with %s for %q", proto, extDNS.IPStr, statusString(resp.Rcode), queryName) 522 if resp.Authoritative { 523 break 524 } 525 continue 526 case dns.RcodeSuccess: 527 // All is well 528 default: 529 // Server gave some error. Log the error, and continue with the next external DNS server 530 logrus.Debugf("[resolver] external DNS %s:%s responded with %s (code %d) for %q", proto, extDNS.IPStr, statusString(resp.Rcode), resp.Rcode, queryName) 531 continue 532 } 533 answers := 0 534 for _, rr := range resp.Answer { 535 h := rr.Header() 536 switch h.Rrtype { 537 case dns.TypeA: 538 answers++ 539 ip := rr.(*dns.A).A 540 logrus.Debugf("[resolver] received A record %q for %q from %s:%s", ip, h.Name, proto, extDNS.IPStr) 541 r.backend.HandleQueryResp(h.Name, ip) 542 case dns.TypeAAAA: 543 answers++ 544 ip := rr.(*dns.AAAA).AAAA 545 logrus.Debugf("[resolver] received AAAA record %q for %q from %s:%s", ip, h.Name, proto, extDNS.IPStr) 546 r.backend.HandleQueryResp(h.Name, ip) 547 } 548 } 549 if resp.Answer == nil || answers == 0 { 550 logrus.Debugf("[resolver] external DNS %s:%s did not return any %s records for %q", proto, extDNS.IPStr, dns.TypeToString[queryType], queryName) 551 } 552 resp.Compress = true 553 break 554 } 555 if resp == nil { 556 return 557 } 558 } 559 560 if err = w.WriteMsg(resp); err != nil { 561 logrus.WithError(err).Errorf("[resolver] failed to write response") 562 } 563 } 564 565 func statusString(responseCode int) string { 566 if s, ok := dns.RcodeToString[responseCode]; ok { 567 return s 568 } 569 return "UNKNOWN" 570 } 571 572 func (r *resolver) forwardQueryStart() bool { 573 r.queryLock.Lock() 574 defer r.queryLock.Unlock() 575 576 if r.count == maxConcurrent { 577 return false 578 } 579 r.count++ 580 581 return true 582 } 583 584 func (r *resolver) forwardQueryEnd() { 585 r.queryLock.Lock() 586 defer r.queryLock.Unlock() 587 588 if r.count == 0 { 589 logrus.Error("[resolver] invalid concurrent query count") 590 } else { 591 r.count-- 592 } 593 }