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/strconv"
    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(make([]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 = make([]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 = make([]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 = make([]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 []string
   176  	if useTCP {
   177  		networks = []string{"tcp"}
   178  	} else {
   179  		networks = []string{"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  	return getSystemDNSConfigNamed("/etc/resolv.conf")
   370  }
   371  
   372  func getSystemDNSConfigNamed(path string) *dnsConfig {
   373  	resolvConf.tryUpdate(path)
   374  	return resolvConf.dnsConfig.Load()
   375  }
   376  
   377  // init initializes conf and is only called via conf.initOnce.
   378  func (conf *resolverConfig) init() {
   379  	// Set dnsConfig and lastChecked so we don't parse
   380  	// resolv.conf twice the first time.
   381  	conf.dnsConfig.Store(dnsReadConfig("/etc/resolv.conf"))
   382  	conf.lastChecked = time.Now()
   383  
   384  	// Prepare ch so that only one update of resolverConfig may
   385  	// run at once.
   386  	conf.ch = make(chan struct{}, 1)
   387  }
   388  
   389  // distantFuture is a sentinel time used for tests to signal that
   390  // resolv.conf should not be rechecked.
   391  var distantFuture = time.Date(3000, 1, 2, 3, 4, 5, 6, time.UTC)
   392  
   393  // tryUpdate tries to update conf with the named resolv.conf file.
   394  // The name variable only exists for testing. It is otherwise always
   395  // "/etc/resolv.conf".
   396  func (conf *resolverConfig) tryUpdate(name string) {
   397  	conf.initOnce.Do(conf.init)
   398  
   399  	dc := conf.dnsConfig.Load()
   400  
   401  	// Currently we should never have a config that does not have any
   402  	// available servers to query, since in such cases the servers field
   403  	// is set to [defaultNS], see dnsReadConfig.
   404  	// This assertion main purpose is for testing, such that we never set
   405  	// the mocked dnsConfig in such way.
   406  	if len(dc.servers) == 0 {
   407  		panic("unreachable")
   408  	}
   409  
   410  	if dc.noReload {
   411  		return
   412  	}
   413  
   414  	// Ensure only one update at a time checks resolv.conf.
   415  	if !conf.tryAcquireSema() {
   416  		return
   417  	}
   418  	defer conf.releaseSema()
   419  
   420  	now := time.Now()
   421  
   422  	// Only recheck the resolv.conf when:
   423  	// - expired (last re-check was more that 5 seconds ago)
   424  	// - the default nameservers are used (the last parse could not load
   425  	//   resolv.conf, or it did not contain any nameservers)
   426  	// - rechecks are not disabled (only possible in case of testing)
   427  	//
   428  	// Note: We only do one check at a time. Other concurrent requests might
   429  	// still use the previous (outdated) version of the configuration file.
   430  	expired := now.After(conf.lastChecked.Add(5 * time.Second))
   431  	rechecksEnabled := conf.lastChecked != distantFuture // for testing purposes
   432  	recheck := (expired || dc.isDefaultNS()) && rechecksEnabled
   433  	if !recheck {
   434  		return
   435  	}
   436  	conf.lastChecked = now
   437  
   438  	switch runtime.GOOS {
   439  	case "windows":
   440  		// There's no file on disk, so don't bother checking
   441  		// and failing.
   442  		//
   443  		// The Windows implementation of dnsReadConfig (called
   444  		// below) ignores the name.
   445  	default:
   446  		var mtime time.Time
   447  		if fi, err := os.Stat(name); err == nil {
   448  			mtime = fi.ModTime()
   449  		}
   450  		if mtime.Equal(conf.dnsConfig.Load().mtime) {
   451  			return
   452  		}
   453  	}
   454  
   455  	dnsConf := dnsReadConfig(name)
   456  	conf.dnsConfig.Store(dnsConf)
   457  }
   458  
   459  func (conf *resolverConfig) tryAcquireSema() bool {
   460  	select {
   461  	case conf.ch <- struct{}{}:
   462  		return true
   463  	default:
   464  		return false
   465  	}
   466  }
   467  
   468  func (conf *resolverConfig) releaseSema() {
   469  	<-conf.ch
   470  }
   471  
   472  func (r *Resolver) lookup(ctx context.Context, name string, qtype dnsmessage.Type, conf *dnsConfig) (dnsmessage.Parser, string, error) {
   473  	if !isDomainName(name) {
   474  		// We used to use "invalid domain name" as the error,
   475  		// but that is a detail of the specific lookup mechanism.
   476  		// Other lookups might allow broader name syntax
   477  		// (for example Multicast DNS allows UTF-8; see RFC 6762).
   478  		// For consistency with libc resolvers, report no such host.
   479  		return dnsmessage.Parser{}, "", newDNSError(errNoSuchHost, name, "")
   480  	}
   481  
   482  	if isLocalhostName(name) {
   483  		// RFC 6761, section 6.3, says non-address queries for
   484  		// localhost names should get negative responses without the
   485  		// query being sent to DNS servers. Address queries never
   486  		// reach here; goLookupIPCNAMEOrder answers them itself.
   487  		return dnsmessage.Parser{}, "", newDNSError(errNoSuchHost, name, "")
   488  	}
   489  
   490  	if conf == nil {
   491  		conf = getSystemDNSConfig()
   492  	}
   493  
   494  	var (
   495  		p      dnsmessage.Parser
   496  		server string
   497  		err    error
   498  	)
   499  	for _, fqdn := range conf.nameList(name) {
   500  		p, server, err = r.tryOneName(ctx, conf, fqdn, qtype)
   501  		if err == nil {
   502  			break
   503  		}
   504  		if nerr, ok := err.(Error); ok && nerr.Temporary() && r.strictErrors() {
   505  			// If we hit a temporary error with StrictErrors enabled,
   506  			// stop immediately instead of trying more names.
   507  			break
   508  		}
   509  	}
   510  	if err == nil {
   511  		return p, server, nil
   512  	}
   513  	if err, ok := err.(*DNSError); ok {
   514  		// Show original name passed to lookup, not suffixed one.
   515  		// In general we might have tried many suffixes; showing
   516  		// just one is misleading. See also golang.org/issue/6324.
   517  		err.Name = name
   518  	}
   519  	return dnsmessage.Parser{}, "", err
   520  }
   521  
   522  // avoidDNS reports whether this is a hostname for which we should not
   523  // use DNS. Currently this includes only .onion, per RFC 7686. See
   524  // golang.org/issue/13705. Does not cover .local names (RFC 6762),
   525  // see golang.org/issue/16739.
   526  func avoidDNS(name string) bool {
   527  	if name == "" {
   528  		return true
   529  	}
   530  	name = stringslite.TrimSuffix(name, ".")
   531  	return stringsHasSuffixFold(name, ".onion")
   532  }
   533  
   534  // isLocalhostName reports whether name is "localhost" or a name
   535  // within the ".localhost" domain, which RFC 6761 reserves to mean
   536  // the IP loopback interface.
   537  func isLocalhostName(name string) bool {
   538  	name = stringslite.TrimSuffix(name, ".")
   539  	return stringsEqualFold(name, "localhost") || stringsHasSuffixFold(name, ".localhost")
   540  }
   541  
   542  // nameList returns a list of names for sequential DNS queries.
   543  func (conf *dnsConfig) nameList(name string) []string {
   544  	// Check name length (see isDomainName).
   545  	rooted := len(name) > 0 && name[len(name)-1] == '.'
   546  	if len(name) > 254 || len(name) == 254 && !rooted {
   547  		return nil
   548  	}
   549  
   550  	// If name is rooted (trailing dot), try only that name.
   551  	if rooted {
   552  		if avoidDNS(name) {
   553  			return nil
   554  		}
   555  		return []string{name}
   556  	}
   557  
   558  	hasNdots := bytealg.CountString(name, '.') >= conf.ndots
   559  	name += "."
   560  
   561  	// Build list of search choices.
   562  	names := make([]string, 0, 1+len(conf.search))
   563  	// If name has enough dots, try unsuffixed first.
   564  	if hasNdots && !avoidDNS(name) {
   565  		names = append(names, name)
   566  	}
   567  	// Try suffixes that are not too long (see isDomainName).
   568  	for _, suffix := range conf.search {
   569  		fqdn := name + suffix
   570  		if !avoidDNS(fqdn) && len(fqdn) <= 254 {
   571  			names = append(names, fqdn)
   572  		}
   573  	}
   574  	// Try unsuffixed, if not tried first above.
   575  	if !hasNdots && !avoidDNS(name) {
   576  		names = append(names, name)
   577  	}
   578  	return names
   579  }
   580  
   581  // hostLookupOrder specifies the order of LookupHost lookup strategies.
   582  // It is basically a simplified representation of nsswitch.conf.
   583  // "files" means /etc/hosts.
   584  type hostLookupOrder int
   585  
   586  const (
   587  	// hostLookupCgo means defer to cgo.
   588  	hostLookupCgo      hostLookupOrder = iota
   589  	hostLookupFilesDNS                 // files first
   590  	hostLookupDNSFiles                 // dns first
   591  	hostLookupFiles                    // only files
   592  	hostLookupDNS                      // only DNS
   593  )
   594  
   595  var lookupOrderName = map[hostLookupOrder]string{
   596  	hostLookupCgo:      "cgo",
   597  	hostLookupFilesDNS: "files,dns",
   598  	hostLookupDNSFiles: "dns,files",
   599  	hostLookupFiles:    "files",
   600  	hostLookupDNS:      "dns",
   601  }
   602  
   603  func (o hostLookupOrder) String() string {
   604  	if s, ok := lookupOrderName[o]; ok {
   605  		return s
   606  	}
   607  	return "hostLookupOrder=" + strconv.Itoa(int(o)) + "??"
   608  }
   609  
   610  func (r *Resolver) goLookupHostOrder(ctx context.Context, name string, order hostLookupOrder, conf *dnsConfig) (addrs []string, err error) {
   611  	if order == hostLookupFilesDNS || order == hostLookupFiles {
   612  		// Use entries from /etc/hosts if they match.
   613  		addrs, _ = lookupStaticHost(name)
   614  		if len(addrs) > 0 {
   615  			return
   616  		}
   617  
   618  		// Localhost names not in the hosts file fall through to
   619  		// goLookupIPCNAMEOrder, which resolves them itself, even when
   620  		// the order permits no DNS.
   621  		if order == hostLookupFiles && !isLocalhostName(name) {
   622  			return nil, newDNSError(errNoSuchHost, name, "")
   623  		}
   624  	}
   625  	ips, _, err := r.goLookupIPCNAMEOrder(ctx, "ip", name, order, conf)
   626  	if err != nil {
   627  		return
   628  	}
   629  	addrs = make([]string, 0, len(ips))
   630  	for _, ip := range ips {
   631  		addrs = append(addrs, ip.String())
   632  	}
   633  	return
   634  }
   635  
   636  // lookup entries from /etc/hosts
   637  func goLookupIPFiles(name string) (addrs []IPAddr, canonical string) {
   638  	addr, canonical := lookupStaticHost(name)
   639  	for _, haddr := range addr {
   640  		haddr, zone := splitHostZone(haddr)
   641  		if ip := ParseIP(haddr); ip != nil {
   642  			addr := IPAddr{IP: ip, Zone: zone}
   643  			addrs = append(addrs, addr)
   644  		}
   645  	}
   646  	sortByRFC6724(addrs)
   647  	return addrs, canonical
   648  }
   649  
   650  // goLookupIP is the native Go implementation of LookupIP.
   651  // The libc versions are in cgo_*.go.
   652  func (r *Resolver) goLookupIP(ctx context.Context, network, host string, order hostLookupOrder, conf *dnsConfig) (addrs []IPAddr, err error) {
   653  	addrs, _, err = r.goLookupIPCNAMEOrder(ctx, network, host, order, conf)
   654  	return
   655  }
   656  
   657  func (r *Resolver) goLookupIPCNAMEOrder(ctx context.Context, network, name string, order hostLookupOrder, conf *dnsConfig) (addrs []IPAddr, cname dnsmessage.Name, err error) {
   658  	if order == hostLookupFilesDNS || order == hostLookupFiles {
   659  		var canonical string
   660  		addrs, canonical = goLookupIPFiles(name)
   661  
   662  		if len(addrs) > 0 {
   663  			var err error
   664  			cname, err = dnsmessage.NewName(canonical)
   665  			if err != nil {
   666  				return nil, dnsmessage.Name{}, err
   667  			}
   668  			return addrs, cname, nil
   669  		}
   670  
   671  		if order == hostLookupFiles && !isLocalhostName(name) {
   672  			return nil, dnsmessage.Name{}, newDNSError(errNoSuchHost, name, "")
   673  		}
   674  	}
   675  
   676  	if !isDomainName(name) {
   677  		// See comment in func lookup above about use of errNoSuchHost.
   678  		return nil, dnsmessage.Name{}, newDNSError(errNoSuchHost, name, "")
   679  	}
   680  
   681  	if isLocalhostName(name) {
   682  		// Per RFC 6761, localhost names mean loopback and must never
   683  		// reach DNS (or be expanded with search domains on the way).
   684  		// The hosts file, checked above, can still override.
   685  		// See go.dev/issue/57757 and go.dev/issue/32017.
   686  		var addrs []IPAddr
   687  		if ipVersion(network) != '4' {
   688  			addrs = append(addrs, IPAddr{IP: IPv6loopback})
   689  		}
   690  		if ipVersion(network) != '6' {
   691  			addrs = append(addrs, IPAddr{IP: IPv4(127, 0, 0, 1)})
   692  		}
   693  		sortByRFC6724(addrs)
   694  		cname, err := dnsmessage.NewName(absDomainName(name))
   695  		if err != nil {
   696  			return nil, dnsmessage.Name{}, err
   697  		}
   698  		return addrs, cname, nil
   699  	}
   700  	type result struct {
   701  		p      dnsmessage.Parser
   702  		server string
   703  		error  error
   704  	}
   705  
   706  	if conf == nil {
   707  		conf = getSystemDNSConfig()
   708  	}
   709  
   710  	lane := make(chan result, 1)
   711  	qtypes := []dnsmessage.Type{dnsmessage.TypeA, dnsmessage.TypeAAAA}
   712  	if network == "CNAME" {
   713  		qtypes = append(qtypes, dnsmessage.TypeCNAME)
   714  	}
   715  	switch ipVersion(network) {
   716  	case '4':
   717  		qtypes = []dnsmessage.Type{dnsmessage.TypeA}
   718  	case '6':
   719  		qtypes = []dnsmessage.Type{dnsmessage.TypeAAAA}
   720  	}
   721  	var queryFn func(fqdn string, qtype dnsmessage.Type)
   722  	var responseFn func(fqdn string, qtype dnsmessage.Type) result
   723  	if conf.singleRequest {
   724  		queryFn = func(fqdn string, qtype dnsmessage.Type) {}
   725  		responseFn = func(fqdn string, qtype dnsmessage.Type) result {
   726  			dnsWaitGroup.Add(1)
   727  			defer dnsWaitGroup.Done()
   728  			p, server, err := r.tryOneName(ctx, conf, fqdn, qtype)
   729  			return result{p, server, err}
   730  		}
   731  	} else {
   732  		queryFn = func(fqdn string, qtype dnsmessage.Type) {
   733  			dnsWaitGroup.Add(1)
   734  			go func(qtype dnsmessage.Type) {
   735  				p, server, err := r.tryOneName(ctx, conf, fqdn, qtype)
   736  				lane <- result{p, server, err}
   737  				dnsWaitGroup.Done()
   738  			}(qtype)
   739  		}
   740  		responseFn = func(fqdn string, qtype dnsmessage.Type) result {
   741  			return <-lane
   742  		}
   743  	}
   744  	var lastErr error
   745  	for _, fqdn := range conf.nameList(name) {
   746  		for _, qtype := range qtypes {
   747  			queryFn(fqdn, qtype)
   748  		}
   749  		hitStrictError := false
   750  		for _, qtype := range qtypes {
   751  			result := responseFn(fqdn, qtype)
   752  			if result.error != nil {
   753  				if nerr, ok := result.error.(Error); ok && nerr.Temporary() && r.strictErrors() {
   754  					// This error will abort the nameList loop.
   755  					hitStrictError = true
   756  					lastErr = result.error
   757  				} else if lastErr == nil || fqdn == name+"." {
   758  					// Prefer error for original name.
   759  					lastErr = result.error
   760  				}
   761  				continue
   762  			}
   763  
   764  			// Presotto says it's okay to assume that servers listed in
   765  			// /etc/resolv.conf are recursive resolvers.
   766  			//
   767  			// We asked for recursion, so it should have included all the
   768  			// answers we need in this one packet.
   769  			//
   770  			// Further, RFC 1034 section 4.3.1 says that "the recursive
   771  			// response to a query will be... The answer to the query,
   772  			// possibly preface by one or more CNAME RRs that specify
   773  			// aliases encountered on the way to an answer."
   774  			//
   775  			// Therefore, we should be able to assume that we can ignore
   776  			// CNAMEs and that the A and AAAA records we requested are
   777  			// for the canonical name.
   778  
   779  		loop:
   780  			for {
   781  				h, err := result.p.AnswerHeader()
   782  				if err != nil && err != dnsmessage.ErrSectionDone {
   783  					lastErr = &DNSError{
   784  						Err:    errCannotUnmarshalDNSMessage.Error(),
   785  						Name:   name,
   786  						Server: result.server,
   787  					}
   788  				}
   789  				if err != nil {
   790  					break
   791  				}
   792  				switch h.Type {
   793  				case dnsmessage.TypeA:
   794  					a, err := result.p.AResource()
   795  					if err != nil {
   796  						lastErr = &DNSError{
   797  							Err:    errCannotUnmarshalDNSMessage.Error(),
   798  							Name:   name,
   799  							Server: result.server,
   800  						}
   801  						break loop
   802  					}
   803  					addrs = append(addrs, IPAddr{IP: IP(a.A[:])})
   804  					if cname.Length == 0 && h.Name.Length != 0 {
   805  						cname = h.Name
   806  					}
   807  
   808  				case dnsmessage.TypeAAAA:
   809  					aaaa, err := result.p.AAAAResource()
   810  					if err != nil {
   811  						lastErr = &DNSError{
   812  							Err:    errCannotUnmarshalDNSMessage.Error(),
   813  							Name:   name,
   814  							Server: result.server,
   815  						}
   816  						break loop
   817  					}
   818  					addrs = append(addrs, IPAddr{IP: IP(aaaa.AAAA[:])})
   819  					if cname.Length == 0 && h.Name.Length != 0 {
   820  						cname = h.Name
   821  					}
   822  
   823  				case dnsmessage.TypeCNAME:
   824  					c, err := result.p.CNAMEResource()
   825  					if err != nil {
   826  						lastErr = &DNSError{
   827  							Err:    errCannotUnmarshalDNSMessage.Error(),
   828  							Name:   name,
   829  							Server: result.server,
   830  						}
   831  						break loop
   832  					}
   833  					if cname.Length == 0 && c.CNAME.Length > 0 {
   834  						cname = c.CNAME
   835  					}
   836  
   837  				default:
   838  					if err := result.p.SkipAnswer(); err != nil {
   839  						lastErr = &DNSError{
   840  							Err:    errCannotUnmarshalDNSMessage.Error(),
   841  							Name:   name,
   842  							Server: result.server,
   843  						}
   844  						break loop
   845  					}
   846  					continue
   847  				}
   848  			}
   849  		}
   850  		if hitStrictError {
   851  			// If either family hit an error with StrictErrors enabled,
   852  			// discard all addresses. This ensures that network flakiness
   853  			// cannot turn a dualstack hostname IPv4/IPv6-only.
   854  			addrs = nil
   855  			break
   856  		}
   857  		if len(addrs) > 0 || network == "CNAME" && cname.Length > 0 {
   858  			break
   859  		}
   860  	}
   861  	if lastErr, ok := lastErr.(*DNSError); ok {
   862  		// Show original name passed to lookup, not suffixed one.
   863  		// In general we might have tried many suffixes; showing
   864  		// just one is misleading. See also golang.org/issue/6324.
   865  		lastErr.Name = name
   866  	}
   867  	sortByRFC6724(addrs)
   868  	if len(addrs) == 0 && !(network == "CNAME" && cname.Length > 0) {
   869  		if order == hostLookupDNSFiles {
   870  			var canonical string
   871  			addrs, canonical = goLookupIPFiles(name)
   872  			if len(addrs) > 0 {
   873  				var err error
   874  				cname, err = dnsmessage.NewName(canonical)
   875  				if err != nil {
   876  					return nil, dnsmessage.Name{}, err
   877  				}
   878  				return addrs, cname, nil
   879  			}
   880  		}
   881  		if lastErr != nil {
   882  			return nil, dnsmessage.Name{}, lastErr
   883  		}
   884  	}
   885  	return addrs, cname, nil
   886  }
   887  
   888  // goLookupCNAME is the native Go (non-cgo) implementation of LookupCNAME.
   889  func (r *Resolver) goLookupCNAME(ctx context.Context, host string, order hostLookupOrder, conf *dnsConfig) (string, error) {
   890  	_, cname, err := r.goLookupIPCNAMEOrder(ctx, "CNAME", host, order, conf)
   891  	return cname.String(), err
   892  }
   893  
   894  // goLookupPTR is the native Go implementation of LookupAddr.
   895  func (r *Resolver) goLookupPTR(ctx context.Context, addr string, order hostLookupOrder, conf *dnsConfig) ([]string, error) {
   896  	if order == hostLookupFiles || order == hostLookupFilesDNS {
   897  		names := lookupStaticAddr(addr)
   898  		if len(names) > 0 {
   899  			return names, nil
   900  		}
   901  
   902  		if order == hostLookupFiles {
   903  			return nil, newDNSError(errNoSuchHost, addr, "")
   904  		}
   905  	}
   906  
   907  	arpa, err := reverseaddr(addr)
   908  	if err != nil {
   909  		return nil, err
   910  	}
   911  	p, server, err := r.lookup(ctx, arpa, dnsmessage.TypePTR, conf)
   912  	if err != nil {
   913  		if dnsErr, ok := errors.AsType[*DNSError](err); ok && dnsErr.IsNotFound {
   914  			if order == hostLookupDNSFiles {
   915  				names := lookupStaticAddr(addr)
   916  				if len(names) > 0 {
   917  					return names, nil
   918  				}
   919  			}
   920  		}
   921  		return nil, err
   922  	}
   923  	var ptrs []string
   924  	for {
   925  		h, err := p.AnswerHeader()
   926  		if err == dnsmessage.ErrSectionDone {
   927  			break
   928  		}
   929  		if err != nil {
   930  			return nil, &DNSError{
   931  				Err:    errCannotUnmarshalDNSMessage.Error(),
   932  				Name:   addr,
   933  				Server: server,
   934  			}
   935  		}
   936  		if h.Type != dnsmessage.TypePTR {
   937  			err := p.SkipAnswer()
   938  			if err != nil {
   939  				return nil, &DNSError{
   940  					Err:    errCannotUnmarshalDNSMessage.Error(),
   941  					Name:   addr,
   942  					Server: server,
   943  				}
   944  			}
   945  			continue
   946  		}
   947  		ptr, err := p.PTRResource()
   948  		if err != nil {
   949  			return nil, &DNSError{
   950  				Err:    errCannotUnmarshalDNSMessage.Error(),
   951  				Name:   addr,
   952  				Server: server,
   953  			}
   954  		}
   955  		ptrs = append(ptrs, ptr.PTR.String())
   956  
   957  	}
   958  
   959  	return ptrs, nil
   960  }
   961  

View as plain text