From eedc01303ba672d878ea73fcd61b66d158e61cc0 Mon Sep 17 00:00:00 2001 From: Sven Luthi Date: Thu, 9 Apr 2026 18:22:31 +0200 Subject: [PATCH] modified: netbox_dns_bridge/catalog_zone_manager.py modified: netbox_dns_bridge/request_handler.py modified: netbox_dns_bridge/utils.py Various little bug fixes. Analysed by AI but fixed by hand to ensure integrity. --- netbox_dns_bridge/catalog_zone_manager.py | 8 ++---- netbox_dns_bridge/request_handler.py | 34 ++++++++++++++--------- netbox_dns_bridge/utils.py | 4 +-- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/netbox_dns_bridge/catalog_zone_manager.py b/netbox_dns_bridge/catalog_zone_manager.py index f7c3484..db341ca 100644 --- a/netbox_dns_bridge/catalog_zone_manager.py +++ b/netbox_dns_bridge/catalog_zone_manager.py @@ -62,12 +62,10 @@ def _set_serial(new_serial: int) -> bool: def _increment_serial() -> None: if not _set_serial(_SERIAL_OBJ.value + 1): logger.warning( - f"Failed to incremenet catalog serial {_SERIAL_OBJ.value}. Will overflow serial back to 1" + f"Catalog serial {_SERIAL_OBJ.value} reached max — wrapping back to 1" ) _set_serial(1) - logger.debug( - f"Catalog zone SOA serial number incremented to {_SERIAL_OBJ.value}" - ) + logger.debug(f"Catalog zone SOA serial number is now {_SERIAL_OBJ.value}") # If a zone has no catz identifier yet, create it: @@ -230,5 +228,5 @@ def _generate_member_identifier() -> None: def update_member_identifier(zone: Zone) -> None: CatalogZoneMemberIdentifier.objects.update_or_create( zone=zone, - defaults={"name": _generate_member_identifier}, + defaults={"name": _generate_member_identifier()}, ) diff --git a/netbox_dns_bridge/request_handler.py b/netbox_dns_bridge/request_handler.py index f47922c..876a139 100644 --- a/netbox_dns_bridge/request_handler.py +++ b/netbox_dns_bridge/request_handler.py @@ -125,13 +125,13 @@ class DNSBaseRequestHandler(socketserver.BaseRequestHandler): response.set_rcode(dns.rcode.REFUSED) if query.had_tsig: - # Add TSIG with BADKEY, but do not sign (no MAC) + # Add TSIG with error code, but do not sign (no MAC, empty keyring) response.use_tsig( keyring={}, # empty; we're not signing keyname=query.keyname, tsig_error=tsig_error, ) - self._deny_request(query) + self._send_response(response.to_wire()) def _send_response(self, data) -> None: raise NotImplementedError @@ -164,14 +164,11 @@ class DNSBaseRequestHandler(socketserver.BaseRequestHandler): self.server.keyring, keyname=query.keyname, original_id=query.id ) else: - # If key is unknown + # unknown key — use an empty keyring so use_tsig() does not + # raise KeyError trying to look up a key that isn't present. response.set_rcode(dns.rcode.REFUSED) - # Generate new key for keyname provided - # keyname = query.keyname - # b64 = base64.b64encode(urandom(32)).decode("ascii") - # newkey = dns.tsigkeyring.from_text({ keyname: b64 }) response.use_tsig( - self.server.keyring, + {}, # empty keyring; no signing keyname=query.keyname, tsig_error=dns.rcode.BADKEY, ) @@ -181,6 +178,13 @@ class DNSBaseRequestHandler(socketserver.BaseRequestHandler): logger.debug(f"{peer} SOA {nb_view.name}/{dname}") def _handle_axfr_request(self, query, zone, peer, nb_view, dname) -> None: + if query.keyname not in self.server.keyring: + logger.error(f"AXFR aborted: keyname {query.keyname} not in keyring") + self._deny_request(query) + return + + tsig_key = self.server.keyring[query.keyname] + rrsets = [] soa_rrset = None for name, rdataset in zone.iterate_rdatasets(): @@ -209,12 +213,9 @@ class DNSBaseRequestHandler(socketserver.BaseRequestHandler): ) # 3. Loop through RRsets - tsig_key = self.server.keyring[query.keyname] tsig_ctx = None for rrset in rrsets: - # logger.debug(f"Iterating over rrset {rrset}. tsig_ctx: {tsig_ctx}") try: - # logger.debug(f"Adding {rrset} to renderer object") r.add_rrset(dns.renderer.ANSWER, rrset) if r.max_size - len(r.output.getvalue()) < self.RESERVED_TSIG: raise dns.exception.TooBig("TSIG wont fit") @@ -247,7 +248,15 @@ class DNSBaseRequestHandler(socketserver.BaseRequestHandler): query.question[0].rdtype, query.question[0].rdclass, ) - r.add_rrset(dns.renderer.ANSWER, rrset) + + try: + r.add_rrset(dns.renderer.ANSWER, rrset) + except dns.exception.TooBig: + logger.error( + f"RRset {rrset.name}/{dns.rdatatype.to_text(rrset.rdtype)} " + f"exceeds MAX_WIRE ({self.MAX_WIRE}) and cannot be sent; " + f"skipping." + ) # 4. Final message with terminating TSIG r.write_header() @@ -262,7 +271,6 @@ class DNSBaseRequestHandler(socketserver.BaseRequestHandler): other_data=b"", request_mac=r.mac if tsig_ctx else query.mac, ) - wire = r.get_wire() self._send_response(wire) diff --git a/netbox_dns_bridge/utils.py b/netbox_dns_bridge/utils.py index c5b2097..df07f4c 100644 --- a/netbox_dns_bridge/utils.py +++ b/netbox_dns_bridge/utils.py @@ -20,8 +20,8 @@ def export_bind_zone_file(nb_zone: netbox_dns.models.Zone, file_path: str): rdata = dns.rdata.from_text(rdclass, rdatatype, record.value) # Create rdataset - rdataset = dns.rdataset.Rdataset(rdclass, rdatatype, record.ttl) - rdataset.add(rdata) + rdataset = dns.rdataset.Rdataset(rdclass, rdatatype) + rdataset.add(rdata, record.ttl) # Add to zone node = dp_zone.find_node(rname, create=True)