Source file src/net/lookup.go

     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  	"errors"
    10  	"internal/nettrace"
    11  	"internal/singleflight"
    12  	"net/netip"
    13  	"sync"
    14  
    15  	"golang.org/x/net/dns/dnsmessage"
    16  )
    17  
    18  // protocols contains minimal mappings between internet protocol
    19  // names and numbers for platforms that don't have a complete list of
    20  // protocol numbers.
    21  //
    22  // See https://www.iana.org/assignments/protocol-numbers
    23  //
    24  // On Unix, this map is augmented by readProtocols via lookupProtocol.
    25  var protocols = map[string]int{
    26  	"icmp":      1,
    27  	"igmp":      2,
    28  	"tcp":       6,
    29  	"udp":       17,
    30  	"ipv6-icmp": 58,
    31  }
    32  
    33  // services contains minimal mappings between services names and port
    34  // numbers for platforms that don't have a complete list of port numbers.
    35  //
    36  // See https://www.iana.org/assignments/service-names-port-numbers
    37  //
    38  // On Unix, this map is augmented by readServices via goLookupPort.
    39  var services = map[string]map[string]int{
    40  	"udp": {
    41  		"domain": 53,
    42  	},
    43  	"tcp": {
    44  		"ftp":         21,
    45  		"ftps":        990,
    46  		"gopher":      70, // ʕ◔ϖ◔ʔ
    47  		"http":        80,
    48  		"https":       443,
    49  		"imap2":       143,
    50  		"imap3":       220,
    51  		"imaps":       993,
    52  		"pop3":        110,
    53  		"pop3s":       995,
    54  		"smtp":        25,
    55  		"submissions": 465,
    56  		"ssh":         22,
    57  		"telnet":      23,
    58  	},
    59  }
    60  
    61  // dnsWaitGroup can be used by tests to wait for all DNS goroutines to
    62  // complete. This avoids races on the test hooks.
    63  var dnsWaitGroup sync.WaitGroup
    64  
    65  const maxProtoLength = len("RSVP-E2E-IGNORE") + 10 // with room to grow
    66  
    67  func lookupProtocolMap(name string) (int, error) {
    68  	var lowerProtocol [maxProtoLength]byte
    69  	n := copy(lowerProtocol[:], name)
    70  	lowerASCIIBytes(lowerProtocol[:n])
    71  	proto, found := protocols[string(lowerProtocol[:n])]
    72  	if !found || n != len(name) {
    73  		return 0, &AddrError{Err: "unknown IP protocol specified", Addr: name}
    74  	}
    75  	return proto, nil
    76  }
    77  
    78  // maxPortBufSize is the longest reasonable name of a service
    79  // (non-numeric port).
    80  // Currently the longest known IANA-unregistered name is
    81  // "mobility-header", so we use that length, plus some slop in case
    82  // something longer is added in the future.
    83  const maxPortBufSize = len("mobility-header") + 10
    84  
    85  func lookupPortMap(network, service string) (port int, error error) {
    86  	switch network {
    87  	case "ip": // no hints
    88  		if p, err := lookupPortMapWithNetwork("tcp", "ip", service); err == nil {
    89  			return p, nil
    90  		}
    91  		return lookupPortMapWithNetwork("udp", "ip", service)
    92  	case "tcp", "tcp4", "tcp6":
    93  		return lookupPortMapWithNetwork("tcp", "tcp", service)
    94  	case "udp", "udp4", "udp6":
    95  		return lookupPortMapWithNetwork("udp", "udp", service)
    96  	}
    97  	return 0, &DNSError{Err: "unknown network", Name: network + "/" + service}
    98  }
    99  
   100  func lookupPortMapWithNetwork(network, errNetwork, service string) (port int, error error) {
   101  	if m, ok := services[network]; ok {
   102  		var lowerService [maxPortBufSize]byte
   103  		n := copy(lowerService[:], service)
   104  		lowerASCIIBytes(lowerService[:n])
   105  		if port, ok := m[string(lowerService[:n])]; ok && n == len(service) {
   106  			return port, nil
   107  		}
   108  		return 0, &DNSError{Err: "unknown port", Name: errNetwork + "/" + service, IsNotFound: true}
   109  	}
   110  	return 0, &DNSError{Err: "unknown network", Name: errNetwork + "/" + service}
   111  }
   112  
   113  // ipVersion returns the provided network's IP version: '4', '6' or 0
   114  // if network does not end in a '4' or '6' byte.
   115  func ipVersion(network string) byte {
   116  	if network == "" {
   117  		return 0
   118  	}
   119  	n := network[len(network)-1]
   120  	if n != '4' && n != '6' {
   121  		n = 0
   122  	}
   123  	return n
   124  }
   125  
   126  // DefaultResolver is the resolver used by the package-level Lookup
   127  // functions and by Dialers without a specified Resolver.
   128  var DefaultResolver = &Resolver{}
   129  
   130  // A Resolver looks up names and numbers.
   131  //
   132  // A nil *Resolver is equivalent to a zero Resolver.
   133  type Resolver struct {
   134  	// PreferGo controls whether Go's built-in DNS resolver is preferred
   135  	// on platforms where it's available. It is equivalent to setting
   136  	// GODEBUG=netdns=go, but scoped to just this resolver.
   137  	PreferGo bool
   138  
   139  	// StrictErrors controls the behavior of temporary errors
   140  	// (including timeout, socket errors, and SERVFAIL) when using
   141  	// Go's built-in resolver. For a query composed of multiple
   142  	// sub-queries (such as an A+AAAA address lookup, or walking the
   143  	// DNS search list), this option causes such errors to abort the
   144  	// whole query instead of returning a partial result. This is
   145  	// not enabled by default because it may affect compatibility
   146  	// with resolvers that process AAAA queries incorrectly.
   147  	StrictErrors bool
   148  
   149  	// Dial optionally specifies an alternate dialer for use by
   150  	// Go's built-in DNS resolver to make TCP and UDP connections
   151  	// to DNS services. The host in the address parameter will
   152  	// always be a literal IP address and not a host name, and the
   153  	// port in the address parameter will be a literal port number
   154  	// and not a service name.
   155  	// If the Conn returned is also a PacketConn, sent and received DNS
   156  	// messages must adhere to RFC 1035 section 4.2.1, "UDP usage".
   157  	// Otherwise, DNS messages transmitted over Conn must adhere
   158  	// to RFC 7766 section 5, "Transport Protocol Selection".
   159  	// If nil, the default dialer is used.
   160  	Dial func(ctx context.Context, network, address string) (Conn, error)
   161  
   162  	// lookupGroup merges LookupIPAddr calls together for lookups for the same
   163  	// host. The lookupGroup key is the LookupIPAddr.host argument.
   164  	// The return values are ([]IPAddr, error).
   165  	lookupGroup singleflight.Group
   166  
   167  	// TODO(bradfitz): optional interface impl override hook
   168  	// TODO(bradfitz): Timeout time.Duration?
   169  }
   170  
   171  func (r *Resolver) preferGo() bool     { return r != nil && r.PreferGo }
   172  func (r *Resolver) strictErrors() bool { return r != nil && r.StrictErrors }
   173  
   174  func (r *Resolver) getLookupGroup() *singleflight.Group {
   175  	if r == nil {
   176  		return &DefaultResolver.lookupGroup
   177  	}
   178  	return &r.lookupGroup
   179  }
   180  
   181  // LookupHost looks up the given host using the local resolver.
   182  // It returns a slice of that host's addresses.
   183  //
   184  // LookupHost uses [context.Background] internally; to specify the context, use
   185  // [Resolver.LookupHost].
   186  func LookupHost(host string) (addrs []string, err error) {
   187  	return DefaultResolver.LookupHost(context.Background(), host)
   188  }
   189  
   190  // LookupHost looks up the given host using the local resolver.
   191  // It returns a slice of that host's addresses.
   192  func (r *Resolver) LookupHost(ctx context.Context, host string) (addrs []string, err error) {
   193  	// Make sure that no matter what we do later, host=="" is rejected.
   194  	if host == "" {
   195  		return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true}
   196  	}
   197  	if _, err := netip.ParseAddr(host); err == nil {
   198  		return []string{host}, nil
   199  	}
   200  	return r.lookupHost(ctx, host)
   201  }
   202  
   203  // LookupIP looks up host using the local resolver.
   204  // It returns a slice of that host's IPv4 and IPv6 addresses.
   205  func LookupIP(host string) ([]IP, error) {
   206  	addrs, err := DefaultResolver.LookupIPAddr(context.Background(), host)
   207  	if err != nil {
   208  		return nil, err
   209  	}
   210  	ips := make([]IP, len(addrs))
   211  	for i, ia := range addrs {
   212  		ips[i] = ia.IP
   213  	}
   214  	return ips, nil
   215  }
   216  
   217  // LookupIPAddr looks up host using the local resolver.
   218  // It returns a slice of that host's IPv4 and IPv6 addresses.
   219  func (r *Resolver) LookupIPAddr(ctx context.Context, host string) ([]IPAddr, error) {
   220  	return r.lookupIPAddr(ctx, "ip", host)
   221  }
   222  
   223  // LookupIP looks up host for the given network using the local resolver.
   224  // It returns a slice of that host's IP addresses of the type specified by
   225  // network.
   226  // network must be one of "ip", "ip4" or "ip6".
   227  func (r *Resolver) LookupIP(ctx context.Context, network, host string) ([]IP, error) {
   228  	afnet, _, err := parseNetwork(ctx, network, false)
   229  	if err != nil {
   230  		return nil, err
   231  	}
   232  	switch afnet {
   233  	case "ip", "ip4", "ip6":
   234  	default:
   235  		return nil, UnknownNetworkError(network)
   236  	}
   237  
   238  	if host == "" {
   239  		return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true}
   240  	}
   241  	addrs, err := r.internetAddrList(ctx, afnet, host)
   242  	if err != nil {
   243  		return nil, err
   244  	}
   245  
   246  	ips := make([]IP, 0, len(addrs))
   247  	for _, addr := range addrs {
   248  		ips = append(ips, addr.(*IPAddr).IP)
   249  	}
   250  	return ips, nil
   251  }
   252  
   253  // LookupNetIP looks up host using the local resolver.
   254  // It returns a slice of that host's IP addresses of the type specified by
   255  // network.
   256  // The network must be one of "ip", "ip4" or "ip6".
   257  func (r *Resolver) LookupNetIP(ctx context.Context, network, host string) ([]netip.Addr, error) {
   258  	// TODO(bradfitz): make this efficient, making the internal net package
   259  	// type throughout be netip.Addr and only converting to the net.IP slice
   260  	// version at the edge. But for now (2021-10-20), this is a wrapper around
   261  	// the old way.
   262  	ips, err := r.LookupIP(ctx, network, host)
   263  	if err != nil {
   264  		return nil, err
   265  	}
   266  	ret := make([]netip.Addr, 0, len(ips))
   267  	for _, ip := range ips {
   268  		if a, ok := netip.AddrFromSlice(ip); ok {
   269  			ret = append(ret, a)
   270  		}
   271  	}
   272  	return ret, nil
   273  }
   274  
   275  // onlyValuesCtx is a context that uses an underlying context
   276  // for value lookup if the underlying context hasn't yet expired.
   277  type onlyValuesCtx struct {
   278  	context.Context
   279  	lookupValues context.Context
   280  }
   281  
   282  var _ context.Context = (*onlyValuesCtx)(nil)
   283  
   284  // Value performs a lookup if the original context hasn't expired.
   285  func (ovc *onlyValuesCtx) Value(key any) any {
   286  	select {
   287  	case <-ovc.lookupValues.Done():
   288  		return nil
   289  	default:
   290  		return ovc.lookupValues.Value(key)
   291  	}
   292  }
   293  
   294  // withUnexpiredValuesPreserved returns a context.Context that only uses lookupCtx
   295  // for its values, otherwise it is never canceled and has no deadline.
   296  // If the lookup context expires, any looked up values will return nil.
   297  // See Issue 28600.
   298  func withUnexpiredValuesPreserved(lookupCtx context.Context) context.Context {
   299  	return &onlyValuesCtx{Context: context.Background(), lookupValues: lookupCtx}
   300  }
   301  
   302  // lookupIPAddr looks up host using the local resolver and particular network.
   303  // It returns a slice of that host's IPv4 and IPv6 addresses.
   304  func (r *Resolver) lookupIPAddr(ctx context.Context, network, host string) ([]IPAddr, error) {
   305  	// Make sure that no matter what we do later, host=="" is rejected.
   306  	if host == "" {
   307  		return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true}
   308  	}
   309  	if ip, err := netip.ParseAddr(host); err == nil {
   310  		return []IPAddr{{IP: IP(ip.AsSlice()).To16(), Zone: ip.Zone()}}, nil
   311  	}
   312  	trace, _ := ctx.Value(nettrace.TraceKey{}).(*nettrace.Trace)
   313  	if trace != nil && trace.DNSStart != nil {
   314  		trace.DNSStart(host)
   315  	}
   316  	// The underlying resolver func is lookupIP by default but it
   317  	// can be overridden by tests. This is needed by net/http, so it
   318  	// uses a context key instead of unexported variables.
   319  	resolverFunc := r.lookupIP
   320  	if alt, _ := ctx.Value(nettrace.LookupIPAltResolverKey{}).(func(context.Context, string, string) ([]IPAddr, error)); alt != nil {
   321  		resolverFunc = alt
   322  	}
   323  
   324  	// We don't want a cancellation of ctx to affect the
   325  	// lookupGroup operation. Otherwise if our context gets
   326  	// canceled it might cause an error to be returned to a lookup
   327  	// using a completely different context. However we need to preserve
   328  	// only the values in context. See Issue 28600.
   329  	lookupGroupCtx, lookupGroupCancel := context.WithCancel(withUnexpiredValuesPreserved(ctx))
   330  
   331  	lookupKey := network + "\000" + host
   332  	dnsWaitGroup.Add(1)
   333  	ch := r.getLookupGroup().DoChan(lookupKey, func() (any, error) {
   334  		return testHookLookupIP(lookupGroupCtx, resolverFunc, network, host)
   335  	})
   336  
   337  	dnsWaitGroupDone := func(ch <-chan singleflight.Result, cancelFn context.CancelFunc) {
   338  		<-ch
   339  		dnsWaitGroup.Done()
   340  		cancelFn()
   341  	}
   342  	select {
   343  	case <-ctx.Done():
   344  		// Our context was canceled. If we are the only
   345  		// goroutine looking up this key, then drop the key
   346  		// from the lookupGroup and cancel the lookup.
   347  		// If there are other goroutines looking up this key,
   348  		// let the lookup continue uncanceled, and let later
   349  		// lookups with the same key share the result.
   350  		// See issues 8602, 20703, 22724.
   351  		if r.getLookupGroup().ForgetUnshared(lookupKey) {
   352  			lookupGroupCancel()
   353  			go dnsWaitGroupDone(ch, func() {})
   354  		} else {
   355  			go dnsWaitGroupDone(ch, lookupGroupCancel)
   356  		}
   357  		ctxErr := ctx.Err()
   358  		err := &DNSError{
   359  			Err:       mapErr(ctxErr).Error(),
   360  			Name:      host,
   361  			IsTimeout: ctxErr == context.DeadlineExceeded,
   362  		}
   363  		if trace != nil && trace.DNSDone != nil {
   364  			trace.DNSDone(nil, false, err)
   365  		}
   366  		return nil, err
   367  	case r := <-ch:
   368  		dnsWaitGroup.Done()
   369  		lookupGroupCancel()
   370  		err := r.Err
   371  		if err != nil {
   372  			if _, ok := err.(*DNSError); !ok {
   373  				isTimeout := false
   374  				if err == context.DeadlineExceeded {
   375  					isTimeout = true
   376  				} else if terr, ok := err.(timeout); ok {
   377  					isTimeout = terr.Timeout()
   378  				}
   379  				err = &DNSError{
   380  					Err:       err.Error(),
   381  					Name:      host,
   382  					IsTimeout: isTimeout,
   383  				}
   384  			}
   385  		}
   386  		if trace != nil && trace.DNSDone != nil {
   387  			addrs, _ := r.Val.([]IPAddr)
   388  			trace.DNSDone(ipAddrsEface(addrs), r.Shared, err)
   389  		}
   390  		return lookupIPReturn(r.Val, err, r.Shared)
   391  	}
   392  }
   393  
   394  // lookupIPReturn turns the return values from singleflight.Do into
   395  // the return values from LookupIP.
   396  func lookupIPReturn(addrsi any, err error, shared bool) ([]IPAddr, error) {
   397  	if err != nil {
   398  		return nil, err
   399  	}
   400  	addrs := addrsi.([]IPAddr)
   401  	if shared {
   402  		clone := make([]IPAddr, len(addrs))
   403  		copy(clone, addrs)
   404  		addrs = clone
   405  	}
   406  	return addrs, nil
   407  }
   408  
   409  // ipAddrsEface returns an empty interface slice of addrs.
   410  func ipAddrsEface(addrs []IPAddr) []any {
   411  	s := make([]any, len(addrs))
   412  	for i, v := range addrs {
   413  		s[i] = v
   414  	}
   415  	return s
   416  }
   417  
   418  // LookupPort looks up the port for the given network and service.
   419  //
   420  // LookupPort uses [context.Background] internally; to specify the context, use
   421  // [Resolver.LookupPort].
   422  func LookupPort(network, service string) (port int, err error) {
   423  	return DefaultResolver.LookupPort(context.Background(), network, service)
   424  }
   425  
   426  // LookupPort looks up the port for the given network and service.
   427  //
   428  // The network must be one of "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6" or "ip".
   429  func (r *Resolver) LookupPort(ctx context.Context, network, service string) (port int, err error) {
   430  	port, needsLookup := parsePort(service)
   431  	if needsLookup {
   432  		switch network {
   433  		case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6", "ip":
   434  		case "": // a hint wildcard for Go 1.0 undocumented behavior
   435  			network = "ip"
   436  		default:
   437  			return 0, &AddrError{Err: "unknown network", Addr: network}
   438  		}
   439  		port, err = r.lookupPort(ctx, network, service)
   440  		if err != nil {
   441  			return 0, err
   442  		}
   443  	}
   444  	if 0 > port || port > 65535 {
   445  		return 0, &AddrError{Err: "invalid port", Addr: service}
   446  	}
   447  	return port, nil
   448  }
   449  
   450  // LookupCNAME returns the canonical name for the given host.
   451  // Callers that do not care about the canonical name can call
   452  // [LookupHost] or [LookupIP] directly; both take care of resolving
   453  // the canonical name as part of the lookup.
   454  //
   455  // A canonical name is the final name after following zero
   456  // or more CNAME records.
   457  // LookupCNAME does not return an error if host does not
   458  // contain DNS "CNAME" records, as long as host resolves to
   459  // address records.
   460  //
   461  // The returned canonical name is validated to be a properly
   462  // formatted presentation-format domain name.
   463  //
   464  // LookupCNAME uses [context.Background] internally; to specify the context, use
   465  // [Resolver.LookupCNAME].
   466  func LookupCNAME(host string) (cname string, err error) {
   467  	return DefaultResolver.LookupCNAME(context.Background(), host)
   468  }
   469  
   470  // LookupCNAME returns the canonical name for the given host.
   471  // Callers that do not care about the canonical name can call
   472  // [LookupHost] or [LookupIP] directly; both take care of resolving
   473  // the canonical name as part of the lookup.
   474  //
   475  // A canonical name is the final name after following zero
   476  // or more CNAME records.
   477  // LookupCNAME does not return an error if host does not
   478  // contain DNS "CNAME" records, as long as host resolves to
   479  // address records.
   480  //
   481  // The returned canonical name is validated to be a properly
   482  // formatted presentation-format domain name.
   483  func (r *Resolver) LookupCNAME(ctx context.Context, host string) (string, error) {
   484  	cname, err := r.lookupCNAME(ctx, host)
   485  	if err != nil {
   486  		return "", err
   487  	}
   488  	if !isDomainName(cname) {
   489  		return "", &DNSError{Err: errMalformedDNSRecordsDetail, Name: host}
   490  	}
   491  	return cname, nil
   492  }
   493  
   494  // LookupSRV tries to resolve an [SRV] query of the given service,
   495  // protocol, and domain name. The proto is "tcp" or "udp".
   496  // The returned records are sorted by priority and randomized
   497  // by weight within a priority.
   498  //
   499  // LookupSRV constructs the DNS name to look up following RFC 2782.
   500  // That is, it looks up _service._proto.name. To accommodate services
   501  // publishing SRV records under non-standard names, if both service
   502  // and proto are empty strings, LookupSRV looks up name directly.
   503  //
   504  // The returned service names are validated to be properly
   505  // formatted presentation-format domain names. If the response contains
   506  // invalid names, those records are filtered out and an error
   507  // will be returned alongside the remaining results, if any.
   508  func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) {
   509  	return DefaultResolver.LookupSRV(context.Background(), service, proto, name)
   510  }
   511  
   512  // LookupSRV tries to resolve an [SRV] query of the given service,
   513  // protocol, and domain name. The proto is "tcp" or "udp".
   514  // The returned records are sorted by priority and randomized
   515  // by weight within a priority.
   516  //
   517  // LookupSRV constructs the DNS name to look up following RFC 2782.
   518  // That is, it looks up _service._proto.name. To accommodate services
   519  // publishing SRV records under non-standard names, if both service
   520  // and proto are empty strings, LookupSRV looks up name directly.
   521  //
   522  // The returned service names are validated to be properly
   523  // formatted presentation-format domain names. If the response contains
   524  // invalid names, those records are filtered out and an error
   525  // will be returned alongside the remaining results, if any.
   526  func (r *Resolver) LookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) {
   527  	cname, addrs, err := r.lookupSRV(ctx, service, proto, name)
   528  	if err != nil {
   529  		return "", nil, err
   530  	}
   531  	if cname != "" && !isDomainName(cname) {
   532  		return "", nil, &DNSError{Err: "SRV header name is invalid", Name: name}
   533  	}
   534  	filteredAddrs := make([]*SRV, 0, len(addrs))
   535  	for _, addr := range addrs {
   536  		if addr == nil {
   537  			continue
   538  		}
   539  		if !isDomainName(addr.Target) {
   540  			continue
   541  		}
   542  		filteredAddrs = append(filteredAddrs, addr)
   543  	}
   544  	if len(addrs) != len(filteredAddrs) {
   545  		return cname, filteredAddrs, &DNSError{Err: errMalformedDNSRecordsDetail, Name: name}
   546  	}
   547  	return cname, filteredAddrs, nil
   548  }
   549  
   550  // LookupMX returns the DNS MX records for the given domain name sorted by preference.
   551  //
   552  // The returned mail server names are validated to be properly
   553  // formatted presentation-format domain names. If the response contains
   554  // invalid names, those records are filtered out and an error
   555  // will be returned alongside the remaining results, if any.
   556  //
   557  // LookupMX uses [context.Background] internally; to specify the context, use
   558  // [Resolver.LookupMX].
   559  func LookupMX(name string) ([]*MX, error) {
   560  	return DefaultResolver.LookupMX(context.Background(), name)
   561  }
   562  
   563  // LookupMX returns the DNS MX records for the given domain name sorted by preference.
   564  //
   565  // The returned mail server names are validated to be properly
   566  // formatted presentation-format domain names. If the response contains
   567  // invalid names, those records are filtered out and an error
   568  // will be returned alongside the remaining results, if any.
   569  func (r *Resolver) LookupMX(ctx context.Context, name string) ([]*MX, error) {
   570  	records, err := r.lookupMX(ctx, name)
   571  	if err != nil {
   572  		return nil, err
   573  	}
   574  	filteredMX := make([]*MX, 0, len(records))
   575  	for _, mx := range records {
   576  		if mx == nil {
   577  			continue
   578  		}
   579  		if !isDomainName(mx.Host) {
   580  			continue
   581  		}
   582  		filteredMX = append(filteredMX, mx)
   583  	}
   584  	if len(records) != len(filteredMX) {
   585  		return filteredMX, &DNSError{Err: errMalformedDNSRecordsDetail, Name: name}
   586  	}
   587  	return filteredMX, nil
   588  }
   589  
   590  // LookupNS returns the DNS NS records for the given domain name.
   591  //
   592  // The returned name server names are validated to be properly
   593  // formatted presentation-format domain names. If the response contains
   594  // invalid names, those records are filtered out and an error
   595  // will be returned alongside the remaining results, if any.
   596  //
   597  // LookupNS uses [context.Background] internally; to specify the context, use
   598  // [Resolver.LookupNS].
   599  func LookupNS(name string) ([]*NS, error) {
   600  	return DefaultResolver.LookupNS(context.Background(), name)
   601  }
   602  
   603  // LookupNS returns the DNS NS records for the given domain name.
   604  //
   605  // The returned name server names are validated to be properly
   606  // formatted presentation-format domain names. If the response contains
   607  // invalid names, those records are filtered out and an error
   608  // will be returned alongside the remaining results, if any.
   609  func (r *Resolver) LookupNS(ctx context.Context, name string) ([]*NS, error) {
   610  	records, err := r.lookupNS(ctx, name)
   611  	if err != nil {
   612  		return nil, err
   613  	}
   614  	filteredNS := make([]*NS, 0, len(records))
   615  	for _, ns := range records {
   616  		if ns == nil {
   617  			continue
   618  		}
   619  		if !isDomainName(ns.Host) {
   620  			continue
   621  		}
   622  		filteredNS = append(filteredNS, ns)
   623  	}
   624  	if len(records) != len(filteredNS) {
   625  		return filteredNS, &DNSError{Err: errMalformedDNSRecordsDetail, Name: name}
   626  	}
   627  	return filteredNS, nil
   628  }
   629  
   630  // LookupTXT returns the DNS TXT records for the given domain name.
   631  //
   632  // LookupTXT uses [context.Background] internally; to specify the context, use
   633  // [Resolver.LookupTXT].
   634  func LookupTXT(name string) ([]string, error) {
   635  	return DefaultResolver.lookupTXT(context.Background(), name)
   636  }
   637  
   638  // LookupTXT returns the DNS TXT records for the given domain name.
   639  func (r *Resolver) LookupTXT(ctx context.Context, name string) ([]string, error) {
   640  	return r.lookupTXT(ctx, name)
   641  }
   642  
   643  // LookupAddr performs a reverse lookup for the given address, returning a list
   644  // of names mapping to that address.
   645  //
   646  // The returned names are validated to be properly formatted presentation-format
   647  // domain names. If the response contains invalid names, those records are filtered
   648  // out and an error will be returned alongside the remaining results, if any.
   649  //
   650  // When using the host C library resolver, at most one result will be
   651  // returned. To bypass the host resolver, use a custom [Resolver].
   652  //
   653  // LookupAddr uses [context.Background] internally; to specify the context, use
   654  // [Resolver.LookupAddr].
   655  func LookupAddr(addr string) (names []string, err error) {
   656  	return DefaultResolver.LookupAddr(context.Background(), addr)
   657  }
   658  
   659  // LookupAddr performs a reverse lookup for the given address, returning a list
   660  // of names mapping to that address.
   661  //
   662  // The returned names are validated to be properly formatted presentation-format
   663  // domain names. If the response contains invalid names, those records are filtered
   664  // out and an error will be returned alongside the remaining results, if any.
   665  func (r *Resolver) LookupAddr(ctx context.Context, addr string) ([]string, error) {
   666  	names, err := r.lookupAddr(ctx, addr)
   667  	if err != nil {
   668  		return nil, err
   669  	}
   670  	filteredNames := make([]string, 0, len(names))
   671  	for _, name := range names {
   672  		if isDomainName(name) {
   673  			filteredNames = append(filteredNames, name)
   674  		}
   675  	}
   676  	if len(names) != len(filteredNames) {
   677  		return filteredNames, &DNSError{Err: errMalformedDNSRecordsDetail, Name: addr}
   678  	}
   679  	return filteredNames, nil
   680  }
   681  
   682  // errMalformedDNSRecordsDetail is the DNSError detail which is returned when a Resolver.Lookup...
   683  // method receives DNS records which contain invalid DNS names. This may be returned alongside
   684  // results which have had the malformed records filtered out.
   685  var errMalformedDNSRecordsDetail = "DNS response contained records which contain invalid names"
   686  
   687  // dial makes a new connection to the provided server (which must be
   688  // an IP address) with the provided network type, using either r.Dial
   689  // (if both r and r.Dial are non-nil) or else Dialer.DialContext.
   690  func (r *Resolver) dial(ctx context.Context, network, server string) (Conn, error) {
   691  	// Calling Dial here is scary -- we have to be sure not to
   692  	// dial a name that will require a DNS lookup, or Dial will
   693  	// call back here to translate it. The DNS config parser has
   694  	// already checked that all the cfg.servers are IP
   695  	// addresses, which Dial will use without a DNS lookup.
   696  	var c Conn
   697  	var err error
   698  	if r != nil && r.Dial != nil {
   699  		c, err = r.Dial(ctx, network, server)
   700  	} else {
   701  		var d Dialer
   702  		c, err = d.DialContext(ctx, network, server)
   703  	}
   704  	if err != nil {
   705  		return nil, mapErr(err)
   706  	}
   707  	return c, nil
   708  }
   709  
   710  // goLookupSRV returns the SRV records for a target name, built either
   711  // from its component service ("sip"), protocol ("tcp"), and name
   712  // ("example.com."), or from name directly (if service and proto are
   713  // both empty).
   714  //
   715  // In either case, the returned target name ("_sip._tcp.example.com.")
   716  // is also returned on success.
   717  //
   718  // The records are sorted by weight.
   719  func (r *Resolver) goLookupSRV(ctx context.Context, service, proto, name string) (target string, srvs []*SRV, err error) {
   720  	if service == "" && proto == "" {
   721  		target = name
   722  	} else {
   723  		target = "_" + service + "._" + proto + "." + name
   724  	}
   725  	p, server, err := r.lookup(ctx, target, dnsmessage.TypeSRV, nil)
   726  	if err != nil {
   727  		return "", nil, err
   728  	}
   729  	var cname dnsmessage.Name
   730  	for {
   731  		h, err := p.AnswerHeader()
   732  		if err == dnsmessage.ErrSectionDone {
   733  			break
   734  		}
   735  		if err != nil {
   736  			return "", nil, &DNSError{
   737  				Err:    "cannot unmarshal DNS message",
   738  				Name:   name,
   739  				Server: server,
   740  			}
   741  		}
   742  		if h.Type != dnsmessage.TypeSRV {
   743  			if err := p.SkipAnswer(); err != nil {
   744  				return "", nil, &DNSError{
   745  					Err:    "cannot unmarshal DNS message",
   746  					Name:   name,
   747  					Server: server,
   748  				}
   749  			}
   750  			continue
   751  		}
   752  		if cname.Length == 0 && h.Name.Length != 0 {
   753  			cname = h.Name
   754  		}
   755  		srv, err := p.SRVResource()
   756  		if err != nil {
   757  			return "", nil, &DNSError{
   758  				Err:    "cannot unmarshal DNS message",
   759  				Name:   name,
   760  				Server: server,
   761  			}
   762  		}
   763  		srvs = append(srvs, &SRV{Target: srv.Target.String(), Port: srv.Port, Priority: srv.Priority, Weight: srv.Weight})
   764  	}
   765  	byPriorityWeight(srvs).sort()
   766  	return cname.String(), srvs, nil
   767  }
   768  
   769  // goLookupMX returns the MX records for name.
   770  func (r *Resolver) goLookupMX(ctx context.Context, name string) ([]*MX, error) {
   771  	p, server, err := r.lookup(ctx, name, dnsmessage.TypeMX, nil)
   772  	if err != nil {
   773  		return nil, err
   774  	}
   775  	var mxs []*MX
   776  	for {
   777  		h, err := p.AnswerHeader()
   778  		if err == dnsmessage.ErrSectionDone {
   779  			break
   780  		}
   781  		if err != nil {
   782  			return nil, &DNSError{
   783  				Err:    "cannot unmarshal DNS message",
   784  				Name:   name,
   785  				Server: server,
   786  			}
   787  		}
   788  		if h.Type != dnsmessage.TypeMX {
   789  			if err := p.SkipAnswer(); err != nil {
   790  				return nil, &DNSError{
   791  					Err:    "cannot unmarshal DNS message",
   792  					Name:   name,
   793  					Server: server,
   794  				}
   795  			}
   796  			continue
   797  		}
   798  		mx, err := p.MXResource()
   799  		if err != nil {
   800  			return nil, &DNSError{
   801  				Err:    "cannot unmarshal DNS message",
   802  				Name:   name,
   803  				Server: server,
   804  			}
   805  		}
   806  		mxs = append(mxs, &MX{Host: mx.MX.String(), Pref: mx.Pref})
   807  
   808  	}
   809  	byPref(mxs).sort()
   810  	return mxs, nil
   811  }
   812  
   813  // goLookupNS returns the NS records for name.
   814  func (r *Resolver) goLookupNS(ctx context.Context, name string) ([]*NS, error) {
   815  	p, server, err := r.lookup(ctx, name, dnsmessage.TypeNS, nil)
   816  	if err != nil {
   817  		return nil, err
   818  	}
   819  	var nss []*NS
   820  	for {
   821  		h, err := p.AnswerHeader()
   822  		if err == dnsmessage.ErrSectionDone {
   823  			break
   824  		}
   825  		if err != nil {
   826  			return nil, &DNSError{
   827  				Err:    "cannot unmarshal DNS message",
   828  				Name:   name,
   829  				Server: server,
   830  			}
   831  		}
   832  		if h.Type != dnsmessage.TypeNS {
   833  			if err := p.SkipAnswer(); err != nil {
   834  				return nil, &DNSError{
   835  					Err:    "cannot unmarshal DNS message",
   836  					Name:   name,
   837  					Server: server,
   838  				}
   839  			}
   840  			continue
   841  		}
   842  		ns, err := p.NSResource()
   843  		if err != nil {
   844  			return nil, &DNSError{
   845  				Err:    "cannot unmarshal DNS message",
   846  				Name:   name,
   847  				Server: server,
   848  			}
   849  		}
   850  		nss = append(nss, &NS{Host: ns.NS.String()})
   851  	}
   852  	return nss, nil
   853  }
   854  
   855  // goLookupTXT returns the TXT records from name.
   856  func (r *Resolver) goLookupTXT(ctx context.Context, name string) ([]string, error) {
   857  	p, server, err := r.lookup(ctx, name, dnsmessage.TypeTXT, nil)
   858  	if err != nil {
   859  		return nil, err
   860  	}
   861  	var txts []string
   862  	for {
   863  		h, err := p.AnswerHeader()
   864  		if err == dnsmessage.ErrSectionDone {
   865  			break
   866  		}
   867  		if err != nil {
   868  			return nil, &DNSError{
   869  				Err:    "cannot unmarshal DNS message",
   870  				Name:   name,
   871  				Server: server,
   872  			}
   873  		}
   874  		if h.Type != dnsmessage.TypeTXT {
   875  			if err := p.SkipAnswer(); err != nil {
   876  				return nil, &DNSError{
   877  					Err:    "cannot unmarshal DNS message",
   878  					Name:   name,
   879  					Server: server,
   880  				}
   881  			}
   882  			continue
   883  		}
   884  		txt, err := p.TXTResource()
   885  		if err != nil {
   886  			return nil, &DNSError{
   887  				Err:    "cannot unmarshal DNS message",
   888  				Name:   name,
   889  				Server: server,
   890  			}
   891  		}
   892  		// Multiple strings in one TXT record need to be
   893  		// concatenated without separator to be consistent
   894  		// with previous Go resolver.
   895  		n := 0
   896  		for _, s := range txt.TXT {
   897  			n += len(s)
   898  		}
   899  		txtJoin := make([]byte, 0, n)
   900  		for _, s := range txt.TXT {
   901  			txtJoin = append(txtJoin, s...)
   902  		}
   903  		if len(txts) == 0 {
   904  			txts = make([]string, 0, 1)
   905  		}
   906  		txts = append(txts, string(txtJoin))
   907  	}
   908  	return txts, nil
   909  }
   910  
   911  func parseCNAMEFromResources(resources []dnsmessage.Resource) (string, error) {
   912  	if len(resources) == 0 {
   913  		return "", errors.New("no CNAME record received")
   914  	}
   915  	c, ok := resources[0].Body.(*dnsmessage.CNAMEResource)
   916  	if !ok {
   917  		return "", errors.New("could not parse CNAME record")
   918  	}
   919  	return c.CNAME.String(), nil
   920  }
   921  

View as plain text