Black Lives Matter. Support the Equal Justice Initiative.

Source file src/net/lookup.go

Documentation: net

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

View as plain text