Source file src/net/dnsclient_unix.go

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

View as plain text