|  | // 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 ( | 
|  | "internal/singleflight" | 
|  | "time" | 
|  | ) | 
|  |  | 
|  | // 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 | 
|  | 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, | 
|  | } | 
|  |  | 
|  | // LookupHost looks up the given host using the local resolver. | 
|  | // It returns an array of that host's addresses. | 
|  | func LookupHost(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 lookupHost(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) { | 
|  | // 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 | 
|  | } | 
|  | addrs, err := lookupIPMerge(host) | 
|  | if err != nil { | 
|  | return | 
|  | } | 
|  | ips = make([]IP, len(addrs)) | 
|  | for i, addr := range addrs { | 
|  | ips[i] = addr.IP | 
|  | } | 
|  | 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) | 
|  | }) | 
|  | return lookupIPReturn(addrsi, err, 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 | 
|  | } | 
|  |  | 
|  | // 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) | 
|  | } | 
|  | } | 
|  |  | 
|  | // 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) | 
|  | 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 lookupCNAME(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 lookupSRV(service, proto, 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) | 
|  | } | 
|  |  | 
|  | // LookupNS returns the DNS NS records for the given domain name. | 
|  | func LookupNS(name string) (nss []*NS, err error) { | 
|  | return lookupNS(name) | 
|  | } | 
|  |  | 
|  | // LookupTXT returns the DNS TXT records for the given domain name. | 
|  | func LookupTXT(name string) (txts []string, err error) { | 
|  | return lookupTXT(name) | 
|  | } | 
|  |  | 
|  | // LookupAddr performs a reverse lookup for the given address, returning a list | 
|  | // of names mapping to that address. | 
|  | func LookupAddr(addr string) (names []string, err error) { | 
|  | return lookupAddr(addr) | 
|  | } |