diff options
author | Nick Hall <nick.hall@deshaw.com> | 2019-02-03 14:02:49 +0000 |
---|---|---|
committer | Nick Hall <nick.hall@deshaw.com> | 2020-08-08 00:25:22 +0100 |
commit | c4878f8e2a4cf095a795bad9f2d9660c4aaad7c3 (patch) | |
tree | f04715c8013db64d44c7fce0a3e91d1508bdcd23 | |
parent | 157e4e907333fae7fb4c5888949258aa7927f6e9 (diff) | |
download | dnspython-c4878f8e2a4cf095a795bad9f2d9660c4aaad7c3.tar.gz |
Add support for TKEY RR type
-rw-r--r-- | dns/rdtypes/ANY/TKEY.py | 117 | ||||
-rw-r--r-- | dns/rdtypes/ANY/__init__.py | 1 | ||||
-rw-r--r-- | tests/test_rdtypeanytkey.py | 96 |
3 files changed, 214 insertions, 0 deletions
diff --git a/dns/rdtypes/ANY/TKEY.py b/dns/rdtypes/ANY/TKEY.py new file mode 100644 index 0000000..959e798 --- /dev/null +++ b/dns/rdtypes/ANY/TKEY.py @@ -0,0 +1,117 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. +# +# 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 NOMINUM 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 base64 +import struct + +import dns.dnssec +import dns.exception +import dns.rdata + + +class TKEY(dns.rdata.Rdata): + + """TKEY Record""" + + __slots__ = ['algorithm', 'inception', 'expiration', 'mode', 'error', + 'key', 'other'] + + def __init__(self, rdclass, rdtype, algorithm, inception, expiration, + mode, error, key, other=b''): + super().__init__(rdclass, rdtype) + object.__setattr__(self, 'algorithm', algorithm) + object.__setattr__(self, 'inception', inception) + object.__setattr__(self, 'expiration', expiration) + object.__setattr__(self, 'mode', mode) + object.__setattr__(self, 'error', error) + object.__setattr__(self, 'key', dns.rdata._constify(key)) + object.__setattr__(self, 'other', dns.rdata._constify(other)) + + def to_text(self, origin=None, relativize=True, **kw): + _algorithm = self.algorithm.choose_relativity(origin, relativize) + text = '%s %u %u %u %u %s' % (str(_algorithm), self.inception, + self.expiration, self.mode, self.error, + dns.rdata._base64ify(self.key, 0)) + if len(self.other): + text += ' %s' % (dns.rdata._base64ify(self.other, 0)) + + return text + + @classmethod + def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True, + relativize_to=None): + algorithm = tok.get_name(relativize=False) + inception = tok.get_uint32() + expiration = tok.get_uint32() + mode = tok.get_uint16() + error = tok.get_uint16() + key_b64 = tok.get_string().encode() + key = base64.b64decode(key_b64) + other_b64 = tok.concatenate_remaining_identifiers().encode() + other = base64.b64decode(other_b64) + + return cls(rdclass, rdtype, algorithm, inception, expiration, mode, + error, key, other) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + self.algorithm.to_wire(file, compress, origin) + file.write(struct.pack("!IIHH", self.inception, self.expiration, + self.mode, self.error)) + file.write(struct.pack("!H", len(self.key))) + file.write(self.key) + file.write(struct.pack("!H", len(self.other))) + if len(self.other): + file.write(self.other) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + algorithm = parser.get_name(origin) + inception, expiration, mode, error = parser.get_struct("!IIHH") + key = parser.get_counted_bytes(2) + other = parser.get_counted_bytes(2) + + return cls(rdclass, rdtype, algorithm, inception, expiration, mode, + error, key, other) + + # Constants for the mode field - from RFC 2930: + # 2.5 The Mode Field + # + # The mode field specifies the general scheme for key agreement or + # the purpose of the TKEY DNS message. Servers and resolvers + # supporting this specification MUST implement the Diffie-Hellman key + # agreement mode and the key deletion mode for queries. All other + # modes are OPTIONAL. A server supporting TKEY that receives a TKEY + # request with a mode it does not support returns the BADMODE error. + # The following values of the Mode octet are defined, available, or + # reserved: + # + # Value Description + # ----- ----------- + # 0 - reserved, see section 7 + # 1 server assignment + # 2 Diffie-Hellman exchange + # 3 GSS-API negotiation + # 4 resolver assignment + # 5 key deletion + # 6-65534 - available, see section 7 + # 65535 - reserved, see section 7 + SERVER_ASSIGNMENT = 1 + DIFFIE_HELLMAN_EXCHANGE = 2 + GSSAPI_NEGOTIATION = 3 + RESOLVER_ASSIGNMENT = 4 + KEY_DELETION = 5 + diff --git a/dns/rdtypes/ANY/__init__.py b/dns/rdtypes/ANY/__init__.py index ea704c8..0d1a740 100644 --- a/dns/rdtypes/ANY/__init__.py +++ b/dns/rdtypes/ANY/__init__.py @@ -51,6 +51,7 @@ __all__ = [ 'SOA', 'SPF', 'SSHFP', + 'TKEY', 'TLSA', 'TSIG', 'TXT', diff --git a/tests/test_rdtypeanytkey.py b/tests/test_rdtypeanytkey.py new file mode 100644 index 0000000..3a3ca57 --- /dev/null +++ b/tests/test_rdtypeanytkey.py @@ -0,0 +1,96 @@ +# -*- coding: utf-8 +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. +# +# 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 NOMINUM 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 +import base64 + +import dns.name +import dns.zone +import dns.rdtypes.ANY.TKEY +from dns.rdataclass import RdataClass +from dns.rdatatype import RdataType + + +class RdtypeAnyTKeyTestCase(unittest.TestCase): + tkey_rdata_text = 'gss-tsig. 1594203795 1594206664 3 0 KEYKEYKEYKEYKEYKEYKEYKEYKEYKEYKEYKEY OTHEROTHEROTHEROTHEROTHEROTHEROT' + tkey_rdata_text_no_other = 'gss-tsig. 1594203795 1594206664 3 0 KEYKEYKEYKEYKEYKEYKEYKEYKEYKEYKEYKEY' + + def testTextOptionalData(self): + # construct the rdata from text and extract the TKEY + tkey = dns.rdata.from_text( + RdataClass.ANY, RdataType.TKEY, + RdtypeAnyTKeyTestCase.tkey_rdata_text, origin='.') + self.assertEqual(type(tkey), dns.rdtypes.ANY.TKEY.TKEY) + + # go to text and compare + tkey_out_text = tkey.to_text(relativize=False) + self.assertEqual(tkey_out_text, + RdtypeAnyTKeyTestCase.tkey_rdata_text) + + def testTextNoOptionalData(self): + # construct the rdata from text and extract the TKEY + tkey = dns.rdata.from_text( + RdataClass.ANY, RdataType.TKEY, + RdtypeAnyTKeyTestCase.tkey_rdata_text_no_other, origin='.') + self.assertEqual(type(tkey), dns.rdtypes.ANY.TKEY.TKEY) + + # go to text and compare + tkey_out_text = tkey.to_text(relativize=False) + self.assertEqual(tkey_out_text, + RdtypeAnyTKeyTestCase.tkey_rdata_text_no_other) + + def testWireOptionalData(self): + key = base64.b64decode('KEYKEYKEYKEYKEYKEYKEYKEYKEYKEYKEYKEY') + other = base64.b64decode('OTHEROTHEROTHEROTHEROTHEROTHEROT') + + # construct the TKEY and compare the text output + tkey = dns.rdtypes.ANY.TKEY.TKEY(dns.rdataclass.ANY, + dns.rdatatype.TKEY, + dns.name.from_text('gss-tsig.'), + 1594203795, 1594206664, + 3, 0, key, other) + self.assertEqual(tkey.to_text(relativize=False), + RdtypeAnyTKeyTestCase.tkey_rdata_text) + + # go to/from wire and compare the text output + wire = tkey.to_wire() + tkey_out_wire = dns.rdata.from_wire(dns.rdataclass.ANY, + dns.rdatatype.TKEY, + wire, 0, len(wire)) + self.assertEqual(tkey_out_wire.to_text(relativize=False), + RdtypeAnyTKeyTestCase.tkey_rdata_text) + + def testWireNoOptionalData(self): + key = base64.b64decode('KEYKEYKEYKEYKEYKEYKEYKEYKEYKEYKEYKEY') + + # construct the TKEY with no 'other' data and compare the text output + tkey = dns.rdtypes.ANY.TKEY.TKEY(dns.rdataclass.ANY, + dns.rdatatype.TKEY, + dns.name.from_text('gss-tsig.'), + 1594203795, 1594206664, + 3, 0, key) + self.assertEqual(tkey.to_text(relativize=False), + RdtypeAnyTKeyTestCase.tkey_rdata_text_no_other) + + # go to/from wire and compare the text output + wire = tkey.to_wire() + tkey_out_wire = dns.rdata.from_wire(dns.rdataclass.ANY, + dns.rdatatype.TKEY, + wire, 0, len(wire)) + self.assertEqual(tkey_out_wire.to_text(relativize=False), + RdtypeAnyTKeyTestCase.tkey_rdata_text_no_other) |