Commit bfc164c6 authored by Paul Marks's avatar Paul Marks Committed by Brad Fitzpatrick

net: add Resolver.StrictErrors

When LookupIP is performing multiple subqueries, this option causes a
timeout/servfail affecting a single query to abort the whole operation,
instead of returning a partial (IPv4/IPv6-only) result.

Similarly, operations that walk the DNS search list will also abort when
encountering one of these errors.

Fixes #17448

Change-Id: Ice22e4aceb555c5a80d19bd1fde8b8fe87ac9517
Reviewed-on: https://go-review.googlesource.com/32572Reviewed-by: default avatarBrad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent b0e91d83
...@@ -200,6 +200,11 @@ func tryOneName(ctx context.Context, cfg *dnsConfig, name string, qtype uint16) ...@@ -200,6 +200,11 @@ func tryOneName(ctx context.Context, cfg *dnsConfig, name string, qtype uint16)
if nerr, ok := err.(Error); ok && nerr.Timeout() { if nerr, ok := err.(Error); ok && nerr.Timeout() {
lastErr.(*DNSError).IsTimeout = true lastErr.(*DNSError).IsTimeout = true
} }
// Set IsTemporary for socket-level errors. Note that this flag
// may also be used to indicate a SERVFAIL response.
if _, ok := err.(*OpError); ok {
lastErr.(*DNSError).IsTemporary = true
}
continue continue
} }
// libresolv continues to the next server when it receives // libresolv continues to the next server when it receives
...@@ -314,7 +319,7 @@ func (conf *resolverConfig) releaseSema() { ...@@ -314,7 +319,7 @@ func (conf *resolverConfig) releaseSema() {
<-conf.ch <-conf.ch
} }
func lookup(ctx context.Context, name string, qtype uint16) (cname string, rrs []dnsRR, err error) { func (r *Resolver) lookup(ctx context.Context, name string, qtype uint16) (cname string, rrs []dnsRR, err error) {
if !isDomainName(name) { if !isDomainName(name) {
// We used to use "invalid domain name" as the error, // We used to use "invalid domain name" as the error,
// but that is a detail of the specific lookup mechanism. // but that is a detail of the specific lookup mechanism.
...@@ -332,6 +337,11 @@ func lookup(ctx context.Context, name string, qtype uint16) (cname string, rrs [ ...@@ -332,6 +337,11 @@ func lookup(ctx context.Context, name string, qtype uint16) (cname string, rrs [
if err == nil { if err == nil {
break break
} }
if nerr, ok := err.(Error); ok && nerr.Temporary() && r.StrictErrors {
// If we hit a temporary error with StrictErrors enabled,
// stop immediately instead of trying more names.
break
}
} }
if err, ok := err.(*DNSError); ok { if err, ok := err.(*DNSError); ok {
// Show original name passed to lookup, not suffixed one. // Show original name passed to lookup, not suffixed one.
...@@ -432,11 +442,11 @@ func (o hostLookupOrder) String() string { ...@@ -432,11 +442,11 @@ func (o hostLookupOrder) String() string {
// Normally we let cgo use the C library resolver instead of // Normally we let cgo use the C library resolver instead of
// depending on our lookup code, so that Go and C get the same // depending on our lookup code, so that Go and C get the same
// answers. // answers.
func goLookupHost(ctx context.Context, name string) (addrs []string, err error) { func (r *Resolver) goLookupHost(ctx context.Context, name string) (addrs []string, err error) {
return goLookupHostOrder(ctx, name, hostLookupFilesDNS) return r.goLookupHostOrder(ctx, name, hostLookupFilesDNS)
} }
func goLookupHostOrder(ctx context.Context, name string, order hostLookupOrder) (addrs []string, err error) { func (r *Resolver) goLookupHostOrder(ctx context.Context, name string, order hostLookupOrder) (addrs []string, err error) {
if order == hostLookupFilesDNS || order == hostLookupFiles { if order == hostLookupFilesDNS || order == hostLookupFiles {
// Use entries from /etc/hosts if they match. // Use entries from /etc/hosts if they match.
addrs = lookupStaticHost(name) addrs = lookupStaticHost(name)
...@@ -444,7 +454,7 @@ func goLookupHostOrder(ctx context.Context, name string, order hostLookupOrder) ...@@ -444,7 +454,7 @@ func goLookupHostOrder(ctx context.Context, name string, order hostLookupOrder)
return return
} }
} }
ips, _, err := goLookupIPCNAMEOrder(ctx, name, order) ips, _, err := r.goLookupIPCNAMEOrder(ctx, name, order)
if err != nil { if err != nil {
return return
} }
...@@ -470,13 +480,13 @@ func goLookupIPFiles(name string) (addrs []IPAddr) { ...@@ -470,13 +480,13 @@ func goLookupIPFiles(name string) (addrs []IPAddr) {
// goLookupIP is the native Go implementation of LookupIP. // goLookupIP is the native Go implementation of LookupIP.
// The libc versions are in cgo_*.go. // The libc versions are in cgo_*.go.
func goLookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) { func (r *Resolver) goLookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) {
order := systemConf().hostLookupOrder(host) order := systemConf().hostLookupOrder(host)
addrs, _, err = goLookupIPCNAMEOrder(ctx, host, order) addrs, _, err = r.goLookupIPCNAMEOrder(ctx, host, order)
return return
} }
func goLookupIPCNAMEOrder(ctx context.Context, name string, order hostLookupOrder) (addrs []IPAddr, cname string, err error) { func (r *Resolver) goLookupIPCNAMEOrder(ctx context.Context, name string, order hostLookupOrder) (addrs []IPAddr, cname string, err error) {
if order == hostLookupFilesDNS || order == hostLookupFiles { if order == hostLookupFilesDNS || order == hostLookupFiles {
addrs = goLookupIPFiles(name) addrs = goLookupIPFiles(name)
if len(addrs) > 0 || order == hostLookupFiles { if len(addrs) > 0 || order == hostLookupFiles {
...@@ -506,11 +516,16 @@ func goLookupIPCNAMEOrder(ctx context.Context, name string, order hostLookupOrde ...@@ -506,11 +516,16 @@ func goLookupIPCNAMEOrder(ctx context.Context, name string, order hostLookupOrde
lane <- racer{cname, rrs, err} lane <- racer{cname, rrs, err}
}(qtype) }(qtype)
} }
hitStrictError := false
for range qtypes { for range qtypes {
racer := <-lane racer := <-lane
if racer.error != nil { if racer.error != nil {
// Prefer error for original name. if nerr, ok := racer.error.(Error); ok && nerr.Temporary() && r.StrictErrors {
if lastErr == nil || fqdn == name+"." { // This error will abort the nameList loop.
hitStrictError = true
lastErr = racer.error
} else if lastErr == nil || fqdn == name+"." {
// Prefer error for original name.
lastErr = racer.error lastErr = racer.error
} }
continue continue
...@@ -520,6 +535,13 @@ func goLookupIPCNAMEOrder(ctx context.Context, name string, order hostLookupOrde ...@@ -520,6 +535,13 @@ func goLookupIPCNAMEOrder(ctx context.Context, name string, order hostLookupOrde
cname = racer.cname cname = racer.cname
} }
} }
if hitStrictError {
// If either family hit an error with StrictErrors enabled,
// discard all addresses. This ensures that network flakiness
// cannot turn a dualstack hostname IPv4/IPv6-only.
addrs = nil
break
}
if len(addrs) > 0 { if len(addrs) > 0 {
break break
} }
...@@ -543,9 +565,9 @@ func goLookupIPCNAMEOrder(ctx context.Context, name string, order hostLookupOrde ...@@ -543,9 +565,9 @@ func goLookupIPCNAMEOrder(ctx context.Context, name string, order hostLookupOrde
} }
// goLookupCNAME is the native Go (non-cgo) implementation of LookupCNAME. // goLookupCNAME is the native Go (non-cgo) implementation of LookupCNAME.
func goLookupCNAME(ctx context.Context, host string) (cname string, err error) { func (r *Resolver) goLookupCNAME(ctx context.Context, host string) (cname string, err error) {
order := systemConf().hostLookupOrder(host) order := systemConf().hostLookupOrder(host)
_, cname, err = goLookupIPCNAMEOrder(ctx, host, order) _, cname, err = r.goLookupIPCNAMEOrder(ctx, host, order)
return return
} }
...@@ -554,7 +576,7 @@ func goLookupCNAME(ctx context.Context, host string) (cname string, err error) { ...@@ -554,7 +576,7 @@ func goLookupCNAME(ctx context.Context, host string) (cname string, err error) {
// only if cgoLookupPTR is the stub in cgo_stub.go). // only if cgoLookupPTR is the stub in cgo_stub.go).
// Normally we let cgo use the C library resolver instead of depending // Normally we let cgo use the C library resolver instead of depending
// on our lookup code, so that Go and C get the same answers. // on our lookup code, so that Go and C get the same answers.
func goLookupPTR(ctx context.Context, addr string) ([]string, error) { func (r *Resolver) goLookupPTR(ctx context.Context, addr string) ([]string, error) {
names := lookupStaticAddr(addr) names := lookupStaticAddr(addr)
if len(names) > 0 { if len(names) > 0 {
return names, nil return names, nil
...@@ -563,7 +585,7 @@ func goLookupPTR(ctx context.Context, addr string) ([]string, error) { ...@@ -563,7 +585,7 @@ func goLookupPTR(ctx context.Context, addr string) ([]string, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
_, rrs, err := lookup(ctx, arpa, dnsTypePTR) _, rrs, err := r.lookup(ctx, arpa, dnsTypePTR)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
This diff is collapsed.
...@@ -97,6 +97,16 @@ type Resolver struct { ...@@ -97,6 +97,16 @@ type Resolver struct {
// GODEBUG=netdns=go, but scoped to just this resolver. // GODEBUG=netdns=go, but scoped to just this resolver.
PreferGo bool PreferGo bool
// StrictErrors controls the behavior of temporary errors
// (including timeout, socket errors, and SERVFAIL) when using
// Go's built-in resolver. For a query composed of multiple
// sub-queries (such as an A+AAAA address lookup, or walking the
// DNS search list), this option causes such errors to abort the
// whole query instead of returning a partial result. This is
// not enabled by default because it may affect compatibility
// with resolvers that process AAAA queries incorrectly.
StrictErrors bool
// TODO(bradfitz): optional interface impl override hook // TODO(bradfitz): optional interface impl override hook
// TODO(bradfitz): Timeout time.Duration? // TODO(bradfitz): Timeout time.Duration?
} }
......
...@@ -57,12 +57,12 @@ func (r *Resolver) lookupHost(ctx context.Context, host string) (addrs []string, ...@@ -57,12 +57,12 @@ func (r *Resolver) lookupHost(ctx context.Context, host string) (addrs []string,
// cgo not available (or netgo); fall back to Go's DNS resolver // cgo not available (or netgo); fall back to Go's DNS resolver
order = hostLookupFilesDNS order = hostLookupFilesDNS
} }
return goLookupHostOrder(ctx, host, order) return r.goLookupHostOrder(ctx, host, order)
} }
func (r *Resolver) lookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) { func (r *Resolver) lookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) {
if r.PreferGo { if r.PreferGo {
return goLookupIP(ctx, host) return r.goLookupIP(ctx, host)
} }
order := systemConf().hostLookupOrder(host) order := systemConf().hostLookupOrder(host)
if order == hostLookupCgo { if order == hostLookupCgo {
...@@ -72,7 +72,7 @@ func (r *Resolver) lookupIP(ctx context.Context, host string) (addrs []IPAddr, e ...@@ -72,7 +72,7 @@ func (r *Resolver) lookupIP(ctx context.Context, host string) (addrs []IPAddr, e
// cgo not available (or netgo); fall back to Go's DNS resolver // cgo not available (or netgo); fall back to Go's DNS resolver
order = hostLookupFilesDNS order = hostLookupFilesDNS
} }
addrs, _, err = goLookupIPCNAMEOrder(ctx, host, order) addrs, _, err = r.goLookupIPCNAMEOrder(ctx, host, order)
return return
} }
...@@ -98,17 +98,17 @@ func (r *Resolver) lookupCNAME(ctx context.Context, name string) (string, error) ...@@ -98,17 +98,17 @@ func (r *Resolver) lookupCNAME(ctx context.Context, name string) (string, error)
return cname, err return cname, err
} }
} }
return goLookupCNAME(ctx, name) return r.goLookupCNAME(ctx, name)
} }
func (*Resolver) lookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) { func (r *Resolver) lookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) {
var target string var target string
if service == "" && proto == "" { if service == "" && proto == "" {
target = name target = name
} else { } else {
target = "_" + service + "._" + proto + "." + name target = "_" + service + "._" + proto + "." + name
} }
cname, rrs, err := lookup(ctx, target, dnsTypeSRV) cname, rrs, err := r.lookup(ctx, target, dnsTypeSRV)
if err != nil { if err != nil {
return "", nil, err return "", nil, err
} }
...@@ -121,8 +121,8 @@ func (*Resolver) lookupSRV(ctx context.Context, service, proto, name string) (st ...@@ -121,8 +121,8 @@ func (*Resolver) lookupSRV(ctx context.Context, service, proto, name string) (st
return cname, srvs, nil return cname, srvs, nil
} }
func (*Resolver) lookupMX(ctx context.Context, name string) ([]*MX, error) { func (r *Resolver) lookupMX(ctx context.Context, name string) ([]*MX, error) {
_, rrs, err := lookup(ctx, name, dnsTypeMX) _, rrs, err := r.lookup(ctx, name, dnsTypeMX)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -135,8 +135,8 @@ func (*Resolver) lookupMX(ctx context.Context, name string) ([]*MX, error) { ...@@ -135,8 +135,8 @@ func (*Resolver) lookupMX(ctx context.Context, name string) ([]*MX, error) {
return mxs, nil return mxs, nil
} }
func (*Resolver) lookupNS(ctx context.Context, name string) ([]*NS, error) { func (r *Resolver) lookupNS(ctx context.Context, name string) ([]*NS, error) {
_, rrs, err := lookup(ctx, name, dnsTypeNS) _, rrs, err := r.lookup(ctx, name, dnsTypeNS)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -148,7 +148,7 @@ func (*Resolver) lookupNS(ctx context.Context, name string) ([]*NS, error) { ...@@ -148,7 +148,7 @@ func (*Resolver) lookupNS(ctx context.Context, name string) ([]*NS, error) {
} }
func (r *Resolver) lookupTXT(ctx context.Context, name string) ([]string, error) { func (r *Resolver) lookupTXT(ctx context.Context, name string) ([]string, error) {
_, rrs, err := lookup(ctx, name, dnsTypeTXT) _, rrs, err := r.lookup(ctx, name, dnsTypeTXT)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -165,5 +165,5 @@ func (r *Resolver) lookupAddr(ctx context.Context, addr string) ([]string, error ...@@ -165,5 +165,5 @@ func (r *Resolver) lookupAddr(ctx context.Context, addr string) ([]string, error
return ptrs, err return ptrs, err
} }
} }
return goLookupPTR(ctx, addr) return r.goLookupPTR(ctx, addr)
} }
...@@ -22,7 +22,7 @@ func TestGoLookupIP(t *testing.T) { ...@@ -22,7 +22,7 @@ func TestGoLookupIP(t *testing.T) {
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
if _, err := goLookupIP(ctx, host); err != nil { if _, err := DefaultResolver.goLookupIP(ctx, host); err != nil {
t.Error(err) t.Error(err)
} }
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment