cmd/cli: discard upstream answers whose question mismatches the request

Defense in depth against cache poisoning: a compromised or misbehaving
upstream can return an answer for a different name than was asked (e.g.
records for attacker.example in response to a query for victim.example).
Such an answer would be cached under the legitimate request key and
served to subsequent queries.

Validate that the upstream answer echoes the request's question
(case-insensitive name plus Qtype/Qclass, per RFC 1035 section 4.1.2)
before serving or caching it. A mismatch is logged at debug level and
the upstream is skipped, failing safe to the next upstream or SERVFAIL.

Refs github.com/Control-D-Inc/ctrld/issues/322
This commit is contained in:
Cuong Manh Le
2026-07-14 03:45:19 +07:00
parent 9cf8bc3b5b
commit 7f3d332b64
3 changed files with 70 additions and 2 deletions
+2 -2
View File
@@ -748,7 +748,7 @@ func TestDoQResolve_PreservesRcode(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
pool := newDOQConnPool(uc, []string{"127.0.0.1"})
pool := newDOQConnPool(ctx, uc, []string{"127.0.0.1"})
t.Cleanup(pool.CloseIdleConnections)
msg := new(dns.Msg)
@@ -797,7 +797,7 @@ func TestDoQResolve_PreservesWrongQuestion(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
pool := newDOQConnPool(uc, []string{"127.0.0.1"})
pool := newDOQConnPool(ctx, uc, []string{"127.0.0.1"})
t.Cleanup(pool.CloseIdleConnections)
msg := new(dns.Msg)