From: Štěpán Balážik Date: Mon, 19 Jan 2026 21:04:32 +0000 (+0100) Subject: Refactor AxfrHandler and hoist it to isctest.asyncserver X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a58952e1f74a4fc6726de955327d540f0d935049;p=thirdparty%2Fbind9.git Refactor AxfrHandler and hoist it to isctest.asyncserver It will be useful in the xfer system test as well. --- diff --git a/bin/tests/system/isctest/asyncserver.py b/bin/tests/system/isctest/asyncserver.py index 080c08c3806..e29d6f7e9a0 100644 --- a/bin/tests/system/isctest/asyncserver.py +++ b/bin/tests/system/isctest/asyncserver.py @@ -11,7 +11,7 @@ See the COPYRIGHT file distributed with this work for additional information regarding copyright ownership. """ -from collections.abc import AsyncGenerator, Callable, Coroutine, Sequence +from collections.abc import AsyncGenerator, Callable, Collection, Coroutine, Sequence from dataclasses import dataclass, field from typing import Any, cast @@ -875,6 +875,63 @@ class ForwarderHandler(ResponseHandler): yield BytesResponseSend(response.result()) +class AxfrHandler(ResponseHandler): + """ + Base class for AXFR response handlers. + + Subclasses must define the `initial_soa`, `zone_contents`, and `final_soa` + properties to specify the content of the AXFR responses. + + The responses are constructed without any regard to zone data. + """ + + @property + @abc.abstractmethod + def initial_soa(self) -> dns.rrset.RRset: + """ + Initial SOA record of response packets sent in response to + AXFR queries. + """ + raise NotImplementedError + + @property + @abc.abstractmethod + def zone_contents(self) -> Collection[dns.rrset.RRset]: + """ + Answer section of the second response packet sent in response to + AXFR queries. + """ + raise NotImplementedError + + @property + @abc.abstractmethod + def final_soa(self) -> dns.rrset.RRset: + """ + Final SOA record of response packets sent in response to + AXFR queries. + """ + raise NotImplementedError + + def match(self, qctx: QueryContext) -> bool: + return qctx.qtype == dns.rdatatype.AXFR + + async def get_responses( + self, qctx: QueryContext + ) -> AsyncGenerator[DnsResponseSend, None]: + qctx.prepare_new_response(with_zone_data=False) + qctx.response.answer.append(self.initial_soa) + yield DnsResponseSend(qctx.response) + + qctx.prepare_new_response(with_zone_data=False) + for rrset_ in self.zone_contents: + qctx.response.answer.append(rrset_) + yield DnsResponseSend(qctx.response) + + qctx.prepare_new_response(with_zone_data=False) + qctx.response.answer.append(self.final_soa) + yield DnsResponseSend(qctx.response) + + @dataclass class _ZoneTreeNode: """ diff --git a/bin/tests/system/ixfr/ans2/ans.py b/bin/tests/system/ixfr/ans2/ans.py index b6a05264690..87f7dbef483 100644 --- a/bin/tests/system/ixfr/ans2/ans.py +++ b/bin/tests/system/ixfr/ans2/ans.py @@ -11,7 +11,7 @@ See the COPYRIGHT file distributed with this work for additional information regarding copyright ownership. """ -from collections.abc import AsyncGenerator, Collection, Iterable +from collections.abc import AsyncGenerator, Collection import abc @@ -21,6 +21,7 @@ import dns.rdatatype import dns.rrset from isctest.asyncserver import ( + AxfrHandler, ControllableAsyncDnsServer, DnsResponseSend, QueryContext, @@ -85,29 +86,6 @@ class SoaHandler(ResponseHandler): yield DnsResponseSend(qctx.response) -class AxfrHandler(ResponseHandler): - @property - @abc.abstractmethod - def answers(self) -> Iterable[Collection[dns.rrset.RRset]]: - """ - Answer sections of response packets sent in response to - AXFR queries. - """ - raise NotImplementedError - - def match(self, qctx: QueryContext) -> bool: - return qctx.qtype == dns.rdatatype.AXFR - - async def get_responses( - self, qctx: QueryContext - ) -> AsyncGenerator[DnsResponseSend, None]: - for answer in self.answers: - response = qctx.prepare_new_response() - for rrset_ in answer: - response.answer.append(rrset_) - yield DnsResponseSend(response) - - class IxfrHandler(ResponseHandler): @property @abc.abstractmethod @@ -130,16 +108,14 @@ class IxfrHandler(ResponseHandler): class InitialAfxrHandler(AxfrHandler): - answers = ( - (soa(1),), - ( - ns(), - txt("initial AXFR"), - a("10.0.0.61", owner="a.nil."), - a("10.0.0.62", owner="b.nil."), - ), - (soa(1),), + initial_soa = soa(1) + zone_contents = ( + ns(), + txt("initial AXFR"), + a("10.0.0.61", owner="a.nil."), + a("10.0.0.62", owner="b.nil."), ) + final_soa = soa(1) class SuccessfulIfxrHandler(IxfrHandler): @@ -169,14 +145,12 @@ class NotExactIxfrHandler(IxfrHandler): class FallbackNotExactAxfrHandler(AxfrHandler): - answers = ( - (soa(3),), - ( - ns(), - txt("fallback AXFR"), - ), - (soa(3),), + initial_soa = soa(3) + zone_contents = ( + ns(), + txt("fallback AXFR"), ) + final_soa = soa(3) class TooManyRecordsIxfrHandler(IxfrHandler): @@ -195,14 +169,12 @@ class TooManyRecordsIxfrHandler(IxfrHandler): class FallbackTooManyRecordsAxfrHandler(AxfrHandler): - answers = ( - (soa(3),), - ( - ns(), - txt("fallback AXFR on too many records"), - ), - (soa(3),), + initial_soa = soa(3) + zone_contents = ( + ns(), + txt("fallback AXFR on too many records"), ) + final_soa = soa(3) class BadSoaOwnerIxfrHandler(IxfrHandler): @@ -216,14 +188,12 @@ class BadSoaOwnerIxfrHandler(IxfrHandler): class FallbackBadSoaOwnerAxfrHandler(AxfrHandler): - answers = ( - (soa(4),), - ( - ns(), - txt("serial 4, fallback AXFR", owner="test.nil."), - ), - (soa(4),), + initial_soa = soa(4) + zone_contents = ( + ns(), + txt("serial 4, fallback AXFR", owner="test.nil."), ) + final_soa = soa(4) def main() -> None: