| // 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" | |
| ) | |
| // protocols contains minimal mappings between internet protocol | |
| // names and numbers for platforms that don't have a complete list of | |
| // 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, | |
| "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 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} | |
| } | |
| if ip := ParseIP(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) { | |
| // 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 []IPAddr{{IP: ip}}, 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) ([]IPAddr, error)); alt != nil { | |
| resolverFunc = alt | |
| } | |
| ch := lookupGroup.DoChan(host, func() (interface{}, error) { | |
| return testHookLookupIP(ctx, resolverFunc, host) | |
| }) | |
| 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) { | |
| 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 { | |
| 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 DNS host for the given name. | |
| // 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 DefaultResolver.lookupCNAME(context.Background(), name) | |
| } | |
| // LookupCNAME returns the canonical DNS host for the given name. | |
| // 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 (r *Resolver) LookupCNAME(ctx context.Context, name string) (cname string, err error) { | |
| return r.lookupCNAME(ctx, 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 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) | |
| } |