Commit 42fcfa9ede for bind

commit 42fcfa9ede8ac532a8800b1bbd95beae9da9635d
Author: Štěpán Balážik <stepan@isc.org>
Date:   Tue Sep 8 19:36:00 2026 +0200

    Match on the transfer state in xfer/ans11 with a matcher

    The SOA handlers read the initial-AXFR flag through a matcher of
    their own, and the AXFR handler sets it while serving, which was
    previously done in match() methods.

    Assisted-by: Claude:claude-fable-5

diff --git a/bin/tests/system/xfer/ans11/ans.py b/bin/tests/system/xfer/ans11/ans.py
index c2282af8ed..8d558217a8 100644
--- a/bin/tests/system/xfer/ans11/ans.py
+++ b/bin/tests/system/xfer/ans11/ans.py
@@ -25,7 +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
+from isctest.asyncserver.matchers import Matcher, Protocol, Qtype

 ZONE = "ixfr-race."
 NS_NAME = f"ns.{ZONE}"
@@ -95,6 +95,21 @@ class TransferState:
         self.initial_axfr_served = False


+class InitialAxfrServed(Matcher):
+    """
+    Match once the initial AXFR has been served.
+    """
+
+    def __init__(self, progress: TransferState) -> None:
+        self._progress = progress
+
+    def match(self, qctx: QueryContext) -> bool:
+        return self._progress.initial_axfr_served
+
+    def __str__(self) -> str:
+        return "after the initial AXFR"
+
+
 class TransferHandler(ResponseHandler):
     """
     Base for the handlers that share a single TransferState.
@@ -108,17 +123,17 @@ class TransferHandler(ResponseHandler):
 class InitialSoaHandler(TransferHandler, StaticResponseHandler):
     answer = [soa(1)]

-    def match(self, qctx: QueryContext) -> bool:
-        return (
-            qctx.qtype == dns.rdatatype.SOA and not self._progress.initial_axfr_served
-        )
+    def __init__(self, progress: TransferState) -> None:
+        super().__init__(progress)
+        self.matcher = Qtype(dns.rdatatype.SOA) & ~InitialAxfrServed(progress)


 class RefreshSoaHandler(TransferHandler, StaticResponseHandler):
     answer = [soa(4)]

-    def match(self, qctx: QueryContext) -> bool:
-        return qctx.qtype == dns.rdatatype.SOA and self._progress.initial_axfr_served
+    def __init__(self, progress: TransferState) -> None:
+        super().__init__(progress)
+        self.matcher = Qtype(dns.rdatatype.SOA) & InitialAxfrServed(progress)


 class InitialAxfrHandler(TransferHandler, AxfrHandler):
@@ -133,11 +148,12 @@ class InitialAxfrHandler(TransferHandler, AxfrHandler):
     ]
     final_soa = soa(1)

-    def match(self, qctx: QueryContext) -> bool:
-        matched = super().match(qctx)
-        if matched:
-            self._progress.initial_axfr_served = True
-        return matched
+    async def get_responses(
+        self, qctx: QueryContext
+    ) -> AsyncGenerator[DnsResponseSend, None]:
+        self._progress.initial_axfr_served = True
+        async for action in super().get_responses(qctx):
+            yield action


 class TruncatedIxfrHandler(ResponseHandler):