]> git.ipfire.org Git - thirdparty/dnspython.git/commitdiff
Add support for EUI48 and EUI64 RR types
authorPetr Spacek <pspacek@redhat.com>
Thu, 3 Dec 2015 12:07:41 +0000 (13:07 +0100)
committerBob Halley <halley@play-bow.org>
Thu, 3 Dec 2015 15:53:12 +0000 (07:53 -0800)
dns/rdatatype.py
dns/rdtypes/ANY/EUI48.py [new file with mode: 0644]
dns/rdtypes/ANY/EUI64.py [new file with mode: 0644]
dns/rdtypes/ANY/__init__.py
dns/rdtypes/__init__.py
dns/rdtypes/euibase.py [new file with mode: 0644]
tests/test_rdtypeanyeui.py [new file with mode: 0644]

index ce496de1bdd9dacd8ff421db4405ab34bed2d7b1..d54b70448e3c5a7dc7efa25fd7687420ad015299 100644 (file)
@@ -84,6 +84,8 @@ CDS = 59
 CDNSKEY = 60
 SPF = 99
 UNSPEC = 103
+EUI48 = 108
+EUI64 = 109
 TKEY = 249
 TSIG = 250
 IXFR = 251
@@ -150,6 +152,8 @@ _by_text = {
     'CDNSKEY' : CDNSKEY,
     'SPF' : SPF,
     'UNSPEC' : UNSPEC,
+    'EUI48': EUI48,
+    'EUI64': EUI64,
     'TKEY' : TKEY,
     'TSIG' : TSIG,
     'IXFR' : IXFR,
diff --git a/dns/rdtypes/ANY/EUI48.py b/dns/rdtypes/ANY/EUI48.py
new file mode 100644 (file)
index 0000000..6ddfa6a
--- /dev/null
@@ -0,0 +1,28 @@
+# Copyright (C) 2015 Red Hat, Inc.
+# Author: Petr Spacek <pspacek@redhat.com>
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED 'AS IS' AND RED HAT DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.euibase
+
+
+class EUI48(dns.rdtypes.euibase.EUIBase):
+    """EUI48 record
+
+    @ivar fingerprint: 48-bit Extended Unique Identifier (EUI-48)
+    @type fingerprint: string
+    @see: rfc7043.txt"""
+
+    byte_len = 6  # 0123456789ab (in hex)
+    text_len = byte_len * 3 - 1  # 01-23-45-67-89-ab
diff --git a/dns/rdtypes/ANY/EUI64.py b/dns/rdtypes/ANY/EUI64.py
new file mode 100644 (file)
index 0000000..41abd7b
--- /dev/null
@@ -0,0 +1,28 @@
+# Copyright (C) 2015 Red Hat, Inc.
+# Author: Petr Spacek <pspacek@redhat.com>
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED 'AS IS' AND RED HAT DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import dns.rdtypes.euibase
+
+
+class EUI64(dns.rdtypes.euibase.EUIBase):
+    """EUI64 record
+
+    @ivar fingerprint: 64-bit Extended Unique Identifier (EUI-64)
+    @type fingerprint: string
+    @see: rfc7043.txt"""
+
+    byte_len = 8  # 0123456789abcdef (in hex)
+    text_len = byte_len * 3 - 1  # 01-23-45-67-89-ab-cd-ef
index 2f27517592c027816ae515bf57aae36f8b7b4b5f..cb528228469f5ad8d2cef006bbfadf2f12d9e0a8 100644 (file)
@@ -25,6 +25,8 @@ __all__ = [
     'DNAME',
     'DNSKEY',
     'DS',
+    'EUI48',
+    'EUI64',
     'GPOS',
     'HINFO',
     'HIP',
index 49db5a3776922a63ac106a7f04b323d9cb7722ec..826efbb6c0f531983749fae102c5800d9fb0bcff 100644 (file)
@@ -18,6 +18,7 @@
 __all__ = [
     'ANY',
     'IN',
+    'euibase',
     'mxbase',
     'nsbase',
 ]
diff --git a/dns/rdtypes/euibase.py b/dns/rdtypes/euibase.py
new file mode 100644 (file)
index 0000000..2af018d
--- /dev/null
@@ -0,0 +1,71 @@
+# Copyright (C) 2015 Red Hat, Inc.
+# Author: Petr Spacek <pspacek@redhat.com>
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED 'AS IS' AND RED HAT DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import binascii
+
+import dns.rdata
+
+
+class EUIBase(dns.rdata.Rdata):
+    """EUIxx record
+
+    @ivar fingerprint: xx-bit Extended Unique Identifier (EUI-xx)
+    @type fingerprint: string
+    @see: rfc7043.txt"""
+
+    __slots__ = ['eui']
+    # define these in subclasses
+    # byte_len = 6  # 0123456789ab (in hex)
+    # text_len = byte_len * 3 - 1  # 01-23-45-67-89-ab
+
+    def __init__(self, rdclass, rdtype, eui):
+        super(EUIBase, self).__init__(rdclass, rdtype)
+        if len(eui) != self.byte_len:
+            raise dns.exception.FormError('EUI%s rdata has to have %s bytes'
+                                          % (self.byte_len * 8, self.byte_len))
+        self.eui = eui
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return dns.rdata._hexify(self.eui, chunksize=2).replace(' ', '-')
+
+    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
+        text = tok.get_string()
+        tok.get_eol()
+        if len(text) != cls.text_len:
+            raise dns.exception.SyntaxError(
+                'Input text must have %s characters' % cls.text_len)
+        expected_dash_idxs = range(2, cls.byte_len * 3 - 1, 3)
+        for i in expected_dash_idxs:
+            if text[i] != '-':
+                raise dns.exception.SyntaxError('Dash expected at position %s'
+                                                % i)
+        text = text.replace('-', '')
+        try:
+            data = binascii.unhexlify(text)
+        except (ValueError, TypeError) as ex:
+            raise dns.exception.SyntaxError('Hex decoding error: %s' % str(ex))
+        return cls(rdclass, rdtype, data)
+
+    from_text = classmethod(from_text)
+
+    def to_wire(self, file, compress=None, origin=None):
+        file.write(self.eui)
+
+    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
+        eui = wire[current:current + rdlen].unwrap()
+        return cls(rdclass, rdtype, eui)
+
+    from_wire = classmethod(from_wire)
diff --git a/tests/test_rdtypeanyeui.py b/tests/test_rdtypeanyeui.py
new file mode 100644 (file)
index 0000000..800d103
--- /dev/null
@@ -0,0 +1,223 @@
+# Copyright (C) 2015 Red Hat, Inc.
+# Author: Petr Spacek <pspacek@redhat.com>
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose with or without fee is hereby granted,
+# provided that the above copyright notice and this permission notice
+# appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED 'AS IS' AND RED HAT DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import unittest
+try:
+    from StringIO import StringIO
+except ImportError:
+    from io import BytesIO as StringIO
+
+import dns.rrset
+import dns.rdtypes.ANY.EUI48
+import dns.exception
+
+
+class RdtypeAnyEUI48TestCase(unittest.TestCase):
+    def testInstOk(self):
+        '''Valid binary input.'''
+        eui = b'\x01\x23\x45\x67\x89\xab'
+        inst = dns.rdtypes.ANY.EUI48.EUI48(dns.rdataclass.IN,
+                                           dns.rdatatype.EUI48,
+                                           eui)
+        self.assertEqual(inst.eui, eui)
+
+    def testInstLength(self):
+        '''Incorrect input length.'''
+        eui = b'\x01\x23\x45\x67\x89\xab\xcd'
+        with self.assertRaises(dns.exception.FormError):
+            dns.rdtypes.ANY.EUI48.EUI48(dns.rdataclass.IN,
+                                        dns.rdatatype.EUI48,
+                                        eui)
+
+    def testFromTextOk(self):
+        '''Valid text input.'''
+        r1 = dns.rrset.from_text('foo', 300, 'IN', 'EUI48',
+                                 '01-23-45-67-89-ab')
+        eui = b'\x01\x23\x45\x67\x89\xab'
+        self.assertEqual(r1[0].eui, eui)
+
+    def testFromTextLength(self):
+        '''Invalid input length.'''
+        with self.assertRaises(dns.exception.SyntaxError):
+            dns.rrset.from_text('foo', 300, 'IN', 'EUI48',
+                                '00-01-23-45-67-89-ab')
+
+    def testFromTextDelim(self):
+        '''Invalid delimiter.'''
+        with self.assertRaises(dns.exception.SyntaxError):
+            dns.rrset.from_text('foo', 300, 'IN', 'EUI48', '01_23-45-67-89-ab')
+
+    def testFromTextExtraDash(self):
+        '''Extra dash instead of hex digit.'''
+        with self.assertRaises(dns.exception.SyntaxError):
+            dns.rrset.from_text('foo', 300, 'IN', 'EUI48', '0--23-45-67-89-ab')
+
+    def testFromTextMultipleTokens(self):
+        '''Invalid input divided to multiple tokens.'''
+        with self.assertRaises(dns.exception.SyntaxError):
+            dns.rrset.from_text('foo', 300, 'IN', 'EUI48', '01 23-45-67-89-ab')
+
+    def testFromTextInvalidHex(self):
+        '''Invalid hexadecimal input.'''
+        with self.assertRaises(dns.exception.SyntaxError):
+            dns.rrset.from_text('foo', 300, 'IN', 'EUI48', 'g0-23-45-67-89-ab')
+
+    def testToTextOk(self):
+        '''Valid text output.'''
+        eui = b'\x01\x23\x45\x67\x89\xab'
+        exp_text = '01-23-45-67-89-ab'
+        inst = dns.rdtypes.ANY.EUI48.EUI48(dns.rdataclass.IN,
+                                           dns.rdatatype.EUI48,
+                                           eui)
+        text = inst.to_text()
+        self.assertEqual(exp_text, text)
+
+    def testToWire(self):
+        '''Valid wire format.'''
+        eui = b'\x01\x23\x45\x67\x89\xab'
+        inst = dns.rdtypes.ANY.EUI48.EUI48(dns.rdataclass.IN,
+                                           dns.rdatatype.EUI48,
+                                           eui)
+        buff = StringIO()
+        inst.to_wire(buff)
+        self.assertEqual(buff.getvalue(), eui)
+
+    def testFromWireOk(self):
+        '''Valid wire format.'''
+        eui = b'\x01\x23\x45\x67\x89\xab'
+        pad_len = 100
+        wire = dns.wiredata.WireData(b'x' * pad_len + eui + b'y' * pad_len * 2)
+        inst = dns.rdtypes.ANY.EUI48.EUI48.from_wire(dns.rdataclass.IN,
+                                                     dns.rdatatype.EUI48,
+                                                     wire,
+                                                     pad_len,
+                                                     len(eui))
+        self.assertEqual(inst.eui, eui)
+
+    def testFromWireLength(self):
+        '''Valid wire format.'''
+        eui = b'\x01\x23\x45\x67\x89'
+        pad_len = 100
+        wire = dns.wiredata.WireData(b'x' * pad_len + eui + b'y' * pad_len * 2)
+        with self.assertRaises(dns.exception.FormError):
+            dns.rdtypes.ANY.EUI48.EUI48.from_wire(dns.rdataclass.IN,
+                                                  dns.rdatatype.EUI48,
+                                                  wire,
+                                                  pad_len,
+                                                  len(eui))
+
+
+class RdtypeAnyEUI64TestCase(unittest.TestCase):
+    def testInstOk(self):
+        '''Valid binary input.'''
+        eui = b'\x01\x23\x45\x67\x89\xab\xcd\xef'
+        inst = dns.rdtypes.ANY.EUI64.EUI64(dns.rdataclass.IN,
+                                           dns.rdatatype.EUI64,
+                                           eui)
+        self.assertEqual(inst.eui, eui)
+
+    def testInstLength(self):
+        '''Incorrect input length.'''
+        eui = b'\x01\x23\x45\x67\x89\xab'
+        with self.assertRaises(dns.exception.FormError):
+            dns.rdtypes.ANY.EUI64.EUI64(dns.rdataclass.IN,
+                                        dns.rdatatype.EUI64,
+                                        eui)
+
+    def testFromTextOk(self):
+        '''Valid text input.'''
+        r1 = dns.rrset.from_text('foo', 300, 'IN', 'EUI64',
+                                 '01-23-45-67-89-ab-cd-ef')
+        eui = b'\x01\x23\x45\x67\x89\xab\xcd\xef'
+        self.assertEqual(r1[0].eui, eui)
+
+    def testFromTextLength(self):
+        '''Invalid input length.'''
+        with self.assertRaises(dns.exception.SyntaxError):
+            dns.rrset.from_text('foo', 300, 'IN', 'EUI64',
+                                '01-23-45-67-89-ab')
+
+    def testFromTextDelim(self):
+        '''Invalid delimiter.'''
+        with self.assertRaises(dns.exception.SyntaxError):
+            dns.rrset.from_text('foo', 300, 'IN', 'EUI64',
+                                '01_23-45-67-89-ab-cd-ef')
+
+    def testFromTextExtraDash(self):
+        '''Extra dash instead of hex digit.'''
+        with self.assertRaises(dns.exception.SyntaxError):
+            dns.rrset.from_text('foo', 300, 'IN', 'EUI64',
+                                '0--23-45-67-89-ab-cd-ef')
+
+    def testFromTextMultipleTokens(self):
+        '''Invalid input divided to multiple tokens.'''
+        with self.assertRaises(dns.exception.SyntaxError):
+            dns.rrset.from_text('foo', 300, 'IN', 'EUI64',
+                                '01 23-45-67-89-ab-cd-ef')
+
+    def testFromTextInvalidHex(self):
+        '''Invalid hexadecimal input.'''
+        with self.assertRaises(dns.exception.SyntaxError):
+            dns.rrset.from_text('foo', 300, 'IN', 'EUI64',
+                                'g0-23-45-67-89-ab-cd-ef')
+
+    def testToTextOk(self):
+        '''Valid text output.'''
+        eui = b'\x01\x23\x45\x67\x89\xab\xcd\xef'
+        exp_text = '01-23-45-67-89-ab-cd-ef'
+        inst = dns.rdtypes.ANY.EUI64.EUI64(dns.rdataclass.IN,
+                                           dns.rdatatype.EUI64,
+                                           eui)
+        text = inst.to_text()
+        self.assertEqual(exp_text, text)
+
+    def testToWire(self):
+        '''Valid wire format.'''
+        eui = b'\x01\x23\x45\x67\x89\xab\xcd\xef'
+        inst = dns.rdtypes.ANY.EUI64.EUI64(dns.rdataclass.IN,
+                                           dns.rdatatype.EUI64,
+                                           eui)
+        buff = StringIO()
+        inst.to_wire(buff)
+        self.assertEqual(buff.getvalue(), eui)
+
+    def testFromWireOk(self):
+        '''Valid wire format.'''
+        eui = b'\x01\x23\x45\x67\x89\xab\xcd\xef'
+        pad_len = 100
+        wire = dns.wiredata.WireData(b'x' * pad_len + eui + b'y' * pad_len * 2)
+        inst = dns.rdtypes.ANY.EUI64.EUI64.from_wire(dns.rdataclass.IN,
+                                                     dns.rdatatype.EUI64,
+                                                     wire,
+                                                     pad_len,
+                                                     len(eui))
+        self.assertEqual(inst.eui, eui)
+
+    def testFromWireLength(self):
+        '''Valid wire format.'''
+        eui = b'\x01\x23\x45\x67\x89'
+        pad_len = 100
+        wire = dns.wiredata.WireData(b'x' * pad_len + eui + b'y' * pad_len * 2)
+        with self.assertRaises(dns.exception.FormError):
+            dns.rdtypes.ANY.EUI64.EUI64.from_wire(dns.rdataclass.IN,
+                                                  dns.rdatatype.EUI64,
+                                                  wire,
+                                                  pad_len,
+                                                  len(eui))
+
+
+if __name__ == '__main__':
+    unittest.main()