github.com/spotify/syslog-redirector-golang@v0.0.0-20140320174030-4859f03d829a/src/pkg/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
     6  
     7  // DNS client: see RFC 1035.
     8  // Has to be linked into package net for Dial.
     9  
    10  // TODO(rsc):
    11  //	Check periodically whether /etc/resolv.conf has changed.
    12  //	Could potentially handle many outstanding lookups faster.
    13  //	Could have a small cache.
    14  //	Random UDP source port (net.Dial should do that for us).
    15  //	Random request IDs.
    16  
    17  package net
    18  
    19  import (
    20  	"io"
    21  	"math/rand"
    22  	"sync"
    23  	"time"
    24  )
    25  
    26  // Send a request on the connection and hope for a reply.
    27  // Up to cfg.attempts attempts.
    28  func exchange(cfg *dnsConfig, c Conn, name string, qtype uint16) (*dnsMsg, error) {
    29  	_, useTCP := c.(*TCPConn)
    30  	if len(name) >= 256 {
    31  		return nil, &DNSError{Err: "name too long", Name: name}
    32  	}
    33  	out := new(dnsMsg)
    34  	out.id = uint16(rand.Int()) ^ uint16(time.Now().UnixNano())
    35  	out.question = []dnsQuestion{
    36  		{name, qtype, dnsClassINET},
    37  	}
    38  	out.recursion_desired = true
    39  	msg, ok := out.Pack()
    40  	if !ok {
    41  		return nil, &DNSError{Err: "internal error - cannot pack message", Name: name}
    42  	}
    43  	if useTCP {
    44  		mlen := uint16(len(msg))
    45  		msg = append([]byte{byte(mlen >> 8), byte(mlen)}, msg...)
    46  	}
    47  	for attempt := 0; attempt < cfg.attempts; attempt++ {
    48  		n, err := c.Write(msg)
    49  		if err != nil {
    50  			return nil, err
    51  		}
    52  
    53  		if cfg.timeout == 0 {
    54  			c.SetReadDeadline(noDeadline)
    55  		} else {
    56  			c.SetReadDeadline(time.Now().Add(time.Duration(cfg.timeout) * time.Second))
    57  		}
    58  		buf := make([]byte, 2000)
    59  		if useTCP {
    60  			n, err = io.ReadFull(c, buf[:2])
    61  			if err != nil {
    62  				if e, ok := err.(Error); ok && e.Timeout() {
    63  					continue
    64  				}
    65  			}
    66  			mlen := int(buf[0])<<8 | int(buf[1])
    67  			if mlen > len(buf) {
    68  				buf = make([]byte, mlen)
    69  			}
    70  			n, err = io.ReadFull(c, buf[:mlen])
    71  		} else {
    72  			n, err = c.Read(buf)
    73  		}
    74  		if err != nil {
    75  			if e, ok := err.(Error); ok && e.Timeout() {
    76  				continue
    77  			}
    78  			return nil, err
    79  		}
    80  		buf = buf[:n]
    81  		in := new(dnsMsg)
    82  		if !in.Unpack(buf) || in.id != out.id {
    83  			continue
    84  		}
    85  		return in, nil
    86  	}
    87  	var server string
    88  	if a := c.RemoteAddr(); a != nil {
    89  		server = a.String()
    90  	}
    91  	return nil, &DNSError{Err: "no answer from server", Name: name, Server: server, IsTimeout: true}
    92  }
    93  
    94  // Do a lookup for a single name, which must be rooted
    95  // (otherwise answer will not find the answers).
    96  func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs []dnsRR, err error) {
    97  	if len(cfg.servers) == 0 {
    98  		return "", nil, &DNSError{Err: "no DNS servers", Name: name}
    99  	}
   100  	for i := 0; i < len(cfg.servers); i++ {
   101  		// Calling Dial here is scary -- we have to be sure
   102  		// not to dial a name that will require a DNS lookup,
   103  		// or Dial will call back here to translate it.
   104  		// The DNS config parser has already checked that
   105  		// all the cfg.servers[i] are IP addresses, which
   106  		// Dial will use without a DNS lookup.
   107  		server := cfg.servers[i] + ":53"
   108  		c, cerr := Dial("udp", server)
   109  		if cerr != nil {
   110  			err = cerr
   111  			continue
   112  		}
   113  		msg, merr := exchange(cfg, c, name, qtype)
   114  		c.Close()
   115  		if merr != nil {
   116  			err = merr
   117  			continue
   118  		}
   119  		if msg.truncated { // see RFC 5966
   120  			c, cerr = Dial("tcp", server)
   121  			if cerr != nil {
   122  				err = cerr
   123  				continue
   124  			}
   125  			msg, merr = exchange(cfg, c, name, qtype)
   126  			c.Close()
   127  			if merr != nil {
   128  				err = merr
   129  				continue
   130  			}
   131  		}
   132  		cname, addrs, err = answer(name, server, msg, qtype)
   133  		if err == nil || err.(*DNSError).Err == noSuchHost {
   134  			break
   135  		}
   136  	}
   137  	return
   138  }
   139  
   140  func convertRR_A(records []dnsRR) []IP {
   141  	addrs := make([]IP, len(records))
   142  	for i, rr := range records {
   143  		a := rr.(*dnsRR_A).A
   144  		addrs[i] = IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a))
   145  	}
   146  	return addrs
   147  }
   148  
   149  func convertRR_AAAA(records []dnsRR) []IP {
   150  	addrs := make([]IP, len(records))
   151  	for i, rr := range records {
   152  		a := make(IP, IPv6len)
   153  		copy(a, rr.(*dnsRR_AAAA).AAAA[:])
   154  		addrs[i] = a
   155  	}
   156  	return addrs
   157  }
   158  
   159  var cfg *dnsConfig
   160  var dnserr error
   161  
   162  func loadConfig() { cfg, dnserr = dnsReadConfig() }
   163  
   164  var onceLoadConfig sync.Once
   165  
   166  func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err error) {
   167  	if !isDomainName(name) {
   168  		return name, nil, &DNSError{Err: "invalid domain name", Name: name}
   169  	}
   170  	onceLoadConfig.Do(loadConfig)
   171  	if dnserr != nil || cfg == nil {
   172  		err = dnserr
   173  		return
   174  	}
   175  	// If name is rooted (trailing dot) or has enough dots,
   176  	// try it by itself first.
   177  	rooted := len(name) > 0 && name[len(name)-1] == '.'
   178  	if rooted || count(name, '.') >= cfg.ndots {
   179  		rname := name
   180  		if !rooted {
   181  			rname += "."
   182  		}
   183  		// Can try as ordinary name.
   184  		cname, addrs, err = tryOneName(cfg, rname, qtype)
   185  		if err == nil {
   186  			return
   187  		}
   188  	}
   189  	if rooted {
   190  		return
   191  	}
   192  
   193  	// Otherwise, try suffixes.
   194  	for i := 0; i < len(cfg.search); i++ {
   195  		rname := name + "." + cfg.search[i]
   196  		if rname[len(rname)-1] != '.' {
   197  			rname += "."
   198  		}
   199  		cname, addrs, err = tryOneName(cfg, rname, qtype)
   200  		if err == nil {
   201  			return
   202  		}
   203  	}
   204  
   205  	// Last ditch effort: try unsuffixed.
   206  	rname := name
   207  	if !rooted {
   208  		rname += "."
   209  	}
   210  	cname, addrs, err = tryOneName(cfg, rname, qtype)
   211  	if err == nil {
   212  		return
   213  	}
   214  	if e, ok := err.(*DNSError); ok {
   215  		// Show original name passed to lookup, not suffixed one.
   216  		// In general we might have tried many suffixes; showing
   217  		// just one is misleading. See also golang.org/issue/6324.
   218  		e.Name = name
   219  	}
   220  	return
   221  }
   222  
   223  // goLookupHost is the native Go implementation of LookupHost.
   224  // Used only if cgoLookupHost refuses to handle the request
   225  // (that is, only if cgoLookupHost is the stub in cgo_stub.go).
   226  // Normally we let cgo use the C library resolver instead of
   227  // depending on our lookup code, so that Go and C get the same
   228  // answers.
   229  func goLookupHost(name string) (addrs []string, err error) {
   230  	// Use entries from /etc/hosts if they match.
   231  	addrs = lookupStaticHost(name)
   232  	if len(addrs) > 0 {
   233  		return
   234  	}
   235  	onceLoadConfig.Do(loadConfig)
   236  	if dnserr != nil || cfg == nil {
   237  		err = dnserr
   238  		return
   239  	}
   240  	ips, err := goLookupIP(name)
   241  	if err != nil {
   242  		return
   243  	}
   244  	addrs = make([]string, 0, len(ips))
   245  	for _, ip := range ips {
   246  		addrs = append(addrs, ip.String())
   247  	}
   248  	return
   249  }
   250  
   251  // goLookupIP is the native Go implementation of LookupIP.
   252  // Used only if cgoLookupIP refuses to handle the request
   253  // (that is, only if cgoLookupIP is the stub in cgo_stub.go).
   254  // Normally we let cgo use the C library resolver instead of
   255  // depending on our lookup code, so that Go and C get the same
   256  // answers.
   257  func goLookupIP(name string) (addrs []IP, err error) {
   258  	// Use entries from /etc/hosts if possible.
   259  	haddrs := lookupStaticHost(name)
   260  	if len(haddrs) > 0 {
   261  		for _, haddr := range haddrs {
   262  			if ip := ParseIP(haddr); ip != nil {
   263  				addrs = append(addrs, ip)
   264  			}
   265  		}
   266  		if len(addrs) > 0 {
   267  			return
   268  		}
   269  	}
   270  	onceLoadConfig.Do(loadConfig)
   271  	if dnserr != nil || cfg == nil {
   272  		err = dnserr
   273  		return
   274  	}
   275  	var records []dnsRR
   276  	var cname string
   277  	var err4, err6 error
   278  	cname, records, err4 = lookup(name, dnsTypeA)
   279  	addrs = convertRR_A(records)
   280  	if cname != "" {
   281  		name = cname
   282  	}
   283  	_, records, err6 = lookup(name, dnsTypeAAAA)
   284  	if err4 != nil && err6 == nil {
   285  		// Ignore A error because AAAA lookup succeeded.
   286  		err4 = nil
   287  	}
   288  	if err6 != nil && len(addrs) > 0 {
   289  		// Ignore AAAA error because A lookup succeeded.
   290  		err6 = nil
   291  	}
   292  	if err4 != nil {
   293  		return nil, err4
   294  	}
   295  	if err6 != nil {
   296  		return nil, err6
   297  	}
   298  
   299  	addrs = append(addrs, convertRR_AAAA(records)...)
   300  	return addrs, nil
   301  }
   302  
   303  // goLookupCNAME is the native Go implementation of LookupCNAME.
   304  // Used only if cgoLookupCNAME refuses to handle the request
   305  // (that is, only if cgoLookupCNAME is the stub in cgo_stub.go).
   306  // Normally we let cgo use the C library resolver instead of
   307  // depending on our lookup code, so that Go and C get the same
   308  // answers.
   309  func goLookupCNAME(name string) (cname string, err error) {
   310  	onceLoadConfig.Do(loadConfig)
   311  	if dnserr != nil || cfg == nil {
   312  		err = dnserr
   313  		return
   314  	}
   315  	_, rr, err := lookup(name, dnsTypeCNAME)
   316  	if err != nil {
   317  		return
   318  	}
   319  	cname = rr[0].(*dnsRR_CNAME).Cname
   320  	return
   321  }