// 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 netimport ()// 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,"submissions": 465,"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.WaitGroupconst maxProtoLength = len("RSVP-E2E-IGNORE") + 10// with room to growfunc lookupProtocolMap( string) (int, error) {var [maxProtoLength]byte := copy([:], )lowerASCIIBytes([:]) , := protocols[string([:])]if ! || != len() {return0, &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") + 10func lookupPortMap(, string) ( int, error) {switch {case"ip": // no hintsif , := lookupPortMapWithNetwork("tcp", "ip", ); == nil {return , nil }returnlookupPortMapWithNetwork("udp", "ip", )case"tcp", "tcp4", "tcp6":returnlookupPortMapWithNetwork("tcp", "tcp", )case"udp", "udp4", "udp6":returnlookupPortMapWithNetwork("udp", "udp", ) }return0, &DNSError{Err: "unknown network", Name: + "/" + }}func lookupPortMapWithNetwork(, , string) ( int, error) {if , := services[]; {var [maxPortBufSize]byte := copy([:], )lowerASCIIBytes([:])if , := [string([:])]; && == len() {return , nil }return0, newDNSError(errUnknownPort, +"/"+, "") }return0, &DNSError{Err: "unknown network", Name: + "/" + }}// 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 == "" {return0 } := [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.varDefaultResolver = &Resolver{}// A Resolver looks up names and numbers.//// A nil *Resolver is equivalent to a zero Resolver.typeResolverstruct {// 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) {returnDefaultResolver.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.if == "" {returnnil, newDNSError(errNoSuchHost, , "") }if , := netip.ParseAddr(); == 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 {returnnil, } := 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 {returnnil, }switch {case"ip", "ip4", "ip6":default:returnnil, UnknownNetworkError() }if == "" {returnnil, newDNSError(errNoSuchHost, , "") } , := .internetAddrList(, , )if != nil {returnnil, } := 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 {returnnil, } := 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():returnnildefault: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.if == "" {returnnil, newDNSError(errNoSuchHost, , "") }if , := netip.ParseAddr(); == nil {return []IPAddr{{IP: IP(.AsSlice()).To16(), Zone: .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. := .lookupIPif , := .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) {returntestHookLookupIP(, , , ) }) := func( <-chansingleflight.Result, context.CancelFunc) { <-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() { ()go (, func() {}) } else {go (, ) } := newDNSError(mapErr(.Err()), , "")if != nil && .DNSDone != nil { .DNSDone(nil, false, ) }returnnil, case := <-:dnsWaitGroup.Done() () := .Errif != nil {if , := .(*DNSError); ! { = newDNSError(mapErr(), , "") } }if != nil && .DNSDone != nil { , := .Val.([]IPAddr) .DNSDone(ipAddrsEface(), .Shared, ) }returnlookupIPReturn(.Val, , .Shared) }}// lookupIPReturn turns the return values from singleflight.Do into// the return values from LookupIP.func lookupIPReturn( any, error, bool) ([]IPAddr, error) {if != nil {returnnil, } := .([]IPAddr)if { := make([]IPAddr, len())copy(, ) = }return , nil}// ipAddrsEface returns an empty interface slice of addrs.func ipAddrsEface( []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) {returnDefaultResolver.LookupPort(context.Background(), , )}// LookupPort looks up the port for the given network and service.//// The network must be one of "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6" or "ip".func ( *Resolver) ( context.Context, , string) ( int, error) { , := parsePort()if {switch {case"tcp", "tcp4", "tcp6", "udp", "udp4", "udp6", "ip":case"": // a hint wildcard for Go 1.0 undocumented behavior = "ip"default:return0, &AddrError{Err: "unknown network", Addr: } } , = .lookupPort(, , )if != nil {return0, } }if0 > || > 65535 {return0, &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) {returnDefaultResolver.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) {returnDefaultResolver.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(, ) }iflen() != 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) {returnDefaultResolver.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 {returnnil, } := make([]*MX, 0, len())for , := range {if == nil {continue }if !isDomainName(.Host) {continue } = append(, ) }iflen() != 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) {returnDefaultResolver.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 {returnnil, } := make([]*NS, 0, len())for , := range {if == nil {continue }if !isDomainName(.Host) {continue } = append(, ) }iflen() != 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) {returnDefaultResolver.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) {returnDefaultResolver.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 {returnnil, } := make([]string, 0, len())for , := range {ifisDomainName() { = append(, ) } }iflen() != 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"// dial makes a new connection to the provided server (which must be// an IP address) with the provided network type, using either r.Dial// (if both r and r.Dial are non-nil) or else Dialer.DialContext.func ( *Resolver) ( context.Context, , string) (Conn, error) {// Calling Dial here is scary -- we have to be sure not to // dial a name that will require a DNS lookup, or Dial will // call back here to translate it. The DNS config parser has // already checked that all the cfg.servers are IP // addresses, which Dial will use without a DNS lookup.varConnvarerrorif != nil && .Dial != nil { , = .Dial(, , ) } else {varDialer , = .DialContext(, , ) }if != nil {returnnil, mapErr() }return , nil}// goLookupSRV returns the SRV records for a target name, built either// from its component service ("sip"), protocol ("tcp"), and name// ("example.com."), or from name directly (if service and proto are// both empty).//// In either case, the returned target name ("_sip._tcp.example.com.")// is also returned on success.//// The records are sorted by weight.func ( *Resolver) ( context.Context, , , string) ( string, []*SRV, error) {if == "" && == "" { = } else { = "_" + + "._" + + "." + } , , := .lookup(, , dnsmessage.TypeSRV, nil)if != nil {return"", nil, }vardnsmessage.Namefor { , := .AnswerHeader()if == dnsmessage.ErrSectionDone {break }if != nil {return"", nil, &DNSError{Err: "cannot unmarshal DNS message",Name: ,Server: , } }if .Type != dnsmessage.TypeSRV {if := .SkipAnswer(); != nil {return"", nil, &DNSError{Err: "cannot unmarshal DNS message",Name: ,Server: , } }continue }if .Length == 0 && .Name.Length != 0 { = .Name } , := .SRVResource()if != nil {return"", nil, &DNSError{Err: "cannot unmarshal DNS message",Name: ,Server: , } } = append(, &SRV{Target: .Target.String(), Port: .Port, Priority: .Priority, Weight: .Weight}) }byPriorityWeight().sort()return .String(), , nil}// goLookupMX returns the MX records for name.func ( *Resolver) ( context.Context, string) ([]*MX, error) { , , := .lookup(, , dnsmessage.TypeMX, nil)if != nil {returnnil, }var []*MXfor { , := .AnswerHeader()if == dnsmessage.ErrSectionDone {break }if != nil {returnnil, &DNSError{Err: "cannot unmarshal DNS message",Name: ,Server: , } }if .Type != dnsmessage.TypeMX {if := .SkipAnswer(); != nil {returnnil, &DNSError{Err: "cannot unmarshal DNS message",Name: ,Server: , } }continue } , := .MXResource()if != nil {returnnil, &DNSError{Err: "cannot unmarshal DNS message",Name: ,Server: , } } = append(, &MX{Host: .MX.String(), Pref: .Pref}) }byPref().sort()return , nil}// goLookupNS returns the NS records for name.func ( *Resolver) ( context.Context, string) ([]*NS, error) { , , := .lookup(, , dnsmessage.TypeNS, nil)if != nil {returnnil, }var []*NSfor { , := .AnswerHeader()if == dnsmessage.ErrSectionDone {break }if != nil {returnnil, &DNSError{Err: "cannot unmarshal DNS message",Name: ,Server: , } }if .Type != dnsmessage.TypeNS {if := .SkipAnswer(); != nil {returnnil, &DNSError{Err: "cannot unmarshal DNS message",Name: ,Server: , } }continue } , := .NSResource()if != nil {returnnil, &DNSError{Err: "cannot unmarshal DNS message",Name: ,Server: , } } = append(, &NS{Host: .NS.String()}) }return , nil}// goLookupTXT returns the TXT records from name.func ( *Resolver) ( context.Context, string) ([]string, error) { , , := .lookup(, , dnsmessage.TypeTXT, nil)if != nil {returnnil, }var []stringfor { , := .AnswerHeader()if == dnsmessage.ErrSectionDone {break }if != nil {returnnil, &DNSError{Err: "cannot unmarshal DNS message",Name: ,Server: , } }if .Type != dnsmessage.TypeTXT {if := .SkipAnswer(); != nil {returnnil, &DNSError{Err: "cannot unmarshal DNS message",Name: ,Server: , } }continue } , := .TXTResource()if != nil {returnnil, &DNSError{Err: "cannot unmarshal DNS message",Name: ,Server: , } }// Multiple strings in one TXT record need to be // concatenated without separator to be consistent // with previous Go resolver. := 0for , := range .TXT { += len() } := make([]byte, 0, )for , := range .TXT { = append(, ...) }iflen() == 0 { = make([]string, 0, 1) } = append(, string()) }return , nil}func parseCNAMEFromResources( []dnsmessage.Resource) (string, error) {iflen() == 0 {return"", errors.New("no CNAME record received") } , := [0].Body.(*dnsmessage.CNAMEResource)if ! {return"", errors.New("could not parse CNAME record") }return .CNAME.String(), nil}
The pages are generated with Goldsv0.7.0-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.