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.
5 // DNS client: see RFC 1035.
6 // Has to be linked into package net for Dial.
9 // Could potentially handle many outstanding lookups faster.
10 // Random UDP source port (net.Dial should do that for us).
11 // Random request IDs.
27 "golang.org/x/net/dns/dnsmessage"
31 // to be used as a useTCP parameter to exchange
35 // Maximum DNS packet size.
36 // Value taken from https://dnsflagday.net/2020/.
37 maxDNSPacketSize = 1232
41 errLameReferral = errors.New("lame referral")
42 errCannotUnmarshalDNSMessage = errors.New("cannot unmarshal DNS message")
43 errCannotMarshalDNSMessage = errors.New("cannot marshal DNS message")
44 errServerMisbehaving = errors.New("server misbehaving")
45 errInvalidDNSResponse = errors.New("invalid DNS response")
46 errNoAnswerFromDNSServer = errors.New("no answer from DNS server")
48 // errServerTemporarilyMisbehaving is like errServerMisbehaving, except
49 // that when it gets translated to a DNSError, the IsTemporary field
51 errServerTemporarilyMisbehaving = errors.New("server misbehaving")
54 func newRequest(q dnsmessage.Question, ad bool) (id uint16, udpReq, tcpReq []byte, err error) {
55 id = uint16(randInt())
56 b := dnsmessage.NewBuilder(make([]byte, 2, 514), dnsmessage.Header{ID: id, RecursionDesired: true, AuthenticData: ad})
57 if err := b.StartQuestions(); err != nil {
58 return 0, nil, nil, err
60 if err := b.Question(q); err != nil {
61 return 0, nil, nil, err
64 // Accept packets up to maxDNSPacketSize. RFC 6891.
65 if err := b.StartAdditionals(); err != nil {
66 return 0, nil, nil, err
68 var rh dnsmessage.ResourceHeader
69 if err := rh.SetEDNS0(maxDNSPacketSize, dnsmessage.RCodeSuccess, false); err != nil {
70 return 0, nil, nil, err
72 if err := b.OPTResource(rh, dnsmessage.OPTResource{}); err != nil {
73 return 0, nil, nil, err
76 tcpReq, err = b.Finish()
78 return 0, nil, nil, err
82 tcpReq[0] = byte(l >> 8)
84 return id, udpReq, tcpReq, nil
87 func checkResponse(reqID uint16, reqQues dnsmessage.Question, respHdr dnsmessage.Header, respQues dnsmessage.Question) bool {
88 if !respHdr.Response {
91 if reqID != respHdr.ID {
94 if reqQues.Type != respQues.Type || reqQues.Class != respQues.Class || !equalASCIIName(reqQues.Name, respQues.Name) {
100 func dnsPacketRoundTrip(c Conn, id uint16, query dnsmessage.Question, b []byte) (dnsmessage.Parser, dnsmessage.Header, error) {
101 if _, err := c.Write(b); err != nil {
102 return dnsmessage.Parser{}, dnsmessage.Header{}, err
105 b = make([]byte, maxDNSPacketSize)
109 return dnsmessage.Parser{}, dnsmessage.Header{}, err
111 var p dnsmessage.Parser
112 // Ignore invalid responses as they may be malicious
113 // forgery attempts. Instead continue waiting until
114 // timeout. See golang.org/issue/13281.
115 h, err := p.Start(b[:n])
119 q, err := p.Question()
120 if err != nil || !checkResponse(id, query, h, q) {
127 func dnsStreamRoundTrip(c Conn, id uint16, query dnsmessage.Question, b []byte) (dnsmessage.Parser, dnsmessage.Header, error) {
128 if _, err := c.Write(b); err != nil {
129 return dnsmessage.Parser{}, dnsmessage.Header{}, err
132 b = make([]byte, 1280) // 1280 is a reasonable initial size for IP over Ethernet, see RFC 4035
133 if _, err := io.ReadFull(c, b[:2]); err != nil {
134 return dnsmessage.Parser{}, dnsmessage.Header{}, err
136 l := int(b[0])<<8 | int(b[1])
140 n, err := io.ReadFull(c, b[:l])
142 return dnsmessage.Parser{}, dnsmessage.Header{}, err
144 var p dnsmessage.Parser
145 h, err := p.Start(b[:n])
147 return dnsmessage.Parser{}, dnsmessage.Header{}, errCannotUnmarshalDNSMessage
149 q, err := p.Question()
151 return dnsmessage.Parser{}, dnsmessage.Header{}, errCannotUnmarshalDNSMessage
153 if !checkResponse(id, query, h, q) {
154 return dnsmessage.Parser{}, dnsmessage.Header{}, errInvalidDNSResponse
159 // exchange sends a query on the connection and hopes for a response.
160 func (r *Resolver) exchange(ctx context.Context, server string, q dnsmessage.Question, timeout time.Duration, useTCP, ad bool) (dnsmessage.Parser, dnsmessage.Header, error) {
161 q.Class = dnsmessage.ClassINET
162 id, udpReq, tcpReq, err := newRequest(q, ad)
164 return dnsmessage.Parser{}, dnsmessage.Header{}, errCannotMarshalDNSMessage
166 var networks []string
168 networks = []string{"tcp"}
170 networks = []string{"udp", "tcp"}
172 for _, network := range networks {
173 ctx, cancel := context.WithDeadline(ctx, time.Now().Add(timeout))
176 c, err := r.dial(ctx, network, server)
178 return dnsmessage.Parser{}, dnsmessage.Header{}, err
180 if d, ok := ctx.Deadline(); ok && !d.IsZero() {
183 var p dnsmessage.Parser
184 var h dnsmessage.Header
185 if _, ok := c.(PacketConn); ok {
186 p, h, err = dnsPacketRoundTrip(c, id, q, udpReq)
188 p, h, err = dnsStreamRoundTrip(c, id, q, tcpReq)
192 return dnsmessage.Parser{}, dnsmessage.Header{}, mapErr(err)
194 if err := p.SkipQuestion(); err != dnsmessage.ErrSectionDone {
195 return dnsmessage.Parser{}, dnsmessage.Header{}, errInvalidDNSResponse
197 if h.Truncated { // see RFC 5966
202 return dnsmessage.Parser{}, dnsmessage.Header{}, errNoAnswerFromDNSServer
205 // checkHeader performs basic sanity checks on the header.
206 func checkHeader(p *dnsmessage.Parser, h dnsmessage.Header) error {
207 rcode := extractExtendedRCode(*p, h)
209 if rcode == dnsmessage.RCodeNameError {
213 _, err := p.AnswerHeader()
214 if err != nil && err != dnsmessage.ErrSectionDone {
215 return errCannotUnmarshalDNSMessage
218 // libresolv continues to the next server when it receives
219 // an invalid referral response. See golang.org/issue/15434.
220 if rcode == dnsmessage.RCodeSuccess && !h.Authoritative && !h.RecursionAvailable && err == dnsmessage.ErrSectionDone {
221 return errLameReferral
224 if rcode != dnsmessage.RCodeSuccess && rcode != dnsmessage.RCodeNameError {
225 // None of the error codes make sense
226 // for the query we sent. If we didn't get
227 // a name error and we didn't get success,
228 // the server is behaving incorrectly or
229 // having temporary trouble.
230 if rcode == dnsmessage.RCodeServerFailure {
231 return errServerTemporarilyMisbehaving
233 return errServerMisbehaving
239 func skipToAnswer(p *dnsmessage.Parser, qtype dnsmessage.Type) error {
241 h, err := p.AnswerHeader()
242 if err == dnsmessage.ErrSectionDone {
246 return errCannotUnmarshalDNSMessage
251 if err := p.SkipAnswer(); err != nil {
252 return errCannotUnmarshalDNSMessage
257 // extractExtendedRCode extracts the extended RCode from the OPT resource (EDNS(0))
258 // If an OPT record is not found, the RCode from the hdr is returned.
259 func extractExtendedRCode(p dnsmessage.Parser, hdr dnsmessage.Header) dnsmessage.RCode {
261 p.SkipAllAuthorities()
263 ahdr, err := p.AdditionalHeader()
267 if ahdr.Type == dnsmessage.TypeOPT {
268 return ahdr.ExtendedRCode(hdr.RCode)
274 // Do a lookup for a single name, which must be rooted
275 // (otherwise answer will not find the answers).
276 func (r *Resolver) tryOneName(ctx context.Context, cfg *dnsConfig, name string, qtype dnsmessage.Type) (dnsmessage.Parser, string, error) {
278 serverOffset := cfg.serverOffset()
279 sLen := uint32(len(cfg.servers))
281 n, err := dnsmessage.NewName(name)
283 return dnsmessage.Parser{}, "", errCannotMarshalDNSMessage
285 q := dnsmessage.Question{
288 Class: dnsmessage.ClassINET,
291 for i := 0; i < cfg.attempts; i++ {
292 for j := uint32(0); j < sLen; j++ {
293 server := cfg.servers[(serverOffset+j)%sLen]
295 p, h, err := r.exchange(ctx, server, q, cfg.timeout, cfg.useTCP, cfg.trustAD)
302 if nerr, ok := err.(Error); ok && nerr.Timeout() {
303 dnsErr.IsTimeout = true
305 // Set IsTemporary for socket-level errors. Note that this flag
306 // may also be used to indicate a SERVFAIL response.
307 if _, ok := err.(*OpError); ok {
308 dnsErr.IsTemporary = true
314 if err := checkHeader(&p, h); err != nil {
320 if err == errServerTemporarilyMisbehaving {
321 dnsErr.IsTemporary = true
323 if err == errNoSuchHost {
324 // The name does not exist, so trying
325 // another server won't help.
327 dnsErr.IsNotFound = true
328 return p, server, dnsErr
334 err = skipToAnswer(&p, qtype)
336 return p, server, nil
343 if err == errNoSuchHost {
344 // The name does not exist, so trying another
345 // server won't help.
347 lastErr.(*DNSError).IsNotFound = true
348 return p, server, lastErr
352 return dnsmessage.Parser{}, "", lastErr
355 // A resolverConfig represents a DNS stub resolver configuration.
356 type resolverConfig struct {
357 initOnce sync.Once // guards init of resolverConfig
359 // ch is used as a semaphore that only allows one lookup at a
360 // time to recheck resolv.conf.
361 ch chan struct{} // guards lastChecked and modTime
362 lastChecked time.Time // last time resolv.conf was checked
364 dnsConfig atomic.Pointer[dnsConfig] // parsed resolv.conf structure used in lookups
367 var resolvConf resolverConfig
369 func getSystemDNSConfig() *dnsConfig {
370 resolvConf.tryUpdate("/etc/resolv.conf")
371 return resolvConf.dnsConfig.Load()
374 // init initializes conf and is only called via conf.initOnce.
375 func (conf *resolverConfig) init() {
376 // Set dnsConfig and lastChecked so we don't parse
377 // resolv.conf twice the first time.
378 conf.dnsConfig.Store(dnsReadConfig("/etc/resolv.conf"))
379 conf.lastChecked = time.Now()
381 // Prepare ch so that only one update of resolverConfig may
383 conf.ch = make(chan struct{}, 1)
386 // tryUpdate tries to update conf with the named resolv.conf file.
387 // The name variable only exists for testing. It is otherwise always
388 // "/etc/resolv.conf".
389 func (conf *resolverConfig) tryUpdate(name string) {
390 conf.initOnce.Do(conf.init)
392 if conf.dnsConfig.Load().noReload {
396 // Ensure only one update at a time checks resolv.conf.
397 if !conf.tryAcquireSema() {
400 defer conf.releaseSema()
403 if conf.lastChecked.After(now.Add(-5 * time.Second)) {
406 conf.lastChecked = now
408 switch runtime.GOOS {
410 // There's no file on disk, so don't bother checking
413 // The Windows implementation of dnsReadConfig (called
414 // below) ignores the name.
417 if fi, err := os.Stat(name); err == nil {
420 if mtime.Equal(conf.dnsConfig.Load().mtime) {
425 dnsConf := dnsReadConfig(name)
426 conf.dnsConfig.Store(dnsConf)
429 func (conf *resolverConfig) tryAcquireSema() bool {
431 case conf.ch <- struct{}{}:
438 func (conf *resolverConfig) releaseSema() {
442 func (r *Resolver) lookup(ctx context.Context, name string, qtype dnsmessage.Type, conf *dnsConfig) (dnsmessage.Parser, string, error) {
443 if !isDomainName(name) {
444 // We used to use "invalid domain name" as the error,
445 // but that is a detail of the specific lookup mechanism.
446 // Other lookups might allow broader name syntax
447 // (for example Multicast DNS allows UTF-8; see RFC 6762).
448 // For consistency with libc resolvers, report no such host.
449 return dnsmessage.Parser{}, "", &DNSError{Err: errNoSuchHost.Error(), Name: name, IsNotFound: true}
453 conf = getSystemDNSConfig()
461 for _, fqdn := range conf.nameList(name) {
462 p, server, err = r.tryOneName(ctx, conf, fqdn, qtype)
466 if nerr, ok := err.(Error); ok && nerr.Temporary() && r.strictErrors() {
467 // If we hit a temporary error with StrictErrors enabled,
468 // stop immediately instead of trying more names.
473 return p, server, nil
475 if err, ok := err.(*DNSError); ok {
476 // Show original name passed to lookup, not suffixed one.
477 // In general we might have tried many suffixes; showing
478 // just one is misleading. See also golang.org/issue/6324.
481 return dnsmessage.Parser{}, "", err
484 // avoidDNS reports whether this is a hostname for which we should not
485 // use DNS. Currently this includes only .onion, per RFC 7686. See
486 // golang.org/issue/13705. Does not cover .local names (RFC 6762),
487 // see golang.org/issue/16739.
488 func avoidDNS(name string) bool {
492 if name[len(name)-1] == '.' {
493 name = name[:len(name)-1]
495 return stringsHasSuffixFold(name, ".onion")
498 // nameList returns a list of names for sequential DNS queries.
499 func (conf *dnsConfig) nameList(name string) []string {
500 // Check name length (see isDomainName).
502 rooted := l > 0 && name[l-1] == '.'
503 if l > 254 || l == 254 && !rooted {
507 // If name is rooted (trailing dot), try only that name.
512 return []string{name}
515 hasNdots := bytealg.CountString(name, '.') >= conf.ndots
519 // Build list of search choices.
520 names := make([]string, 0, 1+len(conf.search))
521 // If name has enough dots, try unsuffixed first.
522 if hasNdots && !avoidDNS(name) {
523 names = append(names, name)
525 // Try suffixes that are not too long (see isDomainName).
526 for _, suffix := range conf.search {
527 fqdn := name + suffix
528 if !avoidDNS(fqdn) && len(fqdn) <= 254 {
529 names = append(names, fqdn)
532 // Try unsuffixed, if not tried first above.
533 if !hasNdots && !avoidDNS(name) {
534 names = append(names, name)
539 // hostLookupOrder specifies the order of LookupHost lookup strategies.
540 // It is basically a simplified representation of nsswitch.conf.
541 // "files" means /etc/hosts.
542 type hostLookupOrder int
545 // hostLookupCgo means defer to cgo.
546 hostLookupCgo hostLookupOrder = iota
547 hostLookupFilesDNS // files first
548 hostLookupDNSFiles // dns first
549 hostLookupFiles // only files
550 hostLookupDNS // only DNS
553 var lookupOrderName = map[hostLookupOrder]string{
554 hostLookupCgo: "cgo",
555 hostLookupFilesDNS: "files,dns",
556 hostLookupDNSFiles: "dns,files",
557 hostLookupFiles: "files",
558 hostLookupDNS: "dns",
561 func (o hostLookupOrder) String() string {
562 if s, ok := lookupOrderName[o]; ok {
565 return "hostLookupOrder=" + itoa.Itoa(int(o)) + "??"
568 func (r *Resolver) goLookupHostOrder(ctx context.Context, name string, order hostLookupOrder, conf *dnsConfig) (addrs []string, err error) {
569 if order == hostLookupFilesDNS || order == hostLookupFiles {
570 // Use entries from /etc/hosts if they match.
571 addrs, _ = lookupStaticHost(name)
576 if order == hostLookupFiles {
577 return nil, &DNSError{Err: errNoSuchHost.Error(), Name: name, IsNotFound: true}
580 ips, _, err := r.goLookupIPCNAMEOrder(ctx, "ip", name, order, conf)
584 addrs = make([]string, 0, len(ips))
585 for _, ip := range ips {
586 addrs = append(addrs, ip.String())
591 // lookup entries from /etc/hosts
592 func goLookupIPFiles(name string) (addrs []IPAddr, canonical string) {
593 addr, canonical := lookupStaticHost(name)
594 for _, haddr := range addr {
595 haddr, zone := splitHostZone(haddr)
596 if ip := ParseIP(haddr); ip != nil {
597 addr := IPAddr{IP: ip, Zone: zone}
598 addrs = append(addrs, addr)
602 return addrs, canonical
605 // goLookupIP is the native Go implementation of LookupIP.
606 // The libc versions are in cgo_*.go.
607 func (r *Resolver) goLookupIP(ctx context.Context, network, host string, order hostLookupOrder, conf *dnsConfig) (addrs []IPAddr, err error) {
608 addrs, _, err = r.goLookupIPCNAMEOrder(ctx, network, host, order, conf)
612 func (r *Resolver) goLookupIPCNAMEOrder(ctx context.Context, network, name string, order hostLookupOrder, conf *dnsConfig) (addrs []IPAddr, cname dnsmessage.Name, err error) {
613 if order == hostLookupFilesDNS || order == hostLookupFiles {
615 addrs, canonical = goLookupIPFiles(name)
619 cname, err = dnsmessage.NewName(canonical)
621 return nil, dnsmessage.Name{}, err
623 return addrs, cname, nil
626 if order == hostLookupFiles {
627 return nil, dnsmessage.Name{}, &DNSError{Err: errNoSuchHost.Error(), Name: name, IsNotFound: true}
631 if !isDomainName(name) {
632 // See comment in func lookup above about use of errNoSuchHost.
633 return nil, dnsmessage.Name{}, &DNSError{Err: errNoSuchHost.Error(), Name: name, IsNotFound: true}
642 conf = getSystemDNSConfig()
645 lane := make(chan result, 1)
646 qtypes := []dnsmessage.Type{dnsmessage.TypeA, dnsmessage.TypeAAAA}
647 if network == "CNAME" {
648 qtypes = append(qtypes, dnsmessage.TypeCNAME)
650 switch ipVersion(network) {
652 qtypes = []dnsmessage.Type{dnsmessage.TypeA}
654 qtypes = []dnsmessage.Type{dnsmessage.TypeAAAA}
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 {
662 defer dnsWaitGroup.Done()
663 p, server, err := r.tryOneName(ctx, conf, fqdn, qtype)
664 return result{p, server, err}
667 queryFn = func(fqdn string, qtype dnsmessage.Type) {
669 go func(qtype dnsmessage.Type) {
670 p, server, err := r.tryOneName(ctx, conf, fqdn, qtype)
671 lane <- result{p, server, err}
675 responseFn = func(fqdn string, qtype dnsmessage.Type) result {
680 for _, fqdn := range conf.nameList(name) {
681 for _, qtype := range qtypes {
684 hitStrictError := false
685 for _, qtype := range qtypes {
686 result := responseFn(fqdn, qtype)
687 if result.error != nil {
688 if nerr, ok := result.error.(Error); ok && nerr.Temporary() && r.strictErrors() {
689 // This error will abort the nameList loop.
690 hitStrictError = true
691 lastErr = result.error
692 } else if lastErr == nil || fqdn == name+"." {
693 // Prefer error for original name.
694 lastErr = result.error
699 // Presotto says it's okay to assume that servers listed in
700 // /etc/resolv.conf are recursive resolvers.
702 // We asked for recursion, so it should have included all the
703 // answers we need in this one packet.
705 // Further, RFC 1034 section 4.3.1 says that "the recursive
706 // response to a query will be... The answer to the query,
707 // possibly preface by one or more CNAME RRs that specify
708 // aliases encountered on the way to an answer."
710 // Therefore, we should be able to assume that we can ignore
711 // CNAMEs and that the A and AAAA records we requested are
712 // for the canonical name.
716 h, err := result.p.AnswerHeader()
717 if err != nil && err != dnsmessage.ErrSectionDone {
719 Err: errCannotUnmarshalDNSMessage.Error(),
721 Server: result.server,
728 case dnsmessage.TypeA:
729 a, err := result.p.AResource()
732 Err: errCannotUnmarshalDNSMessage.Error(),
734 Server: result.server,
738 addrs = append(addrs, IPAddr{IP: IP(a.A[:])})
739 if cname.Length == 0 && h.Name.Length != 0 {
743 case dnsmessage.TypeAAAA:
744 aaaa, err := result.p.AAAAResource()
747 Err: errCannotUnmarshalDNSMessage.Error(),
749 Server: result.server,
753 addrs = append(addrs, IPAddr{IP: IP(aaaa.AAAA[:])})
754 if cname.Length == 0 && h.Name.Length != 0 {
758 case dnsmessage.TypeCNAME:
759 c, err := result.p.CNAMEResource()
762 Err: errCannotUnmarshalDNSMessage.Error(),
764 Server: result.server,
768 if cname.Length == 0 && c.CNAME.Length > 0 {
773 if err := result.p.SkipAnswer(); err != nil {
775 Err: errCannotUnmarshalDNSMessage.Error(),
777 Server: result.server,
786 // If either family hit an error with StrictErrors enabled,
787 // discard all addresses. This ensures that network flakiness
788 // cannot turn a dualstack hostname IPv4/IPv6-only.
792 if len(addrs) > 0 || network == "CNAME" && cname.Length > 0 {
796 if lastErr, ok := lastErr.(*DNSError); ok {
797 // Show original name passed to lookup, not suffixed one.
798 // In general we might have tried many suffixes; showing
799 // just one is misleading. See also golang.org/issue/6324.
803 if len(addrs) == 0 && !(network == "CNAME" && cname.Length > 0) {
804 if order == hostLookupDNSFiles {
806 addrs, canonical = goLookupIPFiles(name)
809 cname, err = dnsmessage.NewName(canonical)
811 return nil, dnsmessage.Name{}, err
813 return addrs, cname, nil
817 return nil, dnsmessage.Name{}, lastErr
820 return addrs, cname, nil
823 // goLookupCNAME is the native Go (non-cgo) implementation of LookupCNAME.
824 func (r *Resolver) goLookupCNAME(ctx context.Context, host string, order hostLookupOrder, conf *dnsConfig) (string, error) {
825 _, cname, err := r.goLookupIPCNAMEOrder(ctx, "CNAME", host, order, conf)
826 return cname.String(), err
829 // goLookupPTR is the native Go implementation of LookupAddr.
830 func (r *Resolver) goLookupPTR(ctx context.Context, addr string, order hostLookupOrder, conf *dnsConfig) ([]string, error) {
831 if order == hostLookupFiles || order == hostLookupFilesDNS {
832 names := lookupStaticAddr(addr)
837 if order == hostLookupFiles {
838 return nil, &DNSError{Err: errNoSuchHost.Error(), Name: addr, IsNotFound: true}
842 arpa, err := reverseaddr(addr)
846 p, server, err := r.lookup(ctx, arpa, dnsmessage.TypePTR, conf)
849 if errors.As(err, &dnsErr) && dnsErr.IsNotFound {
850 if order == hostLookupDNSFiles {
851 names := lookupStaticAddr(addr)
861 h, err := p.AnswerHeader()
862 if err == dnsmessage.ErrSectionDone {
866 return nil, &DNSError{
867 Err: errCannotUnmarshalDNSMessage.Error(),
872 if h.Type != dnsmessage.TypePTR {
873 err := p.SkipAnswer()
875 return nil, &DNSError{
876 Err: errCannotUnmarshalDNSMessage.Error(),
883 ptr, err := p.PTRResource()
885 return nil, &DNSError{
886 Err: errCannotUnmarshalDNSMessage.Error(),
891 ptrs = append(ptrs, ptr.PTR.String())