| // 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" | |
| "sync" | |
| ) | |
| // protocols contains minimal mappings between internet protocol | |
| // names and numbers for platforms that don't have a complete list of | |
| // protocol numbers. | |
| // | |
| // See https://www.iana.org/assignments/protocol-numbers | |
| // | |
| // On Unix, this map is augmented by readProtocols via lookupProtocol. | |
| var protocols = map[string]int{ | |
| "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. | |
| // | |
| // See https://www.iana.org/assignments/service-names-port-numbers | |
| // | |
| // 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, | |
| }, | |
| } | |
| // dnsWaitGroup can be used by tests to wait for all DNS goroutines to | |
| // complete. This avoids races on the test hooks. | |
| var dnsWaitGroup sync.WaitGroup | |
| 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 | |
| } | |
| // maxPortBufSize is the longest reasonable name of a service | |
| // (non-numeric port). | |
| // Currently the longest known IANA-unregistered name is | |
| // "mobility-header", so we use that length, plus some slop in case | |
| // something longer is added in the future. | |
| const maxPortBufSize = len("mobility-header") + 10 | |
| 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 [maxPortBufSize]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} | |
| } | |
| // ipVersion returns the provided network's IP version: '4', '6' or 0 | |
| // if network does not end in a '4' or '6' byte. | |
| func ipVersion(network string) byte { | |
| if network == "" { | |
| return 0 | |
| } | |
| n := network[len(network)-1] | |
| if n != '4' && n != '6' { | |
| n = 0 | |
| } | |
| return n | |
| } | |
| // 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 | |
| // StrictErrors controls the behavior of temporary errors | |
| // (including timeout, socket errors, and SERVFAIL) when using | |
| // Go's built-in resolver. For a query composed of multiple | |
| // sub-queries (such as an A+AAAA address lookup, or walking the | |
| // DNS search list), this option causes such errors to abort the | |
| // whole query instead of returning a partial result. This is | |
| // not enabled by default because it may affect compatibility | |
| // with resolvers that process AAAA queries incorrectly. | |
| StrictErrors bool | |
| // Dial optionally specifies an alternate dialer for use by | |
| // Go's built-in DNS resolver to make TCP and UDP connections | |
| // to DNS services. The host in the address parameter will | |
| // always be a literal IP address and not a host name, and the | |
| // port in the address parameter will be a literal port number | |
| // and not a service name. | |
| // If the Conn returned is also a PacketConn, sent and received DNS | |
| // messages must adhere to RFC 1035 section 4.2.1, "UDP usage". | |
| // Otherwise, DNS messages transmitted over Conn must adhere | |
| // to RFC 7766 section 5, "Transport Protocol Selection". | |
| // If nil, the default dialer is used. | |
| Dial func(ctx context.Context, network, address string) (Conn, error) | |
| // lookupGroup merges LookupIPAddr calls together for lookups for the same | |
| // host. The lookupGroup key is the LookupIPAddr.host argument. | |
| // The return values are ([]IPAddr, error). | |
| lookupGroup singleflight.Group | |
| // TODO(bradfitz): optional interface impl override hook | |
| // TODO(bradfitz): Timeout time.Duration? | |
| } | |
| func (r *Resolver) preferGo() bool { return r != nil && r.PreferGo } | |
| func (r *Resolver) strictErrors() bool { return r != nil && r.StrictErrors } | |
| func (r *Resolver) getLookupGroup() *singleflight.Group { | |
| if r == nil { | |
| return &DefaultResolver.lookupGroup | |
| } | |
| return &r.lookupGroup | |
| } | |
| // LookupHost looks up the given host using the local resolver. | |
| // 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 == "" { | |
| return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true} | |
| } | |
| if ip, _ := parseIPZone(host); ip != nil { | |
| return []string{host}, nil | |
| } | |
| return r.lookupHost(ctx, host) | |
| } | |
| // LookupIP looks up host using the local resolver. | |
| // 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) { | |
| return r.lookupIPAddr(ctx, "ip", host) | |
| } | |
| // LookupIP looks up host for the given network using the local resolver. | |
| // It returns a slice of that host's IP addresses of the type specified by | |
| // network. | |
| // network must be one of "ip", "ip4" or "ip6". | |
| func (r *Resolver) LookupIP(ctx context.Context, network, host string) ([]IP, error) { | |
| afnet, _, err := parseNetwork(ctx, network, false) | |
| if err != nil { | |
| return nil, err | |
| } | |
| switch afnet { | |
| case "ip", "ip4", "ip6": | |
| default: | |
| return nil, UnknownNetworkError(network) | |
| } | |
| addrs, err := r.internetAddrList(ctx, afnet, host) | |
| if err != nil { | |
| return nil, err | |
| } | |
| ips := make([]IP, 0, len(addrs)) | |
| for _, addr := range addrs { | |
| ips = append(ips, addr.(*IPAddr).IP) | |
| } | |
| return ips, nil | |
| } | |
| // onlyValuesCtx is a context that uses an underlying context | |
| // for value lookup if the underlying context hasn't yet expired. | |
| type onlyValuesCtx struct { | |
| context.Context | |
| lookupValues context.Context | |
| } | |
| var _ context.Context = (*onlyValuesCtx)(nil) | |
| // Value performs a lookup if the original context hasn't expired. | |
| func (ovc *onlyValuesCtx) Value(key interface{}) interface{} { | |
| select { | |
| case <-ovc.lookupValues.Done(): | |
| return nil | |
| default: | |
| return ovc.lookupValues.Value(key) | |
| } | |
| } | |
| // withUnexpiredValuesPreserved returns a context.Context that only uses lookupCtx | |
| // for its values, otherwise it is never canceled and has no deadline. | |
| // If the lookup context expires, any looked up values will return nil. | |
| // See Issue 28600. | |
| func withUnexpiredValuesPreserved(lookupCtx context.Context) context.Context { | |
| return &onlyValuesCtx{Context: context.Background(), lookupValues: lookupCtx} | |
| } | |
| // lookupIPAddr looks up host using the local resolver and particular network. | |
| // It returns a slice of that host's IPv4 and IPv6 addresses. | |
| func (r *Resolver) lookupIPAddr(ctx context.Context, network, 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, IsNotFound: true} | |
| } | |
| if ip, zone := parseIPZone(host); ip != nil { | |
| return []IPAddr{{IP: ip, Zone: zone}}, nil | |
| } | |
| trace, _ := ctx.Value(nettrace.TraceKey{}).(*nettrace.Trace) | |
| if trace != nil && trace.DNSStart != nil { | |
| trace.DNSStart(host) | |
| } | |
| // 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, string) ([]IPAddr, error)); alt != nil { | |
| resolverFunc = alt | |
| } | |
| // We don't want a cancellation of ctx to affect the | |
| // lookupGroup operation. Otherwise if our context gets | |
| // canceled it might cause an error to be returned to a lookup | |
| // using a completely different context. However we need to preserve | |
| // only the values in context. See Issue 28600. | |
| lookupGroupCtx, lookupGroupCancel := context.WithCancel(withUnexpiredValuesPreserved(ctx)) | |
| lookupKey := network + "\000" + host | |
| dnsWaitGroup.Add(1) | |
| ch, called := r.getLookupGroup().DoChan(lookupKey, func() (interface{}, error) { | |
| defer dnsWaitGroup.Done() | |
| return testHookLookupIP(lookupGroupCtx, resolverFunc, network, host) | |
| }) | |
| if !called { | |
| dnsWaitGroup.Done() | |
| } | |
| select { | |
| case <-ctx.Done(): | |
| // Our context was canceled. If we are the only | |
| // goroutine looking up this key, then drop the key | |
| // from the lookupGroup and cancel the lookup. | |
| // If there are other goroutines looking up this key, | |
| // let the lookup continue uncanceled, and let later | |
| // lookups with the same key share the result. | |
| // See issues 8602, 20703, 22724. | |
| if r.getLookupGroup().ForgetUnshared(lookupKey) { | |
| lookupGroupCancel() | |
| } else { | |
| go func() { | |
| <-ch | |
| lookupGroupCancel() | |
| }() | |
| } | |
| err := mapErr(ctx.Err()) | |
| if trace != nil && trace.DNSDone != nil { | |
| trace.DNSDone(nil, false, err) | |
| } | |
| return nil, err | |
| case r := <-ch: | |
| lookupGroupCancel() | |
| 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) | |
| } | |
| } | |
| // lookupIPReturn turns the return values from singleflight.Do into | |
| // the return values from LookupIP. | |
| func lookupIPReturn(addrsi interface{}, err error, shared bool) ([]IPAddr, error) { | |
| if err != nil { | |
| return nil, err | |
| } | |
| addrs := addrsi.([]IPAddr) | |
| if shared { | |
| clone := make([]IPAddr, len(addrs)) | |
| copy(clone, addrs) | |
| addrs = clone | |
| } | |
| return addrs, nil | |
| } | |
| // 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) { | |
| 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 { | |
| switch network { | |
| case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6": | |
| case "": // a hint wildcard for Go 1.0 undocumented behavior | |
| network = "ip" | |
| default: | |
| return 0, &AddrError{Err: "unknown network", Addr: network} | |
| } | |
| port, err = r.lookupPort(ctx, network, service) | |
| if err != nil { | |
| return 0, err | |
| } | |
| } | |
| if 0 > port || port > 65535 { | |
| return 0, &AddrError{Err: "invalid port", Addr: service} | |
| } | |
| return port, nil | |
| } | |
| // 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. | |
| // | |
| // 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". | |
| // 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 LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) { | |
| 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 (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) ([]*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 (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 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) | |
| } |