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