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