github.com/mh-cbon/go@v0.0.0-20160603070303-9e112a3fe4c0/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  	"context"
     9  	"internal/nettrace"
    10  	"internal/singleflight"
    11  )
    12  
    13  // protocols contains minimal mappings between internet protocol
    14  // names and numbers for platforms that don't have a complete list of
    15  // protocol numbers.
    16  //
    17  // See http://www.iana.org/assignments/protocol-numbers
    18  var protocols = map[string]int{
    19  	"icmp": 1, "ICMP": 1,
    20  	"igmp": 2, "IGMP": 2,
    21  	"tcp": 6, "TCP": 6,
    22  	"udp": 17, "UDP": 17,
    23  	"ipv6-icmp": 58, "IPV6-ICMP": 58, "IPv6-ICMP": 58,
    24  }
    25  
    26  // LookupHost looks up the given host using the local resolver.
    27  // It returns an array of that host's addresses.
    28  func LookupHost(host string) (addrs []string, err error) {
    29  	// Make sure that no matter what we do later, host=="" is rejected.
    30  	// ParseIP, for example, does accept empty strings.
    31  	if host == "" {
    32  		return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host}
    33  	}
    34  	if ip := ParseIP(host); ip != nil {
    35  		return []string{host}, nil
    36  	}
    37  	return lookupHost(context.Background(), host)
    38  }
    39  
    40  // LookupIP looks up host using the local resolver.
    41  // It returns an array of that host's IPv4 and IPv6 addresses.
    42  func LookupIP(host string) (ips []IP, err error) {
    43  	// Make sure that no matter what we do later, host=="" is rejected.
    44  	// ParseIP, for example, does accept empty strings.
    45  	if host == "" {
    46  		return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host}
    47  	}
    48  	if ip := ParseIP(host); ip != nil {
    49  		return []IP{ip}, nil
    50  	}
    51  	addrs, err := lookupIPMerge(context.Background(), host)
    52  	if err != nil {
    53  		return
    54  	}
    55  	ips = make([]IP, len(addrs))
    56  	for i, addr := range addrs {
    57  		ips[i] = addr.IP
    58  	}
    59  	return
    60  }
    61  
    62  var lookupGroup singleflight.Group
    63  
    64  // lookupIPMerge wraps lookupIP, but makes sure that for any given
    65  // host, only one lookup is in-flight at a time. The returned memory
    66  // is always owned by the caller.
    67  func lookupIPMerge(ctx context.Context, host string) (addrs []IPAddr, err error) {
    68  	addrsi, err, shared := lookupGroup.Do(host, func() (interface{}, error) {
    69  		return testHookLookupIP(ctx, lookupIP, host)
    70  	})
    71  	return lookupIPReturn(addrsi, err, shared)
    72  }
    73  
    74  // lookupIPReturn turns the return values from singleflight.Do into
    75  // the return values from LookupIP.
    76  func lookupIPReturn(addrsi interface{}, err error, shared bool) ([]IPAddr, error) {
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  	addrs := addrsi.([]IPAddr)
    81  	if shared {
    82  		clone := make([]IPAddr, len(addrs))
    83  		copy(clone, addrs)
    84  		addrs = clone
    85  	}
    86  	return addrs, nil
    87  }
    88  
    89  // ipAddrsEface returns an empty interface slice of addrs.
    90  func ipAddrsEface(addrs []IPAddr) []interface{} {
    91  	s := make([]interface{}, len(addrs))
    92  	for i, v := range addrs {
    93  		s[i] = v
    94  	}
    95  	return s
    96  }
    97  
    98  // lookupIPContext looks up a hostname with a context.
    99  //
   100  // TODO(bradfitz): rename this function. All the other
   101  // build-tag-specific lookupIP funcs also take a context now, so this
   102  // name is no longer great. Maybe make this lookupIPMerge and ditch
   103  // the other one, making its callers call this instead with a
   104  // context.Background().
   105  func lookupIPContext(ctx context.Context, host string) (addrs []IPAddr, err error) {
   106  	trace, _ := ctx.Value(nettrace.TraceKey{}).(*nettrace.Trace)
   107  	if trace != nil && trace.DNSStart != nil {
   108  		trace.DNSStart(host)
   109  	}
   110  	// The underlying resolver func is lookupIP by default but it
   111  	// can be overridden by tests. This is needed by net/http, so it
   112  	// uses a context key instead of unexported variables.
   113  	resolverFunc := lookupIP
   114  	if alt, _ := ctx.Value(nettrace.LookupIPAltResolverKey{}).(func(context.Context, string) ([]IPAddr, error)); alt != nil {
   115  		resolverFunc = alt
   116  	}
   117  
   118  	ch := lookupGroup.DoChan(host, func() (interface{}, error) {
   119  		return testHookLookupIP(ctx, resolverFunc, host)
   120  	})
   121  
   122  	select {
   123  	case <-ctx.Done():
   124  		// The DNS lookup timed out for some reason. Force
   125  		// future requests to start the DNS lookup again
   126  		// rather than waiting for the current lookup to
   127  		// complete. See issue 8602.
   128  		err := mapErr(ctx.Err())
   129  		lookupGroup.Forget(host)
   130  		if trace != nil && trace.DNSDone != nil {
   131  			trace.DNSDone(nil, false, err)
   132  		}
   133  		return nil, err
   134  	case r := <-ch:
   135  		if trace != nil && trace.DNSDone != nil {
   136  			addrs, _ := r.Val.([]IPAddr)
   137  			trace.DNSDone(ipAddrsEface(addrs), r.Shared, r.Err)
   138  		}
   139  		return lookupIPReturn(r.Val, r.Err, r.Shared)
   140  	}
   141  }
   142  
   143  // LookupPort looks up the port for the given network and service.
   144  func LookupPort(network, service string) (port int, err error) {
   145  	port, needsLookup := parsePort(service)
   146  	if needsLookup {
   147  		port, err = lookupPort(context.Background(), network, service)
   148  		if err != nil {
   149  			return 0, err
   150  		}
   151  	}
   152  	if 0 > port || port > 65535 {
   153  		return 0, &AddrError{Err: "invalid port", Addr: service}
   154  	}
   155  	return port, nil
   156  }
   157  
   158  // LookupCNAME returns the canonical DNS host for the given name.
   159  // Callers that do not care about the canonical name can call
   160  // LookupHost or LookupIP directly; both take care of resolving
   161  // the canonical name as part of the lookup.
   162  func LookupCNAME(name string) (cname string, err error) {
   163  	return lookupCNAME(context.Background(), name)
   164  }
   165  
   166  // LookupSRV tries to resolve an SRV query of the given service,
   167  // protocol, and domain name. The proto is "tcp" or "udp".
   168  // The returned records are sorted by priority and randomized
   169  // by weight within a priority.
   170  //
   171  // LookupSRV constructs the DNS name to look up following RFC 2782.
   172  // That is, it looks up _service._proto.name. To accommodate services
   173  // publishing SRV records under non-standard names, if both service
   174  // and proto are empty strings, LookupSRV looks up name directly.
   175  func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) {
   176  	return lookupSRV(context.Background(), service, proto, name)
   177  }
   178  
   179  // LookupMX returns the DNS MX records for the given domain name sorted by preference.
   180  func LookupMX(name string) (mxs []*MX, err error) {
   181  	return lookupMX(context.Background(), name)
   182  }
   183  
   184  // LookupNS returns the DNS NS records for the given domain name.
   185  func LookupNS(name string) (nss []*NS, err error) {
   186  	return lookupNS(context.Background(), name)
   187  }
   188  
   189  // LookupTXT returns the DNS TXT records for the given domain name.
   190  func LookupTXT(name string) (txts []string, err error) {
   191  	return lookupTXT(context.Background(), name)
   192  }
   193  
   194  // LookupAddr performs a reverse lookup for the given address, returning a list
   195  // of names mapping to that address.
   196  func LookupAddr(addr string) (names []string, err error) {
   197  	return lookupAddr(context.Background(), addr)
   198  }