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, newDNSError(errUnknownPort, errNetwork+"/"+service, "")
   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, newDNSError(errNoSuchHost, host, "")
   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, newDNSError(errNoSuchHost, host, "")
   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, newDNSError(errNoSuchHost, host, "")
   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  		err := newDNSError(mapErr(ctx.Err()), host, "")
   358  		if trace != nil && trace.DNSDone != nil {
   359  			trace.DNSDone(nil, false, err)
   360  		}
   361  		return nil, err
   362  	case r := <-ch:
   363  		dnsWaitGroup.Done()
   364  		lookupGroupCancel()
   365  		err := r.Err
   366  		if err != nil {
   367  			if _, ok := err.(*DNSError); !ok {
   368  				err = newDNSError(mapErr(err), host, "")
   369  			}
   370  		}
   371  		if trace != nil && trace.DNSDone != nil {
   372  			addrs, _ := r.Val.([]IPAddr)
   373  			trace.DNSDone(ipAddrsEface(addrs), r.Shared, err)
   374  		}
   375  		return lookupIPReturn(r.Val, err, r.Shared)
   376  	}
   377  }
   378  
   379  // lookupIPReturn turns the return values from singleflight.Do into
   380  // the return values from LookupIP.
   381  func lookupIPReturn(addrsi any, err error, shared bool) ([]IPAddr, error) {
   382  	if err != nil {
   383  		return nil, err
   384  	}
   385  	addrs := addrsi.([]IPAddr)
   386  	if shared {
   387  		clone := make([]IPAddr, len(addrs))
   388  		copy(clone, addrs)
   389  		addrs = clone
   390  	}
   391  	return addrs, nil
   392  }
   393  
   394  // ipAddrsEface returns an empty interface slice of addrs.
   395  func ipAddrsEface(addrs []IPAddr) []any {
   396  	s := make([]any, len(addrs))
   397  	for i, v := range addrs {
   398  		s[i] = v
   399  	}
   400  	return s
   401  }
   402  
   403  // LookupPort looks up the port for the given network and service.
   404  //
   405  // LookupPort uses [context.Background] internally; to specify the context, use
   406  // [Resolver.LookupPort].
   407  func LookupPort(network, service string) (port int, err error) {
   408  	return DefaultResolver.LookupPort(context.Background(), network, service)
   409  }
   410  
   411  // LookupPort looks up the port for the given network and service.
   412  //
   413  // The network must be one of "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6" or "ip".
   414  func (r *Resolver) LookupPort(ctx context.Context, network, service string) (port int, err error) {
   415  	port, needsLookup := parsePort(service)
   416  	if needsLookup {
   417  		switch network {
   418  		case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6", "ip":
   419  		case "": // a hint wildcard for Go 1.0 undocumented behavior
   420  			network = "ip"
   421  		default:
   422  			return 0, &AddrError{Err: "unknown network", Addr: network}
   423  		}
   424  		port, err = r.lookupPort(ctx, network, service)
   425  		if err != nil {
   426  			return 0, err
   427  		}
   428  	}
   429  	if 0 > port || port > 65535 {
   430  		return 0, &AddrError{Err: "invalid port", Addr: service}
   431  	}
   432  	return port, nil
   433  }
   434  
   435  // LookupCNAME returns the canonical name for the given host.
   436  // Callers that do not care about the canonical name can call
   437  // [LookupHost] or [LookupIP] directly; both take care of resolving
   438  // the canonical name as part of the lookup.
   439  //
   440  // A canonical name is the final name after following zero
   441  // or more CNAME records.
   442  // LookupCNAME does not return an error if host does not
   443  // contain DNS "CNAME" records, as long as host resolves to
   444  // address records.
   445  //
   446  // The returned canonical name is validated to be a properly
   447  // formatted presentation-format domain name.
   448  //
   449  // LookupCNAME uses [context.Background] internally; to specify the context, use
   450  // [Resolver.LookupCNAME].
   451  func LookupCNAME(host string) (cname string, err error) {
   452  	return DefaultResolver.LookupCNAME(context.Background(), host)
   453  }
   454  
   455  // LookupCNAME returns the canonical name for the given host.
   456  // Callers that do not care about the canonical name can call
   457  // [LookupHost] or [LookupIP] directly; both take care of resolving
   458  // the canonical name as part of the lookup.
   459  //
   460  // A canonical name is the final name after following zero
   461  // or more CNAME records.
   462  // LookupCNAME does not return an error if host does not
   463  // contain DNS "CNAME" records, as long as host resolves to
   464  // address records.
   465  //
   466  // The returned canonical name is validated to be a properly
   467  // formatted presentation-format domain name.
   468  func (r *Resolver) LookupCNAME(ctx context.Context, host string) (string, error) {
   469  	cname, err := r.lookupCNAME(ctx, host)
   470  	if err != nil {
   471  		return "", err
   472  	}
   473  	if !isDomainName(cname) {
   474  		return "", &DNSError{Err: errMalformedDNSRecordsDetail, Name: host}
   475  	}
   476  	return cname, nil
   477  }
   478  
   479  // LookupSRV tries to resolve an [SRV] query of the given service,
   480  // protocol, and domain name. The proto is "tcp" or "udp".
   481  // The returned records are sorted by priority and randomized
   482  // by weight within a priority.
   483  //
   484  // LookupSRV constructs the DNS name to look up following RFC 2782.
   485  // That is, it looks up _service._proto.name. To accommodate services
   486  // publishing SRV records under non-standard names, if both service
   487  // and proto are empty strings, LookupSRV looks up name directly.
   488  //
   489  // The returned service names are validated to be properly
   490  // formatted presentation-format domain names. If the response contains
   491  // invalid names, those records are filtered out and an error
   492  // will be returned alongside the remaining results, if any.
   493  func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) {
   494  	return DefaultResolver.LookupSRV(context.Background(), service, proto, name)
   495  }
   496  
   497  // LookupSRV tries to resolve an [SRV] query of the given service,
   498  // protocol, and domain name. The proto is "tcp" or "udp".
   499  // The returned records are sorted by priority and randomized
   500  // by weight within a priority.
   501  //
   502  // LookupSRV constructs the DNS name to look up following RFC 2782.
   503  // That is, it looks up _service._proto.name. To accommodate services
   504  // publishing SRV records under non-standard names, if both service
   505  // and proto are empty strings, LookupSRV looks up name directly.
   506  //
   507  // The returned service names are validated to be properly
   508  // formatted presentation-format domain names. If the response contains
   509  // invalid names, those records are filtered out and an error
   510  // will be returned alongside the remaining results, if any.
   511  func (r *Resolver) LookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) {
   512  	cname, addrs, err := r.lookupSRV(ctx, service, proto, name)
   513  	if err != nil {
   514  		return "", nil, err
   515  	}
   516  	if cname != "" && !isDomainName(cname) {
   517  		return "", nil, &DNSError{Err: "SRV header name is invalid", Name: name}
   518  	}
   519  	filteredAddrs := make([]*SRV, 0, len(addrs))
   520  	for _, addr := range addrs {
   521  		if addr == nil {
   522  			continue
   523  		}
   524  		if !isDomainName(addr.Target) {
   525  			continue
   526  		}
   527  		filteredAddrs = append(filteredAddrs, addr)
   528  	}
   529  	if len(addrs) != len(filteredAddrs) {
   530  		return cname, filteredAddrs, &DNSError{Err: errMalformedDNSRecordsDetail, Name: name}
   531  	}
   532  	return cname, filteredAddrs, nil
   533  }
   534  
   535  // LookupMX returns the DNS MX records for the given domain name sorted by preference.
   536  //
   537  // The returned mail server names are validated to be properly
   538  // formatted presentation-format domain names. If the response contains
   539  // invalid names, those records are filtered out and an error
   540  // will be returned alongside the remaining results, if any.
   541  //
   542  // LookupMX uses [context.Background] internally; to specify the context, use
   543  // [Resolver.LookupMX].
   544  func LookupMX(name string) ([]*MX, error) {
   545  	return DefaultResolver.LookupMX(context.Background(), name)
   546  }
   547  
   548  // LookupMX returns the DNS MX records for the given domain name sorted by preference.
   549  //
   550  // The returned mail server names are validated to be properly
   551  // formatted presentation-format domain names. If the response contains
   552  // invalid names, those records are filtered out and an error
   553  // will be returned alongside the remaining results, if any.
   554  func (r *Resolver) LookupMX(ctx context.Context, name string) ([]*MX, error) {
   555  	records, err := r.lookupMX(ctx, name)
   556  	if err != nil {
   557  		return nil, err
   558  	}
   559  	filteredMX := make([]*MX, 0, len(records))
   560  	for _, mx := range records {
   561  		if mx == nil {
   562  			continue
   563  		}
   564  		if !isDomainName(mx.Host) {
   565  			continue
   566  		}
   567  		filteredMX = append(filteredMX, mx)
   568  	}
   569  	if len(records) != len(filteredMX) {
   570  		return filteredMX, &DNSError{Err: errMalformedDNSRecordsDetail, Name: name}
   571  	}
   572  	return filteredMX, nil
   573  }
   574  
   575  // LookupNS returns the DNS NS records for the given domain name.
   576  //
   577  // The returned name server names are validated to be properly
   578  // formatted presentation-format domain names. If the response contains
   579  // invalid names, those records are filtered out and an error
   580  // will be returned alongside the remaining results, if any.
   581  //
   582  // LookupNS uses [context.Background] internally; to specify the context, use
   583  // [Resolver.LookupNS].
   584  func LookupNS(name string) ([]*NS, error) {
   585  	return DefaultResolver.LookupNS(context.Background(), name)
   586  }
   587  
   588  // LookupNS returns the DNS NS records for the given domain name.
   589  //
   590  // The returned name server names are validated to be properly
   591  // formatted presentation-format domain names. If the response contains
   592  // invalid names, those records are filtered out and an error
   593  // will be returned alongside the remaining results, if any.
   594  func (r *Resolver) LookupNS(ctx context.Context, name string) ([]*NS, error) {
   595  	records, err := r.lookupNS(ctx, name)
   596  	if err != nil {
   597  		return nil, err
   598  	}
   599  	filteredNS := make([]*NS, 0, len(records))
   600  	for _, ns := range records {
   601  		if ns == nil {
   602  			continue
   603  		}
   604  		if !isDomainName(ns.Host) {
   605  			continue
   606  		}
   607  		filteredNS = append(filteredNS, ns)
   608  	}
   609  	if len(records) != len(filteredNS) {
   610  		return filteredNS, &DNSError{Err: errMalformedDNSRecordsDetail, Name: name}
   611  	}
   612  	return filteredNS, nil
   613  }
   614  
   615  // LookupTXT returns the DNS TXT records for the given domain name.
   616  //
   617  // LookupTXT uses [context.Background] internally; to specify the context, use
   618  // [Resolver.LookupTXT].
   619  func LookupTXT(name string) ([]string, error) {
   620  	return DefaultResolver.lookupTXT(context.Background(), name)
   621  }
   622  
   623  // LookupTXT returns the DNS TXT records for the given domain name.
   624  func (r *Resolver) LookupTXT(ctx context.Context, name string) ([]string, error) {
   625  	return r.lookupTXT(ctx, name)
   626  }
   627  
   628  // LookupAddr performs a reverse lookup for the given address, returning a list
   629  // of names mapping to that address.
   630  //
   631  // The returned names are validated to be properly formatted presentation-format
   632  // domain names. If the response contains invalid names, those records are filtered
   633  // out and an error will be returned alongside the remaining results, if any.
   634  //
   635  // When using the host C library resolver, at most one result will be
   636  // returned. To bypass the host resolver, use a custom [Resolver].
   637  //
   638  // LookupAddr uses [context.Background] internally; to specify the context, use
   639  // [Resolver.LookupAddr].
   640  func LookupAddr(addr string) (names []string, err error) {
   641  	return DefaultResolver.LookupAddr(context.Background(), addr)
   642  }
   643  
   644  // LookupAddr performs a reverse lookup for the given address, returning a list
   645  // of names mapping to that address.
   646  //
   647  // The returned names are validated to be properly formatted presentation-format
   648  // domain names. If the response contains invalid names, those records are filtered
   649  // out and an error will be returned alongside the remaining results, if any.
   650  func (r *Resolver) LookupAddr(ctx context.Context, addr string) ([]string, error) {
   651  	names, err := r.lookupAddr(ctx, addr)
   652  	if err != nil {
   653  		return nil, err
   654  	}
   655  	filteredNames := make([]string, 0, len(names))
   656  	for _, name := range names {
   657  		if isDomainName(name) {
   658  			filteredNames = append(filteredNames, name)
   659  		}
   660  	}
   661  	if len(names) != len(filteredNames) {
   662  		return filteredNames, &DNSError{Err: errMalformedDNSRecordsDetail, Name: addr}
   663  	}
   664  	return filteredNames, nil
   665  }
   666  
   667  // errMalformedDNSRecordsDetail is the DNSError detail which is returned when a Resolver.Lookup...
   668  // method receives DNS records which contain invalid DNS names. This may be returned alongside
   669  // results which have had the malformed records filtered out.
   670  var errMalformedDNSRecordsDetail = "DNS response contained records which contain invalid names"
   671  
   672  // dial makes a new connection to the provided server (which must be
   673  // an IP address) with the provided network type, using either r.Dial
   674  // (if both r and r.Dial are non-nil) or else Dialer.DialContext.
   675  func (r *Resolver) dial(ctx context.Context, network, server string) (Conn, error) {
   676  	// Calling Dial here is scary -- we have to be sure not to
   677  	// dial a name that will require a DNS lookup, or Dial will
   678  	// call back here to translate it. The DNS config parser has
   679  	// already checked that all the cfg.servers are IP
   680  	// addresses, which Dial will use without a DNS lookup.
   681  	var c Conn
   682  	var err error
   683  	if r != nil && r.Dial != nil {
   684  		c, err = r.Dial(ctx, network, server)
   685  	} else {
   686  		var d Dialer
   687  		c, err = d.DialContext(ctx, network, server)
   688  	}
   689  	if err != nil {
   690  		return nil, mapErr(err)
   691  	}
   692  	return c, nil
   693  }
   694  
   695  // goLookupSRV returns the SRV records for a target name, built either
   696  // from its component service ("sip"), protocol ("tcp"), and name
   697  // ("example.com."), or from name directly (if service and proto are
   698  // both empty).
   699  //
   700  // In either case, the returned target name ("_sip._tcp.example.com.")
   701  // is also returned on success.
   702  //
   703  // The records are sorted by weight.
   704  func (r *Resolver) goLookupSRV(ctx context.Context, service, proto, name string) (target string, srvs []*SRV, err error) {
   705  	if service == "" && proto == "" {
   706  		target = name
   707  	} else {
   708  		target = "_" + service + "._" + proto + "." + name
   709  	}
   710  	p, server, err := r.lookup(ctx, target, dnsmessage.TypeSRV, nil)
   711  	if err != nil {
   712  		return "", nil, err
   713  	}
   714  	var cname dnsmessage.Name
   715  	for {
   716  		h, err := p.AnswerHeader()
   717  		if err == dnsmessage.ErrSectionDone {
   718  			break
   719  		}
   720  		if err != nil {
   721  			return "", nil, &DNSError{
   722  				Err:    "cannot unmarshal DNS message",
   723  				Name:   name,
   724  				Server: server,
   725  			}
   726  		}
   727  		if h.Type != dnsmessage.TypeSRV {
   728  			if err := p.SkipAnswer(); err != nil {
   729  				return "", nil, &DNSError{
   730  					Err:    "cannot unmarshal DNS message",
   731  					Name:   name,
   732  					Server: server,
   733  				}
   734  			}
   735  			continue
   736  		}
   737  		if cname.Length == 0 && h.Name.Length != 0 {
   738  			cname = h.Name
   739  		}
   740  		srv, err := p.SRVResource()
   741  		if err != nil {
   742  			return "", nil, &DNSError{
   743  				Err:    "cannot unmarshal DNS message",
   744  				Name:   name,
   745  				Server: server,
   746  			}
   747  		}
   748  		srvs = append(srvs, &SRV{Target: srv.Target.String(), Port: srv.Port, Priority: srv.Priority, Weight: srv.Weight})
   749  	}
   750  	byPriorityWeight(srvs).sort()
   751  	return cname.String(), srvs, nil
   752  }
   753  
   754  // goLookupMX returns the MX records for name.
   755  func (r *Resolver) goLookupMX(ctx context.Context, name string) ([]*MX, error) {
   756  	p, server, err := r.lookup(ctx, name, dnsmessage.TypeMX, nil)
   757  	if err != nil {
   758  		return nil, err
   759  	}
   760  	var mxs []*MX
   761  	for {
   762  		h, err := p.AnswerHeader()
   763  		if err == dnsmessage.ErrSectionDone {
   764  			break
   765  		}
   766  		if err != nil {
   767  			return nil, &DNSError{
   768  				Err:    "cannot unmarshal DNS message",
   769  				Name:   name,
   770  				Server: server,
   771  			}
   772  		}
   773  		if h.Type != dnsmessage.TypeMX {
   774  			if err := p.SkipAnswer(); err != nil {
   775  				return nil, &DNSError{
   776  					Err:    "cannot unmarshal DNS message",
   777  					Name:   name,
   778  					Server: server,
   779  				}
   780  			}
   781  			continue
   782  		}
   783  		mx, err := p.MXResource()
   784  		if err != nil {
   785  			return nil, &DNSError{
   786  				Err:    "cannot unmarshal DNS message",
   787  				Name:   name,
   788  				Server: server,
   789  			}
   790  		}
   791  		mxs = append(mxs, &MX{Host: mx.MX.String(), Pref: mx.Pref})
   792  
   793  	}
   794  	byPref(mxs).sort()
   795  	return mxs, nil
   796  }
   797  
   798  // goLookupNS returns the NS records for name.
   799  func (r *Resolver) goLookupNS(ctx context.Context, name string) ([]*NS, error) {
   800  	p, server, err := r.lookup(ctx, name, dnsmessage.TypeNS, nil)
   801  	if err != nil {
   802  		return nil, err
   803  	}
   804  	var nss []*NS
   805  	for {
   806  		h, err := p.AnswerHeader()
   807  		if err == dnsmessage.ErrSectionDone {
   808  			break
   809  		}
   810  		if err != nil {
   811  			return nil, &DNSError{
   812  				Err:    "cannot unmarshal DNS message",
   813  				Name:   name,
   814  				Server: server,
   815  			}
   816  		}
   817  		if h.Type != dnsmessage.TypeNS {
   818  			if err := p.SkipAnswer(); err != nil {
   819  				return nil, &DNSError{
   820  					Err:    "cannot unmarshal DNS message",
   821  					Name:   name,
   822  					Server: server,
   823  				}
   824  			}
   825  			continue
   826  		}
   827  		ns, err := p.NSResource()
   828  		if err != nil {
   829  			return nil, &DNSError{
   830  				Err:    "cannot unmarshal DNS message",
   831  				Name:   name,
   832  				Server: server,
   833  			}
   834  		}
   835  		nss = append(nss, &NS{Host: ns.NS.String()})
   836  	}
   837  	return nss, nil
   838  }
   839  
   840  // goLookupTXT returns the TXT records from name.
   841  func (r *Resolver) goLookupTXT(ctx context.Context, name string) ([]string, error) {
   842  	p, server, err := r.lookup(ctx, name, dnsmessage.TypeTXT, nil)
   843  	if err != nil {
   844  		return nil, err
   845  	}
   846  	var txts []string
   847  	for {
   848  		h, err := p.AnswerHeader()
   849  		if err == dnsmessage.ErrSectionDone {
   850  			break
   851  		}
   852  		if err != nil {
   853  			return nil, &DNSError{
   854  				Err:    "cannot unmarshal DNS message",
   855  				Name:   name,
   856  				Server: server,
   857  			}
   858  		}
   859  		if h.Type != dnsmessage.TypeTXT {
   860  			if err := p.SkipAnswer(); err != nil {
   861  				return nil, &DNSError{
   862  					Err:    "cannot unmarshal DNS message",
   863  					Name:   name,
   864  					Server: server,
   865  				}
   866  			}
   867  			continue
   868  		}
   869  		txt, err := p.TXTResource()
   870  		if err != nil {
   871  			return nil, &DNSError{
   872  				Err:    "cannot unmarshal DNS message",
   873  				Name:   name,
   874  				Server: server,
   875  			}
   876  		}
   877  		// Multiple strings in one TXT record need to be
   878  		// concatenated without separator to be consistent
   879  		// with previous Go resolver.
   880  		n := 0
   881  		for _, s := range txt.TXT {
   882  			n += len(s)
   883  		}
   884  		txtJoin := make([]byte, 0, n)
   885  		for _, s := range txt.TXT {
   886  			txtJoin = append(txtJoin, s...)
   887  		}
   888  		if len(txts) == 0 {
   889  			txts = make([]string, 0, 1)
   890  		}
   891  		txts = append(txts, string(txtJoin))
   892  	}
   893  	return txts, nil
   894  }
   895  
   896  func parseCNAMEFromResources(resources []dnsmessage.Resource) (string, error) {
   897  	if len(resources) == 0 {
   898  		return "", errors.New("no CNAME record received")
   899  	}
   900  	c, ok := resources[0].Body.(*dnsmessage.CNAMEResource)
   901  	if !ok {
   902  		return "", errors.New("could not parse CNAME record")
   903  	}
   904  	return c.CNAME.String(), nil
   905  }
   906  

View as plain text