github.com/pwn-term/docker@v0.0.0-20210616085119-6e977cce2565/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/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 s.ActivateAndServe() 161 }() 162 163 tcpServer := &dns.Server{Handler: r, Listener: r.tcpListen} 164 r.tcpServer = tcpServer 165 go func() { 166 tcpServer.ActivateAndServe() 167 }() 168 return nil 169 } 170 171 func (r *resolver) Stop() { 172 r.startCh <- struct{}{} 173 defer func() { <-r.startCh }() 174 175 if r.server != nil { 176 r.server.Shutdown() 177 } 178 if r.tcpServer != nil { 179 r.tcpServer.Shutdown() 180 } 181 r.conn = nil 182 r.tcpServer = nil 183 r.err = fmt.Errorf("setup not done yet") 184 r.tStamp = time.Time{} 185 r.count = 0 186 r.queryLock = sync.Mutex{} 187 } 188 189 func (r *resolver) SetExtServers(extDNS []extDNSEntry) { 190 l := len(extDNS) 191 if l > maxExtDNS { 192 l = maxExtDNS 193 } 194 for i := 0; i < l; i++ { 195 r.extDNSList[i] = extDNS[i] 196 } 197 } 198 199 func (r *resolver) NameServer() string { 200 return r.listenAddress 201 } 202 203 func (r *resolver) ResolverOptions() []string { 204 return []string{"ndots:0"} 205 } 206 207 func setCommonFlags(msg *dns.Msg) { 208 msg.RecursionAvailable = true 209 } 210 211 func shuffleAddr(addr []net.IP) []net.IP { 212 for i := len(addr) - 1; i > 0; i-- { 213 r := rand.Intn(i + 1) 214 addr[i], addr[r] = addr[r], addr[i] 215 } 216 return addr 217 } 218 219 func createRespMsg(query *dns.Msg) *dns.Msg { 220 resp := new(dns.Msg) 221 resp.SetReply(query) 222 setCommonFlags(resp) 223 224 return resp 225 } 226 227 func (r *resolver) handleMXQuery(name string, query *dns.Msg) (*dns.Msg, error) { 228 addrv4, _ := r.backend.ResolveName(name, types.IPv4) 229 addrv6, _ := r.backend.ResolveName(name, types.IPv6) 230 231 if addrv4 == nil && addrv6 == nil { 232 return nil, nil 233 } 234 235 // We were able to resolve the name. Respond with an empty list with 236 // RcodeSuccess/NOERROR so that email clients can treat it as "implicit MX" 237 // [RFC 5321 Section-5.1] and issue a Type A/AAAA query for the name. 238 239 resp := createRespMsg(query) 240 return resp, nil 241 } 242 243 func (r *resolver) handleIPQuery(name string, query *dns.Msg, ipType int) (*dns.Msg, error) { 244 var addr []net.IP 245 var ipv6Miss bool 246 addr, ipv6Miss = r.backend.ResolveName(name, ipType) 247 248 if addr == nil && ipv6Miss { 249 // Send a reply without any Answer sections 250 logrus.Debugf("[resolver] lookup name %s present without IPv6 address", name) 251 resp := createRespMsg(query) 252 return resp, nil 253 } 254 if addr == nil { 255 return nil, nil 256 } 257 258 logrus.Debugf("[resolver] lookup for %s: IP %v", name, addr) 259 260 resp := createRespMsg(query) 261 if len(addr) > 1 { 262 addr = shuffleAddr(addr) 263 } 264 if ipType == types.IPv4 { 265 for _, ip := range addr { 266 rr := new(dns.A) 267 rr.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: respTTL} 268 rr.A = ip 269 resp.Answer = append(resp.Answer, rr) 270 } 271 } else { 272 for _, ip := range addr { 273 rr := new(dns.AAAA) 274 rr.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: respTTL} 275 rr.AAAA = ip 276 resp.Answer = append(resp.Answer, rr) 277 } 278 } 279 return resp, nil 280 } 281 282 func (r *resolver) handlePTRQuery(ptr string, query *dns.Msg) (*dns.Msg, error) { 283 var parts []string 284 285 if strings.HasSuffix(ptr, ptrIPv4domain) { 286 parts = strings.Split(ptr, ptrIPv4domain) 287 } else if strings.HasSuffix(ptr, ptrIPv6domain) { 288 parts = strings.Split(ptr, ptrIPv6domain) 289 } else { 290 return nil, fmt.Errorf("invalid PTR query, %v", ptr) 291 } 292 293 host := r.backend.ResolveIP(parts[0]) 294 295 if len(host) == 0 { 296 return nil, nil 297 } 298 299 logrus.Debugf("[resolver] lookup for IP %s: name %s", parts[0], host) 300 fqdn := dns.Fqdn(host) 301 302 resp := new(dns.Msg) 303 resp.SetReply(query) 304 setCommonFlags(resp) 305 306 rr := new(dns.PTR) 307 rr.Hdr = dns.RR_Header{Name: ptr, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: respTTL} 308 rr.Ptr = fqdn 309 resp.Answer = append(resp.Answer, rr) 310 return resp, nil 311 } 312 313 func (r *resolver) handleSRVQuery(svc string, query *dns.Msg) (*dns.Msg, error) { 314 315 srv, ip := r.backend.ResolveService(svc) 316 317 if len(srv) == 0 { 318 return nil, nil 319 } 320 if len(srv) != len(ip) { 321 return nil, fmt.Errorf("invalid reply for SRV query %s", svc) 322 } 323 324 resp := createRespMsg(query) 325 326 for i, r := range srv { 327 rr := new(dns.SRV) 328 rr.Hdr = dns.RR_Header{Name: svc, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: respTTL} 329 rr.Port = r.Port 330 rr.Target = r.Target 331 resp.Answer = append(resp.Answer, rr) 332 333 rr1 := new(dns.A) 334 rr1.Hdr = dns.RR_Header{Name: r.Target, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: respTTL} 335 rr1.A = ip[i] 336 resp.Extra = append(resp.Extra, rr1) 337 } 338 return resp, nil 339 340 } 341 342 func truncateResp(resp *dns.Msg, maxSize int, isTCP bool) { 343 if !isTCP { 344 resp.Truncated = true 345 } 346 347 srv := resp.Question[0].Qtype == dns.TypeSRV 348 // trim the Answer RRs one by one till the whole message fits 349 // within the reply size 350 for resp.Len() > maxSize { 351 resp.Answer = resp.Answer[:len(resp.Answer)-1] 352 353 if srv && len(resp.Extra) > 0 { 354 resp.Extra = resp.Extra[:len(resp.Extra)-1] 355 } 356 } 357 } 358 359 func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) { 360 var ( 361 extConn net.Conn 362 resp *dns.Msg 363 err error 364 ) 365 366 if query == nil || len(query.Question) == 0 { 367 return 368 } 369 370 name := query.Question[0].Name 371 switch query.Question[0].Qtype { 372 case dns.TypeA: 373 resp, err = r.handleIPQuery(name, query, types.IPv4) 374 case dns.TypeAAAA: 375 resp, err = r.handleIPQuery(name, query, types.IPv6) 376 case dns.TypeMX: 377 resp, err = r.handleMXQuery(name, query) 378 case dns.TypePTR: 379 resp, err = r.handlePTRQuery(name, query) 380 case dns.TypeSRV: 381 resp, err = r.handleSRVQuery(name, query) 382 } 383 384 if err != nil { 385 logrus.Error(err) 386 return 387 } 388 389 if resp == nil { 390 // If the backend doesn't support proxying dns request 391 // fail the response 392 if !r.proxyDNS { 393 resp = new(dns.Msg) 394 resp.SetRcode(query, dns.RcodeServerFailure) 395 w.WriteMsg(resp) 396 return 397 } 398 399 // If the user sets ndots > 0 explicitly and the query is 400 // in the root domain don't forward it out. We will return 401 // failure and let the client retry with the search domain 402 // attached 403 switch query.Question[0].Qtype { 404 case dns.TypeA: 405 fallthrough 406 case dns.TypeAAAA: 407 if r.backend.NdotsSet() && !strings.Contains(strings.TrimSuffix(name, "."), ".") { 408 resp = createRespMsg(query) 409 } 410 } 411 } 412 413 proto := w.LocalAddr().Network() 414 maxSize := 0 415 if proto == "tcp" { 416 maxSize = dns.MaxMsgSize - 1 417 } else if proto == "udp" { 418 optRR := query.IsEdns0() 419 if optRR != nil { 420 maxSize = int(optRR.UDPSize()) 421 } 422 if maxSize < defaultRespSize { 423 maxSize = defaultRespSize 424 } 425 } 426 427 if resp != nil { 428 if resp.Len() > maxSize { 429 truncateResp(resp, maxSize, proto == "tcp") 430 } 431 } else { 432 for i := 0; i < maxExtDNS; i++ { 433 extDNS := &r.extDNSList[i] 434 if extDNS.IPStr == "" { 435 break 436 } 437 extConnect := func() { 438 addr := fmt.Sprintf("%s:%d", extDNS.IPStr, 53) 439 extConn, err = net.DialTimeout(proto, addr, extIOTimeout) 440 } 441 442 if extDNS.HostLoopback { 443 extConnect() 444 } else { 445 execErr := r.backend.ExecFunc(extConnect) 446 if execErr != nil { 447 logrus.Warn(execErr) 448 continue 449 } 450 } 451 if err != nil { 452 logrus.Warnf("[resolver] connect failed: %s", err) 453 continue 454 } 455 queryType := dns.TypeToString[query.Question[0].Qtype] 456 logrus.Debugf("[resolver] query %s (%s) from %s, forwarding to %s:%s", name, queryType, 457 extConn.LocalAddr().String(), proto, extDNS.IPStr) 458 459 // Timeout has to be set for every IO operation. 460 extConn.SetDeadline(time.Now().Add(extIOTimeout)) 461 co := &dns.Conn{ 462 Conn: extConn, 463 UDPSize: uint16(maxSize), 464 } 465 defer co.Close() 466 467 // limits the number of outstanding concurrent queries. 468 if !r.forwardQueryStart() { 469 old := r.tStamp 470 r.tStamp = time.Now() 471 if r.tStamp.Sub(old) > logInterval { 472 logrus.Errorf("[resolver] more than %v concurrent queries from %s", maxConcurrent, extConn.LocalAddr().String()) 473 } 474 continue 475 } 476 477 err = co.WriteMsg(query) 478 if err != nil { 479 r.forwardQueryEnd() 480 logrus.Debugf("[resolver] send to DNS server failed, %s", err) 481 continue 482 } 483 484 resp, err = co.ReadMsg() 485 // Truncated DNS replies should be sent to the client so that the 486 // client can retry over TCP 487 if err != nil && (resp == nil || !resp.Truncated) { 488 r.forwardQueryEnd() 489 logrus.Debugf("[resolver] read from DNS server failed, %s", err) 490 continue 491 } 492 r.forwardQueryEnd() 493 494 if resp == nil { 495 logrus.Debugf("[resolver] external DNS %s:%s returned empty response for %q", proto, extDNS.IPStr, name) 496 break 497 } 498 switch resp.Rcode { 499 case dns.RcodeServerFailure, dns.RcodeRefused: 500 // Server returned FAILURE: continue with the next external DNS server 501 // Server returned REFUSED: this can be a transitional status, so continue with the next external DNS server 502 logrus.Debugf("[resolver] external DNS %s:%s responded with %s for %q", proto, extDNS.IPStr, statusString(resp.Rcode), name) 503 continue 504 case dns.RcodeNameError: 505 // Server returned NXDOMAIN. Stop resolution if it's an authoritative answer (see RFC 8020: https://tools.ietf.org/html/rfc8020#section-2) 506 logrus.Debugf("[resolver] external DNS %s:%s responded with %s for %q", proto, extDNS.IPStr, statusString(resp.Rcode), name) 507 if resp.Authoritative { 508 break 509 } 510 continue 511 case dns.RcodeSuccess: 512 // All is well 513 default: 514 // Server gave some error. Log the error, and continue with the next external DNS server 515 logrus.Debugf("[resolver] external DNS %s:%s responded with %s (code %d) for %q", proto, extDNS.IPStr, statusString(resp.Rcode), resp.Rcode, name) 516 continue 517 } 518 answers := 0 519 for _, rr := range resp.Answer { 520 h := rr.Header() 521 switch h.Rrtype { 522 case dns.TypeA: 523 answers++ 524 ip := rr.(*dns.A).A 525 logrus.Debugf("[resolver] received A record %q for %q from %s:%s", ip, h.Name, proto, extDNS.IPStr) 526 r.backend.HandleQueryResp(h.Name, ip) 527 case dns.TypeAAAA: 528 answers++ 529 ip := rr.(*dns.AAAA).AAAA 530 logrus.Debugf("[resolver] received AAAA record %q for %q from %s:%s", ip, h.Name, proto, extDNS.IPStr) 531 r.backend.HandleQueryResp(h.Name, ip) 532 } 533 } 534 if resp.Answer == nil || answers == 0 { 535 logrus.Debugf("[resolver] external DNS %s:%s did not return any %s records for %q", proto, extDNS.IPStr, queryType, name) 536 } 537 resp.Compress = true 538 break 539 } 540 if resp == nil { 541 return 542 } 543 } 544 545 if err = w.WriteMsg(resp); err != nil { 546 logrus.Errorf("[resolver] error writing resolver resp, %s", err) 547 } 548 } 549 550 func statusString(responseCode int) string { 551 if s, ok := dns.RcodeToString[responseCode]; ok { 552 return s 553 } 554 return "UNKNOWN" 555 } 556 557 func (r *resolver) forwardQueryStart() bool { 558 r.queryLock.Lock() 559 defer r.queryLock.Unlock() 560 561 if r.count == maxConcurrent { 562 return false 563 } 564 r.count++ 565 566 return true 567 } 568 569 func (r *resolver) forwardQueryEnd() { 570 r.queryLock.Lock() 571 defer r.queryLock.Unlock() 572 573 if r.count == 0 { 574 logrus.Error("[resolver] invalid concurrent query count") 575 } else { 576 r.count-- 577 } 578 }