Commit 4f8bf0f0af for bind
commit 4f8bf0f0afc636f44779ec11a6a96bb9efbd8e5a
Author: Štěpán Balážik <stepan@isc.org>
Date: Tue Sep 8 19:35:03 2026 +0200
Match on the transport protocol with a matcher
Protocol declares the transport a handler answers on, which was
previously done with a match() method.
Assisted-by: Claude:claude-fable-5
diff --git a/bin/tests/system/bailiwick/bailiwick_ans.py b/bin/tests/system/bailiwick/bailiwick_ans.py
index ce08119fef..6c1a38e0ae 100644
--- a/bin/tests/system/bailiwick/bailiwick_ans.py
+++ b/bin/tests/system/bailiwick/bailiwick_ans.py
@@ -11,7 +11,6 @@
import abc
-import dns.name
import dns.rcode
import dns.rdatatype
@@ -22,6 +21,7 @@ from isctest.asyncserver import (
QueryContext,
ResponseHandler,
)
+from isctest.asyncserver.matchers import Protocol, Qname, Qtype
class ResponseSpoofer(ResponseHandler, abc.ABC):
@@ -44,11 +44,9 @@ class ResponseSpoofer(ResponseHandler, abc.ABC):
def qname(self) -> str:
raise NotImplementedError
- def match(self, qctx: QueryContext) -> bool:
- return (
- qctx.qname == dns.name.from_text(self.qname)
- and qctx.qtype == dns.rdatatype.TXT
- and qctx.protocol == DnsProtocol.UDP
+ def __init__(self) -> None:
+ self.matcher = (
+ Qname(self.qname) & Qtype(dns.rdatatype.TXT) & Protocol(DnsProtocol.UDP)
)
diff --git a/bin/tests/system/cookie/cookie_ans.py b/bin/tests/system/cookie/cookie_ans.py
index e1326d1466..063ee175f7 100644
--- a/bin/tests/system/cookie/cookie_ans.py
+++ b/bin/tests/system/cookie/cookie_ans.py
@@ -25,6 +25,7 @@ from isctest.asyncserver import (
ResponseHandler,
)
from isctest.asyncserver.actions import DnsResponseSend
+from isctest.asyncserver.matchers import Protocol, Qtype
from isctest.name import prepend_label
from isctest.vars.algorithms import ALG_VARS
@@ -122,8 +123,7 @@ class GlueHandler(_SpoofableHandler):
class TcpAHandler(ResponseHandler):
- def match(self, qctx: QueryContext) -> bool:
- return qctx.qtype == dns.rdatatype.A and qctx.protocol == DnsProtocol.TCP
+ matcher = Qtype(dns.rdatatype.A) & Protocol(DnsProtocol.TCP)
async def get_responses(
self, qctx: QueryContext
@@ -157,8 +157,7 @@ class WithtsigUdpAHandler(ResponseHandler):
class UdpAHandler(ResponseHandler):
- def match(self, qctx: QueryContext) -> bool:
- return qctx.qtype == dns.rdatatype.A and qctx.protocol == DnsProtocol.UDP
+ matcher = Qtype(dns.rdatatype.A) & Protocol(DnsProtocol.UDP)
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 bc56dc6255..41a6d49305 100644
--- a/bin/tests/system/isctest/asyncserver/matchers.py
+++ b/bin/tests/system/isctest/asyncserver/matchers.py
@@ -17,7 +17,7 @@ import abc
import dns.name
import dns.rdatatype
-from .context import QueryContext
+from .context import DnsProtocol, QueryContext
M = TypeVar("M", bound="Matcher")
@@ -204,3 +204,18 @@ class Domain(Matcher):
def __str__(self) -> str:
return f"QNAME under [{', '.join(str(name) for name in self._domains)}]"
+
+
+class Protocol(Matcher):
+ """
+ Match queries received over the given transport protocol.
+ """
+
+ def __init__(self, protocol: DnsProtocol) -> None:
+ self._protocol = protocol
+
+ def match(self, qctx: QueryContext) -> bool:
+ return qctx.protocol == self._protocol
+
+ def __str__(self) -> str:
+ return f"over {self._protocol.name}"
diff --git a/bin/tests/system/mismatchtcp/ans2/ans.py b/bin/tests/system/mismatchtcp/ans2/ans.py
index d80a0dfc51..ebb30a350d 100644
--- a/bin/tests/system/mismatchtcp/ans2/ans.py
+++ b/bin/tests/system/mismatchtcp/ans2/ans.py
@@ -11,7 +11,6 @@
from collections.abc import AsyncGenerator
-import dns.name
import dns.rdatatype
from isctest.asyncserver import (
@@ -21,6 +20,7 @@ from isctest.asyncserver import (
ResponseHandler,
)
from isctest.asyncserver.actions import DnsResponseSend
+from isctest.asyncserver.matchers import Protocol, Qname, Qtype
class MismatchedIdOnUdpHandler(ResponseHandler):
@@ -33,12 +33,9 @@ class MismatchedIdOnUdpHandler(ResponseHandler):
the correct answer.
"""
- def match(self, qctx: QueryContext) -> bool:
- return (
- qctx.qname == dns.name.from_text("trigger.example.")
- and qctx.qtype == dns.rdatatype.A
- and qctx.protocol == DnsProtocol.UDP
- )
+ matcher = (
+ Qname("trigger.example.") & Qtype(dns.rdatatype.A) & Protocol(DnsProtocol.UDP)
+ )
async def get_responses(
self, qctx: QueryContext
diff --git a/bin/tests/system/resolver/ans8/ans.py b/bin/tests/system/resolver/ans8/ans.py
index 00ddfcdf91..bea20ff04c 100644
--- a/bin/tests/system/resolver/ans8/ans.py
+++ b/bin/tests/system/resolver/ans8/ans.py
@@ -15,7 +15,6 @@ import abc
import dns.flags
import dns.message
-import dns.name
import dns.rcode
import dns.rdatatype
@@ -27,7 +26,7 @@ from isctest.asyncserver import (
)
from isctest.asyncserver.actions import DnsResponseSend
from isctest.asyncserver.handlers import StaticResponseHandler
-from isctest.asyncserver.matchers import Domain, Qname, Qtype
+from isctest.asyncserver.matchers import Domain, Protocol, Qname, Qtype
from ..resolver_ans import rrset
@@ -57,18 +56,13 @@ class HeaderOnlyHandler(ResponseHandler):
class RefusedOnTcpHandler(HeaderOnlyHandler):
+ matcher = Qname("tcpalso.no-questions.") & Protocol(DnsProtocol.TCP)
flags = dns.flags.QR
rcode = dns.rcode.REFUSED
- def match(self, qctx: QueryContext) -> bool:
- return qctx.protocol == DnsProtocol.TCP and qctx.qname == dns.name.from_text(
- "tcpalso.no-questions."
- )
-
class TcpFallbackHandler(ResponseHandler):
- def match(self, qctx: QueryContext) -> bool:
- return qctx.protocol == DnsProtocol.TCP
+ matcher = Protocol(DnsProtocol.TCP)
async def get_responses(
self, qctx: QueryContext
diff --git a/bin/tests/system/xfer/ans11/ans.py b/bin/tests/system/xfer/ans11/ans.py
index 41d9009bac..c2282af8ed 100644
--- a/bin/tests/system/xfer/ans11/ans.py
+++ b/bin/tests/system/xfer/ans11/ans.py
@@ -25,6 +25,7 @@ from isctest.asyncserver import (
)
from isctest.asyncserver.actions import DnsResponseSend
from isctest.asyncserver.handlers import AxfrHandler, StaticResponseHandler
+from isctest.asyncserver.matchers import Protocol, Qtype
ZONE = "ixfr-race."
NS_NAME = f"ns.{ZONE}"
@@ -144,8 +145,7 @@ class TruncatedIxfrHandler(ResponseHandler):
Set TC on an IXFR received over UDP to force the secondary to retry over TCP.
"""
- def match(self, qctx: QueryContext) -> bool:
- return qctx.qtype == dns.rdatatype.IXFR and qctx.protocol == DnsProtocol.UDP
+ matcher = Qtype(dns.rdatatype.IXFR) & Protocol(DnsProtocol.UDP)
async def get_responses(
self, qctx: QueryContext
@@ -165,8 +165,7 @@ class RaceIxfrHandler(ResponseHandler):
then has to detach the queued second chunk before freeing it.
"""
- def match(self, qctx: QueryContext) -> bool:
- return qctx.qtype == dns.rdatatype.IXFR and qctx.protocol == DnsProtocol.TCP
+ matcher = Qtype(dns.rdatatype.IXFR) & Protocol(DnsProtocol.TCP)
async def get_responses(
self, qctx: QueryContext