Black Lives Matter. Support the Equal Justice Initiative.

Source file src/net/dnsclient_unix.go

Documentation: net

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

View as plain text