github.com/q45/go@v0.0.0-20151101211701-a4fb8c13db3f/src/net/dnsclient_unix.go (about)

     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  	"errors"
    20  	"io"
    21  	"math/rand"
    22  	"os"
    23  	"sync"
    24  	"time"
    25  )
    26  
    27  // A dnsConn represents a DNS transport endpoint.
    28  type dnsConn interface {
    29  	Conn
    30  
    31  	// readDNSResponse reads a DNS response message from the DNS
    32  	// transport endpoint and returns the received DNS response
    33  	// message.
    34  	readDNSResponse() (*dnsMsg, error)
    35  
    36  	// writeDNSQuery writes a DNS query message to the DNS
    37  	// connection endpoint.
    38  	writeDNSQuery(*dnsMsg) error
    39  }
    40  
    41  func (c *UDPConn) readDNSResponse() (*dnsMsg, error) {
    42  	b := make([]byte, 512) // see RFC 1035
    43  	n, err := c.Read(b)
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  	msg := &dnsMsg{}
    48  	if !msg.Unpack(b[:n]) {
    49  		return nil, errors.New("cannot unmarshal DNS message")
    50  	}
    51  	return msg, nil
    52  }
    53  
    54  func (c *UDPConn) writeDNSQuery(msg *dnsMsg) error {
    55  	b, ok := msg.Pack()
    56  	if !ok {
    57  		return errors.New("cannot marshal DNS message")
    58  	}
    59  	if _, err := c.Write(b); err != nil {
    60  		return err
    61  	}
    62  	return nil
    63  }
    64  
    65  func (c *TCPConn) readDNSResponse() (*dnsMsg, error) {
    66  	b := make([]byte, 1280) // 1280 is a reasonable initial size for IP over Ethernet, see RFC 4035
    67  	if _, err := io.ReadFull(c, b[:2]); err != nil {
    68  		return nil, err
    69  	}
    70  	l := int(b[0])<<8 | int(b[1])
    71  	if l > len(b) {
    72  		b = make([]byte, l)
    73  	}
    74  	n, err := io.ReadFull(c, b[:l])
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  	msg := &dnsMsg{}
    79  	if !msg.Unpack(b[:n]) {
    80  		return nil, errors.New("cannot unmarshal DNS message")
    81  	}
    82  	return msg, nil
    83  }
    84  
    85  func (c *TCPConn) writeDNSQuery(msg *dnsMsg) error {
    86  	b, ok := msg.Pack()
    87  	if !ok {
    88  		return errors.New("cannot marshal DNS message")
    89  	}
    90  	l := uint16(len(b))
    91  	b = append([]byte{byte(l >> 8), byte(l)}, b...)
    92  	if _, err := c.Write(b); err != nil {
    93  		return err
    94  	}
    95  	return nil
    96  }
    97  
    98  func (d *Dialer) dialDNS(network, server string) (dnsConn, error) {
    99  	switch network {
   100  	case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6":
   101  	default:
   102  		return nil, UnknownNetworkError(network)
   103  	}
   104  	// Calling Dial here is scary -- we have to be sure not to
   105  	// dial a name that will require a DNS lookup, or Dial will
   106  	// call back here to translate it. The DNS config parser has
   107  	// already checked that all the cfg.servers[i] are IP
   108  	// addresses, which Dial will use without a DNS lookup.
   109  	c, err := d.Dial(network, server)
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  	switch network {
   114  	case "tcp", "tcp4", "tcp6":
   115  		return c.(*TCPConn), nil
   116  	case "udp", "udp4", "udp6":
   117  		return c.(*UDPConn), nil
   118  	}
   119  	panic("unreachable")
   120  }
   121  
   122  // exchange sends a query on the connection and hopes for a response.
   123  func exchange(server, name string, qtype uint16, timeout time.Duration) (*dnsMsg, error) {
   124  	d := Dialer{Timeout: timeout}
   125  	out := dnsMsg{
   126  		dnsMsgHdr: dnsMsgHdr{
   127  			recursion_desired: true,
   128  		},
   129  		question: []dnsQuestion{
   130  			{name, qtype, dnsClassINET},
   131  		},
   132  	}
   133  	for _, network := range []string{"udp", "tcp"} {
   134  		c, err := d.dialDNS(network, server)
   135  		if err != nil {
   136  			return nil, err
   137  		}
   138  		defer c.Close()
   139  		if timeout > 0 {
   140  			c.SetDeadline(time.Now().Add(timeout))
   141  		}
   142  		out.id = uint16(rand.Int()) ^ uint16(time.Now().UnixNano())
   143  		if err := c.writeDNSQuery(&out); err != nil {
   144  			return nil, err
   145  		}
   146  		in, err := c.readDNSResponse()
   147  		if err != nil {
   148  			return nil, err
   149  		}
   150  		if in.id != out.id {
   151  			return nil, errors.New("DNS message ID mismatch")
   152  		}
   153  		if in.truncated { // see RFC 5966
   154  			continue
   155  		}
   156  		return in, nil
   157  	}
   158  	return nil, errors.New("no answer from DNS server")
   159  }
   160  
   161  // Do a lookup for a single name, which must be rooted
   162  // (otherwise answer will not find the answers).
   163  func tryOneName(cfg *dnsConfig, name string, qtype uint16) (string, []dnsRR, error) {
   164  	if len(cfg.servers) == 0 {
   165  		return "", nil, &DNSError{Err: "no DNS servers", Name: name}
   166  	}
   167  	timeout := time.Duration(cfg.timeout) * time.Second
   168  	var lastErr error
   169  	for i := 0; i < cfg.attempts; i++ {
   170  		for _, server := range cfg.servers {
   171  			server = JoinHostPort(server, "53")
   172  			msg, err := exchange(server, name, qtype, timeout)
   173  			if err != nil {
   174  				lastErr = &DNSError{
   175  					Err:    err.Error(),
   176  					Name:   name,
   177  					Server: server,
   178  				}
   179  				if nerr, ok := err.(Error); ok && nerr.Timeout() {
   180  					lastErr.(*DNSError).IsTimeout = true
   181  				}
   182  				continue
   183  			}
   184  			cname, rrs, err := answer(name, server, msg, qtype)
   185  			if err == nil || msg.rcode == dnsRcodeSuccess || msg.rcode == dnsRcodeNameError && msg.recursion_available {
   186  				return cname, rrs, err
   187  			}
   188  			lastErr = err
   189  		}
   190  	}
   191  	return "", nil, lastErr
   192  }
   193  
   194  // addrRecordList converts and returns a list of IP addresses from DNS
   195  // address records (both A and AAAA). Other record types are ignored.
   196  func addrRecordList(rrs []dnsRR) []IPAddr {
   197  	addrs := make([]IPAddr, 0, 4)
   198  	for _, rr := range rrs {
   199  		switch rr := rr.(type) {
   200  		case *dnsRR_A:
   201  			addrs = append(addrs, IPAddr{IP: IPv4(byte(rr.A>>24), byte(rr.A>>16), byte(rr.A>>8), byte(rr.A))})
   202  		case *dnsRR_AAAA:
   203  			ip := make(IP, IPv6len)
   204  			copy(ip, rr.AAAA[:])
   205  			addrs = append(addrs, IPAddr{IP: ip})
   206  		}
   207  	}
   208  	return addrs
   209  }
   210  
   211  // A resolverConfig represents a DNS stub resolver configuration.
   212  type resolverConfig struct {
   213  	initOnce sync.Once // guards init of resolverConfig
   214  
   215  	// ch is used as a semaphore that only allows one lookup at a
   216  	// time to recheck resolv.conf.
   217  	ch          chan struct{} // guards lastChecked and modTime
   218  	lastChecked time.Time     // last time resolv.conf was checked
   219  	modTime     time.Time     // time of resolv.conf modification
   220  
   221  	mu        sync.RWMutex // protects dnsConfig
   222  	dnsConfig *dnsConfig   // parsed resolv.conf structure used in lookups
   223  }
   224  
   225  var resolvConf resolverConfig
   226  
   227  // init initializes conf and is only called via conf.initOnce.
   228  func (conf *resolverConfig) init() {
   229  	// Set dnsConfig, modTime, and lastChecked so we don't parse
   230  	// resolv.conf twice the first time.
   231  	conf.dnsConfig = systemConf().resolv
   232  	if conf.dnsConfig == nil {
   233  		conf.dnsConfig = dnsReadConfig("/etc/resolv.conf")
   234  	}
   235  
   236  	if fi, err := os.Stat("/etc/resolv.conf"); err == nil {
   237  		conf.modTime = fi.ModTime()
   238  	}
   239  	conf.lastChecked = time.Now()
   240  
   241  	// Prepare ch so that only one update of resolverConfig may
   242  	// run at once.
   243  	conf.ch = make(chan struct{}, 1)
   244  }
   245  
   246  // tryUpdate tries to update conf with the named resolv.conf file.
   247  // The name variable only exists for testing. It is otherwise always
   248  // "/etc/resolv.conf".
   249  func (conf *resolverConfig) tryUpdate(name string) {
   250  	conf.initOnce.Do(conf.init)
   251  
   252  	// Ensure only one update at a time checks resolv.conf.
   253  	if !conf.tryAcquireSema() {
   254  		return
   255  	}
   256  	defer conf.releaseSema()
   257  
   258  	now := time.Now()
   259  	if conf.lastChecked.After(now.Add(-5 * time.Second)) {
   260  		return
   261  	}
   262  	conf.lastChecked = now
   263  
   264  	if fi, err := os.Stat(name); err == nil {
   265  		if fi.ModTime().Equal(conf.modTime) {
   266  			return
   267  		}
   268  		conf.modTime = fi.ModTime()
   269  	} else {
   270  		// If modTime wasn't set prior, assume nothing has changed.
   271  		if conf.modTime.IsZero() {
   272  			return
   273  		}
   274  		conf.modTime = time.Time{}
   275  	}
   276  
   277  	dnsConf := dnsReadConfig(name)
   278  	conf.mu.Lock()
   279  	conf.dnsConfig = dnsConf
   280  	conf.mu.Unlock()
   281  }
   282  
   283  func (conf *resolverConfig) tryAcquireSema() bool {
   284  	select {
   285  	case conf.ch <- struct{}{}:
   286  		return true
   287  	default:
   288  		return false
   289  	}
   290  }
   291  
   292  func (conf *resolverConfig) releaseSema() {
   293  	<-conf.ch
   294  }
   295  
   296  func lookup(name string, qtype uint16) (cname string, rrs []dnsRR, err error) {
   297  	if !isDomainName(name) {
   298  		return "", nil, &DNSError{Err: "invalid domain name", Name: name}
   299  	}
   300  	resolvConf.tryUpdate("/etc/resolv.conf")
   301  	resolvConf.mu.RLock()
   302  	conf := resolvConf.dnsConfig
   303  	resolvConf.mu.RUnlock()
   304  	for _, fqdn := range conf.nameList(name) {
   305  		cname, rrs, err = tryOneName(conf, fqdn, qtype)
   306  		if err == nil {
   307  			break
   308  		}
   309  	}
   310  	if err, ok := err.(*DNSError); ok {
   311  		// Show original name passed to lookup, not suffixed one.
   312  		// In general we might have tried many suffixes; showing
   313  		// just one is misleading. See also golang.org/issue/6324.
   314  		err.Name = name
   315  	}
   316  	return
   317  }
   318  
   319  // nameList returns a list of names for sequential DNS queries.
   320  func (conf *dnsConfig) nameList(name string) []string {
   321  	// If name is rooted (trailing dot), try only that name.
   322  	rooted := len(name) > 0 && name[len(name)-1] == '.'
   323  	if rooted {
   324  		return []string{name}
   325  	}
   326  	// Build list of search choices.
   327  	names := make([]string, 0, 1+len(conf.search))
   328  	// If name has enough dots, try unsuffixed first.
   329  	if count(name, '.') >= conf.ndots {
   330  		names = append(names, name+".")
   331  	}
   332  	// Try suffixes.
   333  	for _, suffix := range conf.search {
   334  		suffixed := name + "." + suffix
   335  		if suffixed[len(suffixed)-1] != '.' {
   336  			suffixed += "."
   337  		}
   338  		names = append(names, suffixed)
   339  	}
   340  	// Try unsuffixed, if not tried first above.
   341  	if count(name, '.') < conf.ndots {
   342  		names = append(names, name+".")
   343  	}
   344  	return names
   345  }
   346  
   347  // hostLookupOrder specifies the order of LookupHost lookup strategies.
   348  // It is basically a simplified representation of nsswitch.conf.
   349  // "files" means /etc/hosts.
   350  type hostLookupOrder int
   351  
   352  const (
   353  	// hostLookupCgo means defer to cgo.
   354  	hostLookupCgo      hostLookupOrder = iota
   355  	hostLookupFilesDNS                 // files first
   356  	hostLookupDNSFiles                 // dns first
   357  	hostLookupFiles                    // only files
   358  	hostLookupDNS                      // only DNS
   359  )
   360  
   361  var lookupOrderName = map[hostLookupOrder]string{
   362  	hostLookupCgo:      "cgo",
   363  	hostLookupFilesDNS: "files,dns",
   364  	hostLookupDNSFiles: "dns,files",
   365  	hostLookupFiles:    "files",
   366  	hostLookupDNS:      "dns",
   367  }
   368  
   369  func (o hostLookupOrder) String() string {
   370  	if s, ok := lookupOrderName[o]; ok {
   371  		return s
   372  	}
   373  	return "hostLookupOrder=" + itoa(int(o)) + "??"
   374  }
   375  
   376  // goLookupHost is the native Go implementation of LookupHost.
   377  // Used only if cgoLookupHost refuses to handle the request
   378  // (that is, only if cgoLookupHost is the stub in cgo_stub.go).
   379  // Normally we let cgo use the C library resolver instead of
   380  // depending on our lookup code, so that Go and C get the same
   381  // answers.
   382  func goLookupHost(name string) (addrs []string, err error) {
   383  	return goLookupHostOrder(name, hostLookupFilesDNS)
   384  }
   385  
   386  func goLookupHostOrder(name string, order hostLookupOrder) (addrs []string, err error) {
   387  	if order == hostLookupFilesDNS || order == hostLookupFiles {
   388  		// Use entries from /etc/hosts if they match.
   389  		addrs = lookupStaticHost(name)
   390  		if len(addrs) > 0 || order == hostLookupFiles {
   391  			return
   392  		}
   393  	}
   394  	ips, err := goLookupIPOrder(name, order)
   395  	if err != nil {
   396  		return
   397  	}
   398  	addrs = make([]string, 0, len(ips))
   399  	for _, ip := range ips {
   400  		addrs = append(addrs, ip.String())
   401  	}
   402  	return
   403  }
   404  
   405  // lookup entries from /etc/hosts
   406  func goLookupIPFiles(name string) (addrs []IPAddr) {
   407  	for _, haddr := range lookupStaticHost(name) {
   408  		haddr, zone := splitHostZone(haddr)
   409  		if ip := ParseIP(haddr); ip != nil {
   410  			addr := IPAddr{IP: ip, Zone: zone}
   411  			addrs = append(addrs, addr)
   412  		}
   413  	}
   414  	sortByRFC6724(addrs)
   415  	return
   416  }
   417  
   418  // goLookupIP is the native Go implementation of LookupIP.
   419  // The libc versions are in cgo_*.go.
   420  func goLookupIP(name string) (addrs []IPAddr, err error) {
   421  	return goLookupIPOrder(name, hostLookupFilesDNS)
   422  }
   423  
   424  func goLookupIPOrder(name string, order hostLookupOrder) (addrs []IPAddr, err error) {
   425  	if order == hostLookupFilesDNS || order == hostLookupFiles {
   426  		addrs = goLookupIPFiles(name)
   427  		if len(addrs) > 0 || order == hostLookupFiles {
   428  			return addrs, nil
   429  		}
   430  	}
   431  	if !isDomainName(name) {
   432  		return nil, &DNSError{Err: "invalid domain name", Name: name}
   433  	}
   434  	resolvConf.tryUpdate("/etc/resolv.conf")
   435  	resolvConf.mu.RLock()
   436  	conf := resolvConf.dnsConfig
   437  	resolvConf.mu.RUnlock()
   438  	type racer struct {
   439  		rrs []dnsRR
   440  		error
   441  	}
   442  	lane := make(chan racer, 1)
   443  	qtypes := [...]uint16{dnsTypeA, dnsTypeAAAA}
   444  	var lastErr error
   445  	for _, fqdn := range conf.nameList(name) {
   446  		for _, qtype := range qtypes {
   447  			go func(qtype uint16) {
   448  				_, rrs, err := tryOneName(conf, fqdn, qtype)
   449  				lane <- racer{rrs, err}
   450  			}(qtype)
   451  		}
   452  		for range qtypes {
   453  			racer := <-lane
   454  			if racer.error != nil {
   455  				lastErr = racer.error
   456  				continue
   457  			}
   458  			addrs = append(addrs, addrRecordList(racer.rrs)...)
   459  		}
   460  		if len(addrs) > 0 {
   461  			break
   462  		}
   463  	}
   464  	if lastErr, ok := lastErr.(*DNSError); ok {
   465  		// Show original name passed to lookup, not suffixed one.
   466  		// In general we might have tried many suffixes; showing
   467  		// just one is misleading. See also golang.org/issue/6324.
   468  		lastErr.Name = name
   469  	}
   470  	sortByRFC6724(addrs)
   471  	if len(addrs) == 0 {
   472  		if lastErr != nil {
   473  			return nil, lastErr
   474  		}
   475  		if order == hostLookupDNSFiles {
   476  			addrs = goLookupIPFiles(name)
   477  		}
   478  	}
   479  	return addrs, nil
   480  }
   481  
   482  // goLookupCNAME is the native Go implementation of LookupCNAME.
   483  // Used only if cgoLookupCNAME refuses to handle the request
   484  // (that is, only if cgoLookupCNAME is the stub in cgo_stub.go).
   485  // Normally we let cgo use the C library resolver instead of
   486  // depending on our lookup code, so that Go and C get the same
   487  // answers.
   488  func goLookupCNAME(name string) (cname string, err error) {
   489  	_, rrs, err := lookup(name, dnsTypeCNAME)
   490  	if err != nil {
   491  		return
   492  	}
   493  	cname = rrs[0].(*dnsRR_CNAME).Cname
   494  	return
   495  }
   496  
   497  // goLookupPTR is the native Go implementation of LookupAddr.
   498  // Used only if cgoLookupPTR refuses to handle the request (that is,
   499  // only if cgoLookupPTR is the stub in cgo_stub.go).
   500  // Normally we let cgo use the C library resolver instead of depending
   501  // on our lookup code, so that Go and C get the same answers.
   502  func goLookupPTR(addr string) ([]string, error) {
   503  	names := lookupStaticAddr(addr)
   504  	if len(names) > 0 {
   505  		return names, nil
   506  	}
   507  	arpa, err := reverseaddr(addr)
   508  	if err != nil {
   509  		return nil, err
   510  	}
   511  	_, rrs, err := lookup(arpa, dnsTypePTR)
   512  	if err != nil {
   513  		return nil, err
   514  	}
   515  	ptrs := make([]string, len(rrs))
   516  	for i, rr := range rrs {
   517  		ptrs[i] = rr.(*dnsRR_PTR).Ptr
   518  	}
   519  	return ptrs, nil
   520  }