github.com/roboticscm/goman@v0.0.0-20210203095141-87c07b4a0a55/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  	if len(name) >= 256 {
   168  		return "", nil, &DNSError{Err: "DNS name too long", Name: name}
   169  	}
   170  	timeout := time.Duration(cfg.timeout) * time.Second
   171  	var lastErr error
   172  	for i := 0; i < cfg.attempts; i++ {
   173  		for _, server := range cfg.servers {
   174  			server = JoinHostPort(server, "53")
   175  			msg, err := exchange(server, name, qtype, timeout)
   176  			if err != nil {
   177  				lastErr = &DNSError{
   178  					Err:    err.Error(),
   179  					Name:   name,
   180  					Server: server,
   181  				}
   182  				if nerr, ok := err.(Error); ok && nerr.Timeout() {
   183  					lastErr.(*DNSError).IsTimeout = true
   184  				}
   185  				continue
   186  			}
   187  			cname, addrs, err := answer(name, server, msg, qtype)
   188  			if err == nil || err.(*DNSError).Err == noSuchHost {
   189  				return cname, addrs, err
   190  			}
   191  			lastErr = err
   192  		}
   193  	}
   194  	return "", nil, lastErr
   195  }
   196  
   197  func convertRR_A(records []dnsRR) []IP {
   198  	addrs := make([]IP, len(records))
   199  	for i, rr := range records {
   200  		a := rr.(*dnsRR_A).A
   201  		addrs[i] = IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a))
   202  	}
   203  	return addrs
   204  }
   205  
   206  func convertRR_AAAA(records []dnsRR) []IP {
   207  	addrs := make([]IP, len(records))
   208  	for i, rr := range records {
   209  		a := make(IP, IPv6len)
   210  		copy(a, rr.(*dnsRR_AAAA).AAAA[:])
   211  		addrs[i] = a
   212  	}
   213  	return addrs
   214  }
   215  
   216  var cfg struct {
   217  	ch        chan struct{}
   218  	mu        sync.RWMutex // protects dnsConfig and dnserr
   219  	dnsConfig *dnsConfig
   220  	dnserr    error
   221  }
   222  var onceLoadConfig sync.Once
   223  
   224  // Assume dns config file is /etc/resolv.conf here
   225  func loadDefaultConfig() {
   226  	loadConfig("/etc/resolv.conf", 5*time.Second, nil)
   227  }
   228  
   229  func loadConfig(resolvConfPath string, reloadTime time.Duration, quit <-chan chan struct{}) {
   230  	var mtime time.Time
   231  	cfg.ch = make(chan struct{}, 1)
   232  	if fi, err := os.Stat(resolvConfPath); err != nil {
   233  		cfg.dnserr = err
   234  	} else {
   235  		mtime = fi.ModTime()
   236  		cfg.dnsConfig, cfg.dnserr = dnsReadConfig(resolvConfPath)
   237  	}
   238  	go func() {
   239  		for {
   240  			time.Sleep(reloadTime)
   241  			select {
   242  			case qresp := <-quit:
   243  				qresp <- struct{}{}
   244  				return
   245  			case <-cfg.ch:
   246  			}
   247  
   248  			// In case of error, we keep the previous config
   249  			fi, err := os.Stat(resolvConfPath)
   250  			if err != nil {
   251  				continue
   252  			}
   253  			// If the resolv.conf mtime didn't change, do not reload
   254  			m := fi.ModTime()
   255  			if m.Equal(mtime) {
   256  				continue
   257  			}
   258  			mtime = m
   259  			// In case of error, we keep the previous config
   260  			ncfg, err := dnsReadConfig(resolvConfPath)
   261  			if err != nil || len(ncfg.servers) == 0 {
   262  				continue
   263  			}
   264  			cfg.mu.Lock()
   265  			cfg.dnsConfig = ncfg
   266  			cfg.dnserr = nil
   267  			cfg.mu.Unlock()
   268  		}
   269  	}()
   270  }
   271  
   272  func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err error) {
   273  	if !isDomainName(name) {
   274  		return name, nil, &DNSError{Err: "invalid domain name", Name: name}
   275  	}
   276  	onceLoadConfig.Do(loadDefaultConfig)
   277  
   278  	select {
   279  	case cfg.ch <- struct{}{}:
   280  	default:
   281  	}
   282  
   283  	cfg.mu.RLock()
   284  	defer cfg.mu.RUnlock()
   285  
   286  	if cfg.dnserr != nil || cfg.dnsConfig == nil {
   287  		err = cfg.dnserr
   288  		return
   289  	}
   290  	// If name is rooted (trailing dot) or has enough dots,
   291  	// try it by itself first.
   292  	rooted := len(name) > 0 && name[len(name)-1] == '.'
   293  	if rooted || count(name, '.') >= cfg.dnsConfig.ndots {
   294  		rname := name
   295  		if !rooted {
   296  			rname += "."
   297  		}
   298  		// Can try as ordinary name.
   299  		cname, addrs, err = tryOneName(cfg.dnsConfig, rname, qtype)
   300  		if rooted || err == nil {
   301  			return
   302  		}
   303  	}
   304  
   305  	// Otherwise, try suffixes.
   306  	for i := 0; i < len(cfg.dnsConfig.search); i++ {
   307  		rname := name + "." + cfg.dnsConfig.search[i]
   308  		if rname[len(rname)-1] != '.' {
   309  			rname += "."
   310  		}
   311  		cname, addrs, err = tryOneName(cfg.dnsConfig, rname, qtype)
   312  		if err == nil {
   313  			return
   314  		}
   315  	}
   316  
   317  	// Last ditch effort: try unsuffixed only if we haven't already,
   318  	// that is, name is not rooted and has less than ndots dots.
   319  	if count(name, '.') < cfg.dnsConfig.ndots {
   320  		cname, addrs, err = tryOneName(cfg.dnsConfig, name+".", qtype)
   321  		if err == nil {
   322  			return
   323  		}
   324  	}
   325  
   326  	if e, ok := err.(*DNSError); ok {
   327  		// Show original name passed to lookup, not suffixed one.
   328  		// In general we might have tried many suffixes; showing
   329  		// just one is misleading. See also golang.org/issue/6324.
   330  		e.Name = name
   331  	}
   332  	return
   333  }
   334  
   335  // goLookupHost is the native Go implementation of LookupHost.
   336  // Used only if cgoLookupHost refuses to handle the request
   337  // (that is, only if cgoLookupHost is the stub in cgo_stub.go).
   338  // Normally we let cgo use the C library resolver instead of
   339  // depending on our lookup code, so that Go and C get the same
   340  // answers.
   341  func goLookupHost(name string) (addrs []string, err error) {
   342  	// Use entries from /etc/hosts if they match.
   343  	addrs = lookupStaticHost(name)
   344  	if len(addrs) > 0 {
   345  		return
   346  	}
   347  	ips, err := goLookupIP(name)
   348  	if err != nil {
   349  		return
   350  	}
   351  	addrs = make([]string, 0, len(ips))
   352  	for _, ip := range ips {
   353  		addrs = append(addrs, ip.String())
   354  	}
   355  	return
   356  }
   357  
   358  // goLookupIP is the native Go implementation of LookupIP.
   359  // Used only if cgoLookupIP refuses to handle the request
   360  // (that is, only if cgoLookupIP is the stub in cgo_stub.go).
   361  // Normally we let cgo use the C library resolver instead of
   362  // depending on our lookup code, so that Go and C get the same
   363  // answers.
   364  func goLookupIP(name string) (addrs []IP, err error) {
   365  	// Use entries from /etc/hosts if possible.
   366  	haddrs := lookupStaticHost(name)
   367  	if len(haddrs) > 0 {
   368  		for _, haddr := range haddrs {
   369  			if ip := ParseIP(haddr); ip != nil {
   370  				addrs = append(addrs, ip)
   371  			}
   372  		}
   373  		if len(addrs) > 0 {
   374  			return
   375  		}
   376  	}
   377  	type racer struct {
   378  		qtype uint16
   379  		rrs   []dnsRR
   380  		error
   381  	}
   382  	lane := make(chan racer, 1)
   383  	qtypes := [...]uint16{dnsTypeA, dnsTypeAAAA}
   384  	for _, qtype := range qtypes {
   385  		go func(qtype uint16) {
   386  			_, rrs, err := lookup(name, qtype)
   387  			lane <- racer{qtype, rrs, err}
   388  		}(qtype)
   389  	}
   390  	var lastErr error
   391  	for range qtypes {
   392  		racer := <-lane
   393  		if racer.error != nil {
   394  			lastErr = racer.error
   395  			continue
   396  		}
   397  		switch racer.qtype {
   398  		case dnsTypeA:
   399  			addrs = append(addrs, convertRR_A(racer.rrs)...)
   400  		case dnsTypeAAAA:
   401  			addrs = append(addrs, convertRR_AAAA(racer.rrs)...)
   402  		}
   403  	}
   404  	if len(addrs) == 0 && lastErr != nil {
   405  		return nil, lastErr
   406  	}
   407  	return addrs, nil
   408  }
   409  
   410  // goLookupCNAME is the native Go implementation of LookupCNAME.
   411  // Used only if cgoLookupCNAME refuses to handle the request
   412  // (that is, only if cgoLookupCNAME is the stub in cgo_stub.go).
   413  // Normally we let cgo use the C library resolver instead of
   414  // depending on our lookup code, so that Go and C get the same
   415  // answers.
   416  func goLookupCNAME(name string) (cname string, err error) {
   417  	_, rr, err := lookup(name, dnsTypeCNAME)
   418  	if err != nil {
   419  		return
   420  	}
   421  	cname = rr[0].(*dnsRR_CNAME).Cname
   422  	return
   423  }