aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBryan Mills <bcmills@google.com>2022-04-22 13:20:48 +0000
committerGopher Robot <gobot@golang.org>2022-04-22 13:39:44 +0000
commit440c9312c821d1c116746d46cf05655cb2c1bc39 (patch)
treef3d3e4b840b57b8d197933f2ce7597307ee59fad
parent78fb1d03d39e8357e4790a9f0788ef0a8e7d8ae1 (diff)
downloadgo-440c9312c821d1c116746d46cf05655cb2c1bc39.tar.gz
go-440c9312c821d1c116746d46cf05655cb2c1bc39.zip
Revert "net: permit use of Resolver.PreferGo, netgo on Windows and Plan 9"
This reverts CL 400654. Reason for revert: broke net.TestGoLookupIP on the darwin-amd64-nocgo builder. Updates #33097. Change-Id: Idaf94eda88c9d4401e667a4d31c00ce376d91909 Reviewed-on: https://go-review.googlesource.com/c/go/+/401754 Run-TryBot: Bryan Mills <bcmills@google.com> Auto-Submit: Bryan Mills <bcmills@google.com> Reviewed-by: Than McIntosh <thanm@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
-rw-r--r--src/net/addrselect.go2
-rw-r--r--src/net/cgo_stub.go2
-rw-r--r--src/net/conf.go36
-rw-r--r--src/net/dnsclient_unix.go24
-rw-r--r--src/net/dnsconfig.go43
-rw-r--r--src/net/dnsconfig_unix.go36
-rw-r--r--src/net/dnsconfig_windows.go58
-rw-r--r--src/net/lookup.go226
-rw-r--r--src/net/lookup_plan9.go52
-rw-r--r--src/net/lookup_unix.go205
-rw-r--r--src/net/lookup_windows.go93
-rw-r--r--src/net/net.go9
-rw-r--r--src/net/net_fake.go6
-rw-r--r--src/net/netgo.go9
-rw-r--r--src/net/nss.go2
-rw-r--r--src/net/resolverdialfunc_test.go328
16 files changed, 293 insertions, 838 deletions
diff --git a/src/net/addrselect.go b/src/net/addrselect.go
index 59380b9486..8accdb89e1 100644
--- a/src/net/addrselect.go
+++ b/src/net/addrselect.go
@@ -2,6 +2,8 @@
// 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 298d829f6f..cc84ca47ae 100644
--- a/src/net/cgo_stub.go
+++ b/src/net/cgo_stub.go
@@ -8,6 +8,8 @@ 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 b08bbc7d7a..9d4752173e 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 !js
+//go:build unix
package net
@@ -21,7 +21,7 @@ type conf struct {
forceCgoLookupHost bool
netGo bool // go DNS resolution forced
- netCgo bool // non-go DNS resolution forced (cgo, or win32)
+ netCgo bool // cgo DNS resolution forced
// machine has an /etc/mdns.allow file
hasMDNSAllow bool
@@ -49,23 +49,9 @@ 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 {
@@ -89,10 +75,6 @@ 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.
@@ -147,19 +129,7 @@ func (c *conf) hostLookupOrder(r *Resolver, hostname string) (ret hostLookupOrde
}
fallbackOrder := hostLookupCgo
if c.netGo || r.preferGo() {
- 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
+ fallbackOrder = hostLookupFilesDNS
}
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 9ade767ace..15dbc25830 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 !js
+//go:build unix
// DNS client: see RFC 1035.
// Has to be linked into package net for Dial.
@@ -20,7 +20,6 @@ import (
"internal/itoa"
"io"
"os"
- "runtime"
"sync"
"time"
@@ -379,21 +378,12 @@ func (conf *resolverConfig) tryUpdate(name string) {
}
conf.lastChecked = now
- 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
- }
+ 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
deleted file mode 100644
index 091b548301..0000000000
--- a/src/net/dnsconfig.go
+++ /dev/null
@@ -1,43 +0,0 @@
-// 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 94cd09ec71..7552bc51e6 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 !js && !windows
+//go:build unix
// Read system DNS config from /etc/resolv.conf
@@ -10,9 +10,32 @@ 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{
@@ -133,6 +156,17 @@ 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
deleted file mode 100644
index 5d640da1d7..0000000000
--- a/src/net/dnsconfig_windows.go
+++ /dev/null
@@ -1,58 +0,0 @@
-// 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 7f3d20126c..6fa90f354d 100644
--- a/src/net/lookup.go
+++ b/src/net/lookup.go
@@ -10,8 +10,6 @@ import (
"internal/singleflight"
"net/netip"
"sync"
-
- "golang.org/x/net/dns/dnsmessage"
)
// protocols contains minimal mappings between internet protocol
@@ -667,227 +665,3 @@ 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 445b1294e3..d43a03b778 100644
--- a/src/net/lookup_plan9.go
+++ b/src/net/lookup_plan9.go
@@ -179,27 +179,7 @@ loop:
return
}
-// 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)
- }
+func (r *Resolver) lookupIP(ctx context.Context, _, host string) (addrs []IPAddr, err error) {
lits, err := r.lookupHost(ctx, host)
if err != nil {
return
@@ -243,10 +223,7 @@ func (*Resolver) lookupPort(ctx context.Context, network, service string) (port
return 0, unknownPortError
}
-func (r *Resolver) lookupCNAME(ctx context.Context, name string) (cname string, err error) {
- if r.preferGoOverPlan9() {
- return r.goLookupCNAME(ctx, name)
- }
+func (*Resolver) lookupCNAME(ctx context.Context, name string) (cname string, err error) {
lines, err := queryDNS(ctx, name, "cname")
if err != nil {
if stringsHasSuffix(err.Error(), "dns failure") || stringsHasSuffix(err.Error(), "resource does not exist; negrcode 0") {
@@ -263,10 +240,7 @@ func (r *Resolver) lookupCNAME(ctx context.Context, name string) (cname string,
return "", errors.New("bad response from ndb/dns")
}
-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)
- }
+func (*Resolver) lookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*SRV, err error) {
var target string
if service == "" && proto == "" {
target = name
@@ -295,10 +269,7 @@ func (r *Resolver) lookupSRV(ctx context.Context, service, proto, name string) (
return
}
-func (r *Resolver) lookupMX(ctx context.Context, name string) (mx []*MX, err error) {
- if r.preferGoOverPlan9() {
- return r.goLookupMX(ctx, name)
- }
+func (*Resolver) lookupMX(ctx context.Context, name string) (mx []*MX, err error) {
lines, err := queryDNS(ctx, name, "mx")
if err != nil {
return
@@ -316,10 +287,7 @@ func (r *Resolver) lookupMX(ctx context.Context, name string) (mx []*MX, err err
return
}
-func (r *Resolver) lookupNS(ctx context.Context, name string) (ns []*NS, err error) {
- if r.preferGoOverPlan9() {
- return r.goLookupNS(ctx, name)
- }
+func (*Resolver) lookupNS(ctx context.Context, name string) (ns []*NS, err error) {
lines, err := queryDNS(ctx, name, "ns")
if err != nil {
return
@@ -334,10 +302,7 @@ func (r *Resolver) lookupNS(ctx context.Context, name string) (ns []*NS, err err
return
}
-func (r *Resolver) lookupTXT(ctx context.Context, name string) (txt []string, err error) {
- if r.preferGoOverPlan9() {
- return r.goLookupTXT(ctx, name)
- }
+func (*Resolver) lookupTXT(ctx context.Context, name string) (txt []string, err error) {
lines, err := queryDNS(ctx, name, "txt")
if err != nil {
return
@@ -350,10 +315,7 @@ func (r *Resolver) lookupTXT(ctx context.Context, name string) (txt []string, er
return
}
-func (r *Resolver) lookupAddr(ctx context.Context, addr string) (name []string, err error) {
- if r.preferGoOverPlan9() {
- return r.goLookupPTR(ctx, addr)
- }
+func (*Resolver) lookupAddr(ctx context.Context, addr string) (name []string, err error) {
arpa, err := reverseaddr(addr)
if err != nil {
return
diff --git a/src/net/lookup_unix.go b/src/net/lookup_unix.go
index 4b885e938a..ad4164d865 100644
--- a/src/net/lookup_unix.go
+++ b/src/net/lookup_unix.go
@@ -11,6 +11,8 @@ import (
"internal/bytealg"
"sync"
"syscall"
+
+ "golang.org/x/net/dns/dnsmessage"
)
var onceReadProtocols sync.Once
@@ -53,6 +55,26 @@ 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 {
@@ -107,19 +129,194 @@ 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) {
- return r.goLookupSRV(ctx, service, proto, name)
+ 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
}
func (r *Resolver) lookupMX(ctx context.Context, name string) ([]*MX, error) {
- return r.goLookupMX(ctx, name)
+ 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
}
func (r *Resolver) lookupNS(ctx context.Context, name string) ([]*NS, error) {
- return r.goLookupNS(ctx, name)
+ 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
}
func (r *Resolver) lookupTXT(ctx context.Context, name string) ([]string, error) {
- return r.goLookupTXT(ctx, name)
+ 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
}
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 051f47da39..27e5f86910 100644
--- a/src/net/lookup_windows.go
+++ b/src/net/lookup_windows.go
@@ -82,19 +82,7 @@ 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) lookupIP(ctx context.Context, network, name string) ([]IPAddr, error) {
- if r.preferGoOverWindows() {
- return r.goLookupIP(ctx, network, name)
- }
// TODO(bradfitz,brainman): use ctx more. See TODO below.
var family int32 = syscall.AF_UNSPEC
@@ -181,7 +169,7 @@ func (r *Resolver) lookupIP(ctx context.Context, network, name string) ([]IPAddr
}
func (r *Resolver) lookupPort(ctx context.Context, network, service string) (int, error) {
- if r.preferGoOverWindows() {
+ if r.preferGo() {
return lookupPortMap(network, service)
}
@@ -229,15 +217,12 @@ func (r *Resolver) lookupPort(ctx context.Context, network, service string) (int
return 0, &DNSError{Err: syscall.EINVAL.Error(), Name: network + "/" + service}
}
-func (r *Resolver) lookupCNAME(ctx context.Context, name string) (string, error) {
- if r.preferGoOverWindows() {
- return r.goLookupCNAME(ctx, name)
- }
+func (*Resolver) lookupCNAME(ctx context.Context, name string) (string, error) {
// TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
acquireThread()
defer releaseThread()
- var rec *syscall.DNSRecord
- e := syscall.DnsQuery(name, syscall.DNS_TYPE_CNAME, 0, nil, &rec, nil)
+ var r *syscall.DNSRecord
+ e := syscall.DnsQuery(name, syscall.DNS_TYPE_CNAME, 0, nil, &r, nil)
// windows returns DNS_INFO_NO_RECORDS if there are no CNAME-s
if errno, ok := e.(syscall.Errno); ok && errno == syscall.DNS_INFO_NO_RECORDS {
// if there are no aliases, the canonical name is the input name
@@ -246,17 +231,14 @@ func (r *Resolver) lookupCNAME(ctx context.Context, name string) (string, error)
if e != nil {
return "", &DNSError{Err: winError("dnsquery", e).Error(), Name: name}
}
- defer syscall.DnsRecordListFree(rec, 1)
+ defer syscall.DnsRecordListFree(r, 1)
- resolved := resolveCNAME(syscall.StringToUTF16Ptr(name), rec)
+ resolved := resolveCNAME(syscall.StringToUTF16Ptr(name), r)
cname := windows.UTF16PtrToString(resolved)
return absDomainName(cname), nil
}
-func (r *Resolver) lookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) {
- if r.preferGoOverWindows() {
- return r.goLookupSRV(ctx, service, proto, name)
- }
+func (*Resolver) lookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) {
// TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
acquireThread()
defer releaseThread()
@@ -266,15 +248,15 @@ func (r *Resolver) lookupSRV(ctx context.Context, service, proto, name string) (
} else {
target = "_" + service + "._" + proto + "." + name
}
- var rec *syscall.DNSRecord
- e := syscall.DnsQuery(target, syscall.DNS_TYPE_SRV, 0, nil, &rec, nil)
+ var r *syscall.DNSRecord
+ e := syscall.DnsQuery(target, syscall.DNS_TYPE_SRV, 0, nil, &r, nil)
if e != nil {
return "", nil, &DNSError{Err: winError("dnsquery", e).Error(), Name: target}
}
- defer syscall.DnsRecordListFree(rec, 1)
+ defer syscall.DnsRecordListFree(r, 1)
srvs := make([]*SRV, 0, 10)
- for _, p := range validRecs(rec, syscall.DNS_TYPE_SRV, target) {
+ for _, p := range validRecs(r, syscall.DNS_TYPE_SRV, target) {
v := (*syscall.DNSSRVData)(unsafe.Pointer(&p.Data[0]))
srvs = append(srvs, &SRV{absDomainName(syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Target))[:])), v.Port, v.Priority, v.Weight})
}
@@ -282,22 +264,19 @@ func (r *Resolver) lookupSRV(ctx context.Context, service, proto, name string) (
return absDomainName(target), srvs, nil
}
-func (r *Resolver) lookupMX(ctx context.Context, name string) ([]*MX, error) {
- if r.preferGoOverWindows() {
- return r.goLookupMX(ctx, name)
- }
+func (*Resolver) lookupMX(ctx context.Context, name string) ([]*MX, error) {
// TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
acquireThread()
defer releaseThread()
- var rec *syscall.DNSRecord
- e := syscall.DnsQuery(name, syscall.DNS_TYPE_MX, 0, nil, &rec, nil)
+ var r *syscall.DNSRecord
+ e := syscall.DnsQuery(name, syscall.DNS_TYPE_MX, 0, nil, &r, nil)
if e != nil {
return nil, &DNSError{Err: winError("dnsquery", e).Error(), Name: name}
}
- defer syscall.DnsRecordListFree(rec, 1)
+ defer syscall.DnsRecordListFree(r, 1)
mxs := make([]*MX, 0, 10)
- for _, p := range validRecs(rec, syscall.DNS_TYPE_MX, name) {
+ for _, p := range validRecs(r, syscall.DNS_TYPE_MX, name) {
v := (*syscall.DNSMXData)(unsafe.Pointer(&p.Data[0]))
mxs = append(mxs, &MX{absDomainName(windows.UTF16PtrToString(v.NameExchange)), v.Preference})
}
@@ -305,44 +284,38 @@ func (r *Resolver) lookupMX(ctx context.Context, name string) ([]*MX, error) {
return mxs, nil
}
-func (r *Resolver) lookupNS(ctx context.Context, name string) ([]*NS, error) {
- if r.preferGoOverWindows() {
- return r.goLookupNS(ctx, name)
- }
+func (*Resolver) lookupNS(ctx context.Context, name string) ([]*NS, error) {
// TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
acquireThread()
defer releaseThread()
- var rec *syscall.DNSRecord
- e := syscall.DnsQuery(name, syscall.DNS_TYPE_NS, 0, nil, &rec, nil)
+ var r *syscall.DNSRecord
+ e := syscall.DnsQuery(name, syscall.DNS_TYPE_NS, 0, nil, &r, nil)
if e != nil {
return nil, &DNSError{Err: winError("dnsquery", e).Error(), Name: name}
}
- defer syscall.DnsRecordListFree(rec, 1)
+ defer syscall.DnsRecordListFree(r, 1)
nss := make([]*NS, 0, 10)
- for _, p := range validRecs(rec, syscall.DNS_TYPE_NS, name) {
+ for _, p := range validRecs(r, syscall.DNS_TYPE_NS, name) {
v := (*syscall.DNSPTRData)(unsafe.Pointer(&p.Data[0]))
nss = append(nss, &NS{absDomainName(syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Host))[:]))})
}
return nss, nil
}
-func (r *Resolver) lookupTXT(ctx context.Context, name string) ([]string, error) {
- if r.preferGoOverWindows() {
- return r.lookupTXT(ctx, name)
- }
+func (*Resolver) lookupTXT(ctx context.Context, name string) ([]string, error) {
// TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
acquireThread()
defer releaseThread()
- var rec *syscall.DNSRecord
- e := syscall.DnsQuery(name, syscall.DNS_TYPE_TEXT, 0, nil, &rec, nil)
+ var r *syscall.DNSRecord
+ e := syscall.DnsQuery(name, syscall.DNS_TYPE_TEXT, 0, nil, &r, nil)
if e != nil {
return nil, &DNSError{Err: winError("dnsquery", e).Error(), Name: name}
}
- defer syscall.DnsRecordListFree(rec, 1)
+ defer syscall.DnsRecordListFree(r, 1)
txts := make([]string, 0, 10)
- for _, p := range validRecs(rec, syscall.DNS_TYPE_TEXT, name) {
+ for _, p := range validRecs(r, syscall.DNS_TYPE_TEXT, name) {
d := (*syscall.DNSTXTData)(unsafe.Pointer(&p.Data[0]))
s := ""
for _, v := range (*[1 << 10]*uint16)(unsafe.Pointer(&(d.StringArray[0])))[:d.StringCount:d.StringCount] {
@@ -353,11 +326,7 @@ func (r *Resolver) lookupTXT(ctx context.Context, name string) ([]string, error)
return txts, nil
}
-func (r *Resolver) lookupAddr(ctx context.Context, addr string) ([]string, error) {
- if r.preferGoOverWindows() {
- return r.goLookupPTR(ctx, addr)
- }
-
+func (*Resolver) lookupAddr(ctx context.Context, addr string) ([]string, error) {
// TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
acquireThread()
defer releaseThread()
@@ -365,15 +334,15 @@ func (r *Resolver) lookupAddr(ctx context.Context, addr string) ([]string, error
if err != nil {
return nil, err
}
- var rec *syscall.DNSRecord
- e := syscall.DnsQuery(arpa, syscall.DNS_TYPE_PTR, 0, nil, &rec, nil)
+ var r *syscall.DNSRecord
+ e := syscall.DnsQuery(arpa, syscall.DNS_TYPE_PTR, 0, nil, &r, nil)
if e != nil {
return nil, &DNSError{Err: winError("dnsquery", e).Error(), Name: addr}
}
- defer syscall.DnsRecordListFree(rec, 1)
+ defer syscall.DnsRecordListFree(r, 1)
ptrs := make([]string, 0, 10)
- for _, p := range validRecs(rec, syscall.DNS_TYPE_PTR, arpa) {
+ for _, p := range validRecs(r, syscall.DNS_TYPE_PTR, arpa) {
v := (*syscall.DNSPTRData)(unsafe.Pointer(&p.Data[0]))
ptrs = append(ptrs, absDomainName(windows.UTF16PtrToString(v.Host)))
}
diff --git a/src/net/net.go b/src/net/net.go
index ff56c31c56..7a97b9dcfd 100644
--- a/src/net/net.go
+++ b/src/net/net.go
@@ -61,7 +61,7 @@ The resolver decision can be overridden by setting the netdns value of the
GODEBUG environment variable (see package runtime) to go or cgo, as in:
export GODEBUG=netdns=go # force pure Go resolver
- export GODEBUG=netdns=cgo # force native resolver (cgo, win32)
+ export GODEBUG=netdns=cgo # force cgo resolver
The decision can also be forced while building the Go source tree
by setting the netgo or netcgo build tag.
@@ -73,8 +73,7 @@ join the two settings by a plus sign, as in GODEBUG=netdns=go+1.
On Plan 9, the resolver always accesses /net/cs and /net/dns.
-On Windows, in Go 1.18.x and earlier, the resolver always used C
-library functions, such as GetAddrInfo and DnsQuery.
+On Windows, the resolver always uses C library functions, such as GetAddrInfo and DnsQuery.
*/
package net
@@ -589,9 +588,7 @@ func (e InvalidAddrError) Temporary() bool { return false }
//
// TODO(iant): We could consider changing this to os.ErrDeadlineExceeded
// in the future, if we make
-//
-// errors.Is(os.ErrDeadlineExceeded, context.DeadlineExceeded)
-//
+// errors.Is(os.ErrDeadlineExceeded, context.DeadlineExceeded)
// return true.
var errTimeout error = &timeoutError{}
diff --git a/src/net/net_fake.go b/src/net/net_fake.go
index 6d07d6297a..ee5644c67f 100644
--- a/src/net/net_fake.go
+++ b/src/net/net_fake.go
@@ -16,8 +16,6 @@ import (
"sync"
"syscall"
"time"
-
- "golang.org/x/net/dns/dnsmessage"
)
var listenersMu sync.Mutex
@@ -316,7 +314,3 @@ func (fd *netFD) writeMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oob
func (fd *netFD) dup() (f *os.File, err error) {
return nil, syscall.ENOSYS
}
-
-func (r *Resolver) lookup(ctx context.Context, name string, qtype dnsmessage.Type) (dnsmessage.Parser, string, error) {
- panic("unreachable")
-}
diff --git a/src/net/netgo.go b/src/net/netgo.go
deleted file mode 100644
index f91c91b614..0000000000
--- a/src/net/netgo.go
+++ /dev/null
@@ -1,9 +0,0 @@
-// 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.
-
-//go:build netgo
-
-package net
-
-func init() { netGo = true }
diff --git a/src/net/nss.go b/src/net/nss.go
index c4c608fb61..5df71dc268 100644
--- a/src/net/nss.go
+++ b/src/net/nss.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build unix
+
package net
import (
diff --git a/src/net/resolverdialfunc_test.go b/src/net/resolverdialfunc_test.go
deleted file mode 100644
index 034c636eb6..0000000000
--- a/src/net/resolverdialfunc_test.go
+++ /dev/null
@@ -1,328 +0,0 @@
-// 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.
-
-//go:build !js
-
-// Test that Resolver.Dial can be a func returning an in-memory net.Conn
-// speaking DNS.
-
-package net
-
-import (
- "bytes"
- "context"
- "errors"
- "fmt"
- "reflect"
- "sort"
- "testing"
- "time"
-
- "golang.org/x/net/dns/dnsmessage"
-)
-
-func TestResolverDialFunc(t *testing.T) {
- r := &Resolver{
- PreferGo: true,
- Dial: newResolverDialFunc(&resolverDialHandler{
- StartDial: func(network, address string) error {
- t.Logf("StartDial(%q, %q) ...", network, address)
- return nil
- },
- Question: func(h dnsmessage.Header, q dnsmessage.Question) {
- t.Logf("Header: %+v for %q (type=%v, class=%v)", h,
- q.Name.String(), q.Type, q.Class)
- },
- // TODO: add test without HandleA* hooks specified at all, that Go
- // doesn't issue retries; map to something terminal.
- HandleA: func(w AWriter, name string) error {
- w.AddIP([4]byte{1, 2, 3, 4})
- w.AddIP([4]byte{5, 6, 7, 8})
- return nil
- },
- HandleAAAA: func(w AAAAWriter, name string) error {
- w.AddIP([16]byte{1: 1, 15: 15})
- w.AddIP([16]byte{2: 2, 14: 14})
- return nil
- },
- HandleSRV: func(w SRVWriter, name string) error {
- w.AddSRV(1, 2, 80, "foo.bar.")
- w.AddSRV(2, 3, 81, "bar.baz.")
- return nil
- },
- }),
- }
- ctx := context.Background()
- const fakeDomain = "something-that-is-a-not-a-real-domain.fake-tld."
-
- t.Run("LookupIP", func(t *testing.T) {
- ips, err := r.LookupIP(ctx, "ip", fakeDomain)
- if err != nil {
- t.Fatal(err)
- }
- if got, want := sortedIPStrings(ips), []string{"0:200::e00", "1.2.3.4", "1::f", "5.6.7.8"}; !reflect.DeepEqual(got, want) {
- t.Errorf("LookupIP wrong.\n got: %q\nwant: %q\n", got, want)
- }
- })
-
- t.Run("LookupSRV", func(t *testing.T) {
- _, got, err := r.LookupSRV(ctx, "some-service", "tcp", fakeDomain)
- if err != nil {
- t.Fatal(err)
- }
- want := []*SRV{
- {
- Target: "foo.bar.",
- Port: 80,
- Priority: 1,
- Weight: 2,
- },
- {
- Target: "bar.baz.",
- Port: 81,
- Priority: 2,
- Weight: 3,
- },
- }
- if !reflect.DeepEqual(got, want) {
- t.Errorf("wrong result. got:")
- for _, r := range got {
- t.Logf(" - %+v", r)
- }
- }
- })
-}
-
-func sortedIPStrings(ips []IP) []string {
- ret := make([]string, len(ips))
- for i, ip := range ips {
- ret[i] = ip.String()
- }
- sort.Strings(ret)
- return ret
-}
-
-func newResolverDialFunc(h *resolverDialHandler) func(ctx context.Context, network, address string) (Conn, error) {
- return func(ctx context.Context, network, address string) (Conn, error) {
- a := &resolverFuncConn{
- h: h,
- network: network,
- address: address,
- ttl: 10, // 10 second default if unset
- }
- if h.StartDial != nil {
- if err := h.StartDial(network, address); err != nil {
- return nil, err
- }
- }
- return a, nil
- }
-}
-
-type resolverDialHandler struct {
- // StartDial, if non-nil, is called when Go first calls Resolver.Dial.
- // Any error returned aborts the dial and is returned unwrapped.
- StartDial func(network, address string) error
-
- Question func(dnsmessage.Header, dnsmessage.Question)
-
- // err may be ErrNotExist or ErrRefused; others map to SERVFAIL (RCode2).
- // A nil error means success.
- HandleA func(w AWriter, name string) error
- HandleAAAA func(w AAAAWriter, name string) error
- HandleSRV func(w SRVWriter, name string) error
-}
-
-type ResponseWriter struct{ a *resolverFuncConn }
-
-func (w ResponseWriter) header() dnsmessage.ResourceHeader {
- q := w.a.q
- return dnsmessage.ResourceHeader{
- Name: q.Name,
- Type: q.Type,
- Class: q.Class,
- TTL: w.a.ttl,
- }
-}
-
-// SetTTL sets the TTL for subsequent written resources.
-// Once a resource has been written, SetTTL calls are no-ops.
-// That is, it can only be called at most once, before anything
-// else is written.
-func (w ResponseWriter) SetTTL(seconds uint32) {
- // ... intention is last one wins and mutates all previously
- // written records too, but that's a little annoying.
- // But it's also annoying if the requirement is it needs to be set
- // last.
- // And it's also annoying if it's possible for users to set
- // different TTLs per Answer.
- if w.a.wrote {
- return
- }
- w.a.ttl = seconds
-
-}
-
-type AWriter struct{ ResponseWriter }
-
-func (w AWriter) AddIP(v4 [4]byte) {
- w.a.wrote = true
- err := w.a.builder.AResource(w.header(), dnsmessage.AResource{A: v4})
- if err != nil {
- panic(err)
- }
-}
-
-type AAAAWriter struct{ ResponseWriter }
-
-func (w AAAAWriter) AddIP(v6 [16]byte) {
- w.a.wrote = true
- err := w.a.builder.AAAAResource(w.header(), dnsmessage.AAAAResource{AAAA: v6})
- if err != nil {
- panic(err)
- }
-}
-
-type SRVWriter struct{ ResponseWriter }
-
-// AddSRV adds a SRV record. The target name must end in a period and
-// be 63 bytes or fewer.
-func (w SRVWriter) AddSRV(priority, weight, port uint16, target string) error {
- targetName, err := dnsmessage.NewName(target)
- if err != nil {
- return err
- }
- w.a.wrote = true
- err = w.a.builder.SRVResource(w.header(), dnsmessage.SRVResource{
- Priority: priority,
- Weight: weight,
- Port: port,
- Target: targetName,
- })
- if err != nil {
- panic(err) // internal fault, not user
- }
- return nil
-}
-
-var (
- ErrNotExist = errors.New("name does not exist") // maps to RCode3, NXDOMAIN
- ErrRefused = errors.New("refused") // maps to RCode5, REFUSED
-)
-
-type resolverFuncConn struct {
- h *resolverDialHandler
- ctx context.Context
- network string
- address string
- builder *dnsmessage.Builder
- q dnsmessage.Question
- ttl uint32
- wrote bool
-
- rbuf bytes.Buffer
-}
-
-func (*resolverFuncConn) Close() error { return nil }
-func (*resolverFuncConn) LocalAddr() Addr { return someaddr{} }
-func (*resolverFuncConn) RemoteAddr() Addr { return someaddr{} }
-func (*resolverFuncConn) SetDeadline(t time.Time) error { return nil }
-func (*resolverFuncConn) SetReadDeadline(t time.Time) error { return nil }
-func (*resolverFuncConn) SetWriteDeadline(t time.Time) error { return nil }
-
-func (a *resolverFuncConn) Read(p []byte) (n int, err error) {
- return a.rbuf.Read(p)
-}
-
-func (a *resolverFuncConn) Write(packet []byte) (n int, err error) {
- if len(packet) < 2 {
- return 0, fmt.Errorf("short write of %d bytes; want 2+", len(packet))
- }
- reqLen := int(packet[0])<<8 | int(packet[1])
- req := packet[2:]
- if len(req) != reqLen {
- return 0, fmt.Errorf("packet declared length %d doesn't match body length %d", reqLen, len(req))
- }
-
- var parser dnsmessage.Parser
- h, err := parser.Start(req)
- if err != nil {
- // TODO: hook
- return 0, err
- }
- q, err := parser.Question()
- hadQ := (err == nil)
- if err == nil && a.h.Question != nil {
- a.h.Question(h, q)
- }
- if err != nil && err != dnsmessage.ErrSectionDone {
- return 0, err
- }
-
- resh := h
- resh.Response = true
- resh.Authoritative = true
- if hadQ {
- resh.RCode = dnsmessage.RCodeSuccess
- } else {
- resh.RCode = dnsmessage.RCodeNotImplemented
- }
- a.rbuf.Grow(514)
- a.rbuf.WriteByte('X') // reserved header for beu16 length
- a.rbuf.WriteByte('Y') // reserved header for beu16 length
- builder := dnsmessage.NewBuilder(a.rbuf.Bytes(), resh)
- a.builder = &builder
- if hadQ {
- a.q = q
- a.builder.StartQuestions()
- err := a.builder.Question(q)
- if err != nil {
- return 0, fmt.Errorf("Question: %w", err)
- }
- a.builder.StartAnswers()
- switch q.Type {
- case dnsmessage.TypeA:
- if a.h.HandleA != nil {
- resh.RCode = mapRCode(a.h.HandleA(AWriter{ResponseWriter{a}}, q.Name.String()))
- }
- case dnsmessage.TypeAAAA:
- if a.h.HandleAAAA != nil {
- resh.RCode = mapRCode(a.h.HandleAAAA(AAAAWriter{ResponseWriter{a}}, q.Name.String()))
- }
- case dnsmessage.TypeSRV:
- if a.h.HandleSRV != nil {
- resh.RCode = mapRCode(a.h.HandleSRV(SRVWriter{ResponseWriter{a}}, q.Name.String()))
- }
- }
- }
- tcpRes, err := builder.Finish()
- if err != nil {
- return 0, fmt.Errorf("Finish: %w", err)
- }
-
- n = len(tcpRes) - 2
- tcpRes[0] = byte(n >> 8)
- tcpRes[1] = byte(n)
- a.rbuf.Write(tcpRes[2:])
-
- return len(packet), nil
-}
-
-type someaddr struct{}
-
-func (someaddr) Network() string { return "unused" }
-func (someaddr) String() string { return "unused-someaddr" }
-
-func mapRCode(err error) dnsmessage.RCode {
- switch err {
- case nil:
- return dnsmessage.RCodeSuccess
- case ErrNotExist:
- return dnsmessage.RCodeNameError
- case ErrRefused:
- return dnsmessage.RCodeRefused
- default:
- return dnsmessage.RCodeServerFailure
- }
-}