]> git.ipfire.org Git - thirdparty/dnspython.git/commitdiff
cname and other data check in zonefile reader
authorBob Halley <halley@dnspython.org>
Thu, 2 Dec 2021 15:22:46 +0000 (07:22 -0800)
committerBob Halley <halley@dnspython.org>
Thu, 2 Dec 2021 15:22:46 +0000 (07:22 -0800)
dns/node.py
dns/rdataset.py
dns/zonefile.py
tests/test_zone.py

index 3267de77a7bdc3d9c18671dbd128d9e25c52725d..12e408012b83d9fe7c60ddd7265b7a7c45bae679 100644 (file)
@@ -102,16 +102,10 @@ class Node:
         """
         # Make having just one rdataset at the node fast.
         if len(self.rdatasets) > 0:
-            # We don't want adding RRSIG(CNAME) to delete CNAMEs,
-            # so we treat it as expressing "CNAME intent" for classifying
-            # the node as a CNAME node, even if we haven't added the CNAME
-            # yet.
-            if rdataset.rdtype == dns.rdatatype.CNAME or \
-               (rdataset.rdtype == dns.rdatatype.RRSIG and
-                rdataset.covers == dns.rdatatype.CNAME):
+            if rdataset.implies_cname():
                 self.rdatasets = [rds for rds in self.rdatasets
                                   if rds.ok_for_cname()]
-            else:
+            elif rdataset.implies_other_data():
                 self.rdatasets = [rds for rds in self.rdatasets
                                   if rds.ok_for_other_data()]
         self.rdatasets.append(rdataset)
index 242e30c7981ef9f8bfe6d95d6cdeccdfdd2e0e41..f948d76170c6c38a558f24d67668db3300b78df4 100644 (file)
@@ -353,6 +353,14 @@ class Rdataset(dns.set.Set):
         node?"""
         return not self.implies_cname()
 
+    def implies_other_data(self):
+        """Does this rdataset imply a node is an other data node?
+
+        Note that implies_other_data() is not simply "not implies_cname()" as
+        some types, e.g. NSEC and RRSIG(NSEC) are neutral.
+        """
+        return not self.ok_for_cname()
+
 
 @dns.immutable.immutable
 class ImmutableRdataset(Rdataset):
index 39c7a384a41340552b5bb378bf1530d7fcbd9d9f..4d72c718747cf05f7dca2b865135d25732a6a2d8 100644 (file)
@@ -38,6 +38,26 @@ class UnknownOrigin(dns.exception.DNSException):
     """Unknown origin"""
 
 
+class CNAMEAndOtherData(dns.exception.DNSException):
+    """A node has a CNAME and other data"""
+
+
+def _check_cname_and_other_data(txn, name, rdataset):
+    rdatasets = txn.get_rdatasets(name)
+    if any(rds.implies_cname() for rds in rdatasets):
+        # This is a CNAME node.
+        if not rdataset.ok_for_cname():
+            raise CNAMEAndOtherData('rdataset not ok for CNAME node')
+    elif any(rds.implies_other_data() for rds in rdatasets):
+        # This is an other data node
+        if not rdataset.ok_for_other_data():
+            raise CNAMEAndOtherData('rdataset is a CNAME but node '
+                                    'has other data')
+    # Otherwise the node consists of neutral types that can be
+    # present at either a CNAME or an other data node, e.g. NSEC or
+    # RRSIG(NSEC)
+
+
 class Reader:
 
     """Read a DNS zone file into a transaction."""
@@ -71,6 +91,7 @@ class Reader:
         self.force_ttl = force_ttl
         self.force_rdclass = force_rdclass
         self.force_rdtype = force_rdtype
+        self.txn.check_put_rdataset(_check_cname_and_other_data)
 
     def _eat_line(self):
         while 1:
@@ -445,6 +466,13 @@ class RRsetsReaderTransaction(dns.transaction.Transaction):
     def _get_rdataset(self, name, rdtype, covers):
         return self.rdatasets.get((name, rdtype, covers))
 
+    def _get_rdatasets(self, name):
+        rdatasets = []
+        for (rdataset_name, _, _), rdataset in self.rdatasets.items():
+            if name == rdataset_name:
+                rdatasets.append(rdataset)
+        return rdatasets
+
     def _put_rdataset(self, name, rdataset):
         self.rdatasets[(name, rdataset.rdtype, rdataset.covers)] = rdataset
 
index 1cd58dd153c6c602593dfa079c4362a179a6bd1e..bdc99a35a972e24fd3c759bc0624ccf82d30cb6e 100644 (file)
@@ -224,9 +224,22 @@ web a 10.0.0.4
     nsec @ A RRSIG
     rrsig A 1 3 3600 20200101000000 20030101000000 2143 foo MxFcby9k/yvedMfQgKzhH5er0Mu/vILz 45IkskceFGgiWCn/GxHhai6VAuHAoNUz 4YoU1tVfSCSqQYn6//11U6Nld80jEeC8 aTrO+KKmCaY=
     rrsig NSEC 1 3 3600 20200101000000 20030101000000 2143 foo MxFcby9k/yvedMfQgKzhH5er0Mu/vILz 45IkskceFGgiWCn/GxHhai6VAuHAoNUz 4YoU1tVfSCSqQYn6//11U6Nld80jEeC8 aTrO+KKmCaY=
-    rrsig CNAME 1 3 3600 20200101000000 20030101000000 2143 foo MxFcby9k/yvedMfQgKzhH5er0Mu/vILz 45IkskceFGgiWCn/GxHhai6VAuHAoNUz 4YoU1tVfSCSqQYn6//11U6Nld80jEeC8 aTrO+KKmCaY=
 """
 
+example_cname_and_other_data = """$TTL 3600
+$ORIGIN example.
+@ soa foo bar (1 2 3 4 5)
+@ ns ns1
+@ ns ns2
+ns1 a 10.0.0.1
+ns2 a 10.0.0.2
+www a 10.0.0.3
+web a 10.0.0.4
+    cname www
+    nsec @ A RRSIG
+    rrsig A 1 3 3600 20200101000000 20030101000000 2143 foo MxFcby9k/yvedMfQgKzhH5er0Mu/vILz 45IkskceFGgiWCn/GxHhai6VAuHAoNUz 4YoU1tVfSCSqQYn6//11U6Nld80jEeC8 aTrO+KKmCaY=
+    rrsig NSEC 1 3 3600 20200101000000 20030101000000 2143 foo MxFcby9k/yvedMfQgKzhH5er0Mu/vILz 45IkskceFGgiWCn/GxHhai6VAuHAoNUz 4YoU1tVfSCSqQYn6//11U6Nld80jEeC8 aTrO+KKmCaY=
+"""
 
 _keep_output = True
 
@@ -904,7 +917,7 @@ class ZoneTestCase(unittest.TestCase):
         rds = dns.rdataset.from_text('in', 'cname', 300, 'www')
         z.replace_rdataset('web', rds)
         n = z.find_node('web')
-        self.assertEqual(len(n.rdatasets), 4)
+        self.assertEqual(len(n.rdatasets), 3)
         self.assertEqual(n.find_rdataset(dns.rdataclass.IN,
                                          dns.rdatatype.CNAME),
                          rds)
@@ -913,9 +926,11 @@ class ZoneTestCase(unittest.TestCase):
         self.assertIsNotNone(n.get_rdataset(dns.rdataclass.IN,
                                             dns.rdatatype.RRSIG,
                                             dns.rdatatype.NSEC))
-        self.assertIsNotNone(n.get_rdataset(dns.rdataclass.IN,
-                                            dns.rdatatype.RRSIG,
-                                            dns.rdatatype.CNAME))
+
+    def testCnameAndOtherDataInZonefile(self):
+        with self.assertRaises(dns.zonefile.CNAMEAndOtherData):
+            dns.zone.from_text(example_cname_and_other_data, 'example.',
+                               relativize=True)
 
     def testNameInZoneWithStr(self):
         z = dns.zone.from_text(example_text, 'example.', relativize=False)
@@ -1030,7 +1045,7 @@ class VersionedZoneTestCase(unittest.TestCase):
         with z.writer() as txn:
             txn.replace('web', rds)
         n = z.find_node('web')
-        self.assertEqual(len(n.rdatasets), 4)
+        self.assertEqual(len(n.rdatasets), 3)
         self.assertEqual(n.find_rdataset(dns.rdataclass.IN,
                                          dns.rdatatype.CNAME),
                          rds)
@@ -1039,9 +1054,6 @@ class VersionedZoneTestCase(unittest.TestCase):
         self.assertIsNotNone(n.get_rdataset(dns.rdataclass.IN,
                                             dns.rdatatype.RRSIG,
                                             dns.rdatatype.NSEC))
-        self.assertIsNotNone(n.get_rdataset(dns.rdataclass.IN,
-                                            dns.rdatatype.RRSIG,
-                                            dns.rdatatype.CNAME))
 
 
 if __name__ == '__main__':