github.com/golang-haiku/go-1.4.3@v0.0.0-20190609233734-1f5ae41cc308/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 haiku 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  	"runtime"
    26  )
    27  
    28  // A dnsConn represents a DNS transport endpoint.
    29  type dnsConn interface {
    30  	Conn
    31  
    32  	// readDNSResponse reads a DNS response message from the DNS
    33  	// transport endpoint and returns the received DNS response
    34  	// message.
    35  	readDNSResponse() (*dnsMsg, error)
    36  
    37  	// writeDNSQuery writes a DNS query message to the DNS
    38  	// connection endpoint.
    39  	writeDNSQuery(*dnsMsg) error
    40  }
    41  
    42  func (c *UDPConn) readDNSResponse() (*dnsMsg, error) {
    43  	b := make([]byte, 512) // see RFC 1035
    44  	n, err := c.Read(b)
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  	msg := &dnsMsg{}
    49  	if !msg.Unpack(b[:n]) {
    50  		return nil, errors.New("cannot unmarshal DNS message")
    51  	}
    52  	return msg, nil
    53  }
    54  
    55  func (c *UDPConn) writeDNSQuery(msg *dnsMsg) error {
    56  	b, ok := msg.Pack()
    57  	if !ok {
    58  		return errors.New("cannot marshal DNS message")
    59  	}
    60  	if _, err := c.Write(b); err != nil {
    61  		return err
    62  	}
    63  	return nil
    64  }
    65  
    66  func (c *TCPConn) readDNSResponse() (*dnsMsg, error) {
    67  	b := make([]byte, 1280) // 1280 is a reasonable initial size for IP over Ethernet, see RFC 4035
    68  	if _, err := io.ReadFull(c, b[:2]); err != nil {
    69  		return nil, err
    70  	}
    71  	l := int(b[0])<<8 | int(b[1])
    72  	if l > len(b) {
    73  		b = make([]byte, l)
    74  	}
    75  	n, err := io.ReadFull(c, b[:l])
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  	msg := &dnsMsg{}
    80  	if !msg.Unpack(b[:n]) {
    81  		return nil, errors.New("cannot unmarshal DNS message")
    82  	}
    83  	return msg, nil
    84  }
    85  
    86  func (c *TCPConn) writeDNSQuery(msg *dnsMsg) error {
    87  	b, ok := msg.Pack()
    88  	if !ok {
    89  		return errors.New("cannot marshal DNS message")
    90  	}
    91  	l := uint16(len(b))
    92  	b = append([]byte{byte(l >> 8), byte(l)}, b...)
    93  	if _, err := c.Write(b); err != nil {
    94  		return err
    95  	}
    96  	return nil
    97  }
    98  
    99  func (d *Dialer) dialDNS(network, server string) (dnsConn, error) {
   100  	switch network {
   101  	case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6":
   102  	default:
   103  		return nil, UnknownNetworkError(network)
   104  	}
   105  	// Calling Dial here is scary -- we have to be sure not to
   106  	// dial a name that will require a DNS lookup, or Dial will
   107  	// call back here to translate it. The DNS config parser has
   108  	// already checked that all the cfg.servers[i] are IP
   109  	// addresses, which Dial will use without a DNS lookup.
   110  	c, err := d.Dial(network, server)
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  	switch network {
   115  	case "tcp", "tcp4", "tcp6":
   116  		return c.(*TCPConn), nil
   117  	case "udp", "udp4", "udp6":
   118  		return c.(*UDPConn), nil
   119  	}
   120  	panic("unreachable")
   121  }
   122  
   123  // exchange sends a query on the connection and hopes for a response.
   124  func exchange(server, name string, qtype uint16, timeout time.Duration) (*dnsMsg, error) {
   125  	d := Dialer{Timeout: timeout}
   126  	out := dnsMsg{
   127  		dnsMsgHdr: dnsMsgHdr{
   128  			recursion_desired: true,
   129  		},
   130  		question: []dnsQuestion{
   131  			{name, qtype, dnsClassINET},
   132  		},
   133  	}
   134  	for _, network := range []string{"udp", "tcp"} {
   135  		c, err := d.dialDNS(network, server)
   136  		if err != nil {
   137  			return nil, err
   138  		}
   139  		defer c.Close()
   140  		if timeout > 0 {
   141  			c.SetDeadline(time.Now().Add(timeout))
   142  		}
   143  		out.id = uint16(rand.Int()) ^ uint16(time.Now().UnixNano())
   144  		if err := c.writeDNSQuery(&out); err != nil {
   145  			return nil, err
   146  		}
   147  		in, err := c.readDNSResponse()
   148  		if err != nil {
   149  			return nil, err
   150  		}
   151  		if in.id != out.id {
   152  			return nil, errors.New("DNS message ID mismatch")
   153  		}
   154  		if in.truncated { // see RFC 5966
   155  			continue
   156  		}
   157  		return in, nil
   158  	}
   159  	return nil, errors.New("no answer from DNS server")
   160  }
   161  
   162  // Do a lookup for a single name, which must be rooted
   163  // (otherwise answer will not find the answers).
   164  func tryOneName(cfg *dnsConfig, name string, qtype uint16) (string, []dnsRR, error) {
   165  	if len(cfg.servers) == 0 {
   166  		return "", nil, &DNSError{Err: "no DNS servers", Name: name}
   167  	}
   168  	if len(name) >= 256 {
   169  		return "", nil, &DNSError{Err: "DNS name too long", Name: name}
   170  	}
   171  	timeout := time.Duration(cfg.timeout) * time.Second
   172  	var lastErr error
   173  	for i := 0; i < cfg.attempts; i++ {
   174  		for _, server := range cfg.servers {
   175  			server = JoinHostPort(server, "53")
   176  			msg, err := exchange(server, name, qtype, timeout)
   177  			if err != nil {
   178  				lastErr = &DNSError{
   179  					Err:    err.Error(),
   180  					Name:   name,
   181  					Server: server,
   182  				}
   183  				if nerr, ok := err.(Error); ok && nerr.Timeout() {
   184  					lastErr.(*DNSError).IsTimeout = true
   185  				}
   186  				continue
   187  			}
   188  			cname, addrs, err := answer(name, server, msg, qtype)
   189  			if err == nil || err.(*DNSError).Err == noSuchHost {
   190  				return cname, addrs, err
   191  			}
   192  			lastErr = err
   193  		}
   194  	}
   195  	return "", nil, lastErr
   196  }
   197  
   198  func convertRR_A(records []dnsRR) []IP {
   199  	addrs := make([]IP, len(records))
   200  	for i, rr := range records {
   201  		a := rr.(*dnsRR_A).A
   202  		addrs[i] = IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a))
   203  	}
   204  	return addrs
   205  }
   206  
   207  func convertRR_AAAA(records []dnsRR) []IP {
   208  	addrs := make([]IP, len(records))
   209  	for i, rr := range records {
   210  		a := make(IP, IPv6len)
   211  		copy(a, rr.(*dnsRR_AAAA).AAAA[:])
   212  		addrs[i] = a
   213  	}
   214  	return addrs
   215  }
   216  
   217  var cfg struct {
   218  	ch        chan struct{}
   219  	mu        sync.RWMutex // protects dnsConfig and dnserr
   220  	dnsConfig *dnsConfig
   221  	dnserr    error
   222  }
   223  var onceLoadConfig sync.Once
   224  
   225  // Assume dns config file is /etc/resolv.conf here
   226  func loadDefaultConfig() {
   227  	if runtime.GOOS != "haiku" {
   228  		loadConfig("/etc/resolv.conf", 5*time.Second, nil)
   229  	} else {
   230  		loadConfig("/boot/system/settings/network/resolv.conf", 5*time.Second, nil)
   231  	}
   232  
   233  }
   234  
   235  func loadConfig(resolvConfPath string, reloadTime time.Duration, quit <-chan chan struct{}) {
   236  	var mtime time.Time
   237  	cfg.ch = make(chan struct{}, 1)
   238  	if fi, err := os.Stat(resolvConfPath); err != nil {
   239  		cfg.dnserr = err
   240  	} else {
   241  		mtime = fi.ModTime()
   242  		cfg.dnsConfig, cfg.dnserr = dnsReadConfig(resolvConfPath)
   243  	}
   244  	go func() {
   245  		for {
   246  			time.Sleep(reloadTime)
   247  			select {
   248  			case qresp := <-quit:
   249  				qresp <- struct{}{}
   250  				return
   251  			case <-cfg.ch:
   252  			}
   253  
   254  			// In case of error, we keep the previous config
   255  			fi, err := os.Stat(resolvConfPath)
   256  			if err != nil {
   257  				continue
   258  			}
   259  			// If the resolv.conf mtime didn't change, do not reload
   260  			m := fi.ModTime()
   261  			if m.Equal(mtime) {
   262  				continue
   263  			}
   264  			mtime = m
   265  			// In case of error, we keep the previous config
   266  			ncfg, err := dnsReadConfig(resolvConfPath)
   267  			if err != nil || len(ncfg.servers) == 0 {
   268  				continue
   269  			}
   270  			cfg.mu.Lock()
   271  			cfg.dnsConfig = ncfg
   272  			cfg.dnserr = nil
   273  			cfg.mu.Unlock()
   274  		}
   275  	}()
   276  }
   277  
   278  func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err error) {
   279  	if !isDomainName(name) {
   280  		return name, nil, &DNSError{Err: "invalid domain name", Name: name}
   281  	}
   282  	onceLoadConfig.Do(loadDefaultConfig)
   283  
   284  	select {
   285  	case cfg.ch <- struct{}{}:
   286  	default:
   287  	}
   288  
   289  	cfg.mu.RLock()
   290  	defer cfg.mu.RUnlock()
   291  
   292  	if cfg.dnserr != nil || cfg.dnsConfig == nil {
   293  		err = cfg.dnserr
   294  		return
   295  	}
   296  	// If name is rooted (trailing dot) or has enough dots,
   297  	// try it by itself first.
   298  	rooted := len(name) > 0 && name[len(name)-1] == '.'
   299  	if rooted || count(name, '.') >= cfg.dnsConfig.ndots {
   300  		rname := name
   301  		if !rooted {
   302  			rname += "."
   303  		}
   304  		// Can try as ordinary name.
   305  		cname, addrs, err = tryOneName(cfg.dnsConfig, rname, qtype)
   306  		if rooted || err == nil {
   307  			return
   308  		}
   309  	}
   310  
   311  	// Otherwise, try suffixes.
   312  	for i := 0; i < len(cfg.dnsConfig.search); i++ {
   313  		rname := name + "." + cfg.dnsConfig.search[i]
   314  		if rname[len(rname)-1] != '.' {
   315  			rname += "."
   316  		}
   317  		cname, addrs, err = tryOneName(cfg.dnsConfig, rname, qtype)
   318  		if err == nil {
   319  			return
   320  		}
   321  	}
   322  
   323  	// Last ditch effort: try unsuffixed only if we haven't already,
   324  	// that is, name is not rooted and has less than ndots dots.
   325  	if count(name, '.') < cfg.dnsConfig.ndots {
   326  		cname, addrs, err = tryOneName(cfg.dnsConfig, name+".", qtype)
   327  		if err == nil {
   328  			return
   329  		}
   330  	}
   331  
   332  	if e, ok := err.(*DNSError); ok {
   333  		// Show original name passed to lookup, not suffixed one.
   334  		// In general we might have tried many suffixes; showing
   335  		// just one is misleading. See also golang.org/issue/6324.
   336  		e.Name = name
   337  	}
   338  	return
   339  }
   340  
   341  // goLookupHost is the native Go implementation of LookupHost.
   342  // Used only if cgoLookupHost refuses to handle the request
   343  // (that is, only if cgoLookupHost is the stub in cgo_stub.go).
   344  // Normally we let cgo use the C library resolver instead of
   345  // depending on our lookup code, so that Go and C get the same
   346  // answers.
   347  func goLookupHost(name string) (addrs []string, err error) {
   348  	// Use entries from /etc/hosts if they match.
   349  	addrs = lookupStaticHost(name)
   350  	if len(addrs) > 0 {
   351  		return
   352  	}
   353  	ips, err := goLookupIP(name)
   354  	if err != nil {
   355  		return
   356  	}
   357  	addrs = make([]string, 0, len(ips))
   358  	for _, ip := range ips {
   359  		addrs = append(addrs, ip.String())
   360  	}
   361  	return
   362  }
   363  
   364  // goLookupIP is the native Go implementation of LookupIP.
   365  // Used only if cgoLookupIP refuses to handle the request
   366  // (that is, only if cgoLookupIP is the stub in cgo_stub.go).
   367  // Normally we let cgo use the C library resolver instead of
   368  // depending on our lookup code, so that Go and C get the same
   369  // answers.
   370  func goLookupIP(name string) (addrs []IP, err error) {
   371  	// Use entries from /etc/hosts if possible.
   372  	haddrs := lookupStaticHost(name)
   373  	if len(haddrs) > 0 {
   374  		for _, haddr := range haddrs {
   375  			if ip := ParseIP(haddr); ip != nil {
   376  				addrs = append(addrs, ip)
   377  			}
   378  		}
   379  		if len(addrs) > 0 {
   380  			return
   381  		}
   382  	}
   383  	type racer struct {
   384  		qtype uint16
   385  		rrs   []dnsRR
   386  		error
   387  	}
   388  	lane := make(chan racer, 1)
   389  	qtypes := [...]uint16{dnsTypeA, dnsTypeAAAA}
   390  	for _, qtype := range qtypes {
   391  		go func(qtype uint16) {
   392  			_, rrs, err := lookup(name, qtype)
   393  			lane <- racer{qtype, rrs, err}
   394  		}(qtype)
   395  	}
   396  	var lastErr error
   397  	for range qtypes {
   398  		racer := <-lane
   399  		if racer.error != nil {
   400  			lastErr = racer.error
   401  			continue
   402  		}
   403  		switch racer.qtype {
   404  		case dnsTypeA:
   405  			addrs = append(addrs, convertRR_A(racer.rrs)...)
   406  		case dnsTypeAAAA:
   407  			addrs = append(addrs, convertRR_AAAA(racer.rrs)...)
   408  		}
   409  	}
   410  	if len(addrs) == 0 && lastErr != nil {
   411  		return nil, lastErr
   412  	}
   413  	return addrs, nil
   414  }
   415  
   416  // goLookupCNAME is the native Go implementation of LookupCNAME.
   417  // Used only if cgoLookupCNAME refuses to handle the request
   418  // (that is, only if cgoLookupCNAME is the stub in cgo_stub.go).
   419  // Normally we let cgo use the C library resolver instead of
   420  // depending on our lookup code, so that Go and C get the same
   421  // answers.
   422  func goLookupCNAME(name string) (cname string, err error) {
   423  	_, rr, err := lookup(name, dnsTypeCNAME)
   424  	if err != nil {
   425  		return
   426  	}
   427  	cname = rr[0].(*dnsRR_CNAME).Cname
   428  	return
   429  }