diff options
Diffstat (limited to 'libgo/go/net/lookup.go')
-rw-r--r-- | libgo/go/net/lookup.go | 315 |
1 files changed, 232 insertions, 83 deletions
diff --git a/libgo/go/net/lookup.go b/libgo/go/net/lookup.go index 7aa111ba92..cc2013e432 100644 --- a/libgo/go/net/lookup.go +++ b/libgo/go/net/lookup.go @@ -1,12 +1,13 @@ -// Copyright 2012 The Go Authors. All rights reserved. +// Copyright 2012 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. package net import ( + "context" + "internal/nettrace" "internal/singleflight" - "time" ) // protocols contains minimal mappings between internet protocol @@ -14,17 +15,101 @@ import ( // protocol numbers. // // See http://www.iana.org/assignments/protocol-numbers +// +// On Unix, this map is augmented by readProtocols via lookupProtocol. var protocols = map[string]int{ - "icmp": 1, "ICMP": 1, - "igmp": 2, "IGMP": 2, - "tcp": 6, "TCP": 6, - "udp": 17, "UDP": 17, - "ipv6-icmp": 58, "IPV6-ICMP": 58, "IPv6-ICMP": 58, + "icmp": 1, + "igmp": 2, + "tcp": 6, + "udp": 17, + "ipv6-icmp": 58, +} + +// services contains minimal mappings between services names and port +// numbers for platforms that don't have a complete list of port numbers +// (some Solaris distros, nacl, etc). +// On Unix, this map is augmented by readServices via goLookupPort. +var services = map[string]map[string]int{ + "udp": { + "domain": 53, + }, + "tcp": { + "ftp": 21, + "ftps": 990, + "gopher": 70, // ʕ◔ϖ◔ʔ + "http": 80, + "https": 443, + "imap2": 143, + "imap3": 220, + "imaps": 993, + "pop3": 110, + "pop3s": 995, + "smtp": 25, + "ssh": 22, + "telnet": 23, + }, +} + +const maxProtoLength = len("RSVP-E2E-IGNORE") + 10 // with room to grow + +func lookupProtocolMap(name string) (int, error) { + var lowerProtocol [maxProtoLength]byte + n := copy(lowerProtocol[:], name) + lowerASCIIBytes(lowerProtocol[:n]) + proto, found := protocols[string(lowerProtocol[:n])] + if !found || n != len(name) { + return 0, &AddrError{Err: "unknown IP protocol specified", Addr: name} + } + return proto, nil +} + +const maxServiceLength = len("mobility-header") + 10 // with room to grow + +func lookupPortMap(network, service string) (port int, error error) { + switch network { + case "tcp4", "tcp6": + network = "tcp" + case "udp4", "udp6": + network = "udp" + } + + if m, ok := services[network]; ok { + var lowerService [maxServiceLength]byte + n := copy(lowerService[:], service) + lowerASCIIBytes(lowerService[:n]) + if port, ok := m[string(lowerService[:n])]; ok && n == len(service) { + return port, nil + } + } + return 0, &AddrError{Err: "unknown port", Addr: network + "/" + service} +} + +// DefaultResolver is the resolver used by the package-level Lookup +// functions and by Dialers without a specified Resolver. +var DefaultResolver = &Resolver{} + +// A Resolver looks up names and numbers. +// +// A nil *Resolver is equivalent to a zero Resolver. +type Resolver struct { + // PreferGo controls whether Go's built-in DNS resolver is preferred + // on platforms where it's available. It is equivalent to setting + // GODEBUG=netdns=go, but scoped to just this resolver. + PreferGo bool + + // TODO(bradfitz): optional interface impl override hook + // TODO(bradfitz): Timeout time.Duration? } // LookupHost looks up the given host using the local resolver. -// It returns an array of that host's addresses. +// It returns a slice of that host's addresses. func LookupHost(host string) (addrs []string, err error) { + return DefaultResolver.LookupHost(context.Background(), host) +} + +// LookupHost looks up the given host using the local resolver. +// It returns a slice of that host's addresses. +func (r *Resolver) LookupHost(ctx context.Context, host string) (addrs []string, err error) { // Make sure that no matter what we do later, host=="" is rejected. // ParseIP, for example, does accept empty strings. if host == "" { @@ -33,43 +118,77 @@ func LookupHost(host string) (addrs []string, err error) { if ip := ParseIP(host); ip != nil { return []string{host}, nil } - return lookupHost(host) + return r.lookupHost(ctx, host) } // LookupIP looks up host using the local resolver. -// It returns an array of that host's IPv4 and IPv6 addresses. -func LookupIP(host string) (ips []IP, err error) { +// It returns a slice of that host's IPv4 and IPv6 addresses. +func LookupIP(host string) ([]IP, error) { + addrs, err := DefaultResolver.LookupIPAddr(context.Background(), host) + if err != nil { + return nil, err + } + ips := make([]IP, len(addrs)) + for i, ia := range addrs { + ips[i] = ia.IP + } + return ips, nil +} + +// LookupIPAddr looks up host using the local resolver. +// It returns a slice of that host's IPv4 and IPv6 addresses. +func (r *Resolver) LookupIPAddr(ctx context.Context, host string) ([]IPAddr, error) { // Make sure that no matter what we do later, host=="" is rejected. // ParseIP, for example, does accept empty strings. if host == "" { return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host} } if ip := ParseIP(host); ip != nil { - return []IP{ip}, nil + return []IPAddr{{IP: ip}}, nil } - addrs, err := lookupIPMerge(host) - if err != nil { - return + trace, _ := ctx.Value(nettrace.TraceKey{}).(*nettrace.Trace) + if trace != nil && trace.DNSStart != nil { + trace.DNSStart(host) } - ips = make([]IP, len(addrs)) - for i, addr := range addrs { - ips[i] = addr.IP + // The underlying resolver func is lookupIP by default but it + // can be overridden by tests. This is needed by net/http, so it + // uses a context key instead of unexported variables. + resolverFunc := r.lookupIP + if alt, _ := ctx.Value(nettrace.LookupIPAltResolverKey{}).(func(context.Context, string) ([]IPAddr, error)); alt != nil { + resolverFunc = alt } - return -} -var lookupGroup singleflight.Group - -// lookupIPMerge wraps lookupIP, but makes sure that for any given -// host, only one lookup is in-flight at a time. The returned memory -// is always owned by the caller. -func lookupIPMerge(host string) (addrs []IPAddr, err error) { - addrsi, err, shared := lookupGroup.Do(host, func() (interface{}, error) { - return testHookLookupIP(lookupIP, host) + ch := lookupGroup.DoChan(host, func() (interface{}, error) { + return testHookLookupIP(ctx, resolverFunc, host) }) - return lookupIPReturn(addrsi, err, shared) + + select { + case <-ctx.Done(): + // The DNS lookup timed out for some reason. Force + // future requests to start the DNS lookup again + // rather than waiting for the current lookup to + // complete. See issue 8602. + err := mapErr(ctx.Err()) + lookupGroup.Forget(host) + if trace != nil && trace.DNSDone != nil { + trace.DNSDone(nil, false, err) + } + return nil, err + case r := <-ch: + if trace != nil && trace.DNSDone != nil { + addrs, _ := r.Val.([]IPAddr) + trace.DNSDone(ipAddrsEface(addrs), r.Shared, r.Err) + } + return lookupIPReturn(r.Val, r.Err, r.Shared) + } } +// lookupGroup merges LookupIPAddr calls together for lookups +// for the same host. The lookupGroup key is is the LookupIPAddr.host +// argument. +// The return values are ([]IPAddr, error). +var lookupGroup singleflight.Group + // lookupIPReturn turns the return values from singleflight.Do into // the return values from LookupIP. func lookupIPReturn(addrsi interface{}, err error, shared bool) ([]IPAddr, error) { @@ -85,52 +204,25 @@ func lookupIPReturn(addrsi interface{}, err error, shared bool) ([]IPAddr, error return addrs, nil } -// lookupIPDeadline looks up a hostname with a deadline. -func lookupIPDeadline(host string, deadline time.Time) (addrs []IPAddr, err error) { - if deadline.IsZero() { - return lookupIPMerge(host) - } - - // We could push the deadline down into the name resolution - // functions. However, the most commonly used implementation - // calls getaddrinfo, which has no timeout. - - timeout := deadline.Sub(time.Now()) - if timeout <= 0 { - return nil, errTimeout - } - t := time.NewTimer(timeout) - defer t.Stop() - - ch := lookupGroup.DoChan(host, func() (interface{}, error) { - return testHookLookupIP(lookupIP, host) - }) - - select { - case <-t.C: - // The DNS lookup timed out for some reason. Force - // future requests to start the DNS lookup again - // rather than waiting for the current lookup to - // complete. See issue 8602. - lookupGroup.Forget(host) - - return nil, errTimeout - - case r := <-ch: - return lookupIPReturn(r.Val, r.Err, r.Shared) +// ipAddrsEface returns an empty interface slice of addrs. +func ipAddrsEface(addrs []IPAddr) []interface{} { + s := make([]interface{}, len(addrs)) + for i, v := range addrs { + s[i] = v } + return s } // LookupPort looks up the port for the given network and service. func LookupPort(network, service string) (port int, err error) { - if service == "" { - // Lock in the legacy behavior that an empty string - // means port 0. See Issue 13610. - return 0, nil - } - port, _, ok := dtoi(service, 0) - if !ok && port != big && port != -big { - port, err = lookupPort(network, service) + return DefaultResolver.LookupPort(context.Background(), network, service) +} + +// LookupPort looks up the port for the given network and service. +func (r *Resolver) LookupPort(ctx context.Context, network, service string) (port int, err error) { + port, needsLookup := parsePort(service) + if needsLookup { + port, err = r.lookupPort(ctx, network, service) if err != nil { return 0, err } @@ -141,44 +233,101 @@ func LookupPort(network, service string) (port int, err error) { return port, nil } -// LookupCNAME returns the canonical DNS host for the given name. +// LookupCNAME returns the canonical name for the given host. +// Callers that do not care about the canonical name can call +// LookupHost or LookupIP directly; both take care of resolving +// the canonical name as part of the lookup. +// +// A canonical name is the final name after following zero +// or more CNAME records. +// LookupCNAME does not return an error if host does not +// contain DNS "CNAME" records, as long as host resolves to +// address records. +func LookupCNAME(host string) (cname string, err error) { + return DefaultResolver.lookupCNAME(context.Background(), host) +} + +// LookupCNAME returns the canonical name for the given host. // Callers that do not care about the canonical name can call // LookupHost or LookupIP directly; both take care of resolving // the canonical name as part of the lookup. -func LookupCNAME(name string) (cname string, err error) { - return lookupCNAME(name) +// +// A canonical name is the final name after following zero +// or more CNAME records. +// LookupCNAME does not return an error if host does not +// contain DNS "CNAME" records, as long as host resolves to +// address records. +func (r *Resolver) LookupCNAME(ctx context.Context, host string) (cname string, err error) { + return r.lookupCNAME(ctx, host) } // LookupSRV tries to resolve an SRV query of the given service, -// protocol, and domain name. The proto is "tcp" or "udp". +// protocol, and domain name. The proto is "tcp" or "udp". // The returned records are sorted by priority and randomized // by weight within a priority. // // LookupSRV constructs the DNS name to look up following RFC 2782. -// That is, it looks up _service._proto.name. To accommodate services +// That is, it looks up _service._proto.name. To accommodate services // publishing SRV records under non-standard names, if both service // and proto are empty strings, LookupSRV looks up name directly. func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) { - return lookupSRV(service, proto, name) + return DefaultResolver.lookupSRV(context.Background(), service, proto, name) +} + +// LookupSRV tries to resolve an SRV query of the given service, +// protocol, and domain name. The proto is "tcp" or "udp". +// The returned records are sorted by priority and randomized +// by weight within a priority. +// +// LookupSRV constructs the DNS name to look up following RFC 2782. +// That is, it looks up _service._proto.name. To accommodate services +// publishing SRV records under non-standard names, if both service +// and proto are empty strings, LookupSRV looks up name directly. +func (r *Resolver) LookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*SRV, err error) { + return r.lookupSRV(ctx, service, proto, name) +} + +// LookupMX returns the DNS MX records for the given domain name sorted by preference. +func LookupMX(name string) ([]*MX, error) { + return DefaultResolver.lookupMX(context.Background(), name) } // LookupMX returns the DNS MX records for the given domain name sorted by preference. -func LookupMX(name string) (mxs []*MX, err error) { - return lookupMX(name) +func (r *Resolver) LookupMX(ctx context.Context, name string) ([]*MX, error) { + return r.lookupMX(ctx, name) } // LookupNS returns the DNS NS records for the given domain name. -func LookupNS(name string) (nss []*NS, err error) { - return lookupNS(name) +func LookupNS(name string) ([]*NS, error) { + return DefaultResolver.lookupNS(context.Background(), name) +} + +// LookupNS returns the DNS NS records for the given domain name. +func (r *Resolver) LookupNS(ctx context.Context, name string) ([]*NS, error) { + return r.lookupNS(ctx, name) +} + +// LookupTXT returns the DNS TXT records for the given domain name. +func LookupTXT(name string) ([]string, error) { + return DefaultResolver.lookupTXT(context.Background(), name) } // LookupTXT returns the DNS TXT records for the given domain name. -func LookupTXT(name string) (txts []string, err error) { - return lookupTXT(name) +func (r *Resolver) LookupTXT(ctx context.Context, name string) ([]string, error) { + return r.lookupTXT(ctx, name) } // LookupAddr performs a reverse lookup for the given address, returning a list // of names mapping to that address. +// +// When using the host C library resolver, at most one result will be +// returned. To bypass the host resolver, use a custom Resolver. func LookupAddr(addr string) (names []string, err error) { - return lookupAddr(addr) + return DefaultResolver.lookupAddr(context.Background(), addr) +} + +// LookupAddr performs a reverse lookup for the given address, returning a list +// of names mapping to that address. +func (r *Resolver) LookupAddr(ctx context.Context, addr string) (names []string, err error) { + return r.lookupAddr(ctx, addr) } |