// 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 lookupProtocolMap( 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 lookupPortMap(,  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 ipVersion( 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
}

// 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) ( interface{}) interface{} {
	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 withUnexpiredValuesPreserved( 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() (interface{}, 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() {
				<-
				()
			}()
		}
		 := mapErr(.Err())
		if  != nil && .DNSDone != nil {
			.DNSDone(nil, false, )
		}
		return nil, 
	case  := <-:
		()
		if  != nil && .DNSDone != nil {
			,  := .Val.([]IPAddr)
			.DNSDone(ipAddrsEface(), .Shared, .Err)
		}
		return lookupIPReturn(.Val, .Err, .Shared)
	}
}

// lookupIPReturn turns the return values from singleflight.Do into
// the return values from LookupIP.
func lookupIPReturn( interface{},  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 ipAddrsEface( []IPAddr) []interface{} {
	 := make([]interface{}, 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 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 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 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 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
		}
		// Bypass the hostname validity check for targets which contain only a dot,
		// as this is used to represent a 'Null' MX record.
		if .Host != "." && !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 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 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 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 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 recieves 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"