From: Bob Halley Date: Sat, 13 Apr 2024 16:26:31 +0000 (-0700) Subject: Add EDNS Cookie option. X-Git-Tag: v2.7.0rc1~55 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=32c66483794c3d483d9836fd95a8af1a7d01c3d2;p=thirdparty%2Fdnspython.git Add EDNS Cookie option. --- diff --git a/dns/edns.py b/dns/edns.py index 776e5eeb..ef79b5f0 100644 --- a/dns/edns.py +++ b/dns/edns.py @@ -430,10 +430,45 @@ class NSIDOption(Option): return cls(parser.get_remaining()) +class CookieOption(Option): + def __init__(self, client: bytes, server: bytes): + super().__init__(dns.edns.OptionType.COOKIE) + self.client = client + self.server = server + if len(client) != 8: + raise ValueError("client cookie must be 8 bytes") + if len(server) != 0 and (len(server) < 8 or len(server) > 32): + raise ValueError("server cookie must be empty or between 8 and 32 bytes") + + def to_wire(self, file: Any = None) -> Optional[bytes]: + if file: + file.write(self.client) + if len(self.server) > 0: + file.write(self.server) + return None + else: + return self.client + self.server + + def to_text(self) -> str: + client = binascii.hexlify(self.client).decode() + if len(self.server) > 0: + server = binascii.hexlify(self.server).decode() + else: + server = "" + return f"COOKIE {client}:{server}" + + @classmethod + def from_wire_parser( + cls, otype: Union[OptionType, str], parser: dns.wire.Parser + ) -> Option: + return cls(parser.get_bytes(8), parser.get_remaining()) + + _type_to_class: Dict[OptionType, Any] = { OptionType.ECS: ECSOption, OptionType.EDE: EDEOption, OptionType.NSID: NSIDOption, + OptionType.COOKIE: CookieOption, } diff --git a/doc/rfc.rst b/doc/rfc.rst index c6fffdb5..feae8162 100644 --- a/doc/rfc.rst +++ b/doc/rfc.rst @@ -126,6 +126,9 @@ Misc RFCs `RFC 4343 `_ Case-sensitivity clarification. +`RFC 7873 `_ + Domain Name System (DNS) Cookies + `RFC 8499 `_ DNS Terminology. diff --git a/tests/test_edns.py b/tests/test_edns.py index 367c723c..8e63b21d 100644 --- a/tests/test_edns.py +++ b/tests/test_edns.py @@ -216,6 +216,42 @@ class OptionTestCase(unittest.TestCase): o = dns.edns.option_from_wire(dns.edns.OptionType.NSID, data, 0, len(data)) self.assertEqual(o.nsid, b"\xfe\xff") + def testCookieOption(self): + opt = dns.edns.CookieOption(b"12345678", b"") + io = BytesIO() + opt.to_wire(io) + data = io.getvalue() + self.assertEqual(data, b"12345678") + self.assertEqual(str(opt), "COOKIE 3132333435363738:") + opt = dns.edns.CookieOption(b"12345678", b"abcdefgh") + data = opt.to_wire() + self.assertEqual(data, b"12345678abcdefgh") + self.assertEqual(str(opt), "COOKIE 3132333435363738:6162636465666768") + # maximal server + opt = dns.edns.CookieOption(b"12345678", b"abcdefghabcdefghabcdefghabcdefgh") + io = BytesIO() + opt.to_wire(io) + data = io.getvalue() + self.assertEqual(data, b"12345678abcdefghabcdefghabcdefghabcdefgh") + # from wire + opt2 = dns.edns.option_from_wire(dns.edns.OptionType.COOKIE, data, 0, len(data)) + self.assertEqual(opt.client, opt2.client) + self.assertEqual(opt.server, opt2.server) + # client too short + with self.assertRaises(ValueError): + opt = dns.edns.CookieOption(b"1234567", b"") + # client too long + with self.assertRaises(ValueError): + opt = dns.edns.CookieOption(b"123456789", b"") + # server too short + with self.assertRaises(ValueError): + opt = dns.edns.CookieOption(b"12345678", b"a") + # server too long + with self.assertRaises(ValueError): + opt = dns.edns.CookieOption( + b"12345678", b"abcdefghabcdefghabcdefghabcdefghi" + ) + def test_option_registration(self): U32OptionType = 9999