Declared map variable without initialization; writing to a nil map causes a panic. Use make() to initialize
var protocols = map[string]int{
1// Copyright 2012 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.45package net67import (8 "context"9 "errors"10 "internal/nettrace"11 "internal/singleflight"12 "internal/stringslite"13 "net/netip"14 "sync"1516 "golang.org/x/net/dns/dnsmessage"17)1819// protocols contains minimal mappings between internet protocol20// names and numbers for platforms that don't have a complete list of21// protocol numbers.22//23// See https://www.iana.org/assignments/protocol-numbers24//25// On Unix, this map is augmented by readProtocols via lookupProtocol.26var protocols = map[string]int{27 "icmp": 1,28 "igmp": 2,29 "tcp": 6,30 "udp": 17,31 "ipv6-icmp": 58,32}3334// services contains minimal mappings between services names and port35// numbers for platforms that don't have a complete list of port numbers.36//37// See https://www.iana.org/assignments/service-names-port-numbers38//39// On Unix, this map is augmented by readServices via goLookupPort.40var services = map[string]map[string]int{41 "udp": {42 "domain": 53,43 },44 "tcp": {45 "ftp": 21,46 "ftps": 990,47 "gopher": 70, // ʕ◔ϖ◔ʔ48 "http": 80,49 "https": 443,50 "imap2": 143,51 "imap3": 220,52 "imaps": 993,53 "pop3": 110,54 "pop3s": 995,55 "smtp": 25,56 "submissions": 465,57 "ssh": 22,58 "telnet": 23,59 },60}6162// dnsWaitGroup can be used by tests to wait for all DNS goroutines to63// complete. This avoids races on the test hooks.64var dnsWaitGroup sync.WaitGroup6566const maxProtoLength = len("RSVP-E2E-IGNORE") + 10 // with room to grow6768func lookupProtocolMap(name string) (int, error) {69 var lowerProtocol [maxProtoLength]byte70 n := copy(lowerProtocol[:], name)71 lowerASCIIBytes(lowerProtocol[:n])72 proto, found := protocols[string(lowerProtocol[:n])]73 if !found || n != len(name) {74 return 0, &AddrError{Err: "unknown IP protocol specified", Addr: name}75 }76 return proto, nil77}7879// maxPortBufSize is the longest reasonable name of a service80// (non-numeric port).81// Currently the longest known IANA-unregistered name is82// "mobility-header", so we use that length, plus some slop in case83// something longer is added in the future.84const maxPortBufSize = len("mobility-header") + 108586func lookupPortMap(network, service string) (port int, error error) {87 switch network {88 case "ip": // no hints89 if p, err := lookupPortMapWithNetwork("tcp", "ip", service); err == nil {90 return p, nil91 }92 return lookupPortMapWithNetwork("udp", "ip", service)93 case "tcp", "tcp4", "tcp6":94 return lookupPortMapWithNetwork("tcp", "tcp", service)95 case "udp", "udp4", "udp6":96 return lookupPortMapWithNetwork("udp", "udp", service)97 }98 return 0, &DNSError{Err: "unknown network", Name: network + "/" + service}99}100101func lookupPortMapWithNetwork(network, errNetwork, service string) (port int, error error) {102 if m, ok := services[network]; ok {103 var lowerService [maxPortBufSize]byte104 n := copy(lowerService[:], service)105 lowerASCIIBytes(lowerService[:n])106 if port, ok := m[string(lowerService[:n])]; ok && n == len(service) {107 return port, nil108 }109 return 0, newDNSError(errUnknownPort, errNetwork+"/"+service, "")110 }111 return 0, &DNSError{Err: "unknown network", Name: errNetwork + "/" + service}112}113114// ipVersion returns the provided network's IP version: '4', '6' or 0115// if network does not end in a '4' or '6' byte.116func ipVersion(network string) byte {117 if network == "" {118 return 0119 }120 n := network[len(network)-1]121 if n != '4' && n != '6' {122 n = 0123 }124 return n125}126127// DefaultResolver is the resolver used by the package-level Lookup128// functions and by Dialers without a specified Resolver.129var DefaultResolver = &Resolver{}130131// A Resolver looks up names and numbers.132//133// A nil *Resolver is equivalent to a zero Resolver.134type Resolver struct {135 // PreferGo controls whether Go's built-in DNS resolver is preferred136 // on platforms where it's available. It is equivalent to setting137 // GODEBUG=netdns=go, but scoped to just this resolver.138 PreferGo bool139140 // StrictErrors controls the behavior of temporary errors141 // (including timeout, socket errors, and SERVFAIL) when using142 // Go's built-in resolver. For a query composed of multiple143 // sub-queries (such as an A+AAAA address lookup, or walking the144 // DNS search list), this option causes such errors to abort the145 // whole query instead of returning a partial result. This is146 // not enabled by default because it may affect compatibility147 // with resolvers that process AAAA queries incorrectly.148 StrictErrors bool149150 // Dial optionally specifies an alternate dialer for use by151 // Go's built-in DNS resolver to make TCP and UDP connections152 // to DNS services. The host in the address parameter will153 // always be a literal IP address and not a host name, and the154 // port in the address parameter will be a literal port number155 // and not a service name.156 // If the Conn returned is also a PacketConn, sent and received DNS157 // messages must adhere to RFC 1035 section 4.2.1, "UDP usage".158 // Otherwise, DNS messages transmitted over Conn must adhere159 // to RFC 7766 section 5, "Transport Protocol Selection".160 // If nil, the default dialer is used.161 Dial func(ctx context.Context, network, address string) (Conn, error)162163 // lookupGroup merges LookupIPAddr calls together for lookups for the same164 // host. The lookupGroup key is the LookupIPAddr.host argument.165 // The return values are ([]IPAddr, error).166 lookupGroup singleflight.Group167168 // TODO(bradfitz): optional interface impl override hook169 // TODO(bradfitz): Timeout time.Duration?170}171172func (r *Resolver) preferGo() bool { return r != nil && r.PreferGo }173func (r *Resolver) strictErrors() bool { return r != nil && r.StrictErrors }174175func (r *Resolver) getLookupGroup() *singleflight.Group {176 if r == nil {177 return &DefaultResolver.lookupGroup178 }179 return &r.lookupGroup180}181182// LookupHost looks up the given host using the local resolver.183// It returns a slice of that host's addresses.184//185// LookupHost uses [context.Background] internally; to specify the context, use186// [Resolver.LookupHost].187func LookupHost(host string) (addrs []string, err error) {188 return DefaultResolver.LookupHost(context.Background(), host)189}190191// LookupHost looks up the given host using the local resolver.192// It returns a slice of that host's addresses.193func (r *Resolver) LookupHost(ctx context.Context, host string) (addrs []string, err error) {194 // Make sure that no matter what we do later, host=="" is rejected.195 if host == "" {196 return nil, newDNSError(errNoSuchHost, host, "")197 }198 if _, err := netip.ParseAddr(host); err == nil {199 return []string{host}, nil200 }201 return r.lookupHost(ctx, host)202}203204// LookupIP looks up host using the local resolver.205// It returns a slice of that host's IPv4 and IPv6 addresses.206func LookupIP(host string) ([]IP, error) {207 addrs, err := DefaultResolver.LookupIPAddr(context.Background(), host)208 if err != nil {209 return nil, err210 }211 ips := make([]IP, len(addrs))212 for i, ia := range addrs {213 ips[i] = ia.IP214 }215 return ips, nil216}217218// LookupIPAddr looks up host using the local resolver.219// It returns a slice of that host's IPv4 and IPv6 addresses.220func (r *Resolver) LookupIPAddr(ctx context.Context, host string) ([]IPAddr, error) {221 return r.lookupIPAddr(ctx, "ip", host)222}223224// LookupIP looks up host for the given network using the local resolver.225// It returns a slice of that host's IP addresses of the type specified by226// network.227// network must be one of "ip", "ip4" or "ip6".228func (r *Resolver) LookupIP(ctx context.Context, network, host string) ([]IP, error) {229 afnet, _, err := parseNetwork(ctx, network, false)230 if err != nil {231 return nil, err232 }233 switch afnet {234 case "ip", "ip4", "ip6":235 default:236 return nil, UnknownNetworkError(network)237 }238239 if host == "" {240 return nil, newDNSError(errNoSuchHost, host, "")241 }242 addrs, err := r.internetAddrList(ctx, afnet, host)243 if err != nil {244 return nil, err245 }246247 ips := make([]IP, 0, len(addrs))248 for _, addr := range addrs {249 ips = append(ips, addr.(*IPAddr).IP)250 }251 return ips, nil252}253254// LookupNetIP looks up host using the local resolver.255// It returns a slice of that host's IP addresses of the type specified by256// network.257// The network must be one of "ip", "ip4" or "ip6".258func (r *Resolver) LookupNetIP(ctx context.Context, network, host string) ([]netip.Addr, error) {259 // TODO(bradfitz): make this efficient, making the internal net package260 // type throughout be netip.Addr and only converting to the net.IP slice261 // version at the edge. But for now (2021-10-20), this is a wrapper around262 // the old way.263 ips, err := r.LookupIP(ctx, network, host)264 if err != nil {265 return nil, err266 }267 ret := make([]netip.Addr, 0, len(ips))268 for _, ip := range ips {269 if a, ok := netip.AddrFromSlice(ip); ok {270 ret = append(ret, a)271 }272 }273 return ret, nil274}275276// onlyValuesCtx is a context that uses an underlying context277// for value lookup if the underlying context hasn't yet expired.278type onlyValuesCtx struct {279 context.Context280 lookupValues context.Context281}282283var _ context.Context = (*onlyValuesCtx)(nil)284285// Value performs a lookup if the original context hasn't expired.286func (ovc *onlyValuesCtx) Value(key any) any {287 select {288 case <-ovc.lookupValues.Done():289 return nil290 default:291 return ovc.lookupValues.Value(key)292 }293}294295// withUnexpiredValuesPreserved returns a context.Context that only uses lookupCtx296// for its values, otherwise it is never canceled and has no deadline.297// If the lookup context expires, any looked up values will return nil.298// See Issue 28600.299func withUnexpiredValuesPreserved(lookupCtx context.Context) context.Context {300 return &onlyValuesCtx{Context: context.Background(), lookupValues: lookupCtx}301}302303// lookupIPAddr looks up host using the local resolver and particular network.304// It returns a slice of that host's IPv4 and IPv6 addresses.305func (r *Resolver) lookupIPAddr(ctx context.Context, network, host string) ([]IPAddr, error) {306 // Make sure that no matter what we do later, host=="" is rejected.307 if host == "" {308 return nil, newDNSError(errNoSuchHost, host, "")309 }310 if ip, err := netip.ParseAddr(host); err == nil {311 return []IPAddr{{IP: IP(ip.AsSlice()).To16(), Zone: ip.Zone()}}, nil312 }313 trace, _ := ctx.Value(nettrace.TraceKey{}).(*nettrace.Trace)314 if trace != nil && trace.DNSStart != nil {315 trace.DNSStart(host)316 }317 // The underlying resolver func is lookupIP by default but it318 // can be overridden by tests. This is needed by net/http, so it319 // uses a context key instead of unexported variables.320 resolverFunc := r.lookupIP321 if alt, _ := ctx.Value(nettrace.LookupIPAltResolverKey{}).(func(context.Context, string, string) ([]IPAddr, error)); alt != nil {322 resolverFunc = alt323 }324325 // We don't want a cancellation of ctx to affect the326 // lookupGroup operation. Otherwise if our context gets327 // canceled it might cause an error to be returned to a lookup328 // using a completely different context. However we need to preserve329 // only the values in context. See Issue 28600.330 lookupGroupCtx, lookupGroupCancel := context.WithCancel(withUnexpiredValuesPreserved(ctx))331332 lookupKey := network + "\000" + host333 dnsWaitGroup.Add(1)334 ch := r.getLookupGroup().DoChan(lookupKey, func() (any, error) {335 return testHookLookupIP(lookupGroupCtx, resolverFunc, network, host)336 })337338 dnsWaitGroupDone := func(ch <-chan singleflight.Result, cancelFn context.CancelFunc) {339 <-ch340 dnsWaitGroup.Done()341 cancelFn()342 }343 select {344 case <-ctx.Done():345 // Our context was canceled. If we are the only346 // goroutine looking up this key, then drop the key347 // from the lookupGroup and cancel the lookup.348 // If there are other goroutines looking up this key,349 // let the lookup continue uncanceled, and let later350 // lookups with the same key share the result.351 // See issues 8602, 20703, 22724.352 if r.getLookupGroup().ForgetUnshared(lookupKey) {353 lookupGroupCancel()354 go dnsWaitGroupDone(ch, func() {})355 } else {356 go dnsWaitGroupDone(ch, lookupGroupCancel)357 }358 err := newDNSError(mapErr(ctx.Err()), host, "")359 if trace != nil && trace.DNSDone != nil {360 trace.DNSDone(nil, false, err)361 }362 return nil, err363 case r := <-ch:364 dnsWaitGroup.Done()365 lookupGroupCancel()366 err := r.Err367 if err != nil {368 if _, ok := err.(*DNSError); !ok {369 err = newDNSError(mapErr(err), host, "")370 }371 }372 if trace != nil && trace.DNSDone != nil {373 addrs, _ := r.Val.([]IPAddr)374 trace.DNSDone(ipAddrsEface(addrs), r.Shared, err)375 }376 return lookupIPReturn(r.Val, err, r.Shared)377 }378}379380// lookupIPReturn turns the return values from singleflight.Do into381// the return values from LookupIP.382func lookupIPReturn(addrsi any, err error, shared bool) ([]IPAddr, error) {383 if err != nil {384 return nil, err385 }386 addrs := addrsi.([]IPAddr)387 if shared {388 clone := make([]IPAddr, len(addrs))389 copy(clone, addrs)390 addrs = clone391 }392 return addrs, nil393}394395// ipAddrsEface returns an empty interface slice of addrs.396func ipAddrsEface(addrs []IPAddr) []any {397 s := make([]any, len(addrs))398 for i, v := range addrs {399 s[i] = v400 }401 return s402}403404// LookupPort looks up the port for the given network and service.405//406// LookupPort uses [context.Background] internally; to specify the context, use407// [Resolver.LookupPort].408func LookupPort(network, service string) (port int, err error) {409 return DefaultResolver.LookupPort(context.Background(), network, service)410}411412// LookupPort looks up the port for the given network and service.413//414// The network must be one of "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6" or "ip".415func (r *Resolver) LookupPort(ctx context.Context, network, service string) (port int, err error) {416 port, needsLookup := parsePort(service)417 if needsLookup {418 switch network {419 case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6", "ip":420 case "": // a hint wildcard for Go 1.0 undocumented behavior421 network = "ip"422 default:423 return 0, &AddrError{Err: "unknown network", Addr: network}424 }425 port, err = r.lookupPort(ctx, network, service)426 if err != nil {427 return 0, err428 }429 }430 if 0 > port || port > 65535 {431 return 0, &AddrError{Err: "invalid port", Addr: service}432 }433 return port, nil434}435436// LookupCNAME returns the canonical name for the given host.437// Callers that do not care about the canonical name can call438// [LookupHost] or [LookupIP] directly; both take care of resolving439// the canonical name as part of the lookup.440//441// A canonical name is the final name after following zero442// or more CNAME records.443// LookupCNAME does not return an error if host does not444// contain DNS "CNAME" records, as long as host resolves to445// address records.446//447// The returned canonical name is validated to be a properly448// formatted presentation-format domain name.449//450// LookupCNAME uses [context.Background] internally; to specify the context, use451// [Resolver.LookupCNAME].452func LookupCNAME(host string) (cname string, err error) {453 return DefaultResolver.LookupCNAME(context.Background(), host)454}455456// LookupCNAME returns the canonical name for the given host.457// Callers that do not care about the canonical name can call458// [LookupHost] or [LookupIP] directly; both take care of resolving459// the canonical name as part of the lookup.460//461// A canonical name is the final name after following zero462// or more CNAME records.463// LookupCNAME does not return an error if host does not464// contain DNS "CNAME" records, as long as host resolves to465// address records.466//467// The returned canonical name is validated to be a properly468// formatted presentation-format domain name.469func (r *Resolver) LookupCNAME(ctx context.Context, host string) (string, error) {470 cname, err := r.lookupCNAME(ctx, host)471 if err != nil {472 return "", err473 }474 if !isDomainName(cname) {475 return "", &DNSError{Err: errMalformedDNSRecordsDetail, Name: host}476 }477 return cname, nil478}479480// LookupSRV tries to resolve an [SRV] query of the given service,481// protocol, and domain name. The proto is "tcp" or "udp".482// The returned records are sorted by priority and randomized483// by weight within a priority.484//485// LookupSRV constructs the DNS name to look up following RFC 2782.486// That is, it looks up _service._proto.name. To accommodate services487// publishing SRV records under non-standard names, if both service488// and proto are empty strings, LookupSRV looks up name directly.489//490// The returned cname is the owner name from the first SRV answer491// record, which is typically the constructed DNS name492// (_service._proto.name) but may differ if CNAME records redirect493// the query to another name.494//495// The returned service names are validated to be properly496// formatted presentation-format domain names. If the response contains497// invalid names, those records are filtered out and an error498// will be returned alongside the remaining results, if any.499func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) {500 return DefaultResolver.LookupSRV(context.Background(), service, proto, name)501}502503// LookupSRV tries to resolve an [SRV] query of the given service,504// protocol, and domain name. The proto is "tcp" or "udp".505// The returned records are sorted by priority and randomized506// by weight within a priority.507//508// LookupSRV constructs the DNS name to look up following RFC 2782.509// That is, it looks up _service._proto.name. To accommodate services510// publishing SRV records under non-standard names, if both service511// and proto are empty strings, LookupSRV looks up name directly.512//513// The returned cname is the owner name from the first SRV answer514// record, which is typically the constructed DNS name515// (_service._proto.name) but may differ if CNAME records redirect516// the query to another name.517//518// The returned service names are validated to be properly519// formatted presentation-format domain names. If the response contains520// invalid names, those records are filtered out and an error521// will be returned alongside the remaining results, if any.522func (r *Resolver) LookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) {523 cname, addrs, err := r.lookupSRV(ctx, service, proto, name)524 if err != nil {525 return "", nil, err526 }527 if cname != "" && !isDomainName(cname) {528 return "", nil, &DNSError{Err: "SRV header name is invalid", Name: name}529 }530 filteredAddrs := make([]*SRV, 0, len(addrs))531 for _, addr := range addrs {532 if addr == nil {533 continue534 }535 if !isDomainName(addr.Target) {536 continue537 }538 filteredAddrs = append(filteredAddrs, addr)539 }540 if len(addrs) != len(filteredAddrs) {541 return cname, filteredAddrs, &DNSError{Err: errMalformedDNSRecordsDetail, Name: name}542 }543 return cname, filteredAddrs, nil544}545546// LookupMX returns the DNS MX records for the given domain name sorted by preference.547//548// The returned mail server names are validated to be properly549// formatted presentation-format domain names, or numeric IP addresses.550// If the response contains invalid names, those records are filtered out551// and an error will be returned alongside the remaining results, if any.552//553// LookupMX uses [context.Background] internally; to specify the context, use554// [Resolver.LookupMX].555func LookupMX(name string) ([]*MX, error) {556 return DefaultResolver.LookupMX(context.Background(), name)557}558559// LookupMX returns the DNS MX records for the given domain name sorted by preference.560//561// The returned mail server names are validated to be properly562// formatted presentation-format domain names, or numeric IP addresses.563// If the response contains invalid names, those records are filtered out564// and an error will be returned alongside the remaining results, if any.565func (r *Resolver) LookupMX(ctx context.Context, name string) ([]*MX, error) {566 records, err := r.lookupMX(ctx, name)567 if err != nil {568 return nil, err569 }570 filteredMX := make([]*MX, 0, len(records))571 for _, mx := range records {572 if mx == nil {573 continue574 }575 if !isDomainName(mx.Host) {576 // Check for IP address. In practice we observe577 // these with a trailing dot, so strip that.578 ip, err := netip.ParseAddr(stringslite.TrimSuffix(mx.Host, "."))579 if err != nil || ip.Zone() != "" {580 continue581 }582 }583 filteredMX = append(filteredMX, mx)584 }585 if len(records) != len(filteredMX) {586 return filteredMX, &DNSError{Err: errMalformedDNSRecordsDetail, Name: name}587 }588 return filteredMX, nil589}590591// LookupNS returns the DNS NS records for the given domain name.592//593// The returned name server names are validated to be properly594// formatted presentation-format domain names. If the response contains595// invalid names, those records are filtered out and an error596// will be returned alongside the remaining results, if any.597//598// LookupNS uses [context.Background] internally; to specify the context, use599// [Resolver.LookupNS].600func LookupNS(name string) ([]*NS, error) {601 return DefaultResolver.LookupNS(context.Background(), name)602}603604// LookupNS returns the DNS NS records for the given domain name.605//606// The returned name server names are validated to be properly607// formatted presentation-format domain names. If the response contains608// invalid names, those records are filtered out and an error609// will be returned alongside the remaining results, if any.610func (r *Resolver) LookupNS(ctx context.Context, name string) ([]*NS, error) {611 records, err := r.lookupNS(ctx, name)612 if err != nil {613 return nil, err614 }615 filteredNS := make([]*NS, 0, len(records))616 for _, ns := range records {617 if ns == nil {618 continue619 }620 if !isDomainName(ns.Host) {621 continue622 }623 filteredNS = append(filteredNS, ns)624 }625 if len(records) != len(filteredNS) {626 return filteredNS, &DNSError{Err: errMalformedDNSRecordsDetail, Name: name}627 }628 return filteredNS, nil629}630631// LookupTXT returns the DNS TXT records for the given domain name.632//633// If a DNS TXT record holds multiple strings, they are concatenated as a634// single string.635//636// LookupTXT uses [context.Background] internally; to specify the context, use637// [Resolver.LookupTXT].638func LookupTXT(name string) ([]string, error) {639 return DefaultResolver.lookupTXT(context.Background(), name)640}641642// LookupTXT returns the DNS TXT records for the given domain name.643//644// If a DNS TXT record holds multiple strings, they are concatenated as a645// single string.646func (r *Resolver) LookupTXT(ctx context.Context, name string) ([]string, error) {647 return r.lookupTXT(ctx, name)648}649650// LookupAddr performs a reverse lookup for the given address, returning a list651// of names mapping to that address.652//653// The returned names are validated to be properly formatted presentation-format654// domain names. If the response contains invalid names, those records are filtered655// out and an error will be returned alongside the remaining results, if any.656//657// When using the host C library resolver, at most one result will be658// returned. To bypass the host resolver, use a custom [Resolver].659//660// LookupAddr uses [context.Background] internally; to specify the context, use661// [Resolver.LookupAddr].662func LookupAddr(addr string) (names []string, err error) {663 return DefaultResolver.LookupAddr(context.Background(), addr)664}665666// LookupAddr performs a reverse lookup for the given address, returning a list667// of names mapping to that address.668//669// The returned names are validated to be properly formatted presentation-format670// domain names. If the response contains invalid names, those records are filtered671// out and an error will be returned alongside the remaining results, if any.672func (r *Resolver) LookupAddr(ctx context.Context, addr string) ([]string, error) {673 names, err := r.lookupAddr(ctx, addr)674 if err != nil {675 return nil, err676 }677 filteredNames := make([]string, 0, len(names))678 for _, name := range names {679 if isDomainName(name) {680 filteredNames = append(filteredNames, name)681 }682 }683 if len(names) != len(filteredNames) {684 return filteredNames, &DNSError{Err: errMalformedDNSRecordsDetail, Name: addr}685 }686 return filteredNames, nil687}688689// errMalformedDNSRecordsDetail is the DNSError detail which is returned when a Resolver.Lookup...690// method receives DNS records which contain invalid DNS names. This may be returned alongside691// results which have had the malformed records filtered out.692var errMalformedDNSRecordsDetail = "DNS response contained records which contain invalid names"693694// dial makes a new connection to the provided server (which must be695// an IP address) with the provided network type, using either r.Dial696// (if both r and r.Dial are non-nil) or else Dialer.DialContext.697func (r *Resolver) dial(ctx context.Context, network, server string) (Conn, error) {698 // Calling Dial here is scary -- we have to be sure not to699 // dial a name that will require a DNS lookup, or Dial will700 // call back here to translate it. The DNS config parser has701 // already checked that all the cfg.servers are IP702 // addresses, which Dial will use without a DNS lookup.703 var c Conn704 var err error705 if r != nil && r.Dial != nil {706 c, err = r.Dial(ctx, network, server)707 } else {708 var d Dialer709 c, err = d.DialContext(ctx, network, server)710 }711 if err != nil {712 return nil, mapErr(err)713 }714 return c, nil715}716717// goLookupSRV returns the SRV records for a target name, built either718// from its component service ("sip"), protocol ("tcp"), and name719// ("example.com."), or from name directly (if service and proto are720// both empty).721//722// In either case, the returned target name ("_sip._tcp.example.com.")723// is also returned on success.724//725// The records are sorted by weight.726func (r *Resolver) goLookupSRV(ctx context.Context, service, proto, name string) (target string, srvs []*SRV, err error) {727 if service == "" && proto == "" {728 target = name729 } else {730 target = "_" + service + "._" + proto + "." + name731 }732 p, server, err := r.lookup(ctx, target, dnsmessage.TypeSRV, nil)733 if err != nil {734 return "", nil, err735 }736 var cname dnsmessage.Name737 for {738 h, err := p.AnswerHeader()739 if err == dnsmessage.ErrSectionDone {740 break741 }742 if err != nil {743 return "", nil, &DNSError{744 Err: "cannot unmarshal DNS message",745 Name: name,746 Server: server,747 }748 }749 if h.Type != dnsmessage.TypeSRV {750 if err := p.SkipAnswer(); err != nil {751 return "", nil, &DNSError{752 Err: "cannot unmarshal DNS message",753 Name: name,754 Server: server,755 }756 }757 continue758 }759 if cname.Length == 0 && h.Name.Length != 0 {760 cname = h.Name761 }762 srv, err := p.SRVResource()763 if err != nil {764 return "", nil, &DNSError{765 Err: "cannot unmarshal DNS message",766 Name: name,767 Server: server,768 }769 }770 srvs = append(srvs, &SRV{Target: srv.Target.String(), Port: srv.Port, Priority: srv.Priority, Weight: srv.Weight})771 }772 byPriorityWeight(srvs).sort()773 return cname.String(), srvs, nil774}775776// goLookupMX returns the MX records for name.777func (r *Resolver) goLookupMX(ctx context.Context, name string) ([]*MX, error) {778 p, server, err := r.lookup(ctx, name, dnsmessage.TypeMX, nil)779 if err != nil {780 return nil, err781 }782 var mxs []*MX783 for {784 h, err := p.AnswerHeader()785 if err == dnsmessage.ErrSectionDone {786 break787 }788 if err != nil {789 return nil, &DNSError{790 Err: "cannot unmarshal DNS message",791 Name: name,792 Server: server,793 }794 }795 if h.Type != dnsmessage.TypeMX {796 if err := p.SkipAnswer(); err != nil {797 return nil, &DNSError{798 Err: "cannot unmarshal DNS message",799 Name: name,800 Server: server,801 }802 }803 continue804 }805 mx, err := p.MXResource()806 if err != nil {807 return nil, &DNSError{808 Err: "cannot unmarshal DNS message",809 Name: name,810 Server: server,811 }812 }813 mxs = append(mxs, &MX{Host: mx.MX.String(), Pref: mx.Pref})814815 }816 byPref(mxs).sort()817 return mxs, nil818}819820// goLookupNS returns the NS records for name.821func (r *Resolver) goLookupNS(ctx context.Context, name string) ([]*NS, error) {822 p, server, err := r.lookup(ctx, name, dnsmessage.TypeNS, nil)823 if err != nil {824 return nil, err825 }826 var nss []*NS827 for {828 h, err := p.AnswerHeader()829 if err == dnsmessage.ErrSectionDone {830 break831 }832 if err != nil {833 return nil, &DNSError{834 Err: "cannot unmarshal DNS message",835 Name: name,836 Server: server,837 }838 }839 if h.Type != dnsmessage.TypeNS {840 if err := p.SkipAnswer(); err != nil {841 return nil, &DNSError{842 Err: "cannot unmarshal DNS message",843 Name: name,844 Server: server,845 }846 }847 continue848 }849 ns, err := p.NSResource()850 if err != nil {851 return nil, &DNSError{852 Err: "cannot unmarshal DNS message",853 Name: name,854 Server: server,855 }856 }857 nss = append(nss, &NS{Host: ns.NS.String()})858 }859 return nss, nil860}861862// goLookupTXT returns the TXT records from name.863func (r *Resolver) goLookupTXT(ctx context.Context, name string) ([]string, error) {864 p, server, err := r.lookup(ctx, name, dnsmessage.TypeTXT, nil)865 if err != nil {866 return nil, err867 }868 var txts []string869 for {870 h, err := p.AnswerHeader()871 if err == dnsmessage.ErrSectionDone {872 break873 }874 if err != nil {875 return nil, &DNSError{876 Err: "cannot unmarshal DNS message",877 Name: name,878 Server: server,879 }880 }881 if h.Type != dnsmessage.TypeTXT {882 if err := p.SkipAnswer(); err != nil {883 return nil, &DNSError{884 Err: "cannot unmarshal DNS message",885 Name: name,886 Server: server,887 }888 }889 continue890 }891 txt, err := p.TXTResource()892 if err != nil {893 return nil, &DNSError{894 Err: "cannot unmarshal DNS message",895 Name: name,896 Server: server,897 }898 }899 // Multiple strings in one TXT record need to be900 // concatenated without separator to be consistent901 // with previous Go resolver.902 n := 0903 for _, s := range txt.TXT {904 n += len(s)905 }906 txtJoin := make([]byte, 0, n)907 for _, s := range txt.TXT {908 txtJoin = append(txtJoin, s...)909 }910 if len(txts) == 0 {911 txts = make([]string, 0, 1)912 }913 txts = append(txts, string(txtJoin))914 }915 return txts, nil916}917918func parseCNAMEFromResources(resources []dnsmessage.Resource) (string, error) {919 if len(resources) == 0 {920 return "", errors.New("no CNAME record received")921 }922 c, ok := resources[0].Body.(*dnsmessage.CNAMEResource)923 if !ok {924 return "", errors.New("could not parse CNAME record")925 }926 return c.CNAME.String(), nil927}
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.