net: support single-request resolv.conf option in pure Go resolver

There is a DNS resolution issue in Kubernetes (UDP response packets get dropped due to a race in conntrack between the parallel A and AAAA queries, causing timeouts in DNS queries).

A workaround is to enable single-request / single-request-reopen in resolv.conf in order to use sequential A and AAAA queries instead of parallel queries.

With this PR, the pure Go resolver searches for "single-request" and "single-request-reopen" in resolv.conf and send A and AAAA queries sequentially when found.

Fixes #29644

Change-Id: I906b3484008c1b9adf2e3e9241ea23767e29df59
GitHub-Last-Rev: d481acfb4c
GitHub-Pull-Request: golang/go#29661
Reviewed-on: https://go-review.googlesource.com/c/go/+/157377
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
jfbus 2019-03-26 18:21:53 +00:00 committed by Brad Fitzpatrick
parent 33e5da48d5
commit dbc1703781
6 changed files with 155 additions and 31 deletions

View file

@ -569,34 +569,52 @@ func (r *Resolver) goLookupIPCNAMEOrder(ctx context.Context, name string, order
resolvConf.mu.RLock()
conf := resolvConf.dnsConfig
resolvConf.mu.RUnlock()
type racer struct {
type result struct {
p dnsmessage.Parser
server string
error
}
lane := make(chan racer, 1)
lane := make(chan result, 1)
qtypes := [...]dnsmessage.Type{dnsmessage.TypeA, dnsmessage.TypeAAAA}
var lastErr error
for _, fqdn := range conf.nameList(name) {
for _, qtype := range qtypes {
var queryFn func(fqdn string, qtype dnsmessage.Type)
var responseFn func(fqdn string, qtype dnsmessage.Type) result
if conf.singleRequest {
queryFn = func(fqdn string, qtype dnsmessage.Type) {}
responseFn = func(fqdn string, qtype dnsmessage.Type) result {
dnsWaitGroup.Add(1)
defer dnsWaitGroup.Done()
p, server, err := r.tryOneName(ctx, conf, fqdn, qtype)
return result{p, server, err}
}
} else {
queryFn = func(fqdn string, qtype dnsmessage.Type) {
dnsWaitGroup.Add(1)
go func(qtype dnsmessage.Type) {
p, server, err := r.tryOneName(ctx, conf, fqdn, qtype)
lane <- racer{p, server, err}
lane <- result{p, server, err}
dnsWaitGroup.Done()
}(qtype)
}
responseFn = func(fqdn string, qtype dnsmessage.Type) result {
return <-lane
}
}
var lastErr error
for _, fqdn := range conf.nameList(name) {
for _, qtype := range qtypes {
queryFn(fqdn, qtype)
}
hitStrictError := false
for range qtypes {
racer := <-lane
if racer.error != nil {
if nerr, ok := racer.error.(Error); ok && nerr.Temporary() && r.strictErrors() {
for _, qtype := range qtypes {
result := responseFn(fqdn, qtype)
if result.error != nil {
if nerr, ok := result.error.(Error); ok && nerr.Temporary() && r.strictErrors() {
// This error will abort the nameList loop.
hitStrictError = true
lastErr = racer.error
lastErr = result.error
} else if lastErr == nil || fqdn == name+"." {
// Prefer error for original name.
lastErr = racer.error
lastErr = result.error
}
continue
}
@ -618,12 +636,12 @@ func (r *Resolver) goLookupIPCNAMEOrder(ctx context.Context, name string, order
loop:
for {
h, err := racer.p.AnswerHeader()
h, err := result.p.AnswerHeader()
if err != nil && err != dnsmessage.ErrSectionDone {
lastErr = &DNSError{
Err: "cannot marshal DNS message",
Name: name,
Server: racer.server,
Server: result.server,
}
}
if err != nil {
@ -631,35 +649,35 @@ func (r *Resolver) goLookupIPCNAMEOrder(ctx context.Context, name string, order
}
switch h.Type {
case dnsmessage.TypeA:
a, err := racer.p.AResource()
a, err := result.p.AResource()
if err != nil {
lastErr = &DNSError{
Err: "cannot marshal DNS message",
Name: name,
Server: racer.server,
Server: result.server,
}
break loop
}
addrs = append(addrs, IPAddr{IP: IP(a.A[:])})
case dnsmessage.TypeAAAA:
aaaa, err := racer.p.AAAAResource()
aaaa, err := result.p.AAAAResource()
if err != nil {
lastErr = &DNSError{
Err: "cannot marshal DNS message",
Name: name,
Server: racer.server,
Server: result.server,
}
break loop
}
addrs = append(addrs, IPAddr{IP: IP(aaaa.AAAA[:])})
default:
if err := racer.p.SkipAnswer(); err != nil {
if err := result.p.SkipAnswer(); err != nil {
lastErr = &DNSError{
Err: "cannot marshal DNS message",
Name: name,
Server: racer.server,
Server: result.server,
}
break loop
}