...

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

View as plain text