]> git.ipfire.org Git - thirdparty/dnspython.git/commitdiff
Add EDNS Cookie option.
authorBob Halley <halley@dnspython.org>
Sat, 13 Apr 2024 16:26:31 +0000 (09:26 -0700)
committerBob Halley <halley@dnspython.org>
Sat, 13 Apr 2024 16:26:44 +0000 (09:26 -0700)
dns/edns.py
doc/rfc.rst
tests/test_edns.py

index 776e5eeba7b725ea68e2a1f3ffc7099b7797b709..ef79b5f00f58f9cdcea3092aee40179f56e13ca1 100644 (file)
@@ -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,
 }
 
 
index c6fffdb561ebbde4a4ace6dfaa75f76b0fa93e2f..feae81622897ccc1a2fee7757dcb241082401798 100644 (file)
@@ -126,6 +126,9 @@ Misc RFCs
 `RFC 4343 <https://tools.ietf.org/html/rfc4343>`_
     Case-sensitivity clarification.
 
+`RFC 7873 <https://tools.ietf.org/html/rfc7873>`_
+   Domain Name System (DNS) Cookies
+
 `RFC 8499 <https://tools.ietf.org/html/rfc8499>`_
     DNS Terminology.
 
index 367c723c6668c7cbc046478125730b51bd982202..8e63b21dd74fb5d88e85d0a3e699162bae6d51e9 100644 (file)
@@ -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