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 hurd 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// Random UDP source port (net.Dial should do that for us). 13// Random request IDs. 14 15package net 16 17import ( 18 "context" 19 "errors" 20 "io" 21 "os" 22 "sync" 23 "time" 24 25 "golang.org/x/net/dns/dnsmessage" 26) 27 28const ( 29 // to be used as a useTCP parameter to exchange 30 useTCPOnly = true 31 useUDPOrTCP = false 32) 33 34var ( 35 errLameReferral = errors.New("lame referral") 36 errCannotUnmarshalDNSMessage = errors.New("cannot unmarshal DNS message") 37 errCannotMarshalDNSMessage = errors.New("cannot marshal DNS message") 38 errServerMisbehaving = errors.New("server misbehaving") 39 errInvalidDNSResponse = errors.New("invalid DNS response") 40 errNoAnswerFromDNSServer = errors.New("no answer from DNS server") 41 42 // errServerTemporarilyMisbehaving is like errServerMisbehaving, except 43 // that when it gets translated to a DNSError, the IsTemporary field 44 // gets set to true. 45 errServerTemporarilyMisbehaving = errors.New("server misbehaving") 46) 47 48func newRequest(q dnsmessage.Question) (id uint16, udpReq, tcpReq []byte, err error) { 49 id = uint16(randInt()) 50 b := dnsmessage.NewBuilder(make([]byte, 2, 514), dnsmessage.Header{ID: id, RecursionDesired: true}) 51 b.EnableCompression() 52 if err := b.StartQuestions(); err != nil { 53 return 0, nil, nil, err 54 } 55 if err := b.Question(q); err != nil { 56 return 0, nil, nil, err 57 } 58 tcpReq, err = b.Finish() 59 udpReq = tcpReq[2:] 60 l := len(tcpReq) - 2 61 tcpReq[0] = byte(l >> 8) 62 tcpReq[1] = byte(l) 63 return id, udpReq, tcpReq, err 64} 65 66func checkResponse(reqID uint16, reqQues dnsmessage.Question, respHdr dnsmessage.Header, respQues dnsmessage.Question) bool { 67 if !respHdr.Response { 68 return false 69 } 70 if reqID != respHdr.ID { 71 return false 72 } 73 if reqQues.Type != respQues.Type || reqQues.Class != respQues.Class || !equalASCIIName(reqQues.Name, respQues.Name) { 74 return false 75 } 76 return true 77} 78 79func dnsPacketRoundTrip(c Conn, id uint16, query dnsmessage.Question, b []byte) (dnsmessage.Parser, dnsmessage.Header, error) { 80 if _, err := c.Write(b); err != nil { 81 return dnsmessage.Parser{}, dnsmessage.Header{}, err 82 } 83 84 b = make([]byte, 512) // see RFC 1035 85 for { 86 n, err := c.Read(b) 87 if err != nil { 88 return dnsmessage.Parser{}, dnsmessage.Header{}, err 89 } 90 var p dnsmessage.Parser 91 // Ignore invalid responses as they may be malicious 92 // forgery attempts. Instead continue waiting until 93 // timeout. See golang.org/issue/13281. 94 h, err := p.Start(b[:n]) 95 if err != nil { 96 continue 97 } 98 q, err := p.Question() 99 if err != nil || !checkResponse(id, query, h, q) { 100 continue 101 } 102 return p, h, nil 103 } 104} 105 106func dnsStreamRoundTrip(c Conn, id uint16, query dnsmessage.Question, b []byte) (dnsmessage.Parser, dnsmessage.Header, error) { 107 if _, err := c.Write(b); err != nil { 108 return dnsmessage.Parser{}, dnsmessage.Header{}, err 109 } 110 111 b = make([]byte, 1280) // 1280 is a reasonable initial size for IP over Ethernet, see RFC 4035 112 if _, err := io.ReadFull(c, b[:2]); err != nil { 113 return dnsmessage.Parser{}, dnsmessage.Header{}, err 114 } 115 l := int(b[0])<<8 | int(b[1]) 116 if l > len(b) { 117 b = make([]byte, l) 118 } 119 n, err := io.ReadFull(c, b[:l]) 120 if err != nil { 121 return dnsmessage.Parser{}, dnsmessage.Header{}, err 122 } 123 var p dnsmessage.Parser 124 h, err := p.Start(b[:n]) 125 if err != nil { 126 return dnsmessage.Parser{}, dnsmessage.Header{}, errCannotUnmarshalDNSMessage 127 } 128 q, err := p.Question() 129 if err != nil { 130 return dnsmessage.Parser{}, dnsmessage.Header{}, errCannotUnmarshalDNSMessage 131 } 132 if !checkResponse(id, query, h, q) { 133 return dnsmessage.Parser{}, dnsmessage.Header{}, errInvalidDNSResponse 134 } 135 return p, h, nil 136} 137 138// exchange sends a query on the connection and hopes for a response. 139func (r *Resolver) exchange(ctx context.Context, server string, q dnsmessage.Question, timeout time.Duration, useTCP bool) (dnsmessage.Parser, dnsmessage.Header, error) { 140 q.Class = dnsmessage.ClassINET 141 id, udpReq, tcpReq, err := newRequest(q) 142 if err != nil { 143 return dnsmessage.Parser{}, dnsmessage.Header{}, errCannotMarshalDNSMessage 144 } 145 var networks []string 146 if useTCP { 147 networks = []string{"tcp"} 148 } else { 149 networks = []string{"udp", "tcp"} 150 } 151 for _, network := range networks { 152 ctx, cancel := context.WithDeadline(ctx, time.Now().Add(timeout)) 153 defer cancel() 154 155 c, err := r.dial(ctx, network, server) 156 if err != nil { 157 return dnsmessage.Parser{}, dnsmessage.Header{}, err 158 } 159 if d, ok := ctx.Deadline(); ok && !d.IsZero() { 160 c.SetDeadline(d) 161 } 162 var p dnsmessage.Parser 163 var h dnsmessage.Header 164 if _, ok := c.(PacketConn); ok { 165 p, h, err = dnsPacketRoundTrip(c, id, q, udpReq) 166 } else { 167 p, h, err = dnsStreamRoundTrip(c, id, q, tcpReq) 168 } 169 c.Close() 170 if err != nil { 171 return dnsmessage.Parser{}, dnsmessage.Header{}, mapErr(err) 172 } 173 if err := p.SkipQuestion(); err != dnsmessage.ErrSectionDone { 174 return dnsmessage.Parser{}, dnsmessage.Header{}, errInvalidDNSResponse 175 } 176 if h.Truncated { // see RFC 5966 177 continue 178 } 179 return p, h, nil 180 } 181 return dnsmessage.Parser{}, dnsmessage.Header{}, errNoAnswerFromDNSServer 182} 183 184// checkHeader performs basic sanity checks on the header. 185func checkHeader(p *dnsmessage.Parser, h dnsmessage.Header) error { 186 if h.RCode == dnsmessage.RCodeNameError { 187 return errNoSuchHost 188 } 189 190 _, err := p.AnswerHeader() 191 if err != nil && err != dnsmessage.ErrSectionDone { 192 return errCannotUnmarshalDNSMessage 193 } 194 195 // libresolv continues to the next server when it receives 196 // an invalid referral response. See golang.org/issue/15434. 197 if h.RCode == dnsmessage.RCodeSuccess && !h.Authoritative && !h.RecursionAvailable && err == dnsmessage.ErrSectionDone { 198 return errLameReferral 199 } 200 201 if h.RCode != dnsmessage.RCodeSuccess && h.RCode != dnsmessage.RCodeNameError { 202 // None of the error codes make sense 203 // for the query we sent. If we didn't get 204 // a name error and we didn't get success, 205 // the server is behaving incorrectly or 206 // having temporary trouble. 207 if h.RCode == dnsmessage.RCodeServerFailure { 208 return errServerTemporarilyMisbehaving 209 } 210 return errServerMisbehaving 211 } 212 213 return nil 214} 215 216func skipToAnswer(p *dnsmessage.Parser, qtype dnsmessage.Type) error { 217 for { 218 h, err := p.AnswerHeader() 219 if err == dnsmessage.ErrSectionDone { 220 return errNoSuchHost 221 } 222 if err != nil { 223 return errCannotUnmarshalDNSMessage 224 } 225 if h.Type == qtype { 226 return nil 227 } 228 if err := p.SkipAnswer(); err != nil { 229 return errCannotUnmarshalDNSMessage 230 } 231 } 232} 233 234// Do a lookup for a single name, which must be rooted 235// (otherwise answer will not find the answers). 236func (r *Resolver) tryOneName(ctx context.Context, cfg *dnsConfig, name string, qtype dnsmessage.Type) (dnsmessage.Parser, string, error) { 237 var lastErr error 238 serverOffset := cfg.serverOffset() 239 sLen := uint32(len(cfg.servers)) 240 241 n, err := dnsmessage.NewName(name) 242 if err != nil { 243 return dnsmessage.Parser{}, "", errCannotMarshalDNSMessage 244 } 245 q := dnsmessage.Question{ 246 Name: n, 247 Type: qtype, 248 Class: dnsmessage.ClassINET, 249 } 250 251 for i := 0; i < cfg.attempts; i++ { 252 for j := uint32(0); j < sLen; j++ { 253 server := cfg.servers[(serverOffset+j)%sLen] 254 255 p, h, err := r.exchange(ctx, server, q, cfg.timeout, cfg.useTCP) 256 if err != nil { 257 dnsErr := &DNSError{ 258 Err: err.Error(), 259 Name: name, 260 Server: server, 261 } 262 if nerr, ok := err.(Error); ok && nerr.Timeout() { 263 dnsErr.IsTimeout = true 264 } 265 // Set IsTemporary for socket-level errors. Note that this flag 266 // may also be used to indicate a SERVFAIL response. 267 if _, ok := err.(*OpError); ok { 268 dnsErr.IsTemporary = true 269 } 270 lastErr = dnsErr 271 continue 272 } 273 274 if err := checkHeader(&p, h); err != nil { 275 dnsErr := &DNSError{ 276 Err: err.Error(), 277 Name: name, 278 Server: server, 279 } 280 if err == errServerTemporarilyMisbehaving { 281 dnsErr.IsTemporary = true 282 } 283 if err == errNoSuchHost { 284 // The name does not exist, so trying 285 // another server won't help. 286 287 dnsErr.IsNotFound = true 288 return p, server, dnsErr 289 } 290 lastErr = dnsErr 291 continue 292 } 293 294 err = skipToAnswer(&p, qtype) 295 if err == nil { 296 return p, server, nil 297 } 298 lastErr = &DNSError{ 299 Err: err.Error(), 300 Name: name, 301 Server: server, 302 } 303 if err == errNoSuchHost { 304 // The name does not exist, so trying another 305 // server won't help. 306 307 lastErr.(*DNSError).IsNotFound = true 308 return p, server, lastErr 309 } 310 } 311 } 312 return dnsmessage.Parser{}, "", lastErr 313} 314 315// A resolverConfig represents a DNS stub resolver configuration. 316type resolverConfig struct { 317 initOnce sync.Once // guards init of resolverConfig 318 319 // ch is used as a semaphore that only allows one lookup at a 320 // time to recheck resolv.conf. 321 ch chan struct{} // guards lastChecked and modTime 322 lastChecked time.Time // last time resolv.conf was checked 323 324 mu sync.RWMutex // protects dnsConfig 325 dnsConfig *dnsConfig // parsed resolv.conf structure used in lookups 326} 327 328var resolvConf resolverConfig 329 330// init initializes conf and is only called via conf.initOnce. 331func (conf *resolverConfig) init() { 332 // Set dnsConfig and lastChecked so we don't parse 333 // resolv.conf twice the first time. 334 conf.dnsConfig = systemConf().resolv 335 if conf.dnsConfig == nil { 336 conf.dnsConfig = dnsReadConfig("/etc/resolv.conf") 337 } 338 conf.lastChecked = time.Now() 339 340 // Prepare ch so that only one update of resolverConfig may 341 // run at once. 342 conf.ch = make(chan struct{}, 1) 343} 344 345// tryUpdate tries to update conf with the named resolv.conf file. 346// The name variable only exists for testing. It is otherwise always 347// "/etc/resolv.conf". 348func (conf *resolverConfig) tryUpdate(name string) { 349 conf.initOnce.Do(conf.init) 350 351 // Ensure only one update at a time checks resolv.conf. 352 if !conf.tryAcquireSema() { 353 return 354 } 355 defer conf.releaseSema() 356 357 now := time.Now() 358 if conf.lastChecked.After(now.Add(-5 * time.Second)) { 359 return 360 } 361 conf.lastChecked = now 362 363 var mtime time.Time 364 if fi, err := os.Stat(name); err == nil { 365 mtime = fi.ModTime() 366 } 367 if mtime.Equal(conf.dnsConfig.mtime) { 368 return 369 } 370 371 dnsConf := dnsReadConfig(name) 372 conf.mu.Lock() 373 conf.dnsConfig = dnsConf 374 conf.mu.Unlock() 375} 376 377func (conf *resolverConfig) tryAcquireSema() bool { 378 select { 379 case conf.ch <- struct{}{}: 380 return true 381 default: 382 return false 383 } 384} 385 386func (conf *resolverConfig) releaseSema() { 387 <-conf.ch 388} 389 390func (r *Resolver) lookup(ctx context.Context, name string, qtype dnsmessage.Type) (dnsmessage.Parser, string, error) { 391 if !isDomainName(name) { 392 // We used to use "invalid domain name" as the error, 393 // but that is a detail of the specific lookup mechanism. 394 // Other lookups might allow broader name syntax 395 // (for example Multicast DNS allows UTF-8; see RFC 6762). 396 // For consistency with libc resolvers, report no such host. 397 return dnsmessage.Parser{}, "", &DNSError{Err: errNoSuchHost.Error(), Name: name, IsNotFound: true} 398 } 399 resolvConf.tryUpdate("/etc/resolv.conf") 400 resolvConf.mu.RLock() 401 conf := resolvConf.dnsConfig 402 resolvConf.mu.RUnlock() 403 var ( 404 p dnsmessage.Parser 405 server string 406 err error 407 ) 408 for _, fqdn := range conf.nameList(name) { 409 p, server, err = r.tryOneName(ctx, conf, fqdn, qtype) 410 if err == nil { 411 break 412 } 413 if nerr, ok := err.(Error); ok && nerr.Temporary() && r.strictErrors() { 414 // If we hit a temporary error with StrictErrors enabled, 415 // stop immediately instead of trying more names. 416 break 417 } 418 } 419 if err == nil { 420 return p, server, nil 421 } 422 if err, ok := err.(*DNSError); ok { 423 // Show original name passed to lookup, not suffixed one. 424 // In general we might have tried many suffixes; showing 425 // just one is misleading. See also golang.org/issue/6324. 426 err.Name = name 427 } 428 return dnsmessage.Parser{}, "", err 429} 430 431// avoidDNS reports whether this is a hostname for which we should not 432// use DNS. Currently this includes only .onion, per RFC 7686. See 433// golang.org/issue/13705. Does not cover .local names (RFC 6762), 434// see golang.org/issue/16739. 435func avoidDNS(name string) bool { 436 if name == "" { 437 return true 438 } 439 if name[len(name)-1] == '.' { 440 name = name[:len(name)-1] 441 } 442 return stringsHasSuffixFold(name, ".onion") 443} 444 445// nameList returns a list of names for sequential DNS queries. 446func (conf *dnsConfig) nameList(name string) []string { 447 if avoidDNS(name) { 448 return nil 449 } 450 451 // Check name length (see isDomainName). 452 l := len(name) 453 rooted := l > 0 && name[l-1] == '.' 454 if l > 254 || l == 254 && rooted { 455 return nil 456 } 457 458 // If name is rooted (trailing dot), try only that name. 459 if rooted { 460 return []string{name} 461 } 462 463 hasNdots := count(name, '.') >= conf.ndots 464 name += "." 465 l++ 466 467 // Build list of search choices. 468 names := make([]string, 0, 1+len(conf.search)) 469 // If name has enough dots, try unsuffixed first. 470 if hasNdots { 471 names = append(names, name) 472 } 473 // Try suffixes that are not too long (see isDomainName). 474 for _, suffix := range conf.search { 475 if l+len(suffix) <= 254 { 476 names = append(names, name+suffix) 477 } 478 } 479 // Try unsuffixed, if not tried first above. 480 if !hasNdots { 481 names = append(names, name) 482 } 483 return names 484} 485 486// hostLookupOrder specifies the order of LookupHost lookup strategies. 487// It is basically a simplified representation of nsswitch.conf. 488// "files" means /etc/hosts. 489type hostLookupOrder int 490 491const ( 492 // hostLookupCgo means defer to cgo. 493 hostLookupCgo hostLookupOrder = iota 494 hostLookupFilesDNS // files first 495 hostLookupDNSFiles // dns first 496 hostLookupFiles // only files 497 hostLookupDNS // only DNS 498) 499 500var lookupOrderName = map[hostLookupOrder]string{ 501 hostLookupCgo: "cgo", 502 hostLookupFilesDNS: "files,dns", 503 hostLookupDNSFiles: "dns,files", 504 hostLookupFiles: "files", 505 hostLookupDNS: "dns", 506} 507 508func (o hostLookupOrder) String() string { 509 if s, ok := lookupOrderName[o]; ok { 510 return s 511 } 512 return "hostLookupOrder=" + itoa(int(o)) + "??" 513} 514 515// goLookupHost is the native Go implementation of LookupHost. 516// Used only if cgoLookupHost refuses to handle the request 517// (that is, only if cgoLookupHost is the stub in cgo_stub.go). 518// Normally we let cgo use the C library resolver instead of 519// depending on our lookup code, so that Go and C get the same 520// answers. 521func (r *Resolver) goLookupHost(ctx context.Context, name string) (addrs []string, err error) { 522 return r.goLookupHostOrder(ctx, name, hostLookupFilesDNS) 523} 524 525func (r *Resolver) goLookupHostOrder(ctx context.Context, name string, order hostLookupOrder) (addrs []string, err error) { 526 if order == hostLookupFilesDNS || order == hostLookupFiles { 527 // Use entries from /etc/hosts if they match. 528 addrs = lookupStaticHost(name) 529 if len(addrs) > 0 || order == hostLookupFiles { 530 return 531 } 532 } 533 ips, _, err := r.goLookupIPCNAMEOrder(ctx, name, order) 534 if err != nil { 535 return 536 } 537 addrs = make([]string, 0, len(ips)) 538 for _, ip := range ips { 539 addrs = append(addrs, ip.String()) 540 } 541 return 542} 543 544// lookup entries from /etc/hosts 545func goLookupIPFiles(name string) (addrs []IPAddr) { 546 for _, haddr := range lookupStaticHost(name) { 547 haddr, zone := splitHostZone(haddr) 548 if ip := ParseIP(haddr); ip != nil { 549 addr := IPAddr{IP: ip, Zone: zone} 550 addrs = append(addrs, addr) 551 } 552 } 553 sortByRFC6724(addrs) 554 return 555} 556 557// goLookupIP is the native Go implementation of LookupIP. 558// The libc versions are in cgo_*.go. 559func (r *Resolver) goLookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) { 560 order := systemConf().hostLookupOrder(r, host) 561 addrs, _, err = r.goLookupIPCNAMEOrder(ctx, host, order) 562 return 563} 564 565func (r *Resolver) goLookupIPCNAMEOrder(ctx context.Context, name string, order hostLookupOrder) (addrs []IPAddr, cname dnsmessage.Name, err error) { 566 if order == hostLookupFilesDNS || order == hostLookupFiles { 567 addrs = goLookupIPFiles(name) 568 if len(addrs) > 0 || order == hostLookupFiles { 569 return addrs, dnsmessage.Name{}, nil 570 } 571 } 572 if !isDomainName(name) { 573 // See comment in func lookup above about use of errNoSuchHost. 574 return nil, dnsmessage.Name{}, &DNSError{Err: errNoSuchHost.Error(), Name: name, IsNotFound: true} 575 } 576 resolvConf.tryUpdate("/etc/resolv.conf") 577 resolvConf.mu.RLock() 578 conf := resolvConf.dnsConfig 579 resolvConf.mu.RUnlock() 580 type result struct { 581 p dnsmessage.Parser 582 server string 583 error 584 } 585 lane := make(chan result, 1) 586 qtypes := [...]dnsmessage.Type{dnsmessage.TypeA, dnsmessage.TypeAAAA} 587 var queryFn func(fqdn string, qtype dnsmessage.Type) 588 var responseFn func(fqdn string, qtype dnsmessage.Type) result 589 if conf.singleRequest { 590 queryFn = func(fqdn string, qtype dnsmessage.Type) {} 591 responseFn = func(fqdn string, qtype dnsmessage.Type) result { 592 dnsWaitGroup.Add(1) 593 defer dnsWaitGroup.Done() 594 p, server, err := r.tryOneName(ctx, conf, fqdn, qtype) 595 return result{p, server, err} 596 } 597 } else { 598 queryFn = func(fqdn string, qtype dnsmessage.Type) { 599 dnsWaitGroup.Add(1) 600 go func(qtype dnsmessage.Type) { 601 p, server, err := r.tryOneName(ctx, conf, fqdn, qtype) 602 lane <- result{p, server, err} 603 dnsWaitGroup.Done() 604 }(qtype) 605 } 606 responseFn = func(fqdn string, qtype dnsmessage.Type) result { 607 return <-lane 608 } 609 } 610 var lastErr error 611 for _, fqdn := range conf.nameList(name) { 612 for _, qtype := range qtypes { 613 queryFn(fqdn, qtype) 614 } 615 hitStrictError := false 616 for _, qtype := range qtypes { 617 result := responseFn(fqdn, qtype) 618 if result.error != nil { 619 if nerr, ok := result.error.(Error); ok && nerr.Temporary() && r.strictErrors() { 620 // This error will abort the nameList loop. 621 hitStrictError = true 622 lastErr = result.error 623 } else if lastErr == nil || fqdn == name+"." { 624 // Prefer error for original name. 625 lastErr = result.error 626 } 627 continue 628 } 629 630 // Presotto says it's okay to assume that servers listed in 631 // /etc/resolv.conf are recursive resolvers. 632 // 633 // We asked for recursion, so it should have included all the 634 // answers we need in this one packet. 635 // 636 // Further, RFC 1035 section 4.3.1 says that "the recursive 637 // response to a query will be... The answer to the query, 638 // possibly preface by one or more CNAME RRs that specify 639 // aliases encountered on the way to an answer." 640 // 641 // Therefore, we should be able to assume that we can ignore 642 // CNAMEs and that the A and AAAA records we requested are 643 // for the canonical name. 644 645 loop: 646 for { 647 h, err := result.p.AnswerHeader() 648 if err != nil && err != dnsmessage.ErrSectionDone { 649 lastErr = &DNSError{ 650 Err: "cannot marshal DNS message", 651 Name: name, 652 Server: result.server, 653 } 654 } 655 if err != nil { 656 break 657 } 658 switch h.Type { 659 case dnsmessage.TypeA: 660 a, err := result.p.AResource() 661 if err != nil { 662 lastErr = &DNSError{ 663 Err: "cannot marshal DNS message", 664 Name: name, 665 Server: result.server, 666 } 667 break loop 668 } 669 addrs = append(addrs, IPAddr{IP: IP(a.A[:])}) 670 671 case dnsmessage.TypeAAAA: 672 aaaa, err := result.p.AAAAResource() 673 if err != nil { 674 lastErr = &DNSError{ 675 Err: "cannot marshal DNS message", 676 Name: name, 677 Server: result.server, 678 } 679 break loop 680 } 681 addrs = append(addrs, IPAddr{IP: IP(aaaa.AAAA[:])}) 682 683 default: 684 if err := result.p.SkipAnswer(); err != nil { 685 lastErr = &DNSError{ 686 Err: "cannot marshal DNS message", 687 Name: name, 688 Server: result.server, 689 } 690 break loop 691 } 692 continue 693 } 694 if cname.Length == 0 && h.Name.Length != 0 { 695 cname = h.Name 696 } 697 } 698 } 699 if hitStrictError { 700 // If either family hit an error with StrictErrors enabled, 701 // discard all addresses. This ensures that network flakiness 702 // cannot turn a dualstack hostname IPv4/IPv6-only. 703 addrs = nil 704 break 705 } 706 if len(addrs) > 0 { 707 break 708 } 709 } 710 if lastErr, ok := lastErr.(*DNSError); ok { 711 // Show original name passed to lookup, not suffixed one. 712 // In general we might have tried many suffixes; showing 713 // just one is misleading. See also golang.org/issue/6324. 714 lastErr.Name = name 715 } 716 sortByRFC6724(addrs) 717 if len(addrs) == 0 { 718 if order == hostLookupDNSFiles { 719 addrs = goLookupIPFiles(name) 720 } 721 if len(addrs) == 0 && lastErr != nil { 722 return nil, dnsmessage.Name{}, lastErr 723 } 724 } 725 return addrs, cname, nil 726} 727 728// goLookupCNAME is the native Go (non-cgo) implementation of LookupCNAME. 729func (r *Resolver) goLookupCNAME(ctx context.Context, host string) (string, error) { 730 order := systemConf().hostLookupOrder(r, host) 731 _, cname, err := r.goLookupIPCNAMEOrder(ctx, host, order) 732 return cname.String(), err 733} 734 735// goLookupPTR is the native Go implementation of LookupAddr. 736// Used only if cgoLookupPTR refuses to handle the request (that is, 737// only if cgoLookupPTR is the stub in cgo_stub.go). 738// Normally we let cgo use the C library resolver instead of depending 739// on our lookup code, so that Go and C get the same answers. 740func (r *Resolver) goLookupPTR(ctx context.Context, addr string) ([]string, error) { 741 names := lookupStaticAddr(addr) 742 if len(names) > 0 { 743 return names, nil 744 } 745 arpa, err := reverseaddr(addr) 746 if err != nil { 747 return nil, err 748 } 749 p, server, err := r.lookup(ctx, arpa, dnsmessage.TypePTR) 750 if err != nil { 751 return nil, err 752 } 753 var ptrs []string 754 for { 755 h, err := p.AnswerHeader() 756 if err == dnsmessage.ErrSectionDone { 757 break 758 } 759 if err != nil { 760 return nil, &DNSError{ 761 Err: "cannot marshal DNS message", 762 Name: addr, 763 Server: server, 764 } 765 } 766 if h.Type != dnsmessage.TypePTR { 767 err := p.SkipAnswer() 768 if err != nil { 769 return nil, &DNSError{ 770 Err: "cannot marshal DNS message", 771 Name: addr, 772 Server: server, 773 } 774 } 775 continue 776 } 777 ptr, err := p.PTRResource() 778 if err != nil { 779 return nil, &DNSError{ 780 Err: "cannot marshal DNS message", 781 Name: addr, 782 Server: server, 783 } 784 } 785 ptrs = append(ptrs, ptr.PTR.String()) 786 787 } 788 return ptrs, nil 789} 790