Commit 53a9669d06 for bind
commit 53a9669d062296a9afa6f1ee3ea1321a064100c8
Author: Štěpán Balážik <stepan@isc.org>
Date: Tue Sep 8 19:34:10 2026 +0200
Match on QTYPE with a matcher
Qtype declares the QTYPE a handler answers, which was previously done
with a match() method.
Assisted-by: Claude:claude-fable-5
diff --git a/bin/tests/system/dnssec/ans10/ans.py b/bin/tests/system/dnssec/ans10/ans.py
index 246701be3a..ff8993d7c1 100644
--- a/bin/tests/system/dnssec/ans10/ans.py
+++ b/bin/tests/system/dnssec/ans10/ans.py
@@ -16,11 +16,11 @@ import dns.rrset
from isctest.asyncserver import AsyncDnsServer, QueryContext, ResponseHandler
from isctest.asyncserver.actions import DnsResponseSend
+from isctest.asyncserver.matchers import Qtype
class AddRrsigToAHandler(ResponseHandler):
- def match(self, qctx: QueryContext) -> bool:
- return qctx.qtype == dns.rdatatype.A
+ matcher = Qtype(dns.rdatatype.A)
async def get_responses(
self, qctx: QueryContext
@@ -37,8 +37,7 @@ class AddRrsigToAHandler(ResponseHandler):
class AddNsecToTxtHandler(ResponseHandler):
- def match(self, qctx: QueryContext) -> bool:
- return qctx.qtype == dns.rdatatype.TXT
+ matcher = Qtype(dns.rdatatype.TXT)
async def get_responses(
self, qctx: QueryContext
diff --git a/bin/tests/system/isctest/asyncserver/handlers.py b/bin/tests/system/isctest/asyncserver/handlers.py
index 6acdd1964d..559790e7b1 100644
--- a/bin/tests/system/isctest/asyncserver/handlers.py
+++ b/bin/tests/system/isctest/asyncserver/handlers.py
@@ -32,6 +32,7 @@ from . import (
)
from .actions import BytesResponseSend, DnsResponseSend, ResponseDrop
from .context import Peer, QueryContext
+from .matchers import Matcher, Qtype
def block_reading(peer: Peer, writer_not_the_reader: asyncio.StreamWriter) -> None:
@@ -494,8 +495,7 @@ class AxfrHandler(ResponseHandler):
"""
raise NotImplementedError
- def match(self, qctx: QueryContext) -> bool:
- return qctx.qtype == dns.rdatatype.AXFR
+ matcher: Matcher = Qtype(dns.rdatatype.AXFR)
async def get_responses(
self, qctx: QueryContext
diff --git a/bin/tests/system/isctest/asyncserver/matchers.py b/bin/tests/system/isctest/asyncserver/matchers.py
index 60a8151f41..c9a62f8983 100644
--- a/bin/tests/system/isctest/asyncserver/matchers.py
+++ b/bin/tests/system/isctest/asyncserver/matchers.py
@@ -12,6 +12,7 @@
import abc
import dns.name
+import dns.rdatatype
from .context import QueryContext
@@ -126,3 +127,18 @@ class Qname(Matcher):
def __str__(self) -> str:
return f"QNAME in [{', '.join(str(name) for name in self.qnames)}]"
+
+
+class Qtype(Matcher):
+ """
+ Match queries whose QTYPE is one of the given types.
+ """
+
+ def __init__(self, *qtypes: dns.rdatatype.RdataType) -> None:
+ self.qtypes = qtypes
+
+ def match(self, qctx: QueryContext) -> bool:
+ return qctx.qtype in self.qtypes
+
+ def __str__(self) -> str:
+ return f"QTYPE in [{', '.join(map(dns.rdatatype.to_text, self.qtypes))}]"
diff --git a/bin/tests/system/ixfr/ans2/ans.py b/bin/tests/system/ixfr/ans2/ans.py
index a1234f91e9..64e3ad8182 100644
--- a/bin/tests/system/ixfr/ans2/ans.py
+++ b/bin/tests/system/ixfr/ans2/ans.py
@@ -26,6 +26,7 @@ from isctest.asyncserver import (
from isctest.asyncserver.actions import DnsResponseSend
from isctest.asyncserver.commands import SwitchControlCommand
from isctest.asyncserver.handlers import AxfrHandler
+from isctest.asyncserver.matchers import Qtype
def rrset(owner: str, rdtype: dns.rdatatype.RdataType, rdata: str) -> dns.rrset.RRset:
@@ -74,8 +75,7 @@ class SoaHandler(ResponseHandler):
def __init__(self, serial: int):
self._serial = serial
- def match(self, qctx: QueryContext) -> bool:
- return qctx.qtype == dns.rdatatype.SOA
+ matcher = Qtype(dns.rdatatype.SOA)
async def get_responses(
self, qctx: QueryContext
@@ -94,8 +94,7 @@ class IxfrHandler(ResponseHandler):
"""
raise NotImplementedError
- def match(self, qctx: QueryContext) -> bool:
- return qctx.qtype == dns.rdatatype.IXFR
+ matcher = Qtype(dns.rdatatype.IXFR)
async def get_responses(
self, qctx: QueryContext
diff --git a/bin/tests/system/nsec3/ans7/ans.py b/bin/tests/system/nsec3/ans7/ans.py
index 08ba3e4b22..f6227a3445 100644
--- a/bin/tests/system/nsec3/ans7/ans.py
+++ b/bin/tests/system/nsec3/ans7/ans.py
@@ -34,6 +34,7 @@ import dns.zone
from isctest.asyncserver import AsyncDnsServer, QueryContext, ResponseAction
from isctest.asyncserver.actions import DnsResponseSend
from isctest.asyncserver.handlers import ForwarderHandler, ResponseHandlerWrapper
+from isctest.asyncserver.matchers import Always, Qtype
from isctest.zone import FileZoneKey
# The malicious proxy relays every query to the real signed authoritative
@@ -183,10 +184,7 @@ class RelayForwarder(ForwarderHandler):
def __init__(self, *qtypes: dns.rdatatype.RdataType) -> None:
super().__init__()
- self._qtypes = qtypes
-
- def match(self, qctx: QueryContext) -> bool:
- return not self._qtypes or qctx.qtype in self._qtypes
+ self.matcher = Qtype(*qtypes) if qtypes else Always()
class DsInjector(ResponseHandlerWrapper):
diff --git a/bin/tests/system/nsec3_impersonation/ans1/ans.py b/bin/tests/system/nsec3_impersonation/ans1/ans.py
index 6cd2986368..d0b42bd6bb 100644
--- a/bin/tests/system/nsec3_impersonation/ans1/ans.py
+++ b/bin/tests/system/nsec3_impersonation/ans1/ans.py
@@ -29,6 +29,7 @@ import dns.rrset
from isctest.asyncserver import AsyncDnsServer, QueryContext, ResponseHandler
from isctest.asyncserver.actions import DnsResponseSend
+from isctest.asyncserver.matchers import Qname, Qtype
TTL = 300
TLD = "tld.test."
@@ -176,8 +177,7 @@ class VictimForgedNxdomainHandler(SignedResponseHandler):
This serves the forged response for the victim's domain.
"""
- def match(self, qctx: QueryContext) -> bool:
- return qctx.qname == name(VICTIM) and qctx.qtype == dns.rdatatype.A
+ matcher = Qname(VICTIM) & Qtype(dns.rdatatype.A)
def respond(self, qctx: QueryContext) -> None:
forged_nxdomain(qctx.response, self.keys)
@@ -189,8 +189,7 @@ class ChildDsHandler(SignedResponseHandler):
It is actually a validly signed DS response.
"""
- def match(self, qctx: QueryContext) -> bool:
- return qctx.qname == name(ATTACKER) and qctx.qtype == dns.rdatatype.DS
+ matcher = Qname(ATTACKER) & Qtype(dns.rdatatype.DS)
def respond(self, qctx: QueryContext) -> None:
response = qctx.response
diff --git a/bin/tests/system/rpzrecurse/ans5/ans.py b/bin/tests/system/rpzrecurse/ans5/ans.py
index bc1b2a9f9c..4d4968d442 100644
--- a/bin/tests/system/rpzrecurse/ans5/ans.py
+++ b/bin/tests/system/rpzrecurse/ans5/ans.py
@@ -17,11 +17,11 @@ import dns.rrset
from isctest.asyncserver import AsyncDnsServer, QueryContext, ResponseHandler
from isctest.asyncserver.actions import DnsResponseSend
+from isctest.asyncserver.matchers import Qtype
class ReplyA(ResponseHandler):
- def match(self, qctx: QueryContext) -> bool:
- return qctx.qtype == dns.rdatatype.A
+ matcher = Qtype(dns.rdatatype.A)
async def get_responses(
self, qctx: QueryContext
@@ -34,8 +34,7 @@ class ReplyA(ResponseHandler):
class DelayNs(ResponseHandler):
- def match(self, qctx: QueryContext) -> bool:
- return qctx.qtype == dns.rdatatype.NS
+ matcher = Qtype(dns.rdatatype.NS)
async def get_responses(
self, qctx: QueryContext
diff --git a/bin/tests/system/rpzrecurse/ans6/ans.py b/bin/tests/system/rpzrecurse/ans6/ans.py
index 434ee2bc22..1ecdf74a5e 100644
--- a/bin/tests/system/rpzrecurse/ans6/ans.py
+++ b/bin/tests/system/rpzrecurse/ans6/ans.py
@@ -17,11 +17,11 @@ import dns.rrset
from isctest.asyncserver import AsyncDnsServer, QueryContext, ResponseHandler
from isctest.asyncserver.actions import DnsResponseSend
+from isctest.asyncserver.matchers import Qtype
class ReplyA(ResponseHandler):
- def match(self, qctx: QueryContext) -> bool:
- return qctx.qtype == dns.rdatatype.A
+ matcher = Qtype(dns.rdatatype.A)
async def get_responses(
self, qctx: QueryContext
@@ -34,8 +34,7 @@ class ReplyA(ResponseHandler):
class DelayNs(ResponseHandler):
- def match(self, qctx: QueryContext) -> bool:
- return qctx.qtype == dns.rdatatype.NS
+ matcher = Qtype(dns.rdatatype.NS)
async def get_responses(
self, qctx: QueryContext
diff --git a/bin/tests/system/serve_stale/serve_stale_ans.py b/bin/tests/system/serve_stale/serve_stale_ans.py
index 983b92df8a..9b96eeac48 100644
--- a/bin/tests/system/serve_stale/serve_stale_ans.py
+++ b/bin/tests/system/serve_stale/serve_stale_ans.py
@@ -17,8 +17,9 @@ import dns.rdataclass
import dns.rdatatype
import dns.rrset
-from isctest.asyncserver import QueryContext, ResponseHandler
+from isctest.asyncserver import ResponseHandler
from isctest.asyncserver.handlers import StaticResponseHandler
+from isctest.asyncserver.matchers import Always, Qname, Qtype
def rrset(
@@ -82,20 +83,15 @@ class StaticHandler(StaticResponseHandler):
self._qnames = (
None if qnames is None else [dns.name.from_text(q) for q in qnames]
)
- self._qtypes = qtypes
+ self.matcher = (Qname(*self._qnames) if self._qnames else Always()) & (
+ Qtype(*qtypes) if qtypes else Always()
+ )
self._answer = list(answer)
self._authority = list(authority)
self._additional = list(additional)
self._rcode = rcode
self._delay = delay
- def match(self, qctx: QueryContext) -> bool:
- if self._qnames is not None and qctx.qname not in self._qnames:
- return False
- if self._qtypes is not None and qctx.qtype not in self._qtypes:
- return False
- return True
-
@property
def answer(self) -> Sequence[dns.rrset.RRset]:
return self._answer
diff --git a/bin/tests/system/xfer/ans5/ans.py b/bin/tests/system/xfer/ans5/ans.py
index be83dec455..72c383f90d 100644
--- a/bin/tests/system/xfer/ans5/ans.py
+++ b/bin/tests/system/xfer/ans5/ans.py
@@ -29,6 +29,7 @@ from isctest.asyncserver import (
from isctest.asyncserver.actions import DnsResponseSend
from isctest.asyncserver.commands import SwitchControlCommand
from isctest.asyncserver.handlers import AxfrHandler, ResponseHandlerWrapper
+from isctest.asyncserver.matchers import Qtype
from isctest.vars.algorithms import ALG_VARS
GOOD_KEY_DATA = "LSAnCU+Z"
@@ -184,8 +185,7 @@ class SoaHandler(ResponseHandler):
def __init__(self, serial: int = 1) -> None:
self._serial = serial
- def match(self, qctx: QueryContext) -> bool:
- return qctx.qtype == dns.rdatatype.SOA
+ matcher = Qtype(dns.rdatatype.SOA)
async def get_responses(
self, qctx: QueryContext
@@ -289,8 +289,7 @@ class WrongQnameInFinalSoa(ResponseHandlerWrapper):
class IxfrNotimpHandler(ResponseHandler):
- def match(self, qctx: QueryContext) -> bool:
- return qctx.qtype == dns.rdatatype.IXFR
+ matcher = Qtype(dns.rdatatype.IXFR)
async def get_responses(
self, qctx: QueryContext
diff --git a/bin/tests/system/xfer/ans9/ans.py b/bin/tests/system/xfer/ans9/ans.py
index 675d53de4f..dbe4b8b392 100644
--- a/bin/tests/system/xfer/ans9/ans.py
+++ b/bin/tests/system/xfer/ans9/ans.py
@@ -25,6 +25,7 @@ from isctest.asyncserver import (
from isctest.asyncserver.actions import DnsResponseSend
from isctest.asyncserver.commands import ToggleResponsesCommand
from isctest.asyncserver.handlers import AxfrHandler
+from isctest.asyncserver.matchers import Qname, Qtype
TTL = 300
@@ -78,8 +79,7 @@ class SerialCounted(ResponseHandler):
class SoaHandler(SerialCounted):
- def match(self, qctx: QueryContext) -> bool:
- return qctx.qtype == dns.rdatatype.SOA
+ matcher = Qtype(dns.rdatatype.SOA)
async def get_responses(
self, qctx: QueryContext
@@ -97,8 +97,9 @@ class ZoneAxfrHandler(AxfrHandler, SerialCounted):
zone: str
- def match(self, qctx: QueryContext) -> bool:
- return super().match(qctx) and qctx.qname == dns.name.from_text(self.zone)
+ def __init__(self, serials: SerialCounter) -> None:
+ super().__init__(serials)
+ self.matcher = Qtype(dns.rdatatype.AXFR) & Qname(self.zone)
async def get_responses(
self, qctx: QueryContext