mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			365 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			365 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Go
		
	
	
	
// Copyright 2009 The Go Authors. All rights reserved.
 | 
						|
// Use of this source code is governed by a BSD-style
 | 
						|
// license that can be found in the LICENSE file.
 | 
						|
 | 
						|
// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
 | 
						|
 | 
						|
// DNS client: see RFC 1035.
 | 
						|
// Has to be linked into package net for Dial.
 | 
						|
 | 
						|
// TODO(rsc):
 | 
						|
//	Could potentially handle many outstanding lookups faster.
 | 
						|
//	Could have a small cache.
 | 
						|
//	Random UDP source port (net.Dial should do that for us).
 | 
						|
//	Random request IDs.
 | 
						|
 | 
						|
package net
 | 
						|
 | 
						|
import (
 | 
						|
	"io"
 | 
						|
	"math/rand"
 | 
						|
	"os"
 | 
						|
	"sync"
 | 
						|
	"time"
 | 
						|
)
 | 
						|
 | 
						|
// Send a request on the connection and hope for a reply.
 | 
						|
// Up to cfg.attempts attempts.
 | 
						|
func exchange(cfg *dnsConfig, c Conn, name string, qtype uint16) (*dnsMsg, error) {
 | 
						|
	_, useTCP := c.(*TCPConn)
 | 
						|
	if len(name) >= 256 {
 | 
						|
		return nil, &DNSError{Err: "name too long", Name: name}
 | 
						|
	}
 | 
						|
	out := new(dnsMsg)
 | 
						|
	out.id = uint16(rand.Int()) ^ uint16(time.Now().UnixNano())
 | 
						|
	out.question = []dnsQuestion{
 | 
						|
		{name, qtype, dnsClassINET},
 | 
						|
	}
 | 
						|
	out.recursion_desired = true
 | 
						|
	msg, ok := out.Pack()
 | 
						|
	if !ok {
 | 
						|
		return nil, &DNSError{Err: "internal error - cannot pack message", Name: name}
 | 
						|
	}
 | 
						|
	if useTCP {
 | 
						|
		mlen := uint16(len(msg))
 | 
						|
		msg = append([]byte{byte(mlen >> 8), byte(mlen)}, msg...)
 | 
						|
	}
 | 
						|
	for attempt := 0; attempt < cfg.attempts; attempt++ {
 | 
						|
		n, err := c.Write(msg)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
 | 
						|
		if cfg.timeout == 0 {
 | 
						|
			c.SetReadDeadline(noDeadline)
 | 
						|
		} else {
 | 
						|
			c.SetReadDeadline(time.Now().Add(time.Duration(cfg.timeout) * time.Second))
 | 
						|
		}
 | 
						|
		buf := make([]byte, 2000)
 | 
						|
		if useTCP {
 | 
						|
			n, err = io.ReadFull(c, buf[:2])
 | 
						|
			if err != nil {
 | 
						|
				if e, ok := err.(Error); ok && e.Timeout() {
 | 
						|
					continue
 | 
						|
				}
 | 
						|
			}
 | 
						|
			mlen := int(buf[0])<<8 | int(buf[1])
 | 
						|
			if mlen > len(buf) {
 | 
						|
				buf = make([]byte, mlen)
 | 
						|
			}
 | 
						|
			n, err = io.ReadFull(c, buf[:mlen])
 | 
						|
		} else {
 | 
						|
			n, err = c.Read(buf)
 | 
						|
		}
 | 
						|
		if err != nil {
 | 
						|
			if e, ok := err.(Error); ok && e.Timeout() {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		buf = buf[:n]
 | 
						|
		in := new(dnsMsg)
 | 
						|
		if !in.Unpack(buf) || in.id != out.id {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		return in, nil
 | 
						|
	}
 | 
						|
	var server string
 | 
						|
	if a := c.RemoteAddr(); a != nil {
 | 
						|
		server = a.String()
 | 
						|
	}
 | 
						|
	return nil, &DNSError{Err: "no answer from server", Name: name, Server: server, IsTimeout: true}
 | 
						|
}
 | 
						|
 | 
						|
// Do a lookup for a single name, which must be rooted
 | 
						|
// (otherwise answer will not find the answers).
 | 
						|
func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs []dnsRR, err error) {
 | 
						|
	if len(cfg.servers) == 0 {
 | 
						|
		return "", nil, &DNSError{Err: "no DNS servers", Name: name}
 | 
						|
	}
 | 
						|
	for i := 0; i < len(cfg.servers); i++ {
 | 
						|
		// Calling Dial here is scary -- we have to be sure
 | 
						|
		// not to dial a name that will require a DNS lookup,
 | 
						|
		// or Dial will call back here to translate it.
 | 
						|
		// The DNS config parser has already checked that
 | 
						|
		// all the cfg.servers[i] are IP addresses, which
 | 
						|
		// Dial will use without a DNS lookup.
 | 
						|
		server := cfg.servers[i] + ":53"
 | 
						|
		c, cerr := Dial("udp", server)
 | 
						|
		if cerr != nil {
 | 
						|
			err = cerr
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		msg, merr := exchange(cfg, c, name, qtype)
 | 
						|
		c.Close()
 | 
						|
		if merr != nil {
 | 
						|
			err = merr
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		if msg.truncated { // see RFC 5966
 | 
						|
			c, cerr = Dial("tcp", server)
 | 
						|
			if cerr != nil {
 | 
						|
				err = cerr
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			msg, merr = exchange(cfg, c, name, qtype)
 | 
						|
			c.Close()
 | 
						|
			if merr != nil {
 | 
						|
				err = merr
 | 
						|
				continue
 | 
						|
			}
 | 
						|
		}
 | 
						|
		cname, addrs, err = answer(name, server, msg, qtype)
 | 
						|
		if err == nil || err.(*DNSError).Err == noSuchHost {
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
func convertRR_A(records []dnsRR) []IP {
 | 
						|
	addrs := make([]IP, len(records))
 | 
						|
	for i, rr := range records {
 | 
						|
		a := rr.(*dnsRR_A).A
 | 
						|
		addrs[i] = IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a))
 | 
						|
	}
 | 
						|
	return addrs
 | 
						|
}
 | 
						|
 | 
						|
func convertRR_AAAA(records []dnsRR) []IP {
 | 
						|
	addrs := make([]IP, len(records))
 | 
						|
	for i, rr := range records {
 | 
						|
		a := make(IP, IPv6len)
 | 
						|
		copy(a, rr.(*dnsRR_AAAA).AAAA[:])
 | 
						|
		addrs[i] = a
 | 
						|
	}
 | 
						|
	return addrs
 | 
						|
}
 | 
						|
 | 
						|
var cfg struct {
 | 
						|
	ch        chan struct{}
 | 
						|
	mu        sync.RWMutex // protects dnsConfig and dnserr
 | 
						|
	dnsConfig *dnsConfig
 | 
						|
	dnserr    error
 | 
						|
}
 | 
						|
var onceLoadConfig sync.Once
 | 
						|
 | 
						|
// Assume dns config file is /etc/resolv.conf here
 | 
						|
func loadDefaultConfig() {
 | 
						|
	loadConfig("/etc/resolv.conf", 5*time.Second, nil)
 | 
						|
}
 | 
						|
 | 
						|
func loadConfig(resolvConfPath string, reloadTime time.Duration, quit <-chan chan struct{}) {
 | 
						|
	var mtime time.Time
 | 
						|
	cfg.ch = make(chan struct{}, 1)
 | 
						|
	if fi, err := os.Stat(resolvConfPath); err != nil {
 | 
						|
		cfg.dnserr = err
 | 
						|
	} else {
 | 
						|
		mtime = fi.ModTime()
 | 
						|
		cfg.dnsConfig, cfg.dnserr = dnsReadConfig(resolvConfPath)
 | 
						|
	}
 | 
						|
	go func() {
 | 
						|
		for {
 | 
						|
			time.Sleep(reloadTime)
 | 
						|
			select {
 | 
						|
			case qresp := <-quit:
 | 
						|
				qresp <- struct{}{}
 | 
						|
				return
 | 
						|
			case <-cfg.ch:
 | 
						|
			}
 | 
						|
 | 
						|
			// In case of error, we keep the previous config
 | 
						|
			fi, err := os.Stat(resolvConfPath)
 | 
						|
			if err != nil {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			// If the resolv.conf mtime didn't change, do not reload
 | 
						|
			m := fi.ModTime()
 | 
						|
			if m.Equal(mtime) {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			mtime = m
 | 
						|
			// In case of error, we keep the previous config
 | 
						|
			ncfg, err := dnsReadConfig(resolvConfPath)
 | 
						|
			if err != nil || len(ncfg.servers) == 0 {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			cfg.mu.Lock()
 | 
						|
			cfg.dnsConfig = ncfg
 | 
						|
			cfg.dnserr = nil
 | 
						|
			cfg.mu.Unlock()
 | 
						|
		}
 | 
						|
	}()
 | 
						|
}
 | 
						|
 | 
						|
func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err error) {
 | 
						|
	if !isDomainName(name) {
 | 
						|
		return name, nil, &DNSError{Err: "invalid domain name", Name: name}
 | 
						|
	}
 | 
						|
	onceLoadConfig.Do(loadDefaultConfig)
 | 
						|
 | 
						|
	select {
 | 
						|
	case cfg.ch <- struct{}{}:
 | 
						|
	default:
 | 
						|
	}
 | 
						|
 | 
						|
	cfg.mu.RLock()
 | 
						|
	defer cfg.mu.RUnlock()
 | 
						|
 | 
						|
	if cfg.dnserr != nil || cfg.dnsConfig == nil {
 | 
						|
		err = cfg.dnserr
 | 
						|
		return
 | 
						|
	}
 | 
						|
	// If name is rooted (trailing dot) or has enough dots,
 | 
						|
	// try it by itself first.
 | 
						|
	rooted := len(name) > 0 && name[len(name)-1] == '.'
 | 
						|
	if rooted || count(name, '.') >= cfg.dnsConfig.ndots {
 | 
						|
		rname := name
 | 
						|
		if !rooted {
 | 
						|
			rname += "."
 | 
						|
		}
 | 
						|
		// Can try as ordinary name.
 | 
						|
		cname, addrs, err = tryOneName(cfg.dnsConfig, rname, qtype)
 | 
						|
		if err == nil {
 | 
						|
			return
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if rooted {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	// Otherwise, try suffixes.
 | 
						|
	for i := 0; i < len(cfg.dnsConfig.search); i++ {
 | 
						|
		rname := name + "." + cfg.dnsConfig.search[i]
 | 
						|
		if rname[len(rname)-1] != '.' {
 | 
						|
			rname += "."
 | 
						|
		}
 | 
						|
		cname, addrs, err = tryOneName(cfg.dnsConfig, rname, qtype)
 | 
						|
		if err == nil {
 | 
						|
			return
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// Last ditch effort: try unsuffixed.
 | 
						|
	rname := name
 | 
						|
	if !rooted {
 | 
						|
		rname += "."
 | 
						|
	}
 | 
						|
	cname, addrs, err = tryOneName(cfg.dnsConfig, rname, qtype)
 | 
						|
	if err == nil {
 | 
						|
		return
 | 
						|
	}
 | 
						|
	if e, ok := err.(*DNSError); ok {
 | 
						|
		// Show original name passed to lookup, not suffixed one.
 | 
						|
		// In general we might have tried many suffixes; showing
 | 
						|
		// just one is misleading. See also golang.org/issue/6324.
 | 
						|
		e.Name = name
 | 
						|
	}
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
// goLookupHost is the native Go implementation of LookupHost.
 | 
						|
// Used only if cgoLookupHost refuses to handle the request
 | 
						|
// (that is, only if cgoLookupHost is the stub in cgo_stub.go).
 | 
						|
// Normally we let cgo use the C library resolver instead of
 | 
						|
// depending on our lookup code, so that Go and C get the same
 | 
						|
// answers.
 | 
						|
func goLookupHost(name string) (addrs []string, err error) {
 | 
						|
	// Use entries from /etc/hosts if they match.
 | 
						|
	addrs = lookupStaticHost(name)
 | 
						|
	if len(addrs) > 0 {
 | 
						|
		return
 | 
						|
	}
 | 
						|
	ips, err := goLookupIP(name)
 | 
						|
	if err != nil {
 | 
						|
		return
 | 
						|
	}
 | 
						|
	addrs = make([]string, 0, len(ips))
 | 
						|
	for _, ip := range ips {
 | 
						|
		addrs = append(addrs, ip.String())
 | 
						|
	}
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
// goLookupIP is the native Go implementation of LookupIP.
 | 
						|
// Used only if cgoLookupIP refuses to handle the request
 | 
						|
// (that is, only if cgoLookupIP is the stub in cgo_stub.go).
 | 
						|
// Normally we let cgo use the C library resolver instead of
 | 
						|
// depending on our lookup code, so that Go and C get the same
 | 
						|
// answers.
 | 
						|
func goLookupIP(name string) (addrs []IP, err error) {
 | 
						|
	// Use entries from /etc/hosts if possible.
 | 
						|
	haddrs := lookupStaticHost(name)
 | 
						|
	if len(haddrs) > 0 {
 | 
						|
		for _, haddr := range haddrs {
 | 
						|
			if ip := ParseIP(haddr); ip != nil {
 | 
						|
				addrs = append(addrs, ip)
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if len(addrs) > 0 {
 | 
						|
			return
 | 
						|
		}
 | 
						|
	}
 | 
						|
	var records []dnsRR
 | 
						|
	var cname string
 | 
						|
	var err4, err6 error
 | 
						|
	cname, records, err4 = lookup(name, dnsTypeA)
 | 
						|
	addrs = convertRR_A(records)
 | 
						|
	if cname != "" {
 | 
						|
		name = cname
 | 
						|
	}
 | 
						|
	_, records, err6 = lookup(name, dnsTypeAAAA)
 | 
						|
	if err4 != nil && err6 == nil {
 | 
						|
		// Ignore A error because AAAA lookup succeeded.
 | 
						|
		err4 = nil
 | 
						|
	}
 | 
						|
	if err6 != nil && len(addrs) > 0 {
 | 
						|
		// Ignore AAAA error because A lookup succeeded.
 | 
						|
		err6 = nil
 | 
						|
	}
 | 
						|
	if err4 != nil {
 | 
						|
		return nil, err4
 | 
						|
	}
 | 
						|
	if err6 != nil {
 | 
						|
		return nil, err6
 | 
						|
	}
 | 
						|
 | 
						|
	addrs = append(addrs, convertRR_AAAA(records)...)
 | 
						|
	return addrs, nil
 | 
						|
}
 | 
						|
 | 
						|
// goLookupCNAME is the native Go implementation of LookupCNAME.
 | 
						|
// Used only if cgoLookupCNAME refuses to handle the request
 | 
						|
// (that is, only if cgoLookupCNAME is the stub in cgo_stub.go).
 | 
						|
// Normally we let cgo use the C library resolver instead of
 | 
						|
// depending on our lookup code, so that Go and C get the same
 | 
						|
// answers.
 | 
						|
func goLookupCNAME(name string) (cname string, err error) {
 | 
						|
	_, rr, err := lookup(name, dnsTypeCNAME)
 | 
						|
	if err != nil {
 | 
						|
		return
 | 
						|
	}
 | 
						|
	cname = rr[0].(*dnsRR_CNAME).Cname
 | 
						|
	return
 | 
						|
}
 |