Refuse AXFR over UDP instead of crashing

AXFR is a TCP-only operation (RFC 5936), but the request handler did
not enforce that. When a UDP datagram carrying an AXFR query arrived,
control reached _handle_axfr_request, which builds the multi-message
response and writes each chunk with:

    self.request.sendall(len(wire).to_bytes(2, 'big') + wire)

For a UDP handler self.request is a (data, sock) tuple, not a socket,
so sendall raised AttributeError. The exception was caught by the
outer try/except in UDPRequestHandler.handle, so the server stayed up,
but the client received no response at all and the error was only
visible in a generic 'Error handling request' traceback.

Add a 'transport' class attribute ('tcp' / 'udp') to the concrete
handler classes and, immediately after the qtype check (before TSIG
validation and zone lookup), deny AXFR-over-UDP with REFUSED. The
client now gets a proper refusal and the AXFR handler is never entered
on a UDP socket.
This commit is contained in:
sven authored and Sven Luthi committed 2026-08-11 12:16:45 +02:00
1 parent ad0397bf61
commit bf9035aded
1 file changed
+11
+11
View File
@@ -24,6 +24,8 @@ SETTINGS = settings.PLUGINS_CONFIG["netbox_dns_bridge"]
class DNSBaseRequestHandler(socketserver.BaseRequestHandler): class DNSBaseRequestHandler(socketserver.BaseRequestHandler):
transport = None
def __init__(self, request, client_address, server) -> None: def __init__(self, request, client_address, server) -> None:
self.MAX_WIRE = 65535 self.MAX_WIRE = 65535
self.RESERVED_TSIG = 300 self.RESERVED_TSIG = 300
@@ -265,6 +267,11 @@ class DNSBaseRequestHandler(socketserver.BaseRequestHandler):
self._deny_request(query) self._deny_request(query)
return return
if qtype == dns.rdatatype.AXFR and self.transport != "tcp":
LOGGER.warning(f"Request denied from {peer}: AXFR requires TCP")
self._deny_request(query, dns.rcode.REFUSED)
return
# Identify TSIG key used # Identify TSIG key used
if not query.had_tsig: if not query.had_tsig:
LOGGER.warning(f"Request denied from {peer}: No TSIG key used") LOGGER.warning(f"Request denied from {peer}: No TSIG key used")
@@ -309,6 +316,8 @@ class DNSBaseRequestHandler(socketserver.BaseRequestHandler):
class UDPRequestHandler(DNSBaseRequestHandler): class UDPRequestHandler(DNSBaseRequestHandler):
transport = "udp"
def __init__(self, request, client_address, server) -> None: def __init__(self, request, client_address, server) -> None:
super().__init__(request, client_address, server) super().__init__(request, client_address, server)
@@ -329,6 +338,8 @@ class UDPRequestHandler(DNSBaseRequestHandler):
class TCPRequestHandler(DNSBaseRequestHandler): class TCPRequestHandler(DNSBaseRequestHandler):
transport = "tcp"
def __init__(self, request, client_address, server) -> None: def __init__(self, request, client_address, server) -> None:
super().__init__(request, client_address, server) super().__init__(request, client_address, server)