...
Run Format

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

View as plain text