Commit 0a3cb7ec authored by Alex A Skinner's avatar Alex A Skinner Committed by Brad Fitzpatrick

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
parent c18dc11e
......@@ -17,6 +17,7 @@
package net
import (
"io"
"math/rand"
"sync"
"time"
......@@ -25,6 +26,13 @@ import (
// Send a request on the connection and hope for a reply.
// Up to cfg.attempts attempts.
func exchange(cfg *dnsConfig, c Conn, name string, qtype uint16) (*dnsMsg, error) {
var useTCP bool
switch c.(type) {
case *UDPConn:
useTCP = false
case *TCPConn:
useTCP = true
}
if len(name) >= 256 {
return nil, &DNSError{Err: "name too long", Name: name}
}
......@@ -38,7 +46,10 @@ func exchange(cfg *dnsConfig, c Conn, name string, qtype uint16) (*dnsMsg, error
if !ok {
return nil, &DNSError{Err: "internal error - cannot pack message", Name: name}
}
if useTCP {
mlen := uint16(len(msg))
msg = append([]byte{byte(mlen >> 8), byte(mlen)}, msg...)
}
for attempt := 0; attempt < cfg.attempts; attempt++ {
n, err := c.Write(msg)
if err != nil {
......@@ -50,9 +61,19 @@ func exchange(cfg *dnsConfig, c Conn, name string, qtype uint16) (*dnsMsg, error
} else {
c.SetReadDeadline(time.Now().Add(time.Duration(cfg.timeout) * time.Second))
}
buf := make([]byte, 2000) // More than enough.
n, err = c.Read(buf)
buf := make([]byte, 2000)
if useTCP {
n, err = io.ReadFull(c, buf[:2])
if err != nil {
if e, ok := err.(Error); ok && e.Timeout() {
continue
}
}
buf = make([]byte, uint16(buf[0])<<8+uint16(buf[1]))
n, err = io.ReadFull(c, buf)
} else {
n, err = c.Read(buf)
}
if err != nil {
if e, ok := err.(Error); ok && e.Timeout() {
continue
......@@ -98,6 +119,19 @@ func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs
err = merr
continue
}
if msg.truncated { // see RFC 5966
c, cerr = Dial("tcp", server)
if cerr != nil {
err = cerr
continue
}
msg, merr = exchange(cfg, c, name, qtype)
c.Close()
if merr != nil {
err = merr
continue
}
}
cname, addrs, err = answer(name, server, msg, qtype)
if err == nil || err.(*DNSError).Err == noSuchHost {
break
......
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package net
import (
"runtime"
"testing"
)
func TestTCPLookup(t *testing.T) {
if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
t.Skip("skipping unix dns test")
}
if testing.Short() || !*testExternal {
t.Skip("skipping test to avoid external network")
}
c, err := Dial("tcp", "8.8.8.8:53")
defer c.Close()
if err != nil {
t.Fatalf("Dial failed: %v", err)
}
cfg := &dnsConfig{timeout: 10, attempts: 3}
_, err = exchange(cfg, c, "com.", dnsTypeALL)
if err != nil {
t.Fatalf("exchange failed: %v", 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