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
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:
"""
information regarding copyright ownership.
"""
-from collections.abc import AsyncGenerator, Collection, Iterable
+from collections.abc import AsyncGenerator, Collection
import abc
import dns.rrset
from isctest.asyncserver import (
+ AxfrHandler,
ControllableAsyncDnsServer,
DnsResponseSend,
QueryContext,
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
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):
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):
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):
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: