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

View as plain text