From c19e6716a86593528ae3d4904bf1cefcfcd477b9 Mon Sep 17 00:00:00 2001 From: Bob Halley Date: Mon, 1 Jun 2020 08:23:03 -0700 Subject: [PATCH] Fix remaining canonical form problems, and add a test. [Issue #496] --- dns/rdtypes/ANY/NSEC.py | 9 +++++++++ dns/rdtypes/IN/KX.py | 2 +- dns/rdtypes/IN/NAPTR.py | 10 +++++++++ dns/rdtypes/IN/PX.py | 5 +++++ dns/rdtypes/IN/SRV.py | 1 - tests/test_rdata.py | 45 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 70 insertions(+), 2 deletions(-) diff --git a/dns/rdtypes/ANY/NSEC.py b/dns/rdtypes/ANY/NSEC.py index 63203b5d..7da00e9e 100644 --- a/dns/rdtypes/ANY/NSEC.py +++ b/dns/rdtypes/ANY/NSEC.py @@ -15,6 +15,7 @@ # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +import io import struct import dns.exception @@ -92,6 +93,14 @@ class NSEC(dns.rdata.Rdata): file.write(struct.pack('!BB', window, len(bitmap))) file.write(bitmap) + def to_digestable(self, origin=None): + file = io.BytesIO() + file.write(self.next.to_digestable(origin)) + for (window, bitmap) in self.windows: + file.write(struct.pack('!BB', window, len(bitmap))) + file.write(bitmap) + return file.getvalue() + @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): (next, cused) = dns.name.from_wire(wire[: current + rdlen], current) diff --git a/dns/rdtypes/IN/KX.py b/dns/rdtypes/IN/KX.py index 1318a582..ebf8fd77 100644 --- a/dns/rdtypes/IN/KX.py +++ b/dns/rdtypes/IN/KX.py @@ -18,6 +18,6 @@ import dns.rdtypes.mxbase -class KX(dns.rdtypes.mxbase.UncompressedMX): +class KX(dns.rdtypes.mxbase.UncompressedDowncasingMX): """KX record""" diff --git a/dns/rdtypes/IN/NAPTR.py b/dns/rdtypes/IN/NAPTR.py index 9668dce2..c68efc38 100644 --- a/dns/rdtypes/IN/NAPTR.py +++ b/dns/rdtypes/IN/NAPTR.py @@ -15,6 +15,7 @@ # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +import io import struct import dns.exception @@ -84,6 +85,15 @@ class NAPTR(dns.rdata.Rdata): _write_string(file, self.regexp) self.replacement.to_wire(file, compress, origin) + def to_digestable(self, origin=None): + file = io.BytesIO() + two_ints = struct.pack("!HH", self.order, self.preference) + file.write(two_ints) + _write_string(file, self.flags) + _write_string(file, self.service) + _write_string(file, self.regexp) + return file.getvalue() + self.replacement.to_digestable(origin) + @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): (order, preference) = struct.unpack('!HH', wire[current: current + 4]) diff --git a/dns/rdtypes/IN/PX.py b/dns/rdtypes/IN/PX.py index 109a873e..e11f2655 100644 --- a/dns/rdtypes/IN/PX.py +++ b/dns/rdtypes/IN/PX.py @@ -56,6 +56,11 @@ class PX(dns.rdata.Rdata): self.map822.to_wire(file, None, origin) self.mapx400.to_wire(file, None, origin) + def to_digestable(self, origin=None): + return struct.pack("!H", self.preference) + \ + self.map822.to_digestable(origin) + \ + self.mapx400.to_digestable(origin) + @classmethod def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None): (preference, ) = struct.unpack('!H', wire[current: current + 2]) diff --git a/dns/rdtypes/IN/SRV.py b/dns/rdtypes/IN/SRV.py index bfd25ea6..8c08f0b0 100644 --- a/dns/rdtypes/IN/SRV.py +++ b/dns/rdtypes/IN/SRV.py @@ -59,7 +59,6 @@ class SRV(dns.rdata.Rdata): self.target.to_wire(file, compress, origin) def to_digestable(self, origin=None): - # TODO how to avoid code duplication here? This is mostly identical to self.to_wire. f = io.BytesIO() f.write(struct.pack("!HHH", self.priority, self.weight, self.port)) f.write(self.target.to_digestable(origin)) diff --git a/tests/test_rdata.py b/tests/test_rdata.py index b994a625..a88b977d 100644 --- a/tests/test_rdata.py +++ b/tests/test_rdata.py @@ -16,11 +16,13 @@ # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +import io import unittest import dns.name import dns.rdata import dns.rdataclass +import dns.rdataset import dns.rdatatype import tests.stxt_module @@ -128,5 +130,48 @@ class RdataTestCase(unittest.TestCase): idna_codec=dns.name.IDNA_2008) self.assertEqual(str(rdata.target), 'xn--knigsgchen-b4a3dun') + def test_digestable_downcasing(self): + # Make sure all the types listed in RFC 4034 section 6.2 are + # downcased properly, except for: + # + # types we don't implement: MD, MF, MB, MG, MR, MINFO, SIG, + # NXT, A6 + # + # types that don't have names: HINFO + # + # types where the canonical form isn't relevant: RRSIG + # + cases = [ + ('SOA', 'NAME NAME 1 2 3 4 5'), + ('AFSDB', '0 NAME'), + ('CNAME', 'NAME'), + ('DNAME', 'NAME'), + ('KX', '10 NAME'), + ('MX', '10 NAME'), + ('NS', 'NAME'), + ('NSEC', 'NAME A'), + ('NAPTR', '0 0 a B c NAME'), + ('PTR', 'NAME'), + ('PX', '65535 NAME NAME'), + ('RP', 'NAME NAME'), + ('RT', '0 NAME'), + ('SRV', '0 0 0 NAME'), + ] + for rdtype, text in cases: + upper_origin = dns.name.from_text('EXAMPLE') + lower_origin = dns.name.from_text('example') + canonical_text = text.replace('NAME', 'name') + rdata = dns.rdata.from_text(dns.rdataclass.IN, rdtype, text, + origin=upper_origin, relativize=False) + canonical_rdata = dns.rdata.from_text(dns.rdataclass.IN, rdtype, + canonical_text, + origin=lower_origin, + relativize=False) + digestable_wire = rdata.to_digestable() + f = io.BytesIO() + canonical_rdata.to_wire(f) + expected_wire = f.getvalue() + self.assertEqual(digestable_wire, expected_wire) + if __name__ == '__main__': unittest.main() -- 2.47.3