net: use DNS over TCP when use-vc is set in resolv.conf

Fixes #29358
This commit is contained in:
jfbus 2019-01-07 08:30:58 +01:00
parent c226f6432d
commit 8ce0afdb2b
5 changed files with 54 additions and 6 deletions

View File

@ -131,13 +131,19 @@ func dnsStreamRoundTrip(c Conn, id uint16, query dnsmessage.Question, b []byte)
}
// exchange sends a query on the connection and hopes for a response.
func (r *Resolver) exchange(ctx context.Context, server string, q dnsmessage.Question, timeout time.Duration) (dnsmessage.Parser, dnsmessage.Header, error) {
func (r *Resolver) exchange(ctx context.Context, server string, q dnsmessage.Question, timeout time.Duration, usetcp bool) (dnsmessage.Parser, dnsmessage.Header, error) {
q.Class = dnsmessage.ClassINET
id, udpReq, tcpReq, err := newRequest(q)
if err != nil {
return dnsmessage.Parser{}, dnsmessage.Header{}, errCannotMarshalDNSMessage
}
for _, network := range []string{"udp", "tcp"} {
var networks []string
if usetcp {
networks = []string{"tcp"}
} else {
networks = []string{"udp", "tcp"}
}
for _, network := range networks {
ctx, cancel := context.WithDeadline(ctx, time.Now().Add(timeout))
defer cancel()
@ -241,7 +247,7 @@ func (r *Resolver) tryOneName(ctx context.Context, cfg *dnsConfig, name string,
for j := uint32(0); j < sLen; j++ {
server := cfg.servers[(serverOffset+j)%sLen]
p, h, err := r.exchange(ctx, server, q, cfg.timeout)
p, h, err := r.exchange(ctx, server, q, cfg.timeout, cfg.usetcp)
if err != nil {
dnsErr := &DNSError{
Err: err.Error(),

View File

@ -81,7 +81,7 @@ func TestDNSTransportFallback(t *testing.T) {
for _, tt := range dnsTransportFallbackTests {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
_, h, err := r.exchange(ctx, tt.server, tt.question, time.Second)
_, h, err := r.exchange(ctx, tt.server, tt.question, time.Second, false)
if err != nil {
t.Error(err)
continue
@ -137,7 +137,7 @@ func TestSpecialDomainName(t *testing.T) {
for _, tt := range specialDomainNameTests {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
_, h, err := r.exchange(ctx, server, tt.question, 3*time.Second)
_, h, err := r.exchange(ctx, server, tt.question, 3*time.Second, false)
if err != nil {
t.Error(err)
continue
@ -1564,7 +1564,7 @@ func TestDNSDialTCP(t *testing.T) {
}
r := Resolver{PreferGo: true, Dial: fake.DialContext}
ctx := context.Background()
_, _, err := r.exchange(ctx, "0.0.0.0", mustQuestion("com.", dnsmessage.TypeALL, dnsmessage.ClassINET), time.Second)
_, _, err := r.exchange(ctx, "0.0.0.0", mustQuestion("com.", dnsmessage.TypeALL, dnsmessage.ClassINET), time.Second, false)
if err != nil {
t.Fatal("exhange failed:", err)
}
@ -1695,3 +1695,30 @@ func TestSingleRequestLookup(t *testing.T) {
}
}
}
// Issue 29358. Add configuration knob to force TCP-only DNS requests in the pure Go resolver.
func TestDNSUseTCP(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, 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, true)
if err != nil {
t.Fatal("exchange failed:", err)
}
}

View File

@ -33,6 +33,7 @@ type dnsConfig struct {
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.
@ -123,6 +124,8 @@ func dnsReadConfig(filename string) *dnsConfig {
// This option disables the behavior and makes glibc
// perform the IPv6 and IPv4 requests sequentially."
conf.singleRequest = true
case s == "use-vc":
conf.usetcp = true
default:
conf.unknownOpt = true
}

View File

@ -124,6 +124,17 @@ var dnsReadConfigTests = []struct {
search: []string{"domain.local."},
},
},
{
name: "testdata/use-vc-resolv.conf",
want: &dnsConfig{
servers: defaultNS,
ndots: 1,
usetcp: true,
timeout: 5 * time.Second,
attempts: 2,
search: []string{"domain.local."},
},
},
}
func TestDNSReadConfig(t *testing.T) {

1
src/net/testdata/use-vc-resolv.conf vendored Normal file
View File

@ -0,0 +1 @@
options use-vc