Skip to content

net: allow TCP only DNS requests in the pure Go resolver on unix #29367

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions src/net/conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ type conf struct {
netGo bool // go DNS resolution forced
netCgo bool // cgo DNS resolution forced

// use TCP for netGo DNS queries
preferTCP bool

// machine has an /etc/mdns.allow file
hasMDNSAllow bool

Expand All @@ -44,10 +47,11 @@ func systemConf() *conf {
}

func initConfVal() {
dnsMode, debugLevel := goDebugNetDNS()
dnsMode, debugLevel, preferTCP := goDebugNetDNS()
confVal.dnsDebugLevel = debugLevel
confVal.netGo = netGo || dnsMode == "go"
confVal.netCgo = netCgo || dnsMode == "cgo"
confVal.preferTCP = preferTCP

if confVal.dnsDebugLevel > 0 {
defer func() {
Expand Down Expand Up @@ -286,18 +290,22 @@ func (c *conf) hostLookupOrder(r *Resolver, hostname string) (ret hostLookupOrde
// 2 // debug level 2
// cgo // use cgo for DNS lookups
// go // use go for DNS lookups
// tcp // use TCP for DNS lookups (go resolver only)
// cgo+1 // use cgo for DNS lookups + debug level 1
// 1+cgo // same
// cgo+2 // same, but debug level 2
// go+tcp // use go and TCP
// etc.
func goDebugNetDNS() (dnsMode string, debugLevel int) {
func goDebugNetDNS() (dnsMode string, debugLevel int, preferTCP bool) {
goDebug := goDebugString("netdns")
parsePart := func(s string) {
if s == "" {
return
}
if '0' <= s[0] && s[0] <= '9' {
debugLevel, _, _ = dtoi(s)
} else if s == "tcp" {
preferTCP = true
} else {
dnsMode = s
}
Expand Down
8 changes: 7 additions & 1 deletion src/net/dnsclient_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,13 @@ func (r *Resolver) exchange(ctx context.Context, server string, q dnsmessage.Que
if err != nil {
return dnsmessage.Parser{}, dnsmessage.Header{}, errCannotMarshalDNSMessage
}
for _, network := range []string{"udp", "tcp"} {
var networks []string
if r.PreferTCP || systemConf().preferTCP {
networks = []string{"tcp"}
} else {
networks = []string{"udp", "tcp"}
}
for _, network := range networks {
ctx, cancel := context.WithDeadline(ctx, time.Now().Add(timeout))
defer cancel()

Expand Down
27 changes: 27 additions & 0 deletions src/net/dnsclient_unix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1621,3 +1621,30 @@ func TestTXTRecordTwoStrings(t *testing.T) {
t.Errorf("txt[1], got %q, want %q", txt[1], want)
}
}

// Issue 29358. Add configuration knob to force TCP-only DNS requests in the pure Go resolver.
func TestPreferTCP(t *testing.T) {
fake := fakeDNSServer{
rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
r := dnsmessage.Message{
Header: dnsmessage.Header{
ID: q.Header.ID,
Response: true,
RCode: dnsmessage.RCodeSuccess,
},
Questions: q.Questions,
}
if n == "udp" {
t.Fatal("udp protocol was used instead of tcp")
}
return r, nil
},
}
r := Resolver{PreferGo: true, PreferTCP: true, Dial: fake.DialContext}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
_, _, err := r.exchange(ctx, "0.0.0.0", mustQuestion("com.", dnsmessage.TypeALL, dnsmessage.ClassINET), time.Second)
if err != nil {
t.Fatal("exchange failed:", err)
}
}
5 changes: 5 additions & 0 deletions src/net/lookup.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ type Resolver struct {
// GODEBUG=netdns=go, but scoped to just this resolver.
PreferGo bool

// PreferTCP controls whether Go's built-in DNS resolver will use
// TCP instead of UDP with a fallback to TCP. It is equivalent to setting
// GODEBUG=netdns=go+tcp, but scoped to just this resolver.
PreferTCP 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
Expand Down
3 changes: 3 additions & 0 deletions src/net/net.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ GODEBUG environment variable (see package runtime) to go or cgo, as in:
The decision can also be forced while building the Go source tree
by setting the netgo or netcgo build tag.

The pure Go resolver can be configured to use only TCP to communicate
by using the tcp option, as in GODEBUG=netdns=go,tcp.

A numeric netdns setting, as in GODEBUG=netdns=1, causes the resolver
to print debugging information about its decisions.
To force a particular resolver while also printing debugging information,
Expand Down