Source File
lookup.go
Belonging Package
net
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package net
import (
)
// protocols contains minimal mappings between internet protocol
// names and numbers for platforms that don't have a complete list of
// protocol numbers.
//
// See https://www.iana.org/assignments/protocol-numbers
//
// On Unix, this map is augmented by readProtocols via lookupProtocol.
var protocols = map[string]int{
"icmp": 1,
"igmp": 2,
"tcp": 6,
"udp": 17,
"ipv6-icmp": 58,
}
// services contains minimal mappings between services names and port
// numbers for platforms that don't have a complete list of port numbers.
//
// See https://www.iana.org/assignments/service-names-port-numbers
//
// On Unix, this map is augmented by readServices via goLookupPort.
var services = map[string]map[string]int{
"udp": {
"domain": 53,
},
"tcp": {
"ftp": 21,
"ftps": 990,
"gopher": 70, // ʕ◔ϖ◔ʔ
"http": 80,
"https": 443,
"imap2": 143,
"imap3": 220,
"imaps": 993,
"pop3": 110,
"pop3s": 995,
"smtp": 25,
"ssh": 22,
"telnet": 23,
},
}
// dnsWaitGroup can be used by tests to wait for all DNS goroutines to
// complete. This avoids races on the test hooks.
var dnsWaitGroup sync.WaitGroup
const maxProtoLength = len("RSVP-E2E-IGNORE") + 10 // with room to grow
func ( string) (int, error) {
var [maxProtoLength]byte
:= copy([:], )
lowerASCIIBytes([:])
, := protocols[string([:])]
if ! || != len() {
return 0, &AddrError{Err: "unknown IP protocol specified", Addr: }
}
return , nil
}
// maxPortBufSize is the longest reasonable name of a service
// (non-numeric port).
// Currently the longest known IANA-unregistered name is
// "mobility-header", so we use that length, plus some slop in case
// something longer is added in the future.
const maxPortBufSize = len("mobility-header") + 10
func (, string) ( int, error) {
switch {
case "tcp4", "tcp6":
= "tcp"
case "udp4", "udp6":
= "udp"
}
if , := services[]; {
var [maxPortBufSize]byte
:= copy([:], )
lowerASCIIBytes([:])
if , := [string([:])]; && == len() {
return , nil
}
}
return 0, &AddrError{Err: "unknown port", Addr: + "/" + }
}
// ipVersion returns the provided network's IP version: '4', '6' or 0
// if network does not end in a '4' or '6' byte.
func ( string) byte {
if == "" {
return 0
}
:= [len()-1]
if != '4' && != '6' {
= 0
}
return
}
// DefaultResolver is the resolver used by the package-level Lookup
// functions and by Dialers without a specified Resolver.
var DefaultResolver = &Resolver{}
// A Resolver looks up names and numbers.
//
// A nil *Resolver is equivalent to a zero Resolver.
type Resolver struct {
// PreferGo controls whether Go's built-in DNS resolver is preferred
// on platforms where it's available. It is equivalent to setting
// GODEBUG=netdns=go, but scoped to just this resolver.
PreferGo bool
// StrictErrors controls the behavior of temporary errors
// (including timeout, socket errors, and SERVFAIL) when using
// Go's built-in resolver. For a query composed of multiple
// sub-queries (such as an A+AAAA address lookup, or walking the
// DNS search list), this option causes such errors to abort the
// whole query instead of returning a partial result. This is
// not enabled by default because it may affect compatibility
// with resolvers that process AAAA queries incorrectly.
StrictErrors bool
// Dial optionally specifies an alternate dialer for use by
// Go's built-in DNS resolver to make TCP and UDP connections
// to DNS services. The host in the address parameter will
// always be a literal IP address and not a host name, and the
// port in the address parameter will be a literal port number
// and not a service name.
// If the Conn returned is also a PacketConn, sent and received DNS
// messages must adhere to RFC 1035 section 4.2.1, "UDP usage".
// Otherwise, DNS messages transmitted over Conn must adhere
// to RFC 7766 section 5, "Transport Protocol Selection".
// If nil, the default dialer is used.
Dial func(ctx context.Context, network, address string) (Conn, error)
// lookupGroup merges LookupIPAddr calls together for lookups for the same
// host. The lookupGroup key is the LookupIPAddr.host argument.
// The return values are ([]IPAddr, error).
lookupGroup singleflight.Group
// TODO(bradfitz): optional interface impl override hook
// TODO(bradfitz): Timeout time.Duration?
}
func ( *Resolver) () bool { return != nil && .PreferGo }
func ( *Resolver) () bool { return != nil && .StrictErrors }
func ( *Resolver) () *singleflight.Group {
if == nil {
return &DefaultResolver.lookupGroup
}
return &.lookupGroup
}
// LookupHost looks up the given host using the local resolver.
// It returns a slice of that host's addresses.
//
// LookupHost uses context.Background internally; to specify the context, use
// Resolver.LookupHost.
func ( string) ( []string, error) {
return DefaultResolver.LookupHost(context.Background(), )
}
// LookupHost looks up the given host using the local resolver.
// It returns a slice of that host's addresses.
func ( *Resolver) ( context.Context, string) ( []string, error) {
// Make sure that no matter what we do later, host=="" is rejected.
// parseIP, for example, does accept empty strings.
if == "" {
return nil, &DNSError{Err: errNoSuchHost.Error(), Name: , IsNotFound: true}
}
if , := parseIPZone(); != nil {
return []string{}, nil
}
return .lookupHost(, )
}
// LookupIP looks up host using the local resolver.
// It returns a slice of that host's IPv4 and IPv6 addresses.
func ( string) ([]IP, error) {
, := DefaultResolver.LookupIPAddr(context.Background(), )
if != nil {
return nil,
}
:= make([]IP, len())
for , := range {
[] = .IP
}
return , nil
}
// LookupIPAddr looks up host using the local resolver.
// It returns a slice of that host's IPv4 and IPv6 addresses.
func ( *Resolver) ( context.Context, string) ([]IPAddr, error) {
return .lookupIPAddr(, "ip", )
}
// LookupIP looks up host for the given network using the local resolver.
// It returns a slice of that host's IP addresses of the type specified by
// network.
// network must be one of "ip", "ip4" or "ip6".
func ( *Resolver) ( context.Context, , string) ([]IP, error) {
, , := parseNetwork(, , false)
if != nil {
return nil,
}
switch {
case "ip", "ip4", "ip6":
default:
return nil, UnknownNetworkError()
}
, := .internetAddrList(, , )
if != nil {
return nil,
}
:= make([]IP, 0, len())
for , := range {
= append(, .(*IPAddr).IP)
}
return , nil
}
// LookupNetIP looks up host using the local resolver.
// It returns a slice of that host's IP addresses of the type specified by
// network.
// The network must be one of "ip", "ip4" or "ip6".
func ( *Resolver) ( context.Context, , string) ([]netip.Addr, error) {
// TODO(bradfitz): make this efficient, making the internal net package
// type throughout be netip.Addr and only converting to the net.IP slice
// version at the edge. But for now (2021-10-20), this is a wrapper around
// the old way.
, := .LookupIP(, , )
if != nil {
return nil,
}
:= make([]netip.Addr, 0, len())
for , := range {
if , := netip.AddrFromSlice(); {
= append(, )
}
}
return , nil
}
// onlyValuesCtx is a context that uses an underlying context
// for value lookup if the underlying context hasn't yet expired.
type onlyValuesCtx struct {
context.Context
lookupValues context.Context
}
var _ context.Context = (*onlyValuesCtx)(nil)
// Value performs a lookup if the original context hasn't expired.
func ( *onlyValuesCtx) ( any) any {
select {
case <-.lookupValues.Done():
return nil
default:
return .lookupValues.Value()
}
}
// withUnexpiredValuesPreserved returns a context.Context that only uses lookupCtx
// for its values, otherwise it is never canceled and has no deadline.
// If the lookup context expires, any looked up values will return nil.
// See Issue 28600.
func ( context.Context) context.Context {
return &onlyValuesCtx{Context: context.Background(), lookupValues: }
}
// lookupIPAddr looks up host using the local resolver and particular network.
// It returns a slice of that host's IPv4 and IPv6 addresses.
func ( *Resolver) ( context.Context, , string) ([]IPAddr, error) {
// Make sure that no matter what we do later, host=="" is rejected.
// parseIP, for example, does accept empty strings.
if == "" {
return nil, &DNSError{Err: errNoSuchHost.Error(), Name: , IsNotFound: true}
}
if , := parseIPZone(); != nil {
return []IPAddr{{IP: , Zone: }}, nil
}
, := .Value(nettrace.TraceKey{}).(*nettrace.Trace)
if != nil && .DNSStart != nil {
.DNSStart()
}
// The underlying resolver func is lookupIP by default but it
// can be overridden by tests. This is needed by net/http, so it
// uses a context key instead of unexported variables.
:= .lookupIP
if , := .Value(nettrace.LookupIPAltResolverKey{}).(func(context.Context, string, string) ([]IPAddr, error)); != nil {
=
}
// We don't want a cancellation of ctx to affect the
// lookupGroup operation. Otherwise if our context gets
// canceled it might cause an error to be returned to a lookup
// using a completely different context. However we need to preserve
// only the values in context. See Issue 28600.
, := context.WithCancel(withUnexpiredValuesPreserved())
:= + "\000" +
dnsWaitGroup.Add(1)
, := .getLookupGroup().DoChan(, func() (any, error) {
defer dnsWaitGroup.Done()
return testHookLookupIP(, , , )
})
if ! {
dnsWaitGroup.Done()
}
select {
case <-.Done():
// Our context was canceled. If we are the only
// goroutine looking up this key, then drop the key
// from the lookupGroup and cancel the lookup.
// If there are other goroutines looking up this key,
// let the lookup continue uncanceled, and let later
// lookups with the same key share the result.
// See issues 8602, 20703, 22724.
if .getLookupGroup().ForgetUnshared() {
()
} else {
go func() {
<-
()
}()
}
:= .Err()
:= &DNSError{
Err: mapErr().Error(),
Name: ,
IsTimeout: == context.DeadlineExceeded,
}
if != nil && .DNSDone != nil {
.DNSDone(nil, false, )
}
return nil,
case := <-:
()
:= .Err
if != nil {
if , := .(*DNSError); ! {
:= false
if == context.DeadlineExceeded {
= true
} else if , := .(timeout); {
= .Timeout()
}
= &DNSError{
Err: .Error(),
Name: ,
IsTimeout: ,
}
}
}
if != nil && .DNSDone != nil {
, := .Val.([]IPAddr)
.DNSDone(ipAddrsEface(), .Shared, )
}
return lookupIPReturn(.Val, , .Shared)
}
}
// lookupIPReturn turns the return values from singleflight.Do into
// the return values from LookupIP.
func ( any, error, bool) ([]IPAddr, error) {
if != nil {
return nil,
}
:= .([]IPAddr)
if {
:= make([]IPAddr, len())
copy(, )
=
}
return , nil
}
// ipAddrsEface returns an empty interface slice of addrs.
func ( []IPAddr) []any {
:= make([]any, len())
for , := range {
[] =
}
return
}
// LookupPort looks up the port for the given network and service.
//
// LookupPort uses context.Background internally; to specify the context, use
// Resolver.LookupPort.
func (, string) ( int, error) {
return DefaultResolver.LookupPort(context.Background(), , )
}
// LookupPort looks up the port for the given network and service.
func ( *Resolver) ( context.Context, , string) ( int, error) {
, := parsePort()
if {
switch {
case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6":
case "": // a hint wildcard for Go 1.0 undocumented behavior
= "ip"
default:
return 0, &AddrError{Err: "unknown network", Addr: }
}
, = .lookupPort(, , )
if != nil {
return 0,
}
}
if 0 > || > 65535 {
return 0, &AddrError{Err: "invalid port", Addr: }
}
return , nil
}
// LookupCNAME returns the canonical name for the given host.
// Callers that do not care about the canonical name can call
// LookupHost or LookupIP directly; both take care of resolving
// the canonical name as part of the lookup.
//
// A canonical name is the final name after following zero
// or more CNAME records.
// LookupCNAME does not return an error if host does not
// contain DNS "CNAME" records, as long as host resolves to
// address records.
//
// The returned canonical name is validated to be a properly
// formatted presentation-format domain name.
//
// LookupCNAME uses context.Background internally; to specify the context, use
// Resolver.LookupCNAME.
func ( string) ( string, error) {
return DefaultResolver.LookupCNAME(context.Background(), )
}
// LookupCNAME returns the canonical name for the given host.
// Callers that do not care about the canonical name can call
// LookupHost or LookupIP directly; both take care of resolving
// the canonical name as part of the lookup.
//
// A canonical name is the final name after following zero
// or more CNAME records.
// LookupCNAME does not return an error if host does not
// contain DNS "CNAME" records, as long as host resolves to
// address records.
//
// The returned canonical name is validated to be a properly
// formatted presentation-format domain name.
func ( *Resolver) ( context.Context, string) (string, error) {
, := .lookupCNAME(, )
if != nil {
return "",
}
if !isDomainName() {
return "", &DNSError{Err: errMalformedDNSRecordsDetail, Name: }
}
return , nil
}
// LookupSRV tries to resolve an SRV query of the given service,
// protocol, and domain name. The proto is "tcp" or "udp".
// The returned records are sorted by priority and randomized
// by weight within a priority.
//
// LookupSRV constructs the DNS name to look up following RFC 2782.
// That is, it looks up _service._proto.name. To accommodate services
// publishing SRV records under non-standard names, if both service
// and proto are empty strings, LookupSRV looks up name directly.
//
// The returned service names are validated to be properly
// formatted presentation-format domain names. If the response contains
// invalid names, those records are filtered out and an error
// will be returned alongside the remaining results, if any.
func (, , string) ( string, []*SRV, error) {
return DefaultResolver.LookupSRV(context.Background(), , , )
}
// LookupSRV tries to resolve an SRV query of the given service,
// protocol, and domain name. The proto is "tcp" or "udp".
// The returned records are sorted by priority and randomized
// by weight within a priority.
//
// LookupSRV constructs the DNS name to look up following RFC 2782.
// That is, it looks up _service._proto.name. To accommodate services
// publishing SRV records under non-standard names, if both service
// and proto are empty strings, LookupSRV looks up name directly.
//
// The returned service names are validated to be properly
// formatted presentation-format domain names. If the response contains
// invalid names, those records are filtered out and an error
// will be returned alongside the remaining results, if any.
func ( *Resolver) ( context.Context, , , string) (string, []*SRV, error) {
, , := .lookupSRV(, , , )
if != nil {
return "", nil,
}
if != "" && !isDomainName() {
return "", nil, &DNSError{Err: "SRV header name is invalid", Name: }
}
:= make([]*SRV, 0, len())
for , := range {
if == nil {
continue
}
if !isDomainName(.Target) {
continue
}
= append(, )
}
if len() != len() {
return , , &DNSError{Err: errMalformedDNSRecordsDetail, Name: }
}
return , , nil
}
// LookupMX returns the DNS MX records for the given domain name sorted by preference.
//
// The returned mail server names are validated to be properly
// formatted presentation-format domain names. If the response contains
// invalid names, those records are filtered out and an error
// will be returned alongside the remaining results, if any.
//
// LookupMX uses context.Background internally; to specify the context, use
// Resolver.LookupMX.
func ( string) ([]*MX, error) {
return DefaultResolver.LookupMX(context.Background(), )
}
// LookupMX returns the DNS MX records for the given domain name sorted by preference.
//
// The returned mail server names are validated to be properly
// formatted presentation-format domain names. If the response contains
// invalid names, those records are filtered out and an error
// will be returned alongside the remaining results, if any.
func ( *Resolver) ( context.Context, string) ([]*MX, error) {
, := .lookupMX(, )
if != nil {
return nil,
}
:= make([]*MX, 0, len())
for , := range {
if == nil {
continue
}
if !isDomainName(.Host) {
continue
}
= append(, )
}
if len() != len() {
return , &DNSError{Err: errMalformedDNSRecordsDetail, Name: }
}
return , nil
}
// LookupNS returns the DNS NS records for the given domain name.
//
// The returned name server names are validated to be properly
// formatted presentation-format domain names. If the response contains
// invalid names, those records are filtered out and an error
// will be returned alongside the remaining results, if any.
//
// LookupNS uses context.Background internally; to specify the context, use
// Resolver.LookupNS.
func ( string) ([]*NS, error) {
return DefaultResolver.LookupNS(context.Background(), )
}
// LookupNS returns the DNS NS records for the given domain name.
//
// The returned name server names are validated to be properly
// formatted presentation-format domain names. If the response contains
// invalid names, those records are filtered out and an error
// will be returned alongside the remaining results, if any.
func ( *Resolver) ( context.Context, string) ([]*NS, error) {
, := .lookupNS(, )
if != nil {
return nil,
}
:= make([]*NS, 0, len())
for , := range {
if == nil {
continue
}
if !isDomainName(.Host) {
continue
}
= append(, )
}
if len() != len() {
return , &DNSError{Err: errMalformedDNSRecordsDetail, Name: }
}
return , nil
}
// LookupTXT returns the DNS TXT records for the given domain name.
//
// LookupTXT uses context.Background internally; to specify the context, use
// Resolver.LookupTXT.
func ( string) ([]string, error) {
return DefaultResolver.lookupTXT(context.Background(), )
}
// LookupTXT returns the DNS TXT records for the given domain name.
func ( *Resolver) ( context.Context, string) ([]string, error) {
return .lookupTXT(, )
}
// LookupAddr performs a reverse lookup for the given address, returning a list
// of names mapping to that address.
//
// The returned names are validated to be properly formatted presentation-format
// domain names. If the response contains invalid names, those records are filtered
// out and an error will be returned alongside the remaining results, if any.
//
// When using the host C library resolver, at most one result will be
// returned. To bypass the host resolver, use a custom Resolver.
//
// LookupAddr uses context.Background internally; to specify the context, use
// Resolver.LookupAddr.
func ( string) ( []string, error) {
return DefaultResolver.LookupAddr(context.Background(), )
}
// LookupAddr performs a reverse lookup for the given address, returning a list
// of names mapping to that address.
//
// The returned names are validated to be properly formatted presentation-format
// domain names. If the response contains invalid names, those records are filtered
// out and an error will be returned alongside the remaining results, if any.
func ( *Resolver) ( context.Context, string) ([]string, error) {
, := .lookupAddr(, )
if != nil {
return nil,
}
:= make([]string, 0, len())
for , := range {
if isDomainName() {
= append(, )
}
}
if len() != len() {
return , &DNSError{Err: errMalformedDNSRecordsDetail, Name: }
}
return , nil
}
// errMalformedDNSRecordsDetail is the DNSError detail which is returned when a Resolver.Lookup...
// method receives DNS records which contain invalid DNS names. This may be returned alongside
// results which have had the malformed records filtered out.
var errMalformedDNSRecordsDetail = "DNS response contained records which contain invalid names"
The pages are generated with Golds v0.4.9. (GOOS=linux GOARCH=amd64)