]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Refactor the mismatchtcp ans2 response server 12528/head
authorŠtěpán Balážik <stepan@isc.org>
Thu, 23 Jul 2026 20:49:16 +0000 (22:49 +0200)
committerŠtěpán Balážik <stepan@isc.org>
Wed, 12 Aug 2026 10:12:11 +0000 (10:12 +0000)
Match the Kaminsky-spoof case declaratively with a QnameQtypeHandler
and a protocol check, replacing the imperative match and the UDP/TCP
branch.  TCP queries fall through to the normal zone answer, as before.

Assisted-by: Claude:claude-fable-5
bin/tests/system/mismatchtcp/ans2/ans.py

index 365a6f22b652537fefecb516f0ca787ae32732c4..cac05400a76a9d1a1b262d167c67d115af325c02 100644 (file)
@@ -9,56 +9,46 @@
 # See the COPYRIGHT file distributed with this work for additional
 # information regarding copyright ownership.
 
-"""
-Authoritative server that simulates Kaminsky-style off-path spoofing on UDP:
-for every UDP query for trigger.example./A it sends one response with a
-deliberately flipped DNS message id.  A resolver that escalates to TCP on
-the first id mismatch will still get the correct answer over TCP, which
-this server serves normally.
-"""
-
 from collections.abc import AsyncGenerator
 
-import dns.name
 import dns.rdatatype
 
 from isctest.asyncserver import (
     AsyncDnsServer,
     DnsProtocol,
     DnsResponseSend,
+    QnameQtypeHandler,
     QueryContext,
     ResponseAction,
-    ResponseHandler,
 )
 
 
-class MismatchOnUdpHandler(ResponseHandler):
+class MismatchedIdOnUdpHandler(QnameQtypeHandler):
     """
-    Spoof UDP queries for trigger.example./A with a properly-formed
-    response whose DNS message id does not match the request.  Answer
-    the same query normally on TCP using the zone data prepared by the
-    framework.
+    Simulate Kaminsky-style off-path spoofing: answer every UDP query for
+    trigger.example./A with the correct response prepared from zone data,
+    but with a deliberately flipped DNS message id.  TCP queries do not
+    match this handler and are answered from zone data as usual, so a
+    resolver that escalates to TCP on the first id mismatch still gets
+    the correct answer.
     """
 
-    def __init__(self) -> None:
-        self._trigger = dns.name.from_text("trigger.example.")
+    qnames = ["trigger.example."]
+    qtypes = [dns.rdatatype.A]
 
     def match(self, qctx: QueryContext) -> bool:
-        return qctx.qname == self._trigger and qctx.qtype == dns.rdatatype.A
+        return qctx.protocol == DnsProtocol.UDP and super().match(qctx)
 
     async def get_responses(
         self, qctx: QueryContext
     ) -> AsyncGenerator[ResponseAction, None]:
-        if qctx.protocol == DnsProtocol.UDP:
-            qctx.response.id = qctx.query.id ^ 0xFFFF
-            yield DnsResponseSend(qctx.response)
-        else:
-            yield DnsResponseSend(qctx.response)
+        qctx.response.id = qctx.query.id ^ 0xFFFF
+        yield DnsResponseSend(qctx.response)
 
 
 def main() -> None:
     server = AsyncDnsServer()
-    server.install_response_handler(MismatchOnUdpHandler())
+    server.install_response_handler(MismatchedIdOnUdpHandler())
     server.run()