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

View as plain text