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

View as plain text