diff options
Diffstat (limited to 'gcc-4.9/libgo/go/net/dnsclient_unix.go')
-rw-r--r-- | gcc-4.9/libgo/go/net/dnsclient_unix.go | 321 |
1 files changed, 321 insertions, 0 deletions
diff --git a/gcc-4.9/libgo/go/net/dnsclient_unix.go b/gcc-4.9/libgo/go/net/dnsclient_unix.go new file mode 100644 index 000000000..16cf420dc --- /dev/null +++ b/gcc-4.9/libgo/go/net/dnsclient_unix.go @@ -0,0 +1,321 @@ +// 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 netbsd openbsd + +// DNS client: see RFC 1035. +// Has to be linked into package net for Dial. + +// TODO(rsc): +// Check periodically whether /etc/resolv.conf has changed. +// 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" + "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 *dnsConfig +var dnserr error + +func loadConfig() { cfg, dnserr = dnsReadConfig() } + +var onceLoadConfig sync.Once + +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(loadConfig) + if dnserr != nil || cfg == nil { + err = 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.ndots { + rname := name + if !rooted { + rname += "." + } + // Can try as ordinary name. + cname, addrs, err = tryOneName(cfg, rname, qtype) + if err == nil { + return + } + } + if rooted { + return + } + + // Otherwise, try suffixes. + for i := 0; i < len(cfg.search); i++ { + rname := name + "." + cfg.search[i] + if rname[len(rname)-1] != '.' { + rname += "." + } + cname, addrs, err = tryOneName(cfg, rname, qtype) + if err == nil { + return + } + } + + // Last ditch effort: try unsuffixed. + rname := name + if !rooted { + rname += "." + } + cname, addrs, err = tryOneName(cfg, 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 + } + onceLoadConfig.Do(loadConfig) + if dnserr != nil || cfg == nil { + err = dnserr + 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 + } + } + onceLoadConfig.Do(loadConfig) + if dnserr != nil || cfg == nil { + err = dnserr + 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) { + onceLoadConfig.Do(loadConfig) + if dnserr != nil || cfg == nil { + err = dnserr + return + } + _, rr, err := lookup(name, dnsTypeCNAME) + if err != nil { + return + } + cname = rr[0].(*dnsRR_CNAME).Cname + return +} |