github.com/tidwall/go@v0.0.0-20170415222209-6694a6888b7d/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  //
    19  // On Unix, this map is augmented by readProtocols via lookupProtocol.
    20  var protocols = map[string]int{
    21  	"icmp":      1,
    22  	"igmp":      2,
    23  	"tcp":       6,
    24  	"udp":       17,
    25  	"ipv6-icmp": 58,
    26  }
    27  
    28  // services contains minimal mappings between services names and port
    29  // numbers for platforms that don't have a complete list of port numbers
    30  // (some Solaris distros, nacl, etc).
    31  // On Unix, this map is augmented by readServices via goLookupPort.
    32  var services = map[string]map[string]int{
    33  	"udp": {
    34  		"domain": 53,
    35  	},
    36  	"tcp": {
    37  		"ftp":    21,
    38  		"ftps":   990,
    39  		"gopher": 70, // ʕ◔ϖ◔ʔ
    40  		"http":   80,
    41  		"https":  443,
    42  		"imap2":  143,
    43  		"imap3":  220,
    44  		"imaps":  993,
    45  		"pop3":   110,
    46  		"pop3s":  995,
    47  		"smtp":   25,
    48  		"ssh":    22,
    49  		"telnet": 23,
    50  	},
    51  }
    52  
    53  const maxProtoLength = len("RSVP-E2E-IGNORE") + 10 // with room to grow
    54  
    55  func lookupProtocolMap(name string) (int, error) {
    56  	var lowerProtocol [maxProtoLength]byte
    57  	n := copy(lowerProtocol[:], name)
    58  	lowerASCIIBytes(lowerProtocol[:n])
    59  	proto, found := protocols[string(lowerProtocol[:n])]
    60  	if !found || n != len(name) {
    61  		return 0, &AddrError{Err: "unknown IP protocol specified", Addr: name}
    62  	}
    63  	return proto, nil
    64  }
    65  
    66  const maxServiceLength = len("mobility-header") + 10 // with room to grow
    67  
    68  func lookupPortMap(network, service string) (port int, error error) {
    69  	switch network {
    70  	case "tcp4", "tcp6":
    71  		network = "tcp"
    72  	case "udp4", "udp6":
    73  		network = "udp"
    74  	}
    75  
    76  	if m, ok := services[network]; ok {
    77  		var lowerService [maxServiceLength]byte
    78  		n := copy(lowerService[:], service)
    79  		lowerASCIIBytes(lowerService[:n])
    80  		if port, ok := m[string(lowerService[:n])]; ok && n == len(service) {
    81  			return port, nil
    82  		}
    83  	}
    84  	return 0, &AddrError{Err: "unknown port", Addr: network + "/" + service}
    85  }
    86  
    87  // DefaultResolver is the resolver used by the package-level Lookup
    88  // functions and by Dialers without a specified Resolver.
    89  var DefaultResolver = &Resolver{}
    90  
    91  // A Resolver looks up names and numbers.
    92  //
    93  // A nil *Resolver is equivalent to a zero Resolver.
    94  type Resolver struct {
    95  	// PreferGo controls whether Go's built-in DNS resolver is preferred
    96  	// on platforms where it's available. It is equivalent to setting
    97  	// GODEBUG=netdns=go, but scoped to just this resolver.
    98  	PreferGo bool
    99  
   100  	// StrictErrors controls the behavior of temporary errors
   101  	// (including timeout, socket errors, and SERVFAIL) when using
   102  	// Go's built-in resolver. For a query composed of multiple
   103  	// sub-queries (such as an A+AAAA address lookup, or walking the
   104  	// DNS search list), this option causes such errors to abort the
   105  	// whole query instead of returning a partial result. This is
   106  	// not enabled by default because it may affect compatibility
   107  	// with resolvers that process AAAA queries incorrectly.
   108  	StrictErrors bool
   109  
   110  	// TODO(bradfitz): optional interface impl override hook
   111  	// TODO(bradfitz): Timeout time.Duration?
   112  }
   113  
   114  // LookupHost looks up the given host using the local resolver.
   115  // It returns a slice of that host's addresses.
   116  func LookupHost(host string) (addrs []string, err error) {
   117  	return DefaultResolver.LookupHost(context.Background(), host)
   118  }
   119  
   120  // LookupHost looks up the given host using the local resolver.
   121  // It returns a slice of that host's addresses.
   122  func (r *Resolver) LookupHost(ctx context.Context, host string) (addrs []string, err error) {
   123  	// Make sure that no matter what we do later, host=="" is rejected.
   124  	// ParseIP, for example, does accept empty strings.
   125  	if host == "" {
   126  		return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host}
   127  	}
   128  	if ip := ParseIP(host); ip != nil {
   129  		return []string{host}, nil
   130  	}
   131  	return r.lookupHost(ctx, host)
   132  }
   133  
   134  // LookupIP looks up host using the local resolver.
   135  // It returns a slice of that host's IPv4 and IPv6 addresses.
   136  func LookupIP(host string) ([]IP, error) {
   137  	addrs, err := DefaultResolver.LookupIPAddr(context.Background(), host)
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  	ips := make([]IP, len(addrs))
   142  	for i, ia := range addrs {
   143  		ips[i] = ia.IP
   144  	}
   145  	return ips, nil
   146  }
   147  
   148  // LookupIPAddr looks up host using the local resolver.
   149  // It returns a slice of that host's IPv4 and IPv6 addresses.
   150  func (r *Resolver) LookupIPAddr(ctx context.Context, host string) ([]IPAddr, error) {
   151  	// Make sure that no matter what we do later, host=="" is rejected.
   152  	// ParseIP, for example, does accept empty strings.
   153  	if host == "" {
   154  		return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host}
   155  	}
   156  	if ip := ParseIP(host); ip != nil {
   157  		return []IPAddr{{IP: ip}}, nil
   158  	}
   159  	trace, _ := ctx.Value(nettrace.TraceKey{}).(*nettrace.Trace)
   160  	if trace != nil && trace.DNSStart != nil {
   161  		trace.DNSStart(host)
   162  	}
   163  	// The underlying resolver func is lookupIP by default but it
   164  	// can be overridden by tests. This is needed by net/http, so it
   165  	// uses a context key instead of unexported variables.
   166  	resolverFunc := r.lookupIP
   167  	if alt, _ := ctx.Value(nettrace.LookupIPAltResolverKey{}).(func(context.Context, string) ([]IPAddr, error)); alt != nil {
   168  		resolverFunc = alt
   169  	}
   170  
   171  	ch := lookupGroup.DoChan(host, func() (interface{}, error) {
   172  		return testHookLookupIP(ctx, resolverFunc, host)
   173  	})
   174  
   175  	select {
   176  	case <-ctx.Done():
   177  		// The DNS lookup timed out for some reason. Force
   178  		// future requests to start the DNS lookup again
   179  		// rather than waiting for the current lookup to
   180  		// complete. See issue 8602.
   181  		err := mapErr(ctx.Err())
   182  		lookupGroup.Forget(host)
   183  		if trace != nil && trace.DNSDone != nil {
   184  			trace.DNSDone(nil, false, err)
   185  		}
   186  		return nil, err
   187  	case r := <-ch:
   188  		if trace != nil && trace.DNSDone != nil {
   189  			addrs, _ := r.Val.([]IPAddr)
   190  			trace.DNSDone(ipAddrsEface(addrs), r.Shared, r.Err)
   191  		}
   192  		return lookupIPReturn(r.Val, r.Err, r.Shared)
   193  	}
   194  }
   195  
   196  // lookupGroup merges LookupIPAddr calls together for lookups
   197  // for the same host. The lookupGroup key is is the LookupIPAddr.host
   198  // argument.
   199  // The return values are ([]IPAddr, error).
   200  var lookupGroup singleflight.Group
   201  
   202  // lookupIPReturn turns the return values from singleflight.Do into
   203  // the return values from LookupIP.
   204  func lookupIPReturn(addrsi interface{}, err error, shared bool) ([]IPAddr, error) {
   205  	if err != nil {
   206  		return nil, err
   207  	}
   208  	addrs := addrsi.([]IPAddr)
   209  	if shared {
   210  		clone := make([]IPAddr, len(addrs))
   211  		copy(clone, addrs)
   212  		addrs = clone
   213  	}
   214  	return addrs, nil
   215  }
   216  
   217  // ipAddrsEface returns an empty interface slice of addrs.
   218  func ipAddrsEface(addrs []IPAddr) []interface{} {
   219  	s := make([]interface{}, len(addrs))
   220  	for i, v := range addrs {
   221  		s[i] = v
   222  	}
   223  	return s
   224  }
   225  
   226  // LookupPort looks up the port for the given network and service.
   227  func LookupPort(network, service string) (port int, err error) {
   228  	return DefaultResolver.LookupPort(context.Background(), network, service)
   229  }
   230  
   231  // LookupPort looks up the port for the given network and service.
   232  func (r *Resolver) LookupPort(ctx context.Context, network, service string) (port int, err error) {
   233  	port, needsLookup := parsePort(service)
   234  	if needsLookup {
   235  		port, err = r.lookupPort(ctx, network, service)
   236  		if err != nil {
   237  			return 0, err
   238  		}
   239  	}
   240  	if 0 > port || port > 65535 {
   241  		return 0, &AddrError{Err: "invalid port", Addr: service}
   242  	}
   243  	return port, nil
   244  }
   245  
   246  // LookupCNAME returns the canonical name for the given host.
   247  // Callers that do not care about the canonical name can call
   248  // LookupHost or LookupIP directly; both take care of resolving
   249  // the canonical name as part of the lookup.
   250  //
   251  // A canonical name is the final name after following zero
   252  // or more CNAME records.
   253  // LookupCNAME does not return an error if host does not
   254  // contain DNS "CNAME" records, as long as host resolves to
   255  // address records.
   256  func LookupCNAME(host string) (cname string, err error) {
   257  	return DefaultResolver.lookupCNAME(context.Background(), host)
   258  }
   259  
   260  // LookupCNAME returns the canonical name for the given host.
   261  // Callers that do not care about the canonical name can call
   262  // LookupHost or LookupIP directly; both take care of resolving
   263  // the canonical name as part of the lookup.
   264  //
   265  // A canonical name is the final name after following zero
   266  // or more CNAME records.
   267  // LookupCNAME does not return an error if host does not
   268  // contain DNS "CNAME" records, as long as host resolves to
   269  // address records.
   270  func (r *Resolver) LookupCNAME(ctx context.Context, host string) (cname string, err error) {
   271  	return r.lookupCNAME(ctx, host)
   272  }
   273  
   274  // LookupSRV tries to resolve an SRV query of the given service,
   275  // protocol, and domain name. The proto is "tcp" or "udp".
   276  // The returned records are sorted by priority and randomized
   277  // by weight within a priority.
   278  //
   279  // LookupSRV constructs the DNS name to look up following RFC 2782.
   280  // That is, it looks up _service._proto.name. To accommodate services
   281  // publishing SRV records under non-standard names, if both service
   282  // and proto are empty strings, LookupSRV looks up name directly.
   283  func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) {
   284  	return DefaultResolver.lookupSRV(context.Background(), service, proto, name)
   285  }
   286  
   287  // LookupSRV tries to resolve an SRV query of the given service,
   288  // protocol, and domain name. The proto is "tcp" or "udp".
   289  // The returned records are sorted by priority and randomized
   290  // by weight within a priority.
   291  //
   292  // LookupSRV constructs the DNS name to look up following RFC 2782.
   293  // That is, it looks up _service._proto.name. To accommodate services
   294  // publishing SRV records under non-standard names, if both service
   295  // and proto are empty strings, LookupSRV looks up name directly.
   296  func (r *Resolver) LookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*SRV, err error) {
   297  	return r.lookupSRV(ctx, service, proto, name)
   298  }
   299  
   300  // LookupMX returns the DNS MX records for the given domain name sorted by preference.
   301  func LookupMX(name string) ([]*MX, error) {
   302  	return DefaultResolver.lookupMX(context.Background(), name)
   303  }
   304  
   305  // LookupMX returns the DNS MX records for the given domain name sorted by preference.
   306  func (r *Resolver) LookupMX(ctx context.Context, name string) ([]*MX, error) {
   307  	return r.lookupMX(ctx, name)
   308  }
   309  
   310  // LookupNS returns the DNS NS records for the given domain name.
   311  func LookupNS(name string) ([]*NS, error) {
   312  	return DefaultResolver.lookupNS(context.Background(), name)
   313  }
   314  
   315  // LookupNS returns the DNS NS records for the given domain name.
   316  func (r *Resolver) LookupNS(ctx context.Context, name string) ([]*NS, error) {
   317  	return r.lookupNS(ctx, name)
   318  }
   319  
   320  // LookupTXT returns the DNS TXT records for the given domain name.
   321  func LookupTXT(name string) ([]string, error) {
   322  	return DefaultResolver.lookupTXT(context.Background(), name)
   323  }
   324  
   325  // LookupTXT returns the DNS TXT records for the given domain name.
   326  func (r *Resolver) LookupTXT(ctx context.Context, name string) ([]string, error) {
   327  	return r.lookupTXT(ctx, name)
   328  }
   329  
   330  // LookupAddr performs a reverse lookup for the given address, returning a list
   331  // of names mapping to that address.
   332  //
   333  // When using the host C library resolver, at most one result will be
   334  // returned. To bypass the host resolver, use a custom Resolver.
   335  func LookupAddr(addr string) (names []string, err error) {
   336  	return DefaultResolver.lookupAddr(context.Background(), addr)
   337  }
   338  
   339  // LookupAddr performs a reverse lookup for the given address, returning a list
   340  // of names mapping to that address.
   341  func (r *Resolver) LookupAddr(ctx context.Context, addr string) (names []string, err error) {
   342  	return r.lookupAddr(ctx, addr)
   343  }