net/dns/resolver: set TC flag when UDP responses exceed size limits (#18157)
The forwarder was not setting the Truncated (TC) flag when UDP DNS responses exceeded either the EDNS buffer size (if present) or the RFC 1035 default 512-byte limit. This affected DoH, TCP fallback, and UDP response paths. The fix ensures checkResponseSizeAndSetTC is called in all code paths that return UDP responses, enforcing both EDNS and default UDP size limits. Added comprehensive unit tests and consolidated duplicate test helpers. Updates #18107 Signed-off-by: Brendan Creane <bcreane@gmail.com>
This commit is contained in:
@@ -1572,3 +1572,102 @@ func TestServfail(t *testing.T) {
|
||||
t.Errorf("response was %X, want %X", pkt, wantPkt)
|
||||
}
|
||||
}
|
||||
|
||||
// TestLocalResponseTCFlagIntegration tests that checkResponseSizeAndSetTC is
|
||||
// correctly applied to local DNS responses through the Resolver.Query integration path.
|
||||
// This complements the unit test in forwarder_test.go by verifying the end-to-end behavior.
|
||||
func TestLocalResponseTCFlagIntegration(t *testing.T) {
|
||||
r := newResolver(t)
|
||||
defer r.Close()
|
||||
|
||||
r.SetConfig(dnsCfg)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
query []byte
|
||||
family string
|
||||
wantTCSet bool
|
||||
desc string
|
||||
}{
|
||||
{
|
||||
name: "UDP_small_local_response_no_TC",
|
||||
query: dnspacket("test1.ipn.dev.", dns.TypeA, noEdns),
|
||||
family: "udp",
|
||||
wantTCSet: false,
|
||||
desc: "Small local response (< 512 bytes) should not have TC flag set",
|
||||
},
|
||||
{
|
||||
name: "TCP_local_response_no_TC",
|
||||
query: dnspacket("test1.ipn.dev.", dns.TypeA, noEdns),
|
||||
family: "tcp",
|
||||
wantTCSet: false,
|
||||
desc: "TCP queries should skip TC flag setting (even for large responses)",
|
||||
},
|
||||
{
|
||||
name: "UDP_EDNS_request_small_response",
|
||||
query: dnspacket("test1.ipn.dev.", dns.TypeA, 1500),
|
||||
family: "udp",
|
||||
wantTCSet: false,
|
||||
desc: "Small response with EDNS request should not have TC flag set",
|
||||
},
|
||||
{
|
||||
name: "UDP_IPv6_response_no_TC",
|
||||
query: dnspacket("test2.ipn.dev.", dns.TypeAAAA, noEdns),
|
||||
family: "udp",
|
||||
wantTCSet: false,
|
||||
desc: "Small IPv6 local response should not have TC flag set",
|
||||
},
|
||||
{
|
||||
name: "UDP_reverse_lookup_no_TC",
|
||||
query: dnspacket("4.3.2.1.in-addr.arpa.", dns.TypePTR, noEdns),
|
||||
family: "udp",
|
||||
wantTCSet: false,
|
||||
desc: "Small reverse lookup response should not have TC flag set",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
response, err := r.Query(context.Background(), tt.query, tt.family, netip.AddrPort{})
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
|
||||
if len(response) < headerBytes {
|
||||
t.Fatalf("Response too small: %d bytes", len(response))
|
||||
}
|
||||
|
||||
hasTC := truncatedFlagSet(response)
|
||||
if hasTC != tt.wantTCSet {
|
||||
t.Errorf("%s: TC flag = %v, want %v (response size=%d bytes)", tt.desc, hasTC, tt.wantTCSet, len(response))
|
||||
}
|
||||
|
||||
// Verify response is valid by parsing it (if possible)
|
||||
// Note: unpackResponse may not support all record types (e.g., PTR)
|
||||
parsed, err := unpackResponse(response)
|
||||
if err == nil {
|
||||
// Verify the truncated field in parsed response matches the flag
|
||||
if parsed.truncated != hasTC {
|
||||
t.Errorf("Parsed truncated field (%v) doesn't match TC flag (%v)", parsed.truncated, hasTC)
|
||||
}
|
||||
} else {
|
||||
// For unsupported types, just verify we can parse the header
|
||||
var parser dns.Parser
|
||||
h, err := parser.Start(response)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to parse DNS header: %v", err)
|
||||
} else {
|
||||
// Verify header truncated flag matches
|
||||
if h.Truncated != hasTC {
|
||||
t.Errorf("Header truncated field (%v) doesn't match TC flag (%v)", h.Truncated, hasTC)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Verify response size is reasonable (local responses are typically small)
|
||||
if len(response) > 1000 {
|
||||
t.Logf("Warning: Local response is unusually large: %d bytes", len(response))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user