diff options
-rw-r--r-- | src/net/addrselect.go | 2 | ||||
-rw-r--r-- | src/net/cgo_stub.go | 2 | ||||
-rw-r--r-- | src/net/conf.go | 36 | ||||
-rw-r--r-- | src/net/dnsclient_unix.go | 24 | ||||
-rw-r--r-- | src/net/dnsconfig.go | 43 | ||||
-rw-r--r-- | src/net/dnsconfig_unix.go | 36 | ||||
-rw-r--r-- | src/net/dnsconfig_windows.go | 58 | ||||
-rw-r--r-- | src/net/lookup.go | 226 | ||||
-rw-r--r-- | src/net/lookup_plan9.go | 52 | ||||
-rw-r--r-- | src/net/lookup_unix.go | 205 | ||||
-rw-r--r-- | src/net/lookup_windows.go | 93 | ||||
-rw-r--r-- | src/net/net.go | 9 | ||||
-rw-r--r-- | src/net/net_fake.go | 6 | ||||
-rw-r--r-- | src/net/netgo.go | 9 | ||||
-rw-r--r-- | src/net/nss.go | 2 | ||||
-rw-r--r-- | src/net/resolverdialfunc_test.go | 328 |
16 files changed, 838 insertions, 293 deletions
diff --git a/src/net/addrselect.go b/src/net/addrselect.go index 8accdb89e14..59380b94868 100644 --- a/src/net/addrselect.go +++ b/src/net/addrselect.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build unix - // Minimal RFC 6724 address selection. package net diff --git a/src/net/cgo_stub.go b/src/net/cgo_stub.go index cc84ca47aed..298d829f6fa 100644 --- a/src/net/cgo_stub.go +++ b/src/net/cgo_stub.go @@ -8,8 +8,6 @@ package net import "context" -func init() { netGo = true } - type addrinfoErrno int func (eai addrinfoErrno) Error() string { return "<nil>" } diff --git a/src/net/conf.go b/src/net/conf.go index 9d4752173e1..b08bbc7d7a1 100644 --- a/src/net/conf.go +++ b/src/net/conf.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build unix +//go:build !js package net @@ -21,7 +21,7 @@ type conf struct { forceCgoLookupHost bool netGo bool // go DNS resolution forced - netCgo bool // cgo DNS resolution forced + netCgo bool // non-go DNS resolution forced (cgo, or win32) // machine has an /etc/mdns.allow file hasMDNSAllow bool @@ -49,9 +49,23 @@ func initConfVal() { confVal.dnsDebugLevel = debugLevel confVal.netGo = netGo || dnsMode == "go" confVal.netCgo = netCgo || dnsMode == "cgo" + if !confVal.netGo && !confVal.netCgo && (runtime.GOOS == "windows" || runtime.GOOS == "plan9") { + // Neither of these platforms actually use cgo. + // + // The meaning of "cgo" mode in the net package is + // really "the native OS way", which for libc meant + // cgo on the original platforms that motivated + // PreferGo support before Windows and Plan9 got support, + // at which time the GODEBUG=netdns=go and GODEBUG=netdns=cgo + // names were already kinda locked in. + confVal.netCgo = true + } if confVal.dnsDebugLevel > 0 { defer func() { + if confVal.dnsDebugLevel > 1 { + println("go package net: confVal.netCgo =", confVal.netCgo, " netGo =", confVal.netGo) + } switch { case confVal.netGo: if netGo { @@ -75,6 +89,10 @@ func initConfVal() { return } + if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { + return + } + // If any environment-specified resolver options are specified, // force cgo. Note that LOCALDOMAIN can change behavior merely // by being specified with the empty string. @@ -129,7 +147,19 @@ func (c *conf) hostLookupOrder(r *Resolver, hostname string) (ret hostLookupOrde } fallbackOrder := hostLookupCgo if c.netGo || r.preferGo() { - fallbackOrder = hostLookupFilesDNS + switch c.goos { + case "windows": + // TODO(bradfitz): implement files-based + // lookup on Windows too? I guess /etc/hosts + // kinda exists on Windows. But for now, only + // do DNS. + fallbackOrder = hostLookupDNS + default: + fallbackOrder = hostLookupFilesDNS + } + } + if c.goos == "windows" || c.goos == "plan9" { + return fallbackOrder } if c.forceCgoLookupHost || c.resolv.unknownOpt || c.goos == "android" { return fallbackOrder diff --git a/src/net/dnsclient_unix.go b/src/net/dnsclient_unix.go index 15dbc258302..9ade767ace6 100644 --- a/src/net/dnsclient_unix.go +++ b/src/net/dnsclient_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build unix +//go:build !js // DNS client: see RFC 1035. // Has to be linked into package net for Dial. @@ -20,6 +20,7 @@ import ( "internal/itoa" "io" "os" + "runtime" "sync" "time" @@ -378,12 +379,21 @@ func (conf *resolverConfig) tryUpdate(name string) { } conf.lastChecked = now - var mtime time.Time - if fi, err := os.Stat(name); err == nil { - mtime = fi.ModTime() - } - if mtime.Equal(conf.dnsConfig.mtime) { - return + switch runtime.GOOS { + case "windows": + // There's no file on disk, so don't bother checking + // and failing. + // + // The Windows implementation of dnsReadConfig (called + // below) ignores the name. + default: + var mtime time.Time + if fi, err := os.Stat(name); err == nil { + mtime = fi.ModTime() + } + if mtime.Equal(conf.dnsConfig.mtime) { + return + } } dnsConf := dnsReadConfig(name) diff --git a/src/net/dnsconfig.go b/src/net/dnsconfig.go new file mode 100644 index 00000000000..091b5483013 --- /dev/null +++ b/src/net/dnsconfig.go @@ -0,0 +1,43 @@ +// Copyright 2022 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 ( + "os" + "sync/atomic" + "time" +) + +var ( + defaultNS = []string{"127.0.0.1:53", "[::1]:53"} + getHostname = os.Hostname // variable for testing +) + +type dnsConfig struct { + servers []string // server addresses (in host:port form) to use + search []string // rooted suffixes to append to local name + ndots int // number of dots in name to trigger absolute lookup + timeout time.Duration // wait before giving up on a query, including retries + attempts int // lost packets before giving up on server + rotate bool // round robin among servers + unknownOpt bool // anything unknown was encountered + lookup []string // OpenBSD top-level database "lookup" order + err error // any error that occurs during open of resolv.conf + mtime time.Time // time of resolv.conf modification + soffset uint32 // used by serverOffset + singleRequest bool // use sequential A and AAAA queries instead of parallel queries + useTCP bool // force usage of TCP for DNS resolutions +} + +// serverOffset returns an offset that can be used to determine +// indices of servers in c.servers when making queries. +// When the rotate option is enabled, this offset increases. +// Otherwise it is always 0. +func (c *dnsConfig) serverOffset() uint32 { + if c.rotate { + return atomic.AddUint32(&c.soffset, 1) - 1 // return 0 to start + } + return 0 +} diff --git a/src/net/dnsconfig_unix.go b/src/net/dnsconfig_unix.go index 7552bc51e65..94cd09ec710 100644 --- a/src/net/dnsconfig_unix.go +++ b/src/net/dnsconfig_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build unix +//go:build !js && !windows // Read system DNS config from /etc/resolv.conf @@ -10,32 +10,9 @@ package net import ( "internal/bytealg" - "os" - "sync/atomic" "time" ) -var ( - defaultNS = []string{"127.0.0.1:53", "[::1]:53"} - getHostname = os.Hostname // variable for testing -) - -type dnsConfig struct { - servers []string // server addresses (in host:port form) to use - search []string // rooted suffixes to append to local name - ndots int // number of dots in name to trigger absolute lookup - timeout time.Duration // wait before giving up on a query, including retries - attempts int // lost packets before giving up on server - rotate bool // round robin among servers - unknownOpt bool // anything unknown was encountered - lookup []string // OpenBSD top-level database "lookup" order - err error // any error that occurs during open of resolv.conf - mtime time.Time // time of resolv.conf modification - soffset uint32 // used by serverOffset - singleRequest bool // use sequential A and AAAA queries instead of parallel queries - useTCP bool // force usage of TCP for DNS resolutions -} - // See resolv.conf(5) on a Linux machine. func dnsReadConfig(filename string) *dnsConfig { conf := &dnsConfig{ @@ -156,17 +133,6 @@ func dnsReadConfig(filename string) *dnsConfig { return conf } -// serverOffset returns an offset that can be used to determine -// indices of servers in c.servers when making queries. -// When the rotate option is enabled, this offset increases. -// Otherwise it is always 0. -func (c *dnsConfig) serverOffset() uint32 { - if c.rotate { - return atomic.AddUint32(&c.soffset, 1) - 1 // return 0 to start - } - return 0 -} - func dnsDefaultSearch() []string { hn, err := getHostname() if err != nil { diff --git a/src/net/dnsconfig_windows.go b/src/net/dnsconfig_windows.go new file mode 100644 index 00000000000..5d640da1d74 --- /dev/null +++ b/src/net/dnsconfig_windows.go @@ -0,0 +1,58 @@ +// Copyright 2022 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 ( + "syscall" + "time" +) + +func dnsReadConfig(ignoredFilename string) (conf *dnsConfig) { + conf = &dnsConfig{ + ndots: 1, + timeout: 5 * time.Second, + attempts: 2, + } + defer func() { + if len(conf.servers) == 0 { + conf.servers = defaultNS + } + }() + aas, err := adapterAddresses() + if err != nil { + return + } + // TODO(bradfitz): this just collects all the DNS servers on all + // the interfaces in some random order. It should order it by + // default route, or only use the default route(s) instead. + // In practice, however, it mostly works. + for _, aa := range aas { + for dns := aa.FirstDnsServerAddress; dns != nil; dns = dns.Next { + sa, err := dns.Address.Sockaddr.Sockaddr() + if err != nil { + continue + } + var ip IP + switch sa := sa.(type) { + case *syscall.SockaddrInet4: + ip = IPv4(sa.Addr[0], sa.Addr[1], sa.Addr[2], sa.Addr[3]) + case *syscall.SockaddrInet6: + ip = make(IP, IPv6len) + copy(ip, sa.Addr[:]) + if ip[0] == 0xfe && ip[1] == 0xc0 { + // Ignore these fec0/10 ones. Windows seems to + // populate them as defaults on its misc rando + // interfaces. + continue + } + default: + // Unexpected type. + continue + } + conf.servers = append(conf.servers, JoinHostPort(ip.String(), "53")) + } + } + return conf +} diff --git a/src/net/lookup.go b/src/net/lookup.go index 6fa90f354d4..7f3d20126c9 100644 --- a/src/net/lookup.go +++ b/src/net/lookup.go @@ -10,6 +10,8 @@ import ( "internal/singleflight" "net/netip" "sync" + + "golang.org/x/net/dns/dnsmessage" ) // protocols contains minimal mappings between internet protocol @@ -665,3 +667,227 @@ func (r *Resolver) LookupAddr(ctx context.Context, addr string) ([]string, error // 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 (r *Resolver) dial(ctx context.Context, network, server 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. + var c Conn + var err error + if r != nil && r.Dial != nil { + c, err = r.Dial(ctx, network, server) + } else { + var d Dialer + c, err = d.DialContext(ctx, network, server) + } + if err != nil { + return nil, mapErr(err) + } + return c, 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 (r *Resolver) goLookupSRV(ctx context.Context, service, proto, name string) (target string, srvs []*SRV, err error) { + if service == "" && proto == "" { + target = name + } else { + target = "_" + service + "._" + proto + "." + name + } + p, server, err := r.lookup(ctx, target, dnsmessage.TypeSRV) + if err != nil { + return "", nil, err + } + var cname dnsmessage.Name + for { + h, err := p.AnswerHeader() + if err == dnsmessage.ErrSectionDone { + break + } + if err != nil { + return "", nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + if h.Type != dnsmessage.TypeSRV { + if err := p.SkipAnswer(); err != nil { + return "", nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + continue + } + if cname.Length == 0 && h.Name.Length != 0 { + cname = h.Name + } + srv, err := p.SRVResource() + if err != nil { + return "", nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + srvs = append(srvs, &SRV{Target: srv.Target.String(), Port: srv.Port, Priority: srv.Priority, Weight: srv.Weight}) + } + byPriorityWeight(srvs).sort() + return cname.String(), srvs, nil +} + +// goLookupMX returns the MX records for name. +func (r *Resolver) goLookupMX(ctx context.Context, name string) ([]*MX, error) { + p, server, err := r.lookup(ctx, name, dnsmessage.TypeMX) + if err != nil { + return nil, err + } + var mxs []*MX + for { + h, err := p.AnswerHeader() + if err == dnsmessage.ErrSectionDone { + break + } + if err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + if h.Type != dnsmessage.TypeMX { + if err := p.SkipAnswer(); err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + continue + } + mx, err := p.MXResource() + if err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + mxs = append(mxs, &MX{Host: mx.MX.String(), Pref: mx.Pref}) + + } + byPref(mxs).sort() + return mxs, nil +} + +// goLookupNS returns the NS records for name. +func (r *Resolver) goLookupNS(ctx context.Context, name string) ([]*NS, error) { + p, server, err := r.lookup(ctx, name, dnsmessage.TypeNS) + if err != nil { + return nil, err + } + var nss []*NS + for { + h, err := p.AnswerHeader() + if err == dnsmessage.ErrSectionDone { + break + } + if err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + if h.Type != dnsmessage.TypeNS { + if err := p.SkipAnswer(); err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + continue + } + ns, err := p.NSResource() + if err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + nss = append(nss, &NS{Host: ns.NS.String()}) + } + return nss, nil +} + +// goLookupTXT returns the TXT records from name. +func (r *Resolver) goLookupTXT(ctx context.Context, name string) ([]string, error) { + p, server, err := r.lookup(ctx, name, dnsmessage.TypeTXT) + if err != nil { + return nil, err + } + var txts []string + for { + h, err := p.AnswerHeader() + if err == dnsmessage.ErrSectionDone { + break + } + if err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + if h.Type != dnsmessage.TypeTXT { + if err := p.SkipAnswer(); err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + continue + } + txt, err := p.TXTResource() + if err != nil { + return nil, &DNSError{ + Err: "cannot unmarshal DNS message", + Name: name, + Server: server, + } + } + // Multiple strings in one TXT record need to be + // concatenated without separator to be consistent + // with previous Go resolver. + n := 0 + for _, s := range txt.TXT { + n += len(s) + } + txtJoin := make([]byte, 0, n) + for _, s := range txt.TXT { + txtJoin = append(txtJoin, s...) + } + if len(txts) == 0 { + txts = make([]string, 0, 1) + } + txts = append(txts, string(txtJoin)) + } + return txts, nil +} diff --git a/src/net/lookup_plan9.go b/src/net/lookup_plan9.go index d43a03b778d..445b1294e35 100644 --- a/src/net/lookup_plan9.go +++ b/src/net/lookup_plan9.go @@ -179,7 +179,27 @@ loop: return } -func (r *Resolver) lookupIP(ctx context.Context, _, host string) (addrs []IPAddr, err error) { +// preferGoOverPlan9 reports whether the resolver should use the +// "PreferGo" implementation rather than asking plan9 services +// for the answers. +func (r *Resolver) preferGoOverPlan9() bool { + conf := systemConf() + order := conf.hostLookupOrder(r, "") // name is unused + + // TODO(bradfitz): for now we only permit use of the PreferGo + // implementation when there's a non-nil Resolver with a + // non-nil Dialer. This is a sign that they the code is trying + // to use their DNS-speaking net.Conn (such as an in-memory + // DNS cache) and they don't want to actually hit the network. + // Once we add support for looking the default DNS servers + // from plan9, though, then we can relax this. + return order != hostLookupCgo && r != nil && r.Dial != nil +} + +func (r *Resolver) lookupIP(ctx context.Context, network, host string) (addrs []IPAddr, err error) { + if r.preferGoOverPlan9() { + return r.goLookupIP(ctx, network, host) + } lits, err := r.lookupHost(ctx, host) if err != nil { return @@ -223,7 +243,10 @@ func (*Resolver) lookupPort(ctx context.Context, network, service string) (port return 0, unknownPortError } -func (*Resolver) lookupCNAME(ctx context.Context, name string) (cname string, err error) { +func (r *Resolver) lookupCNAME(ctx context.Context, name string) (cname string, err error) { + if r.preferGoOverPlan9() { + return r.goLookupCNAME(ctx, name) + } lines, err := queryDNS(ctx, name, "cname") if err != nil { if stringsHasSuffix(err.Error(), "dns failure") || stringsHasSuffix(err.Error(), "resource does not exist; negrcode 0") { @@ -240,7 +263,10 @@ func (*Resolver) lookupCNAME(ctx context.Context, name string) (cname string, er return "", errors.New("bad response from ndb/dns") } -func (*Resolver) lookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*SRV, err error) { +func (r *Resolver) lookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*SRV, err error) { + if r.preferGoOverPlan9() { + return r.goLookupSRV(ctx, service, proto, name) + } var target string if service == "" && proto == "" { target = name @@ -269,7 +295,10 @@ func (*Resolver) lookupSRV(ctx context.Context, service, proto, name string) (cn return } -func (*Resolver) lookupMX(ctx context.Context, name string) (mx []*MX, err error) { +func (r *Resolver) lookupMX(ctx context.Context, name string) (mx []*MX, err error) { + if r.preferGoOverPlan9() { + return r.goLookupMX(ctx, name) + } lines, err := queryDNS(ctx, name, "mx") if err != nil { return @@ -287,7 +316,10 @@ func (*Resolver) lookupMX(ctx context.Context, name string) (mx []*MX, err error return } -func (*Resolver) lookupNS(ctx context.Context, name string) (ns []*NS, err error) { +func (r *Resolver) lookupNS(ctx context.Context, name string) (ns []*NS, err error) { + if r.preferGoOverPlan9() { + return r.goLookupNS(ctx, name) + } lines, err := queryDNS(ctx, name, "ns") if err != nil { return @@ -302,7 +334,10 @@ func (*Resolver) lookupNS(ctx context.Context, name string) (ns []*NS, err error return } -func (*Resolver) lookupTXT(ctx context.Context, name string) (txt []string, err error) { +func (r *Resolver) lookupTXT(ctx context.Context, name string) (txt []string, err error) { + if r.preferGoOverPlan9() { + return r.goLookupTXT(ctx, name) + } lines, err := queryDNS(ctx, name, "txt") if err != nil { return @@ -315,7 +350,10 @@ func (*Resolver) lookupTXT(ctx context.Context, name string) (txt []string, err return } -func (*Resolver) lookupAddr(ctx context.Context, addr string) (name []string, err error) { +func (r *Resolver) lookupAddr(ctx context.Context, addr string) (name []string, err error) { + if r.preferGoOverPlan9() { + return r.goLookupPTR(ctx, addr) + } arpa, err := reverseaddr(addr) if err != nil { return diff --git a/src/net/lookup_unix.go b/src/net/lookup_unix.go index ad4164d8651..4b885e938a0 100644 --- a/src/net/lookup_unix.go +++ b/src/net/lookup_unix.go @@ -11,8 +11,6 @@ import ( "internal/bytealg" "sync" "syscall" - - "golang.org/x/net/dns/dnsmessage" ) var onceReadProtocols sync.Once @@ -55,26 +53,6 @@ func lookupProtocol(_ context.Context, name string) (int, error) { return lookupProtocolMap(name) } -func (r *Resolver) dial(ctx context.Context, network, server 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. - var c Conn - var err error - if r != nil && r.Dial != nil { - c, err = r.Dial(ctx, network, server) - } else { - var d Dialer - c, err = d.DialContext(ctx, network, server) - } - if err != nil { - return nil, mapErr(err) - } - return c, nil -} - func (r *Resolver) lookupHost(ctx context.Context, host string) (addrs []string, err error) { order := systemConf().hostLookupOrder(r, host) if !r.preferGo() && order == hostLookupCgo { @@ -129,194 +107,19 @@ func (r *Resolver) lookupCNAME(ctx context.Context, name string) (string, error) } func (r *Resolver) lookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) { - var target string - if service == "" && proto == "" { - target = name - } else { - target = "_" + service + "._" + proto + "." + name - } - p, server, err := r.lookup(ctx, target, dnsmessage.TypeSRV) - if err != nil { - return "", nil, err - } - var srvs []*SRV - var cname dnsmessage.Name - for { - h, err := p.AnswerHeader() - if err == dnsmessage.ErrSectionDone { - break - } - if err != nil { - return "", nil, &DNSError{ - Err: "cannot unmarshal DNS message", - Name: name, - Server: server, - } - } - if h.Type != dnsmessage.TypeSRV { - if err := p.SkipAnswer(); err != nil { - return "", nil, &DNSError{ - Err: "cannot unmarshal DNS message", - Name: name, - Server: server, - } - } - continue - } - if cname.Length == 0 && h.Name.Length != 0 { - cname = h.Name - } - srv, err := p.SRVResource() - if err != nil { - return "", nil, &DNSError{ - Err: "cannot unmarshal DNS message", - Name: name, - Server: server, - } - } - srvs = append(srvs, &SRV{Target: srv.Target.String(), Port: srv.Port, Priority: srv.Priority, Weight: srv.Weight}) - } - byPriorityWeight(srvs).sort() - return cname.String(), srvs, nil + return r.goLookupSRV(ctx, service, proto, name) } func (r *Resolver) lookupMX(ctx context.Context, name string) ([]*MX, error) { - p, server, err := r.lookup(ctx, name, dnsmessage.TypeMX) - if err != nil { - return nil, err - } - var mxs []*MX - for { - h, err := p.AnswerHeader() - if err == dnsmessage.ErrSectionDone { - break - } - if err != nil { - return nil, &DNSError{ - Err: "cannot unmarshal DNS message", - Name: name, - Server: server, - } - } - if h.Type != dnsmessage.TypeMX { - if err := p.SkipAnswer(); err != nil { - return nil, &DNSError{ - Err: "cannot unmarshal DNS message", - Name: name, - Server: server, - } - } - continue - } - mx, err := p.MXResource() - if err != nil { - return nil, &DNSError{ - Err: "cannot unmarshal DNS message", - Name: name, - Server: server, - } - } - mxs = append(mxs, &MX{Host: mx.MX.String(), Pref: mx.Pref}) - - } - byPref(mxs).sort() - return mxs, nil + return r.goLookupMX(ctx, name) } func (r *Resolver) lookupNS(ctx context.Context, name string) ([]*NS, error) { - p, server, err := r.lookup(ctx, name, dnsmessage.TypeNS) - if err != nil { - return nil, err - } - var nss []*NS - for { - h, err := p.AnswerHeader() - if err == dnsmessage.ErrSectionDone { - break - } - if err != nil { - return nil, &DNSError{ - Err: "cannot unmarshal DNS message", - Name: name, - Server: server, - } - } - if h.Type != dnsmessage.TypeNS { - if err := p.SkipAnswer(); err != nil { - return nil, &DNSError{ - Err: "cannot unmarshal DNS message", - Name: name, - Server: server, - } - } - continue - } - ns, err := p.NSResource() - if err != nil { - return nil, &DNSError{ - Err: "cannot unmarshal DNS message", - Name: name, - Server: server, - } - } - nss = append(nss, &NS{Host: ns.NS.String()}) - } - return nss, nil + return r.goLookupNS(ctx, name) } func (r *Resolver) lookupTXT(ctx context.Context, name string) ([]string, error) { - p, server, err := r.lookup(ctx, name, dnsmessage.TypeTXT) - if err != nil { - return nil, err - } - var txts []string - for { - h, err := p.AnswerHeader() - if err == dnsmessage.ErrSectionDone { - break - } - if err != nil { - return nil, &DNSError{ - Err: "cannot unmarshal DNS message", - Name: name, - Server: server, - } - } - if h.Type != dnsmessage.TypeTXT { - if err := p.SkipAnswer(); err != nil { - return nil, &DNSError{ - Err: "cannot unmarshal DNS message", - Name: name, - Server: server, - } - } - continue - } - txt, err := p.TXTResource() - if err != nil { - return nil, &DNSError{ - Err: "cannot unmarshal DNS message", - Name: name, - Server: server, - } - } - // Multiple strings in one TXT record need to be - // concatenated without separator to be consistent - // with previous Go resolver. - n := 0 - for _, s := range txt.TXT { - n += len(s) - } - txtJoin := make([]byte, 0, n) - for _, s := range txt.TXT { - txtJoin = append(txtJoin, s...) - } - if len(txts) == 0 { - txts = make([]string, 0, 1) - } - txts = append(txts, string(txtJoin)) - } - return txts, nil + return r.goLookupTXT(ctx, name) } func (r *Resolver) lookupAddr(ctx context.Context, addr string) ([]string, error) { diff --git a/src/net/lookup_windows.go b/src/net/lookup_windows.go index 27e5f86910e..051f47da392 100644 --- a/src/net/lookup_windows.go +++ b/src/net/lookup_windows.go @@ -82,7 +82,19 @@ func (r *Resolver) lookupHost(ctx context.Context, name string) ([]string, error return addrs, nil } +// preferGoOverWindows reports whether the resolver should use the +// pure Go implementation rather than making win32 calls to ask the +// kernel for its answer. +func (r *Resolver) preferGoOverWindows() bool { + conf := systemConf() + order := conf.hostLookupOrder(r, "") // name is unused + return order != hostLookupCgo +} + func (r *Resolver |