Skip to content

Commit 0a3cb7e

Browse files
axaxsbradfitz
authored andcommitted
net: implement DNS TCP fallback query if UDP response is truncated
Fixes #5686. R=golang-dev, bradfitz, mikioh.mikioh CC=golang-dev https://golang.org/cl/12458043
1 parent c18dc11 commit 0a3cb7e

File tree

2 files changed

+67
-4
lines changed

2 files changed

+67
-4
lines changed

src/pkg/net/dnsclient_unix.go

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package net
1818

1919
import (
20+
"io"
2021
"math/rand"
2122
"sync"
2223
"time"
@@ -25,6 +26,13 @@ import (
2526
// Send a request on the connection and hope for a reply.
2627
// Up to cfg.attempts attempts.
2728
func exchange(cfg *dnsConfig, c Conn, name string, qtype uint16) (*dnsMsg, error) {
29+
var useTCP bool
30+
switch c.(type) {
31+
case *UDPConn:
32+
useTCP = false
33+
case *TCPConn:
34+
useTCP = true
35+
}
2836
if len(name) >= 256 {
2937
return nil, &DNSError{Err: "name too long", Name: name}
3038
}
@@ -38,7 +46,10 @@ func exchange(cfg *dnsConfig, c Conn, name string, qtype uint16) (*dnsMsg, error
3846
if !ok {
3947
return nil, &DNSError{Err: "internal error - cannot pack message", Name: name}
4048
}
41-
49+
if useTCP {
50+
mlen := uint16(len(msg))
51+
msg = append([]byte{byte(mlen >> 8), byte(mlen)}, msg...)
52+
}
4253
for attempt := 0; attempt < cfg.attempts; attempt++ {
4354
n, err := c.Write(msg)
4455
if err != nil {
@@ -50,9 +61,19 @@ func exchange(cfg *dnsConfig, c Conn, name string, qtype uint16) (*dnsMsg, error
5061
} else {
5162
c.SetReadDeadline(time.Now().Add(time.Duration(cfg.timeout) * time.Second))
5263
}
53-
54-
buf := make([]byte, 2000) // More than enough.
55-
n, err = c.Read(buf)
64+
buf := make([]byte, 2000)
65+
if useTCP {
66+
n, err = io.ReadFull(c, buf[:2])
67+
if err != nil {
68+
if e, ok := err.(Error); ok && e.Timeout() {
69+
continue
70+
}
71+
}
72+
buf = make([]byte, uint16(buf[0])<<8+uint16(buf[1]))
73+
n, err = io.ReadFull(c, buf)
74+
} else {
75+
n, err = c.Read(buf)
76+
}
5677
if err != nil {
5778
if e, ok := err.(Error); ok && e.Timeout() {
5879
continue
@@ -98,6 +119,19 @@ func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs
98119
err = merr
99120
continue
100121
}
122+
if msg.truncated { // see RFC 5966
123+
c, cerr = Dial("tcp", server)
124+
if cerr != nil {
125+
err = cerr
126+
continue
127+
}
128+
msg, merr = exchange(cfg, c, name, qtype)
129+
c.Close()
130+
if merr != nil {
131+
err = merr
132+
continue
133+
}
134+
}
101135
cname, addrs, err = answer(name, server, msg, qtype)
102136
if err == nil || err.(*DNSError).Err == noSuchHost {
103137
break

src/pkg/net/dnsclient_unix_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2013 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package net
6+
7+
import (
8+
"runtime"
9+
"testing"
10+
)
11+
12+
func TestTCPLookup(t *testing.T) {
13+
if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
14+
t.Skip("skipping unix dns test")
15+
}
16+
if testing.Short() || !*testExternal {
17+
t.Skip("skipping test to avoid external network")
18+
}
19+
c, err := Dial("tcp", "8.8.8.8:53")
20+
defer c.Close()
21+
if err != nil {
22+
t.Fatalf("Dial failed: %v", err)
23+
}
24+
cfg := &dnsConfig{timeout: 10, attempts: 3}
25+
_, err = exchange(cfg, c, "com.", dnsTypeALL)
26+
if err != nil {
27+
t.Fatalf("exchange failed: %v", err)
28+
}
29+
}

0 commit comments

Comments
 (0)