"""
# 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)
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):
"""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."""
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:
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
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
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)
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)
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)
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__':