github.com/tidwall/go@v0.0.0-20170415222209-6694a6888b7d/src/net/dnsclient_unix.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // +build darwin dragonfly freebsd linux netbsd openbsd solaris 6 7 // DNS client: see RFC 1035. 8 // Has to be linked into package net for Dial. 9 10 // TODO(rsc): 11 // Could potentially handle many outstanding lookups faster. 12 // Could have a small cache. 13 // Random UDP source port (net.Dial should do that for us). 14 // Random request IDs. 15 16 package net 17 18 import ( 19 "context" 20 "errors" 21 "io" 22 "math/rand" 23 "os" 24 "sync" 25 "time" 26 ) 27 28 // A dnsDialer provides dialing suitable for DNS queries. 29 type dnsDialer interface { 30 dialDNS(ctx context.Context, network, addr string) (dnsConn, error) 31 } 32 33 var testHookDNSDialer = func() dnsDialer { return &Dialer{} } 34 35 // A dnsConn represents a DNS transport endpoint. 36 type dnsConn interface { 37 io.Closer 38 39 SetDeadline(time.Time) error 40 41 // dnsRoundTrip executes a single DNS transaction, returning a 42 // DNS response message for the provided DNS query message. 43 dnsRoundTrip(query *dnsMsg) (*dnsMsg, error) 44 } 45 46 func (c *UDPConn) dnsRoundTrip(query *dnsMsg) (*dnsMsg, error) { 47 return dnsRoundTripUDP(c, query) 48 } 49 50 // dnsRoundTripUDP implements the dnsRoundTrip interface for RFC 1035's 51 // "UDP usage" transport mechanism. c should be a packet-oriented connection, 52 // such as a *UDPConn. 53 func dnsRoundTripUDP(c io.ReadWriter, query *dnsMsg) (*dnsMsg, error) { 54 b, ok := query.Pack() 55 if !ok { 56 return nil, errors.New("cannot marshal DNS message") 57 } 58 if _, err := c.Write(b); err != nil { 59 return nil, err 60 } 61 62 b = make([]byte, 512) // see RFC 1035 63 for { 64 n, err := c.Read(b) 65 if err != nil { 66 return nil, err 67 } 68 resp := &dnsMsg{} 69 if !resp.Unpack(b[:n]) || !resp.IsResponseTo(query) { 70 // Ignore invalid responses as they may be malicious 71 // forgery attempts. Instead continue waiting until 72 // timeout. See golang.org/issue/13281. 73 continue 74 } 75 return resp, nil 76 } 77 } 78 79 func (c *TCPConn) dnsRoundTrip(out *dnsMsg) (*dnsMsg, error) { 80 return dnsRoundTripTCP(c, out) 81 } 82 83 // dnsRoundTripTCP implements the dnsRoundTrip interface for RFC 1035's 84 // "TCP usage" transport mechanism. c should be a stream-oriented connection, 85 // such as a *TCPConn. 86 func dnsRoundTripTCP(c io.ReadWriter, query *dnsMsg) (*dnsMsg, error) { 87 b, ok := query.Pack() 88 if !ok { 89 return nil, errors.New("cannot marshal DNS message") 90 } 91 l := len(b) 92 b = append([]byte{byte(l >> 8), byte(l)}, b...) 93 if _, err := c.Write(b); err != nil { 94 return nil, err 95 } 96 97 b = make([]byte, 1280) // 1280 is a reasonable initial size for IP over Ethernet, see RFC 4035 98 if _, err := io.ReadFull(c, b[:2]); err != nil { 99 return nil, err 100 } 101 l = int(b[0])<<8 | int(b[1]) 102 if l > len(b) { 103 b = make([]byte, l) 104 } 105 n, err := io.ReadFull(c, b[:l]) 106 if err != nil { 107 return nil, err 108 } 109 resp := &dnsMsg{} 110 if !resp.Unpack(b[:n]) { 111 return nil, errors.New("cannot unmarshal DNS message") 112 } 113 if !resp.IsResponseTo(query) { 114 return nil, errors.New("invalid DNS response") 115 } 116 return resp, nil 117 } 118 119 func (d *Dialer) dialDNS(ctx context.Context, network, server string) (dnsConn, error) { 120 switch network { 121 case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6": 122 default: 123 return nil, UnknownNetworkError(network) 124 } 125 // Calling Dial here is scary -- we have to be sure not to 126 // dial a name that will require a DNS lookup, or Dial will 127 // call back here to translate it. The DNS config parser has 128 // already checked that all the cfg.servers are IP 129 // addresses, which Dial will use without a DNS lookup. 130 c, err := d.DialContext(ctx, network, server) 131 if err != nil { 132 return nil, mapErr(err) 133 } 134 switch network { 135 case "tcp", "tcp4", "tcp6": 136 return c.(*TCPConn), nil 137 case "udp", "udp4", "udp6": 138 return c.(*UDPConn), nil 139 } 140 panic("unreachable") 141 } 142 143 // exchange sends a query on the connection and hopes for a response. 144 func exchange(ctx context.Context, server, name string, qtype uint16, timeout time.Duration) (*dnsMsg, error) { 145 d := testHookDNSDialer() 146 out := dnsMsg{ 147 dnsMsgHdr: dnsMsgHdr{ 148 recursion_desired: true, 149 }, 150 question: []dnsQuestion{ 151 {name, qtype, dnsClassINET}, 152 }, 153 } 154 for _, network := range []string{"udp", "tcp"} { 155 // TODO(mdempsky): Refactor so defers from UDP-based 156 // exchanges happen before TCP-based exchange. 157 158 ctx, cancel := context.WithDeadline(ctx, time.Now().Add(timeout)) 159 defer cancel() 160 161 c, err := d.dialDNS(ctx, network, server) 162 if err != nil { 163 return nil, err 164 } 165 defer c.Close() 166 if d, ok := ctx.Deadline(); ok && !d.IsZero() { 167 c.SetDeadline(d) 168 } 169 out.id = uint16(rand.Int()) ^ uint16(time.Now().UnixNano()) 170 in, err := c.dnsRoundTrip(&out) 171 if err != nil { 172 return nil, mapErr(err) 173 } 174 if in.truncated { // see RFC 5966 175 continue 176 } 177 return in, nil 178 } 179 return nil, errors.New("no answer from DNS server") 180 } 181 182 // Do a lookup for a single name, which must be rooted 183 // (otherwise answer will not find the answers). 184 func tryOneName(ctx context.Context, cfg *dnsConfig, name string, qtype uint16) (string, []dnsRR, error) { 185 var lastErr error 186 serverOffset := cfg.serverOffset() 187 sLen := uint32(len(cfg.servers)) 188 189 for i := 0; i < cfg.attempts; i++ { 190 for j := uint32(0); j < sLen; j++ { 191 server := cfg.servers[(serverOffset+j)%sLen] 192 193 msg, err := exchange(ctx, server, name, qtype, cfg.timeout) 194 if err != nil { 195 lastErr = &DNSError{ 196 Err: err.Error(), 197 Name: name, 198 Server: server, 199 } 200 if nerr, ok := err.(Error); ok && nerr.Timeout() { 201 lastErr.(*DNSError).IsTimeout = true 202 } 203 // Set IsTemporary for socket-level errors. Note that this flag 204 // may also be used to indicate a SERVFAIL response. 205 if _, ok := err.(*OpError); ok { 206 lastErr.(*DNSError).IsTemporary = true 207 } 208 continue 209 } 210 // libresolv continues to the next server when it receives 211 // an invalid referral response. See golang.org/issue/15434. 212 if msg.rcode == dnsRcodeSuccess && !msg.authoritative && !msg.recursion_available && len(msg.answer) == 0 && len(msg.extra) == 0 { 213 lastErr = &DNSError{Err: "lame referral", Name: name, Server: server} 214 continue 215 } 216 cname, rrs, err := answer(name, server, msg, qtype) 217 // If answer errored for rcodes dnsRcodeSuccess or dnsRcodeNameError, 218 // it means the response in msg was not useful and trying another 219 // server probably won't help. Return now in those cases. 220 // TODO: indicate this in a more obvious way, such as a field on DNSError? 221 if err == nil || msg.rcode == dnsRcodeSuccess || msg.rcode == dnsRcodeNameError { 222 return cname, rrs, err 223 } 224 lastErr = err 225 } 226 } 227 return "", nil, lastErr 228 } 229 230 // addrRecordList converts and returns a list of IP addresses from DNS 231 // address records (both A and AAAA). Other record types are ignored. 232 func addrRecordList(rrs []dnsRR) []IPAddr { 233 addrs := make([]IPAddr, 0, 4) 234 for _, rr := range rrs { 235 switch rr := rr.(type) { 236 case *dnsRR_A: 237 addrs = append(addrs, IPAddr{IP: IPv4(byte(rr.A>>24), byte(rr.A>>16), byte(rr.A>>8), byte(rr.A))}) 238 case *dnsRR_AAAA: 239 ip := make(IP, IPv6len) 240 copy(ip, rr.AAAA[:]) 241 addrs = append(addrs, IPAddr{IP: ip}) 242 } 243 } 244 return addrs 245 } 246 247 // A resolverConfig represents a DNS stub resolver configuration. 248 type resolverConfig struct { 249 initOnce sync.Once // guards init of resolverConfig 250 251 // ch is used as a semaphore that only allows one lookup at a 252 // time to recheck resolv.conf. 253 ch chan struct{} // guards lastChecked and modTime 254 lastChecked time.Time // last time resolv.conf was checked 255 256 mu sync.RWMutex // protects dnsConfig 257 dnsConfig *dnsConfig // parsed resolv.conf structure used in lookups 258 } 259 260 var resolvConf resolverConfig 261 262 // init initializes conf and is only called via conf.initOnce. 263 func (conf *resolverConfig) init() { 264 // Set dnsConfig and lastChecked so we don't parse 265 // resolv.conf twice the first time. 266 conf.dnsConfig = systemConf().resolv 267 if conf.dnsConfig == nil { 268 conf.dnsConfig = dnsReadConfig("/etc/resolv.conf") 269 } 270 conf.lastChecked = time.Now() 271 272 // Prepare ch so that only one update of resolverConfig may 273 // run at once. 274 conf.ch = make(chan struct{}, 1) 275 } 276 277 // tryUpdate tries to update conf with the named resolv.conf file. 278 // The name variable only exists for testing. It is otherwise always 279 // "/etc/resolv.conf". 280 func (conf *resolverConfig) tryUpdate(name string) { 281 conf.initOnce.Do(conf.init) 282 283 // Ensure only one update at a time checks resolv.conf. 284 if !conf.tryAcquireSema() { 285 return 286 } 287 defer conf.releaseSema() 288 289 now := time.Now() 290 if conf.lastChecked.After(now.Add(-5 * time.Second)) { 291 return 292 } 293 conf.lastChecked = now 294 295 var mtime time.Time 296 if fi, err := os.Stat(name); err == nil { 297 mtime = fi.ModTime() 298 } 299 if mtime.Equal(conf.dnsConfig.mtime) { 300 return 301 } 302 303 dnsConf := dnsReadConfig(name) 304 conf.mu.Lock() 305 conf.dnsConfig = dnsConf 306 conf.mu.Unlock() 307 } 308 309 func (conf *resolverConfig) tryAcquireSema() bool { 310 select { 311 case conf.ch <- struct{}{}: 312 return true 313 default: 314 return false 315 } 316 } 317 318 func (conf *resolverConfig) releaseSema() { 319 <-conf.ch 320 } 321 322 func (r *Resolver) lookup(ctx context.Context, name string, qtype uint16) (cname string, rrs []dnsRR, err error) { 323 if !isDomainName(name) { 324 // We used to use "invalid domain name" as the error, 325 // but that is a detail of the specific lookup mechanism. 326 // Other lookups might allow broader name syntax 327 // (for example Multicast DNS allows UTF-8; see RFC 6762). 328 // For consistency with libc resolvers, report no such host. 329 return "", nil, &DNSError{Err: errNoSuchHost.Error(), Name: name} 330 } 331 resolvConf.tryUpdate("/etc/resolv.conf") 332 resolvConf.mu.RLock() 333 conf := resolvConf.dnsConfig 334 resolvConf.mu.RUnlock() 335 for _, fqdn := range conf.nameList(name) { 336 cname, rrs, err = tryOneName(ctx, conf, fqdn, qtype) 337 if err == nil { 338 break 339 } 340 if nerr, ok := err.(Error); ok && nerr.Temporary() && r.StrictErrors { 341 // If we hit a temporary error with StrictErrors enabled, 342 // stop immediately instead of trying more names. 343 break 344 } 345 } 346 if err, ok := err.(*DNSError); ok { 347 // Show original name passed to lookup, not suffixed one. 348 // In general we might have tried many suffixes; showing 349 // just one is misleading. See also golang.org/issue/6324. 350 err.Name = name 351 } 352 return 353 } 354 355 // avoidDNS reports whether this is a hostname for which we should not 356 // use DNS. Currently this includes only .onion, per RFC 7686. See 357 // golang.org/issue/13705. Does not cover .local names (RFC 6762), 358 // see golang.org/issue/16739. 359 func avoidDNS(name string) bool { 360 if name == "" { 361 return true 362 } 363 if name[len(name)-1] == '.' { 364 name = name[:len(name)-1] 365 } 366 return stringsHasSuffixFold(name, ".onion") 367 } 368 369 // nameList returns a list of names for sequential DNS queries. 370 func (conf *dnsConfig) nameList(name string) []string { 371 if avoidDNS(name) { 372 return nil 373 } 374 375 // Check name length (see isDomainName). 376 l := len(name) 377 rooted := l > 0 && name[l-1] == '.' 378 if l > 254 || l == 254 && rooted { 379 return nil 380 } 381 382 // If name is rooted (trailing dot), try only that name. 383 if rooted { 384 return []string{name} 385 } 386 387 hasNdots := count(name, '.') >= conf.ndots 388 name += "." 389 l++ 390 391 // Build list of search choices. 392 names := make([]string, 0, 1+len(conf.search)) 393 // If name has enough dots, try unsuffixed first. 394 if hasNdots { 395 names = append(names, name) 396 } 397 // Try suffixes that are not too long (see isDomainName). 398 for _, suffix := range conf.search { 399 if l+len(suffix) <= 254 { 400 names = append(names, name+suffix) 401 } 402 } 403 // Try unsuffixed, if not tried first above. 404 if !hasNdots { 405 names = append(names, name) 406 } 407 return names 408 } 409 410 // hostLookupOrder specifies the order of LookupHost lookup strategies. 411 // It is basically a simplified representation of nsswitch.conf. 412 // "files" means /etc/hosts. 413 type hostLookupOrder int 414 415 const ( 416 // hostLookupCgo means defer to cgo. 417 hostLookupCgo hostLookupOrder = iota 418 hostLookupFilesDNS // files first 419 hostLookupDNSFiles // dns first 420 hostLookupFiles // only files 421 hostLookupDNS // only DNS 422 ) 423 424 var lookupOrderName = map[hostLookupOrder]string{ 425 hostLookupCgo: "cgo", 426 hostLookupFilesDNS: "files,dns", 427 hostLookupDNSFiles: "dns,files", 428 hostLookupFiles: "files", 429 hostLookupDNS: "dns", 430 } 431 432 func (o hostLookupOrder) String() string { 433 if s, ok := lookupOrderName[o]; ok { 434 return s 435 } 436 return "hostLookupOrder=" + itoa(int(o)) + "??" 437 } 438 439 // goLookupHost is the native Go implementation of LookupHost. 440 // Used only if cgoLookupHost refuses to handle the request 441 // (that is, only if cgoLookupHost is the stub in cgo_stub.go). 442 // Normally we let cgo use the C library resolver instead of 443 // depending on our lookup code, so that Go and C get the same 444 // answers. 445 func (r *Resolver) goLookupHost(ctx context.Context, name string) (addrs []string, err error) { 446 return r.goLookupHostOrder(ctx, name, hostLookupFilesDNS) 447 } 448 449 func (r *Resolver) goLookupHostOrder(ctx context.Context, name string, order hostLookupOrder) (addrs []string, err error) { 450 if order == hostLookupFilesDNS || order == hostLookupFiles { 451 // Use entries from /etc/hosts if they match. 452 addrs = lookupStaticHost(name) 453 if len(addrs) > 0 || order == hostLookupFiles { 454 return 455 } 456 } 457 ips, _, err := r.goLookupIPCNAMEOrder(ctx, name, order) 458 if err != nil { 459 return 460 } 461 addrs = make([]string, 0, len(ips)) 462 for _, ip := range ips { 463 addrs = append(addrs, ip.String()) 464 } 465 return 466 } 467 468 // lookup entries from /etc/hosts 469 func goLookupIPFiles(name string) (addrs []IPAddr) { 470 for _, haddr := range lookupStaticHost(name) { 471 haddr, zone := splitHostZone(haddr) 472 if ip := ParseIP(haddr); ip != nil { 473 addr := IPAddr{IP: ip, Zone: zone} 474 addrs = append(addrs, addr) 475 } 476 } 477 sortByRFC6724(addrs) 478 return 479 } 480 481 // goLookupIP is the native Go implementation of LookupIP. 482 // The libc versions are in cgo_*.go. 483 func (r *Resolver) goLookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) { 484 order := systemConf().hostLookupOrder(host) 485 addrs, _, err = r.goLookupIPCNAMEOrder(ctx, host, order) 486 return 487 } 488 489 func (r *Resolver) goLookupIPCNAMEOrder(ctx context.Context, name string, order hostLookupOrder) (addrs []IPAddr, cname string, err error) { 490 if order == hostLookupFilesDNS || order == hostLookupFiles { 491 addrs = goLookupIPFiles(name) 492 if len(addrs) > 0 || order == hostLookupFiles { 493 return addrs, name, nil 494 } 495 } 496 if !isDomainName(name) { 497 // See comment in func lookup above about use of errNoSuchHost. 498 return nil, "", &DNSError{Err: errNoSuchHost.Error(), Name: name} 499 } 500 resolvConf.tryUpdate("/etc/resolv.conf") 501 resolvConf.mu.RLock() 502 conf := resolvConf.dnsConfig 503 resolvConf.mu.RUnlock() 504 type racer struct { 505 cname string 506 rrs []dnsRR 507 error 508 } 509 lane := make(chan racer, 1) 510 qtypes := [...]uint16{dnsTypeA, dnsTypeAAAA} 511 var lastErr error 512 for _, fqdn := range conf.nameList(name) { 513 for _, qtype := range qtypes { 514 go func(qtype uint16) { 515 cname, rrs, err := tryOneName(ctx, conf, fqdn, qtype) 516 lane <- racer{cname, rrs, err} 517 }(qtype) 518 } 519 hitStrictError := false 520 for range qtypes { 521 racer := <-lane 522 if racer.error != nil { 523 if nerr, ok := racer.error.(Error); ok && nerr.Temporary() && r.StrictErrors { 524 // This error will abort the nameList loop. 525 hitStrictError = true 526 lastErr = racer.error 527 } else if lastErr == nil || fqdn == name+"." { 528 // Prefer error for original name. 529 lastErr = racer.error 530 } 531 continue 532 } 533 addrs = append(addrs, addrRecordList(racer.rrs)...) 534 if cname == "" { 535 cname = racer.cname 536 } 537 } 538 if hitStrictError { 539 // If either family hit an error with StrictErrors enabled, 540 // discard all addresses. This ensures that network flakiness 541 // cannot turn a dualstack hostname IPv4/IPv6-only. 542 addrs = nil 543 break 544 } 545 if len(addrs) > 0 { 546 break 547 } 548 } 549 if lastErr, ok := lastErr.(*DNSError); ok { 550 // Show original name passed to lookup, not suffixed one. 551 // In general we might have tried many suffixes; showing 552 // just one is misleading. See also golang.org/issue/6324. 553 lastErr.Name = name 554 } 555 sortByRFC6724(addrs) 556 if len(addrs) == 0 { 557 if order == hostLookupDNSFiles { 558 addrs = goLookupIPFiles(name) 559 } 560 if len(addrs) == 0 && lastErr != nil { 561 return nil, "", lastErr 562 } 563 } 564 return addrs, cname, nil 565 } 566 567 // goLookupCNAME is the native Go (non-cgo) implementation of LookupCNAME. 568 func (r *Resolver) goLookupCNAME(ctx context.Context, host string) (cname string, err error) { 569 order := systemConf().hostLookupOrder(host) 570 _, cname, err = r.goLookupIPCNAMEOrder(ctx, host, order) 571 return 572 } 573 574 // goLookupPTR is the native Go implementation of LookupAddr. 575 // Used only if cgoLookupPTR refuses to handle the request (that is, 576 // only if cgoLookupPTR is the stub in cgo_stub.go). 577 // Normally we let cgo use the C library resolver instead of depending 578 // on our lookup code, so that Go and C get the same answers. 579 func (r *Resolver) goLookupPTR(ctx context.Context, addr string) ([]string, error) { 580 names := lookupStaticAddr(addr) 581 if len(names) > 0 { 582 return names, nil 583 } 584 arpa, err := reverseaddr(addr) 585 if err != nil { 586 return nil, err 587 } 588 _, rrs, err := r.lookup(ctx, arpa, dnsTypePTR) 589 if err != nil { 590 return nil, err 591 } 592 ptrs := make([]string, len(rrs)) 593 for i, rr := range rrs { 594 ptrs[i] = rr.(*dnsRR_PTR).Ptr 595 } 596 return ptrs, nil 597 }