diff options
Diffstat (limited to 'src/pkg/net/dnsclient_unix.go')
-rw-r--r-- | src/pkg/net/dnsclient_unix.go | 364 |
1 files changed, 0 insertions, 364 deletions
diff --git a/src/pkg/net/dnsclient_unix.go b/src/pkg/net/dnsclient_unix.go deleted file mode 100644 index 3713efd0e..000000000 --- a/src/pkg/net/dnsclient_unix.go +++ /dev/null @@ -1,364 +0,0 @@ -// 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 -} |