Black Lives Matter. Support the Equal Justice Initiative.

Source file src/net/lookup.go

Documentation: net

     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  	"sync"
    12  )
    13  
    14  // protocols contains minimal mappings between internet protocol
    15  // names and numbers for platforms that don't have a complete list of
    16  // protocol numbers.
    17  //
    18  // See https://www.iana.org/assignments/protocol-numbers
    19  //
    20  // On Unix, this map is augmented by readProtocols via lookupProtocol.
    21  var protocols = map[string]int{
    22  	"icmp":      1,
    23  	"igmp":      2,
    24  	"tcp":       6,
    25  	"udp":       17,
    26  	"ipv6-icmp": 58,
    27  }
    28  
    29  // services contains minimal mappings between services names and port
    30  // numbers for platforms that don't have a complete list of port numbers.
    31  //
    32  // See https://www.iana.org/assignments/service-names-port-numbers
    33  //
    34  // On Unix, this map is augmented by readServices via goLookupPort.
    35  var services = map[string]map[string]int{
    36  	"udp": {
    37  		"domain": 53,
    38  	},
    39  	"tcp": {
    40  		"ftp":    21,
    41  		"ftps":   990,
    42  		"gopher": 70, // ʕ◔ϖ◔ʔ
    43  		"http":   80,
    44  		"https":  443,
    45  		"imap2":  143,
    46  		"imap3":  220,
    47  		"imaps":  993,
    48  		"pop3":   110,
    49  		"pop3s":  995,
    50  		"smtp":   25,
    51  		"ssh":    22,
    52  		"telnet": 23,
    53  	},
    54  }
    55  
    56  // dnsWaitGroup can be used by tests to wait for all DNS goroutines to
    57  // complete. This avoids races on the test hooks.
    58  var dnsWaitGroup sync.WaitGroup
    59  
    60  const maxProtoLength = len("RSVP-E2E-IGNORE") + 10 // with room to grow
    61  
    62  func lookupProtocolMap(name string) (int, error) {
    63  	var lowerProtocol [maxProtoLength]byte
    64  	n := copy(lowerProtocol[:], name)
    65  	lowerASCIIBytes(lowerProtocol[:n])
    66  	proto, found := protocols[string(lowerProtocol[:n])]
    67  	if !found || n != len(name) {
    68  		return 0, &AddrError{Err: "unknown IP protocol specified", Addr: name}
    69  	}
    70  	return proto, nil
    71  }
    72  
    73  // maxPortBufSize is the longest reasonable name of a service
    74  // (non-numeric port).
    75  // Currently the longest known IANA-unregistered name is
    76  // "mobility-header", so we use that length, plus some slop in case
    77  // something longer is added in the future.
    78  const maxPortBufSize = len("mobility-header") + 10
    79  
    80  func lookupPortMap(network, service string) (port int, error error) {
    81  	switch network {
    82  	case "tcp4", "tcp6":
    83  		network = "tcp"
    84  	case "udp4", "udp6":
    85  		network = "udp"
    86  	}
    87  
    88  	if m, ok := services[network]; ok {
    89  		var lowerService [maxPortBufSize]byte
    90  		n := copy(lowerService[:], service)
    91  		lowerASCIIBytes(lowerService[:n])
    92  		if port, ok := m[string(lowerService[:n])]; ok && n == len(service) {
    93  			return port, nil
    94  		}
    95  	}
    96  	return 0, &AddrError{Err: "unknown port", Addr: network + "/" + service}
    97  }
    98  
    99  // ipVersion returns the provided network's IP version: '4', '6' or 0
   100  // if network does not end in a '4' or '6' byte.
   101  func ipVersion(network string) byte {
   102  	if network == "" {
   103  		return 0
   104  	}
   105  	n := network[len(network)-1]
   106  	if n != '4' && n != '6' {
   107  		n = 0
   108  	}
   109  	return n
   110  }
   111  
   112  // DefaultResolver is the resolver used by the package-level Lookup
   113  // functions and by Dialers without a specified Resolver.
   114  var DefaultResolver = &Resolver{}
   115  
   116  // A Resolver looks up names and numbers.
   117  //
   118  // A nil *Resolver is equivalent to a zero Resolver.
   119  type Resolver struct {
   120  	// PreferGo controls whether Go's built-in DNS resolver is preferred
   121  	// on platforms where it's available. It is equivalent to setting
   122  	// GODEBUG=netdns=go, but scoped to just this resolver.
   123  	PreferGo bool
   124  
   125  	// StrictErrors controls the behavior of temporary errors
   126  	// (including timeout, socket errors, and SERVFAIL) when using
   127  	// Go's built-in resolver. For a query composed of multiple
   128  	// sub-queries (such as an A+AAAA address lookup, or walking the
   129  	// DNS search list), this option causes such errors to abort the
   130  	// whole query instead of returning a partial result. This is
   131  	// not enabled by default because it may affect compatibility
   132  	// with resolvers that process AAAA queries incorrectly.
   133  	StrictErrors bool
   134  
   135  	// Dial optionally specifies an alternate dialer for use by
   136  	// Go's built-in DNS resolver to make TCP and UDP connections
   137  	// to DNS services. The host in the address parameter will
   138  	// always be a literal IP address and not a host name, and the
   139  	// port in the address parameter will be a literal port number
   140  	// and not a service name.
   141  	// If the Conn returned is also a PacketConn, sent and received DNS
   142  	// messages must adhere to RFC 1035 section 4.2.1, "UDP usage".
   143  	// Otherwise, DNS messages transmitted over Conn must adhere
   144  	// to RFC 7766 section 5, "Transport Protocol Selection".
   145  	// If nil, the default dialer is used.
   146  	Dial func(ctx context.Context, network, address string) (Conn, error)
   147  
   148  	// lookupGroup merges LookupIPAddr calls together for lookups for the same
   149  	// host. The lookupGroup key is the LookupIPAddr.host argument.
   150  	// The return values are ([]IPAddr, error).
   151  	lookupGroup singleflight.Group
   152  
   153  	// TODO(bradfitz): optional interface impl override hook
   154  	// TODO(bradfitz): Timeout time.Duration?
   155  }
   156  
   157  func (r *Resolver) preferGo() bool     { return r != nil && r.PreferGo }
   158  func (r *Resolver) strictErrors() bool { return r != nil && r.StrictErrors }
   159  
   160  func (r *Resolver) getLookupGroup() *singleflight.Group {
   161  	if r == nil {
   162  		return &DefaultResolver.lookupGroup
   163  	}
   164  	return &r.lookupGroup
   165  }
   166  
   167  // LookupHost looks up the given host using the local resolver.
   168  // It returns a slice of that host's addresses.
   169  func LookupHost(host string) (addrs []string, err error) {
   170  	return DefaultResolver.LookupHost(context.Background(), host)
   171  }
   172  
   173  // LookupHost looks up the given host using the local resolver.
   174  // It returns a slice of that host's addresses.
   175  func (r *Resolver) LookupHost(ctx context.Context, host string) (addrs []string, err error) {
   176  	// Make sure that no matter what we do later, host=="" is rejected.
   177  	// parseIP, for example, does accept empty strings.
   178  	if host == "" {
   179  		return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true}
   180  	}
   181  	if ip, _ := parseIPZone(host); ip != nil {
   182  		return []string{host}, nil
   183  	}
   184  	return r.lookupHost(ctx, host)
   185  }
   186  
   187  // LookupIP looks up host using the local resolver.
   188  // It returns a slice of that host's IPv4 and IPv6 addresses.
   189  func LookupIP(host string) ([]IP, error) {
   190  	addrs, err := DefaultResolver.LookupIPAddr(context.Background(), host)
   191  	if err != nil {
   192  		return nil, err
   193  	}
   194  	ips := make([]IP, len(addrs))
   195  	for i, ia := range addrs {
   196  		ips[i] = ia.IP
   197  	}
   198  	return ips, nil
   199  }
   200  
   201  // LookupIPAddr looks up host using the local resolver.
   202  // It returns a slice of that host's IPv4 and IPv6 addresses.
   203  func (r *Resolver) LookupIPAddr(ctx context.Context, host string) ([]IPAddr, error) {
   204  	return r.lookupIPAddr(ctx, "ip", host)
   205  }
   206  
   207  // LookupIP looks up host for the given network using the local resolver.
   208  // It returns a slice of that host's IP addresses of the type specified by
   209  // network.
   210  // network must be one of "ip", "ip4" or "ip6".
   211  func (r *Resolver) LookupIP(ctx context.Context, network, host string) ([]IP, error) {
   212  	afnet, _, err := parseNetwork(ctx, network, false)
   213  	if err != nil {
   214  		return nil, err
   215  	}
   216  	switch afnet {
   217  	case "ip", "ip4", "ip6":
   218  	default:
   219  		return nil, UnknownNetworkError(network)
   220  	}
   221  	addrs, err := r.internetAddrList(ctx, afnet, host)
   222  	if err != nil {
   223  		return nil, err
   224  	}
   225  	ips := make([]IP, 0, len(addrs))
   226  	for _, addr := range addrs {
   227  		ips = append(ips, addr.(*IPAddr).IP)
   228  	}
   229  	return ips, nil
   230  }
   231  
   232  // onlyValuesCtx is a context that uses an underlying context
   233  // for value lookup if the underlying context hasn't yet expired.
   234  type onlyValuesCtx struct {
   235  	context.Context
   236  	lookupValues context.Context
   237  }
   238  
   239  var _ context.Context = (*onlyValuesCtx)(nil)
   240  
   241  // Value performs a lookup if the original context hasn't expired.
   242  func (ovc *onlyValuesCtx) Value(key interface{}) interface{} {
   243  	select {
   244  	case <-ovc.lookupValues.Done():
   245  		return nil
   246  	default:
   247  		return ovc.lookupValues.Value(key)
   248  	}
   249  }
   250  
   251  // withUnexpiredValuesPreserved returns a context.Context that only uses lookupCtx
   252  // for its values, otherwise it is never canceled and has no deadline.
   253  // If the lookup context expires, any looked up values will return nil.
   254  // See Issue 28600.
   255  func withUnexpiredValuesPreserved(lookupCtx context.Context) context.Context {
   256  	return &onlyValuesCtx{Context: context.Background(), lookupValues: lookupCtx}
   257  }
   258  
   259  // lookupIPAddr looks up host using the local resolver and particular network.
   260  // It returns a slice of that host's IPv4 and IPv6 addresses.
   261  func (r *Resolver) lookupIPAddr(ctx context.Context, network, host string) ([]IPAddr, error) {
   262  	// Make sure that no matter what we do later, host=="" is rejected.
   263  	// parseIP, for example, does accept empty strings.
   264  	if host == "" {
   265  		return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true}
   266  	}
   267  	if ip, zone := parseIPZone(host); ip != nil {
   268  		return []IPAddr{{IP: ip, Zone: zone}}, nil
   269  	}
   270  	trace, _ := ctx.Value(nettrace.TraceKey{}).(*nettrace.Trace)
   271  	if trace != nil && trace.DNSStart != nil {
   272  		trace.DNSStart(host)
   273  	}
   274  	// The underlying resolver func is lookupIP by default but it
   275  	// can be overridden by tests. This is needed by net/http, so it
   276  	// uses a context key instead of unexported variables.
   277  	resolverFunc := r.lookupIP
   278  	if alt, _ := ctx.Value(nettrace.LookupIPAltResolverKey{}).(func(context.Context, string, string) ([]IPAddr, error)); alt != nil {
   279  		resolverFunc = alt
   280  	}
   281  
   282  	// We don't want a cancellation of ctx to affect the
   283  	// lookupGroup operation. Otherwise if our context gets
   284  	// canceled it might cause an error to be returned to a lookup
   285  	// using a completely different context. However we need to preserve
   286  	// only the values in context. See Issue 28600.
   287  	lookupGroupCtx, lookupGroupCancel := context.WithCancel(withUnexpiredValuesPreserved(ctx))
   288  
   289  	lookupKey := network + "\000" + host
   290  	dnsWaitGroup.Add(1)
   291  	ch, called := r.getLookupGroup().DoChan(lookupKey, func() (interface{}, error) {
   292  		defer dnsWaitGroup.Done()
   293  		return testHookLookupIP(lookupGroupCtx, resolverFunc, network, host)
   294  	})
   295  	if !called {
   296  		dnsWaitGroup.Done()
   297  	}
   298  
   299  	select {
   300  	case <-ctx.Done():
   301  		// Our context was canceled. If we are the only
   302  		// goroutine looking up this key, then drop the key
   303  		// from the lookupGroup and cancel the lookup.
   304  		// If there are other goroutines looking up this key,
   305  		// let the lookup continue uncanceled, and let later
   306  		// lookups with the same key share the result.
   307  		// See issues 8602, 20703, 22724.
   308  		if r.getLookupGroup().ForgetUnshared(lookupKey) {
   309  			lookupGroupCancel()
   310  		} else {
   311  			go func() {
   312  				<-ch
   313  				lookupGroupCancel()
   314  			}()
   315  		}
   316  		err := mapErr(ctx.Err())
   317  		if trace != nil && trace.DNSDone != nil {
   318  			trace.DNSDone(nil, false, err)
   319  		}
   320  		return nil, err
   321  	case r := <-ch:
   322  		lookupGroupCancel()
   323  		if trace != nil && trace.DNSDone != nil {
   324  			addrs, _ := r.Val.([]IPAddr)
   325  			trace.DNSDone(ipAddrsEface(addrs), r.Shared, r.Err)
   326  		}
   327  		return lookupIPReturn(r.Val, r.Err, r.Shared)
   328  	}
   329  }
   330  
   331  // lookupIPReturn turns the return values from singleflight.Do into
   332  // the return values from LookupIP.
   333  func lookupIPReturn(addrsi interface{}, err error, shared bool) ([]IPAddr, error) {
   334  	if err != nil {
   335  		return nil, err
   336  	}
   337  	addrs := addrsi.([]IPAddr)
   338  	if shared {
   339  		clone := make([]IPAddr, len(addrs))
   340  		copy(clone, addrs)
   341  		addrs = clone
   342  	}
   343  	return addrs, nil
   344  }
   345  
   346  // ipAddrsEface returns an empty interface slice of addrs.
   347  func ipAddrsEface(addrs []IPAddr) []interface{} {
   348  	s := make([]interface{}, len(addrs))
   349  	for i, v := range addrs {
   350  		s[i] = v
   351  	}
   352  	return s
   353  }
   354  
   355  // LookupPort looks up the port for the given network and service.
   356  func LookupPort(network, service string) (port int, err error) {
   357  	return DefaultResolver.LookupPort(context.Background(), network, service)
   358  }
   359  
   360  // LookupPort looks up the port for the given network and service.
   361  func (r *Resolver) LookupPort(ctx context.Context, network, service string) (port int, err error) {
   362  	port, needsLookup := parsePort(service)
   363  	if needsLookup {
   364  		switch network {
   365  		case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6":
   366  		case "": // a hint wildcard for Go 1.0 undocumented behavior
   367  			network = "ip"
   368  		default:
   369  			return 0, &AddrError{Err: "unknown network", Addr: network}
   370  		}
   371  		port, err = r.lookupPort(ctx, network, service)
   372  		if err != nil {
   373  			return 0, err
   374  		}
   375  	}
   376  	if 0 > port || port > 65535 {
   377  		return 0, &AddrError{Err: "invalid port", Addr: service}
   378  	}
   379  	return port, nil
   380  }
   381  
   382  // LookupCNAME returns the canonical name for the given host.
   383  // Callers that do not care about the canonical name can call
   384  // LookupHost or LookupIP directly; both take care of resolving
   385  // the canonical name as part of the lookup.
   386  //
   387  // A canonical name is the final name after following zero
   388  // or more CNAME records.
   389  // LookupCNAME does not return an error if host does not
   390  // contain DNS "CNAME" records, as long as host resolves to
   391  // address records.
   392  //
   393  // The returned canonical name is validated to be a properly
   394  // formatted presentation-format domain name.
   395  func LookupCNAME(host string) (cname string, err error) {
   396  	return DefaultResolver.LookupCNAME(context.Background(), host)
   397  }
   398  
   399  // LookupCNAME returns the canonical name for the given host.
   400  // Callers that do not care about the canonical name can call
   401  // LookupHost or LookupIP directly; both take care of resolving
   402  // the canonical name as part of the lookup.
   403  //
   404  // A canonical name is the final name after following zero
   405  // or more CNAME records.
   406  // LookupCNAME does not return an error if host does not
   407  // contain DNS "CNAME" records, as long as host resolves to
   408  // address records.
   409  //
   410  // The returned canonical name is validated to be a properly
   411  // formatted presentation-format domain name.
   412  func (r *Resolver) LookupCNAME(ctx context.Context, host string) (string, error) {
   413  	cname, err := r.lookupCNAME(ctx, host)
   414  	if err != nil {
   415  		return "", err
   416  	}
   417  	if !isDomainName(cname) {
   418  		return "", &DNSError{Err: "CNAME target is invalid", Name: host}
   419  	}
   420  	return cname, nil
   421  }
   422  
   423  // LookupSRV tries to resolve an SRV query of the given service,
   424  // protocol, and domain name. The proto is "tcp" or "udp".
   425  // The returned records are sorted by priority and randomized
   426  // by weight within a priority.
   427  //
   428  // LookupSRV constructs the DNS name to look up following RFC 2782.
   429  // That is, it looks up _service._proto.name. To accommodate services
   430  // publishing SRV records under non-standard names, if both service
   431  // and proto are empty strings, LookupSRV looks up name directly.
   432  //
   433  // The returned service names are validated to be properly
   434  // formatted presentation-format domain names.
   435  func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) {
   436  	return DefaultResolver.LookupSRV(context.Background(), service, proto, name)
   437  }
   438  
   439  // LookupSRV tries to resolve an SRV query of the given service,
   440  // protocol, and domain name. The proto is "tcp" or "udp".
   441  // The returned records are sorted by priority and randomized
   442  // by weight within a priority.
   443  //
   444  // LookupSRV constructs the DNS name to look up following RFC 2782.
   445  // That is, it looks up _service._proto.name. To accommodate services
   446  // publishing SRV records under non-standard names, if both service
   447  // and proto are empty strings, LookupSRV looks up name directly.
   448  //
   449  // The returned service names are validated to be properly
   450  // formatted presentation-format domain names.
   451  func (r *Resolver) LookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) {
   452  	cname, addrs, err := r.lookupSRV(ctx, service, proto, name)
   453  	if err != nil {
   454  		return "", nil, err
   455  	}
   456  	if cname != "" && !isDomainName(cname) {
   457  		return "", nil, &DNSError{Err: "SRV header name is invalid", Name: name}
   458  	}
   459  	for _, addr := range addrs {
   460  		if addr == nil {
   461  			continue
   462  		}
   463  		if !isDomainName(addr.Target) {
   464  			return "", nil, &DNSError{Err: "SRV target is invalid", Name: name}
   465  		}
   466  	}
   467  	return cname, addrs, nil
   468  }
   469  
   470  // LookupMX returns the DNS MX records for the given domain name sorted by preference.
   471  //
   472  // The returned mail server names are validated to be properly
   473  // formatted presentation-format domain names.
   474  func LookupMX(name string) ([]*MX, error) {
   475  	return DefaultResolver.LookupMX(context.Background(), name)
   476  }
   477  
   478  // LookupMX returns the DNS MX records for the given domain name sorted by preference.
   479  //
   480  // The returned mail server names are validated to be properly
   481  // formatted presentation-format domain names.
   482  func (r *Resolver) LookupMX(ctx context.Context, name string) ([]*MX, error) {
   483  	records, err := r.lookupMX(ctx, name)
   484  	if err != nil {
   485  		return nil, err
   486  	}
   487  	for _, mx := range records {
   488  		if mx == nil {
   489  			continue
   490  		}
   491  		if !isDomainName(mx.Host) {
   492  			return nil, &DNSError{Err: "MX target is invalid", Name: name}
   493  		}
   494  	}
   495  	return records, nil
   496  }
   497  
   498  // LookupNS returns the DNS NS records for the given domain name.
   499  //
   500  // The returned name server names are validated to be properly
   501  // formatted presentation-format domain names.
   502  func LookupNS(name string) ([]*NS, error) {
   503  	return DefaultResolver.LookupNS(context.Background(), name)
   504  }
   505  
   506  // LookupNS returns the DNS NS records for the given domain name.
   507  //
   508  // The returned name server names are validated to be properly
   509  // formatted presentation-format domain names.
   510  func (r *Resolver) LookupNS(ctx context.Context, name string) ([]*NS, error) {
   511  	records, err := r.lookupNS(ctx, name)
   512  	if err != nil {
   513  		return nil, err
   514  	}
   515  	for _, ns := range records {
   516  		if ns == nil {
   517  			continue
   518  		}
   519  		if !isDomainName(ns.Host) {
   520  			return nil, &DNSError{Err: "NS target is invalid", Name: name}
   521  		}
   522  	}
   523  	return records, nil
   524  }
   525  
   526  // LookupTXT returns the DNS TXT records for the given domain name.
   527  func LookupTXT(name string) ([]string, error) {
   528  	return DefaultResolver.lookupTXT(context.Background(), name)
   529  }
   530  
   531  // LookupTXT returns the DNS TXT records for the given domain name.
   532  func (r *Resolver) LookupTXT(ctx context.Context, name string) ([]string, error) {
   533  	return r.lookupTXT(ctx, name)
   534  }
   535  
   536  // LookupAddr performs a reverse lookup for the given address, returning a list
   537  // of names mapping to that address.
   538  //
   539  // The returned names are validated to be properly formatted presentation-format
   540  // domain names.
   541  //
   542  // When using the host C library resolver, at most one result will be
   543  // returned. To bypass the host resolver, use a custom Resolver.
   544  func LookupAddr(addr string) (names []string, err error) {
   545  	return DefaultResolver.LookupAddr(context.Background(), addr)
   546  }
   547  
   548  // LookupAddr performs a reverse lookup for the given address, returning a list
   549  // of names mapping to that address.
   550  //
   551  // The returned names are validated to be properly formatted presentation-format
   552  // domain names.
   553  func (r *Resolver) LookupAddr(ctx context.Context, addr string) ([]string, error) {
   554  	names, err := r.lookupAddr(ctx, addr)
   555  	if err != nil {
   556  		return nil, err
   557  	}
   558  	for _, name := range names {
   559  		if !isDomainName(name) {
   560  			return nil, &DNSError{Err: "PTR target is invalid", Name: addr}
   561  		}
   562  	}
   563  	return names, nil
   564  }
   565  

View as plain text