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