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