github.com/sean-/go@v0.0.0-20151219100004-97f854cd7bb6/src/net/lookup.go (about)

     1  // Copyright 2012 The Go Authors.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package net
     6  
     7  import (
     8  	"internal/singleflight"
     9  	"time"
    10  )
    11  
    12  // protocols contains minimal mappings between internet protocol
    13  // names and numbers for platforms that don't have a complete list of
    14  // protocol numbers.
    15  //
    16  // See http://www.iana.org/assignments/protocol-numbers
    17  var protocols = map[string]int{
    18  	"icmp": 1, "ICMP": 1,
    19  	"igmp": 2, "IGMP": 2,
    20  	"tcp": 6, "TCP": 6,
    21  	"udp": 17, "UDP": 17,
    22  	"ipv6-icmp": 58, "IPV6-ICMP": 58, "IPv6-ICMP": 58,
    23  }
    24  
    25  // LookupHost looks up the given host using the local resolver.
    26  // It returns an array of that host's addresses.
    27  func LookupHost(host string) (addrs []string, err error) {
    28  	// Make sure that no matter what we do later, host=="" is rejected.
    29  	// ParseIP, for example, does accept empty strings.
    30  	if host == "" {
    31  		return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host}
    32  	}
    33  	if ip := ParseIP(host); ip != nil {
    34  		return []string{host}, nil
    35  	}
    36  	return lookupHost(host)
    37  }
    38  
    39  // LookupIP looks up host using the local resolver.
    40  // It returns an array of that host's IPv4 and IPv6 addresses.
    41  func LookupIP(host string) (ips []IP, err error) {
    42  	// Make sure that no matter what we do later, host=="" is rejected.
    43  	// ParseIP, for example, does accept empty strings.
    44  	if host == "" {
    45  		return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host}
    46  	}
    47  	if ip := ParseIP(host); ip != nil {
    48  		return []IP{ip}, nil
    49  	}
    50  	addrs, err := lookupIPMerge(host)
    51  	if err != nil {
    52  		return
    53  	}
    54  	ips = make([]IP, len(addrs))
    55  	for i, addr := range addrs {
    56  		ips[i] = addr.IP
    57  	}
    58  	return
    59  }
    60  
    61  var lookupGroup singleflight.Group
    62  
    63  // lookupIPMerge wraps lookupIP, but makes sure that for any given
    64  // host, only one lookup is in-flight at a time. The returned memory
    65  // is always owned by the caller.
    66  func lookupIPMerge(host string) (addrs []IPAddr, err error) {
    67  	addrsi, err, shared := lookupGroup.Do(host, func() (interface{}, error) {
    68  		return testHookLookupIP(lookupIP, host)
    69  	})
    70  	return lookupIPReturn(addrsi, err, shared)
    71  }
    72  
    73  // lookupIPReturn turns the return values from singleflight.Do into
    74  // the return values from LookupIP.
    75  func lookupIPReturn(addrsi interface{}, err error, shared bool) ([]IPAddr, error) {
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  	addrs := addrsi.([]IPAddr)
    80  	if shared {
    81  		clone := make([]IPAddr, len(addrs))
    82  		copy(clone, addrs)
    83  		addrs = clone
    84  	}
    85  	return addrs, nil
    86  }
    87  
    88  // lookupIPDeadline looks up a hostname with a deadline.
    89  func lookupIPDeadline(host string, deadline time.Time) (addrs []IPAddr, err error) {
    90  	if deadline.IsZero() {
    91  		return lookupIPMerge(host)
    92  	}
    93  
    94  	// We could push the deadline down into the name resolution
    95  	// functions.  However, the most commonly used implementation
    96  	// calls getaddrinfo, which has no timeout.
    97  
    98  	timeout := deadline.Sub(time.Now())
    99  	if timeout <= 0 {
   100  		return nil, errTimeout
   101  	}
   102  	t := time.NewTimer(timeout)
   103  	defer t.Stop()
   104  
   105  	ch := lookupGroup.DoChan(host, func() (interface{}, error) {
   106  		return testHookLookupIP(lookupIP, host)
   107  	})
   108  
   109  	select {
   110  	case <-t.C:
   111  		// The DNS lookup timed out for some reason.  Force
   112  		// future requests to start the DNS lookup again
   113  		// rather than waiting for the current lookup to
   114  		// complete.  See issue 8602.
   115  		lookupGroup.Forget(host)
   116  
   117  		return nil, errTimeout
   118  
   119  	case r := <-ch:
   120  		return lookupIPReturn(r.Val, r.Err, r.Shared)
   121  	}
   122  }
   123  
   124  // LookupPort looks up the port for the given network and service.
   125  func LookupPort(network, service string) (port int, err error) {
   126  	if service == "" {
   127  		// Lock in the legacy behavior that an empty string
   128  		// means port 0. See Issue 13610.
   129  		return 0, nil
   130  	}
   131  	port, _, ok := dtoi(service, 0)
   132  	if !ok && port != big && port != -big {
   133  		port, err = lookupPort(network, service)
   134  		if err != nil {
   135  			return 0, err
   136  		}
   137  	}
   138  	if 0 > port || port > 65535 {
   139  		return 0, &AddrError{Err: "invalid port", Addr: service}
   140  	}
   141  	return port, nil
   142  }
   143  
   144  // LookupCNAME returns the canonical DNS host for the given name.
   145  // Callers that do not care about the canonical name can call
   146  // LookupHost or LookupIP directly; both take care of resolving
   147  // the canonical name as part of the lookup.
   148  func LookupCNAME(name string) (cname string, err error) {
   149  	return lookupCNAME(name)
   150  }
   151  
   152  // LookupSRV tries to resolve an SRV query of the given service,
   153  // protocol, and domain name.  The proto is "tcp" or "udp".
   154  // The returned records are sorted by priority and randomized
   155  // by weight within a priority.
   156  //
   157  // LookupSRV constructs the DNS name to look up following RFC 2782.
   158  // That is, it looks up _service._proto.name.  To accommodate services
   159  // publishing SRV records under non-standard names, if both service
   160  // and proto are empty strings, LookupSRV looks up name directly.
   161  func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) {
   162  	return lookupSRV(service, proto, name)
   163  }
   164  
   165  // LookupMX returns the DNS MX records for the given domain name sorted by preference.
   166  func LookupMX(name string) (mxs []*MX, err error) {
   167  	return lookupMX(name)
   168  }
   169  
   170  // LookupNS returns the DNS NS records for the given domain name.
   171  func LookupNS(name string) (nss []*NS, err error) {
   172  	return lookupNS(name)
   173  }
   174  
   175  // LookupTXT returns the DNS TXT records for the given domain name.
   176  func LookupTXT(name string) (txts []string, err error) {
   177  	return lookupTXT(name)
   178  }
   179  
   180  // LookupAddr performs a reverse lookup for the given address, returning a list
   181  // of names mapping to that address.
   182  func LookupAddr(addr string) (names []string, err error) {
   183  	return lookupAddr(addr)
   184  }