]> git.ipfire.org Git - thirdparty/dnspython.git/commitdiff
Zone transfers should ignore glue that is not a subdomain of the
authorBob Halley <halley@dnspython.org>
Sat, 15 Nov 2025 15:53:25 +0000 (07:53 -0800)
committerBob Halley <halley@dnspython.org>
Sat, 15 Nov 2025 15:53:25 +0000 (07:53 -0800)
origin [#1236].

Ideally, we'd actually store this somewhere as in theory it is
needed for pathological cross zone dependencies.  We are
not doing this as in practice such dependencies break a lot of
DNS software, and are usually not viable "in the wild".

dns/xfr.py
dns/zone.py
tests/test_xfr.py

index 219fdc8caf91c881b4b9ed2a28df8ce638a57871..d0f44da8fcdeca31c2add1604bac3df086c95fcd 100644 (file)
@@ -92,7 +92,10 @@ class Inbound:
             raise ValueError("rdtype is not IXFR or AXFR")
         self.serial = serial
         self.is_udp = is_udp
-        (_, _, self.origin) = txn_manager.origin_information()
+        (_, _, origin) = txn_manager.origin_information()
+        if origin is None:
+            raise ValueError("transaction manager must supply an origin for XFRs")
+        self.origin = origin
         self.soa_rdataset: dns.rdataset.Rdataset | None = None
         self.done = False
         self.expecting_SOA = False
@@ -232,6 +235,12 @@ class Inbound:
                 # Note we are falling through into the code below
                 # so whatever rdataset this was gets written.
                 #
+            # Ignore glue that is not a subdomain of the origin.  For pathological
+            # cases it would be good if we could keep it in some side location, but
+            # dnspython zones don't have a place or an API for that, so we just ignore
+            # it at this time.
+            if not name.is_subdomain(self.origin):
+                continue
             # Add or remove the data
             if self.delete_mode:
                 self.txn.delete_exact(name, rdataset)
index f916ffee5529cef278b7585a31c6f4376e896952..0dfecc92e2b29804454f4966fd91ff11a0551426 100644 (file)
@@ -34,7 +34,6 @@ from typing import (
 )
 
 import dns.exception
-import dns.grange
 import dns.immutable
 import dns.name
 import dns.node
@@ -47,7 +46,6 @@ import dns.rdtypes.ANY.ZONEMD
 import dns.rrset
 import dns.tokenizer
 import dns.transaction
-import dns.ttl
 import dns.zonefile
 from dns.zonetypes import DigestHashAlgorithm, DigestScheme, _digest_hashers
 
index 257397c265f7ada566393ba12977efcbc7a04be2..10b33a5b201c1509730a31c54285ab32c003cf12 100644 (file)
@@ -84,6 +84,22 @@ example. IN AXFR
 @ 3600 IN SOA foo bar 1 2 3 4 7
 """
 
+axfr_with_nonsubdomain_glue = """id 1
+opcode QUERY
+rcode NOERROR
+flags AA
+;QUESTION
+example. IN AXFR
+;ANSWER
+@ 3600 IN SOA foo bar 1 2 3 4 5
+bar.foo 300 IN MX 0 blaz.foo
+ns1 3600 IN A 10.0.0.1
+ns2 3600 IN A 10.0.0.2
+sub 300 IN NS ns1.other.
+ns1.other. 300 IN A 1.2.3.4
+@ 3600 IN SOA foo bar 1 2 3 4 5
+"""
+
 ixfr = """id 1
 opcode QUERY
 rcode NOERROR
@@ -274,6 +290,7 @@ example. IN IXFR
 @ 3600 IN NS ns1
 @ 3600 IN NS ns2
 """
+
 ixfr_axfr2 = """id 1
 opcode QUERY
 rcode NOERROR
@@ -331,6 +348,15 @@ def test_axfr_unexpected_origin():
             xfr.process_message(m)
 
 
+def test_axfr_with_non_subdomain_glue():
+    z = dns.versioned.Zone("example.")
+    m = dns.message.from_text(
+        axfr_with_nonsubdomain_glue, origin=z.origin, one_rr_per_rrset=True
+    )
+    with dns.xfr.Inbound(z, dns.rdatatype.AXFR) as xfr:
+        xfr.process_message(m)
+
+
 def test_basic_ixfr():
     z = dns.zone.from_text(base, "example.", zone_factory=dns.versioned.Zone)
     m = dns.message.from_text(ixfr, origin=z.origin, one_rr_per_rrset=True)