|  | // 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) | 
|  | } |