net/dns/resolver: permit larger max responses, signal truncation

This raises the maximum DNS response message size from 512 to 4095. This
should be large enough for almost all situations that do not need TCP.
We still do not recognize EDNS, so we will still forward requests that
claim support for a larger response size than 4095 (that will be solved
later). For now, when a response comes back that is too large to fit in
our receive buffer, we now set the truncation flag in the DNS header,
which is an improvement from before but will prompt attempts to use TCP
which isn't supported yet.

On Windows, WSARecvFrom into a buffer that's too small returns an error
in addition to the data. On other OSes, the extra data is silently
discarded. In this case, we prefer the latter so need to catch the error
on Windows.

Partially addresses #1123

Signed-off-by: Adrian Dewhurst <adrian@tailscale.com>
This commit is contained in:
Adrian Dewhurst
2021-06-07 17:16:07 -04:00
committed by Adrian Dewhurst
parent fc5fba0fbf
commit 8b11937eaf
7 changed files with 199 additions and 9 deletions
+23 -2
View File
@@ -194,8 +194,14 @@ func (f *forwarder) recv(conn *fwdConn) {
return
default:
}
out := make([]byte, maxResponseBytes)
// The 1 extra byte is to detect packet truncation.
out := make([]byte, maxResponseBytes+1)
n := conn.read(out)
var truncated bool
if n > maxResponseBytes {
n = maxResponseBytes
truncated = true
}
if n == 0 {
continue
}
@@ -206,6 +212,19 @@ func (f *forwarder) recv(conn *fwdConn) {
out = out[:n]
txid := getTxID(out)
if truncated {
const dnsFlagTruncated = 0x200
flags := binary.BigEndian.Uint16(out[2:4])
flags |= dnsFlagTruncated
binary.BigEndian.PutUint16(out[2:4], flags)
// TODO(#2067): Remove any incomplete records? RFC 1035 section 6.2
// states that truncation should head drop so that the authority
// section can be preserved if possible. However, the UDP read with
// a too-small buffer has already dropped the end, so that's the
// best we can do.
}
f.mu.Lock()
record, found := f.txMap[txid]
@@ -287,6 +306,8 @@ func (f *forwarder) forward(query packet) error {
}
f.mu.Unlock()
// TODO(#2066): EDNS size clamping
for _, resolver := range resolvers {
f.send(query.bs, resolver)
}
@@ -429,7 +450,7 @@ func (c *fwdConn) read(out []byte) int {
c.mu.Unlock()
n, _, err := conn.ReadFrom(out)
if err == nil {
if err == nil || packetWasTruncated(err) {
// Success.
return n
}