about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/dns/rdtypes
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/dns/rdtypes
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-master.tar.gz
two version of R2R are here HEAD master
Diffstat (limited to '.venv/lib/python3.12/site-packages/dns/rdtypes')
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/AFSDB.py45
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/AMTRELAY.py91
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/AVC.py26
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CAA.py71
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CDNSKEY.py33
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CDS.py29
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CERT.py116
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CNAME.py28
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CSYNC.py68
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DLV.py24
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DNAME.py27
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DNSKEY.py33
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DS.py24
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/EUI48.py30
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/EUI64.py30
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/GPOS.py126
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/HINFO.py64
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/HIP.py85
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/ISDN.py78
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/L32.py41
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/L64.py47
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/LOC.py353
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/LP.py42
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/MX.py24
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NID.py47
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NINFO.py26
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NS.py24
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NSEC.py67
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NSEC3.py126
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NSEC3PARAM.py69
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/OPENPGPKEY.py53
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/OPT.py77
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/PTR.py24
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RESINFO.py24
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RP.py58
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RRSIG.py157
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RT.py24
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SMIMEA.py9
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SOA.py86
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SPF.py26
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SSHFP.py68
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TKEY.py142
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TLSA.py9
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TSIG.py160
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TXT.py24
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/URI.py79
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/WALLET.py9
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/X25.py57
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/ZONEMD.py66
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__init__.py70
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/CH/A.py59
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/CH/__init__.py22
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/IN/A.py51
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/IN/AAAA.py51
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/IN/APL.py150
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/IN/DHCID.py54
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/IN/HTTPS.py9
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/IN/IPSECKEY.py91
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/IN/KX.py24
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/IN/NAPTR.py110
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/IN/NSAP.py60
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/IN/NSAP_PTR.py24
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/IN/PX.py73
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/IN/SRV.py75
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/IN/SVCB.py9
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/IN/WKS.py100
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__init__.py35
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/__init__.py33
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/dnskeybase.py87
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/dsbase.py85
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/euibase.py70
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/mxbase.py87
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/nsbase.py63
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/svcbbase.py585
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/tlsabase.py71
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/txtbase.py106
-rw-r--r--.venv/lib/python3.12/site-packages/dns/rdtypes/util.py257
77 files changed, 5557 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/AFSDB.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/AFSDB.py
new file mode 100644
index 00000000..06a3b970
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/AFSDB.py
@@ -0,0 +1,45 @@
+# 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 dns.immutable
+import dns.rdtypes.mxbase
+
+
+@dns.immutable.immutable
+class AFSDB(dns.rdtypes.mxbase.UncompressedDowncasingMX):
+    """AFSDB record"""
+
+    # Use the property mechanism to make "subtype" an alias for the
+    # "preference" attribute, and "hostname" an alias for the "exchange"
+    # attribute.
+    #
+    # This lets us inherit the UncompressedMX implementation but lets
+    # the caller use appropriate attribute names for the rdata type.
+    #
+    # We probably lose some performance vs. a cut-and-paste
+    # implementation, but this way we don't copy code, and that's
+    # good.
+
+    @property
+    def subtype(self):
+        "the AFSDB subtype"
+        return self.preference
+
+    @property
+    def hostname(self):
+        "the AFSDB hostname"
+        return self.exchange
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/AMTRELAY.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/AMTRELAY.py
new file mode 100644
index 00000000..ed2b072b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/AMTRELAY.py
@@ -0,0 +1,91 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2006, 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 struct
+
+import dns.exception
+import dns.immutable
+import dns.rdtypes.util
+
+
+class Relay(dns.rdtypes.util.Gateway):
+    name = "AMTRELAY relay"
+
+    @property
+    def relay(self):
+        return self.gateway
+
+
+@dns.immutable.immutable
+class AMTRELAY(dns.rdata.Rdata):
+    """AMTRELAY record"""
+
+    # see: RFC 8777
+
+    __slots__ = ["precedence", "discovery_optional", "relay_type", "relay"]
+
+    def __init__(
+        self, rdclass, rdtype, precedence, discovery_optional, relay_type, relay
+    ):
+        super().__init__(rdclass, rdtype)
+        relay = Relay(relay_type, relay)
+        self.precedence = self._as_uint8(precedence)
+        self.discovery_optional = self._as_bool(discovery_optional)
+        self.relay_type = relay.type
+        self.relay = relay.relay
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        relay = Relay(self.relay_type, self.relay).to_text(origin, relativize)
+        return "%d %d %d %s" % (
+            self.precedence,
+            self.discovery_optional,
+            self.relay_type,
+            relay,
+        )
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        precedence = tok.get_uint8()
+        discovery_optional = tok.get_uint8()
+        if discovery_optional > 1:
+            raise dns.exception.SyntaxError("expecting 0 or 1")
+        discovery_optional = bool(discovery_optional)
+        relay_type = tok.get_uint8()
+        if relay_type > 0x7F:
+            raise dns.exception.SyntaxError("expecting an integer <= 127")
+        relay = Relay.from_text(relay_type, tok, origin, relativize, relativize_to)
+        return cls(
+            rdclass, rdtype, precedence, discovery_optional, relay_type, relay.relay
+        )
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        relay_type = self.relay_type | (self.discovery_optional << 7)
+        header = struct.pack("!BB", self.precedence, relay_type)
+        file.write(header)
+        Relay(self.relay_type, self.relay).to_wire(file, compress, origin, canonicalize)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        (precedence, relay_type) = parser.get_struct("!BB")
+        discovery_optional = bool(relay_type >> 7)
+        relay_type &= 0x7F
+        relay = Relay.from_wire_parser(relay_type, parser, origin)
+        return cls(
+            rdclass, rdtype, precedence, discovery_optional, relay_type, relay.relay
+        )
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/AVC.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/AVC.py
new file mode 100644
index 00000000..a27ae2d6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/AVC.py
@@ -0,0 +1,26 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2016 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 dns.immutable
+import dns.rdtypes.txtbase
+
+
+@dns.immutable.immutable
+class AVC(dns.rdtypes.txtbase.TXTBase):
+    """AVC record"""
+
+    # See: IANA dns parameters for AVC
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CAA.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CAA.py
new file mode 100644
index 00000000..2e6a7e7e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CAA.py
@@ -0,0 +1,71 @@
+# 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 struct
+
+import dns.exception
+import dns.immutable
+import dns.rdata
+import dns.tokenizer
+
+
+@dns.immutable.immutable
+class CAA(dns.rdata.Rdata):
+    """CAA (Certification Authority Authorization) record"""
+
+    # see: RFC 6844
+
+    __slots__ = ["flags", "tag", "value"]
+
+    def __init__(self, rdclass, rdtype, flags, tag, value):
+        super().__init__(rdclass, rdtype)
+        self.flags = self._as_uint8(flags)
+        self.tag = self._as_bytes(tag, True, 255)
+        if not tag.isalnum():
+            raise ValueError("tag is not alphanumeric")
+        self.value = self._as_bytes(value)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return '%u %s "%s"' % (
+            self.flags,
+            dns.rdata._escapify(self.tag),
+            dns.rdata._escapify(self.value),
+        )
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        flags = tok.get_uint8()
+        tag = tok.get_string().encode()
+        value = tok.get_string().encode()
+        return cls(rdclass, rdtype, flags, tag, value)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        file.write(struct.pack("!B", self.flags))
+        l = len(self.tag)
+        assert l < 256
+        file.write(struct.pack("!B", l))
+        file.write(self.tag)
+        file.write(self.value)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        flags = parser.get_uint8()
+        tag = parser.get_counted_bytes()
+        value = parser.get_remaining()
+        return cls(rdclass, rdtype, flags, tag, value)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CDNSKEY.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CDNSKEY.py
new file mode 100644
index 00000000..b613409f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CDNSKEY.py
@@ -0,0 +1,33 @@
+# 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 dns.immutable
+import dns.rdtypes.dnskeybase  # lgtm[py/import-and-import-from]
+
+# pylint: disable=unused-import
+from dns.rdtypes.dnskeybase import (  # noqa: F401  lgtm[py/unused-import]
+    REVOKE,
+    SEP,
+    ZONE,
+)
+
+# pylint: enable=unused-import
+
+
+@dns.immutable.immutable
+class CDNSKEY(dns.rdtypes.dnskeybase.DNSKEYBase):
+    """CDNSKEY record"""
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CDS.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CDS.py
new file mode 100644
index 00000000..8312b972
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CDS.py
@@ -0,0 +1,29 @@
+# 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 dns.immutable
+import dns.rdtypes.dsbase
+
+
+@dns.immutable.immutable
+class CDS(dns.rdtypes.dsbase.DSBase):
+    """CDS record"""
+
+    _digest_length_by_type = {
+        **dns.rdtypes.dsbase.DSBase._digest_length_by_type,
+        0: 1,  # delete, RFC 8078 Sec. 4 (including Errata ID 5049)
+    }
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CERT.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CERT.py
new file mode 100644
index 00000000..f369cc85
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CERT.py
@@ -0,0 +1,116 @@
+# 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 base64
+import struct
+
+import dns.dnssectypes
+import dns.exception
+import dns.immutable
+import dns.rdata
+import dns.tokenizer
+
+_ctype_by_value = {
+    1: "PKIX",
+    2: "SPKI",
+    3: "PGP",
+    4: "IPKIX",
+    5: "ISPKI",
+    6: "IPGP",
+    7: "ACPKIX",
+    8: "IACPKIX",
+    253: "URI",
+    254: "OID",
+}
+
+_ctype_by_name = {
+    "PKIX": 1,
+    "SPKI": 2,
+    "PGP": 3,
+    "IPKIX": 4,
+    "ISPKI": 5,
+    "IPGP": 6,
+    "ACPKIX": 7,
+    "IACPKIX": 8,
+    "URI": 253,
+    "OID": 254,
+}
+
+
+def _ctype_from_text(what):
+    v = _ctype_by_name.get(what)
+    if v is not None:
+        return v
+    return int(what)
+
+
+def _ctype_to_text(what):
+    v = _ctype_by_value.get(what)
+    if v is not None:
+        return v
+    return str(what)
+
+
+@dns.immutable.immutable
+class CERT(dns.rdata.Rdata):
+    """CERT record"""
+
+    # see RFC 4398
+
+    __slots__ = ["certificate_type", "key_tag", "algorithm", "certificate"]
+
+    def __init__(
+        self, rdclass, rdtype, certificate_type, key_tag, algorithm, certificate
+    ):
+        super().__init__(rdclass, rdtype)
+        self.certificate_type = self._as_uint16(certificate_type)
+        self.key_tag = self._as_uint16(key_tag)
+        self.algorithm = self._as_uint8(algorithm)
+        self.certificate = self._as_bytes(certificate)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        certificate_type = _ctype_to_text(self.certificate_type)
+        return "%s %d %s %s" % (
+            certificate_type,
+            self.key_tag,
+            dns.dnssectypes.Algorithm.to_text(self.algorithm),
+            dns.rdata._base64ify(self.certificate, **kw),
+        )
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        certificate_type = _ctype_from_text(tok.get_string())
+        key_tag = tok.get_uint16()
+        algorithm = dns.dnssectypes.Algorithm.from_text(tok.get_string())
+        b64 = tok.concatenate_remaining_identifiers().encode()
+        certificate = base64.b64decode(b64)
+        return cls(rdclass, rdtype, certificate_type, key_tag, algorithm, certificate)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        prefix = struct.pack(
+            "!HHB", self.certificate_type, self.key_tag, self.algorithm
+        )
+        file.write(prefix)
+        file.write(self.certificate)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        (certificate_type, key_tag, algorithm) = parser.get_struct("!HHB")
+        certificate = parser.get_remaining()
+        return cls(rdclass, rdtype, certificate_type, key_tag, algorithm, certificate)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CNAME.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CNAME.py
new file mode 100644
index 00000000..665e407c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CNAME.py
@@ -0,0 +1,28 @@
+# 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 dns.immutable
+import dns.rdtypes.nsbase
+
+
+@dns.immutable.immutable
+class CNAME(dns.rdtypes.nsbase.NSBase):
+    """CNAME record
+
+    Note: although CNAME is officially a singleton type, dnspython allows
+    non-singleton CNAME rdatasets because such sets have been commonly
+    used by BIND and other nameservers for load balancing."""
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CSYNC.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CSYNC.py
new file mode 100644
index 00000000..2f972f6e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CSYNC.py
@@ -0,0 +1,68 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2004-2007, 2009-2011, 2016 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 struct
+
+import dns.exception
+import dns.immutable
+import dns.name
+import dns.rdata
+import dns.rdatatype
+import dns.rdtypes.util
+
+
+@dns.immutable.immutable
+class Bitmap(dns.rdtypes.util.Bitmap):
+    type_name = "CSYNC"
+
+
+@dns.immutable.immutable
+class CSYNC(dns.rdata.Rdata):
+    """CSYNC record"""
+
+    __slots__ = ["serial", "flags", "windows"]
+
+    def __init__(self, rdclass, rdtype, serial, flags, windows):
+        super().__init__(rdclass, rdtype)
+        self.serial = self._as_uint32(serial)
+        self.flags = self._as_uint16(flags)
+        if not isinstance(windows, Bitmap):
+            windows = Bitmap(windows)
+        self.windows = tuple(windows.windows)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        text = Bitmap(self.windows).to_text()
+        return "%d %d%s" % (self.serial, self.flags, text)
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        serial = tok.get_uint32()
+        flags = tok.get_uint16()
+        bitmap = Bitmap.from_text(tok)
+        return cls(rdclass, rdtype, serial, flags, bitmap)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        file.write(struct.pack("!IH", self.serial, self.flags))
+        Bitmap(self.windows).to_wire(file)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        (serial, flags) = parser.get_struct("!IH")
+        bitmap = Bitmap.from_wire_parser(parser)
+        return cls(rdclass, rdtype, serial, flags, bitmap)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DLV.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DLV.py
new file mode 100644
index 00000000..6c134f18
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DLV.py
@@ -0,0 +1,24 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 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 dns.immutable
+import dns.rdtypes.dsbase
+
+
+@dns.immutable.immutable
+class DLV(dns.rdtypes.dsbase.DSBase):
+    """DLV record"""
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DNAME.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DNAME.py
new file mode 100644
index 00000000..bbf9186c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DNAME.py
@@ -0,0 +1,27 @@
+# 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 dns.immutable
+import dns.rdtypes.nsbase
+
+
+@dns.immutable.immutable
+class DNAME(dns.rdtypes.nsbase.UncompressedNS):
+    """DNAME record"""
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        self.target.to_wire(file, None, origin, canonicalize)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DNSKEY.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DNSKEY.py
new file mode 100644
index 00000000..6d961a9f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DNSKEY.py
@@ -0,0 +1,33 @@
+# 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 dns.immutable
+import dns.rdtypes.dnskeybase  # lgtm[py/import-and-import-from]
+
+# pylint: disable=unused-import
+from dns.rdtypes.dnskeybase import (  # noqa: F401  lgtm[py/unused-import]
+    REVOKE,
+    SEP,
+    ZONE,
+)
+
+# pylint: enable=unused-import
+
+
+@dns.immutable.immutable
+class DNSKEY(dns.rdtypes.dnskeybase.DNSKEYBase):
+    """DNSKEY record"""
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DS.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DS.py
new file mode 100644
index 00000000..58b3108d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DS.py
@@ -0,0 +1,24 @@
+# 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 dns.immutable
+import dns.rdtypes.dsbase
+
+
+@dns.immutable.immutable
+class DS(dns.rdtypes.dsbase.DSBase):
+    """DS record"""
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/EUI48.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/EUI48.py
new file mode 100644
index 00000000..c843be50
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/EUI48.py
@@ -0,0 +1,30 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# 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.immutable
+import dns.rdtypes.euibase
+
+
+@dns.immutable.immutable
+class EUI48(dns.rdtypes.euibase.EUIBase):
+    """EUI48 record"""
+
+    # see: rfc7043.txt
+
+    byte_len = 6  # 0123456789ab (in hex)
+    text_len = byte_len * 3 - 1  # 01-23-45-67-89-ab
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/EUI64.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/EUI64.py
new file mode 100644
index 00000000..f6d7e257
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/EUI64.py
@@ -0,0 +1,30 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# 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.immutable
+import dns.rdtypes.euibase
+
+
+@dns.immutable.immutable
+class EUI64(dns.rdtypes.euibase.EUIBase):
+    """EUI64 record"""
+
+    # see: rfc7043.txt
+
+    byte_len = 8  # 0123456789abcdef (in hex)
+    text_len = byte_len * 3 - 1  # 01-23-45-67-89-ab-cd-ef
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/GPOS.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/GPOS.py
new file mode 100644
index 00000000..d79f4a06
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/GPOS.py
@@ -0,0 +1,126 @@
+# 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 struct
+
+import dns.exception
+import dns.immutable
+import dns.rdata
+import dns.tokenizer
+
+
+def _validate_float_string(what):
+    if len(what) == 0:
+        raise dns.exception.FormError
+    if what[0] == b"-"[0] or what[0] == b"+"[0]:
+        what = what[1:]
+    if what.isdigit():
+        return
+    try:
+        (left, right) = what.split(b".")
+    except ValueError:
+        raise dns.exception.FormError
+    if left == b"" and right == b"":
+        raise dns.exception.FormError
+    if not left == b"" and not left.decode().isdigit():
+        raise dns.exception.FormError
+    if not right == b"" and not right.decode().isdigit():
+        raise dns.exception.FormError
+
+
+@dns.immutable.immutable
+class GPOS(dns.rdata.Rdata):
+    """GPOS record"""
+
+    # see: RFC 1712
+
+    __slots__ = ["latitude", "longitude", "altitude"]
+
+    def __init__(self, rdclass, rdtype, latitude, longitude, altitude):
+        super().__init__(rdclass, rdtype)
+        if isinstance(latitude, float) or isinstance(latitude, int):
+            latitude = str(latitude)
+        if isinstance(longitude, float) or isinstance(longitude, int):
+            longitude = str(longitude)
+        if isinstance(altitude, float) or isinstance(altitude, int):
+            altitude = str(altitude)
+        latitude = self._as_bytes(latitude, True, 255)
+        longitude = self._as_bytes(longitude, True, 255)
+        altitude = self._as_bytes(altitude, True, 255)
+        _validate_float_string(latitude)
+        _validate_float_string(longitude)
+        _validate_float_string(altitude)
+        self.latitude = latitude
+        self.longitude = longitude
+        self.altitude = altitude
+        flat = self.float_latitude
+        if flat < -90.0 or flat > 90.0:
+            raise dns.exception.FormError("bad latitude")
+        flong = self.float_longitude
+        if flong < -180.0 or flong > 180.0:
+            raise dns.exception.FormError("bad longitude")
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return (
+            f"{self.latitude.decode()} {self.longitude.decode()} "
+            f"{self.altitude.decode()}"
+        )
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        latitude = tok.get_string()
+        longitude = tok.get_string()
+        altitude = tok.get_string()
+        return cls(rdclass, rdtype, latitude, longitude, altitude)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        l = len(self.latitude)
+        assert l < 256
+        file.write(struct.pack("!B", l))
+        file.write(self.latitude)
+        l = len(self.longitude)
+        assert l < 256
+        file.write(struct.pack("!B", l))
+        file.write(self.longitude)
+        l = len(self.altitude)
+        assert l < 256
+        file.write(struct.pack("!B", l))
+        file.write(self.altitude)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        latitude = parser.get_counted_bytes()
+        longitude = parser.get_counted_bytes()
+        altitude = parser.get_counted_bytes()
+        return cls(rdclass, rdtype, latitude, longitude, altitude)
+
+    @property
+    def float_latitude(self):
+        "latitude as a floating point value"
+        return float(self.latitude)
+
+    @property
+    def float_longitude(self):
+        "longitude as a floating point value"
+        return float(self.longitude)
+
+    @property
+    def float_altitude(self):
+        "altitude as a floating point value"
+        return float(self.altitude)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/HINFO.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/HINFO.py
new file mode 100644
index 00000000..06ad3487
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/HINFO.py
@@ -0,0 +1,64 @@
+# 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 struct
+
+import dns.exception
+import dns.immutable
+import dns.rdata
+import dns.tokenizer
+
+
+@dns.immutable.immutable
+class HINFO(dns.rdata.Rdata):
+    """HINFO record"""
+
+    # see: RFC 1035
+
+    __slots__ = ["cpu", "os"]
+
+    def __init__(self, rdclass, rdtype, cpu, os):
+        super().__init__(rdclass, rdtype)
+        self.cpu = self._as_bytes(cpu, True, 255)
+        self.os = self._as_bytes(os, True, 255)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return f'"{dns.rdata._escapify(self.cpu)}" "{dns.rdata._escapify(self.os)}"'
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        cpu = tok.get_string(max_length=255)
+        os = tok.get_string(max_length=255)
+        return cls(rdclass, rdtype, cpu, os)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        l = len(self.cpu)
+        assert l < 256
+        file.write(struct.pack("!B", l))
+        file.write(self.cpu)
+        l = len(self.os)
+        assert l < 256
+        file.write(struct.pack("!B", l))
+        file.write(self.os)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        cpu = parser.get_counted_bytes()
+        os = parser.get_counted_bytes()
+        return cls(rdclass, rdtype, cpu, os)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/HIP.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/HIP.py
new file mode 100644
index 00000000..f3157da7
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/HIP.py
@@ -0,0 +1,85 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2010, 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 binascii
+import struct
+
+import dns.exception
+import dns.immutable
+import dns.rdata
+import dns.rdatatype
+
+
+@dns.immutable.immutable
+class HIP(dns.rdata.Rdata):
+    """HIP record"""
+
+    # see: RFC 5205
+
+    __slots__ = ["hit", "algorithm", "key", "servers"]
+
+    def __init__(self, rdclass, rdtype, hit, algorithm, key, servers):
+        super().__init__(rdclass, rdtype)
+        self.hit = self._as_bytes(hit, True, 255)
+        self.algorithm = self._as_uint8(algorithm)
+        self.key = self._as_bytes(key, True)
+        self.servers = self._as_tuple(servers, self._as_name)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        hit = binascii.hexlify(self.hit).decode()
+        key = base64.b64encode(self.key).replace(b"\n", b"").decode()
+        text = ""
+        servers = []
+        for server in self.servers:
+            servers.append(server.choose_relativity(origin, relativize))
+        if len(servers) > 0:
+            text += " " + " ".join(x.to_unicode() for x in servers)
+        return "%u %s %s%s" % (self.algorithm, hit, key, text)
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        algorithm = tok.get_uint8()
+        hit = binascii.unhexlify(tok.get_string().encode())
+        key = base64.b64decode(tok.get_string().encode())
+        servers = []
+        for token in tok.get_remaining():
+            server = tok.as_name(token, origin, relativize, relativize_to)
+            servers.append(server)
+        return cls(rdclass, rdtype, hit, algorithm, key, servers)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        lh = len(self.hit)
+        lk = len(self.key)
+        file.write(struct.pack("!BBH", lh, self.algorithm, lk))
+        file.write(self.hit)
+        file.write(self.key)
+        for server in self.servers:
+            server.to_wire(file, None, origin, False)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        (lh, algorithm, lk) = parser.get_struct("!BBH")
+        hit = parser.get_bytes(lh)
+        key = parser.get_bytes(lk)
+        servers = []
+        while parser.remaining() > 0:
+            server = parser.get_name(origin)
+            servers.append(server)
+        return cls(rdclass, rdtype, hit, algorithm, key, servers)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/ISDN.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/ISDN.py
new file mode 100644
index 00000000..6428a0a8
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/ISDN.py
@@ -0,0 +1,78 @@
+# 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 struct
+
+import dns.exception
+import dns.immutable
+import dns.rdata
+import dns.tokenizer
+
+
+@dns.immutable.immutable
+class ISDN(dns.rdata.Rdata):
+    """ISDN record"""
+
+    # see: RFC 1183
+
+    __slots__ = ["address", "subaddress"]
+
+    def __init__(self, rdclass, rdtype, address, subaddress):
+        super().__init__(rdclass, rdtype)
+        self.address = self._as_bytes(address, True, 255)
+        self.subaddress = self._as_bytes(subaddress, True, 255)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        if self.subaddress:
+            return (
+                f'"{dns.rdata._escapify(self.address)}" '
+                f'"{dns.rdata._escapify(self.subaddress)}"'
+            )
+        else:
+            return f'"{dns.rdata._escapify(self.address)}"'
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        address = tok.get_string()
+        tokens = tok.get_remaining(max_tokens=1)
+        if len(tokens) >= 1:
+            subaddress = tokens[0].unescape().value
+        else:
+            subaddress = ""
+        return cls(rdclass, rdtype, address, subaddress)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        l = len(self.address)
+        assert l < 256
+        file.write(struct.pack("!B", l))
+        file.write(self.address)
+        l = len(self.subaddress)
+        if l > 0:
+            assert l < 256
+            file.write(struct.pack("!B", l))
+            file.write(self.subaddress)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        address = parser.get_counted_bytes()
+        if parser.remaining() > 0:
+            subaddress = parser.get_counted_bytes()
+        else:
+            subaddress = b""
+        return cls(rdclass, rdtype, address, subaddress)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/L32.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/L32.py
new file mode 100644
index 00000000..09804c2d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/L32.py
@@ -0,0 +1,41 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+import struct
+
+import dns.immutable
+import dns.rdata
+
+
+@dns.immutable.immutable
+class L32(dns.rdata.Rdata):
+    """L32 record"""
+
+    # see: rfc6742.txt
+
+    __slots__ = ["preference", "locator32"]
+
+    def __init__(self, rdclass, rdtype, preference, locator32):
+        super().__init__(rdclass, rdtype)
+        self.preference = self._as_uint16(preference)
+        self.locator32 = self._as_ipv4_address(locator32)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return f"{self.preference} {self.locator32}"
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        preference = tok.get_uint16()
+        nodeid = tok.get_identifier()
+        return cls(rdclass, rdtype, preference, nodeid)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        file.write(struct.pack("!H", self.preference))
+        file.write(dns.ipv4.inet_aton(self.locator32))
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        preference = parser.get_uint16()
+        locator32 = parser.get_remaining()
+        return cls(rdclass, rdtype, preference, locator32)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/L64.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/L64.py
new file mode 100644
index 00000000..fb76808e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/L64.py
@@ -0,0 +1,47 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+import struct
+
+import dns.immutable
+import dns.rdtypes.util
+
+
+@dns.immutable.immutable
+class L64(dns.rdata.Rdata):
+    """L64 record"""
+
+    # see: rfc6742.txt
+
+    __slots__ = ["preference", "locator64"]
+
+    def __init__(self, rdclass, rdtype, preference, locator64):
+        super().__init__(rdclass, rdtype)
+        self.preference = self._as_uint16(preference)
+        if isinstance(locator64, bytes):
+            if len(locator64) != 8:
+                raise ValueError("invalid locator64")
+            self.locator64 = dns.rdata._hexify(locator64, 4, b":")
+        else:
+            dns.rdtypes.util.parse_formatted_hex(locator64, 4, 4, ":")
+            self.locator64 = locator64
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return f"{self.preference} {self.locator64}"
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        preference = tok.get_uint16()
+        locator64 = tok.get_identifier()
+        return cls(rdclass, rdtype, preference, locator64)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        file.write(struct.pack("!H", self.preference))
+        file.write(dns.rdtypes.util.parse_formatted_hex(self.locator64, 4, 4, ":"))
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        preference = parser.get_uint16()
+        locator64 = parser.get_remaining()
+        return cls(rdclass, rdtype, preference, locator64)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/LOC.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/LOC.py
new file mode 100644
index 00000000..1153cf03
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/LOC.py
@@ -0,0 +1,353 @@
+# 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 struct
+
+import dns.exception
+import dns.immutable
+import dns.rdata
+
+_pows = tuple(10**i for i in range(0, 11))
+
+# default values are in centimeters
+_default_size = 100.0
+_default_hprec = 1000000.0
+_default_vprec = 1000.0
+
+# for use by from_wire()
+_MAX_LATITUDE = 0x80000000 + 90 * 3600000
+_MIN_LATITUDE = 0x80000000 - 90 * 3600000
+_MAX_LONGITUDE = 0x80000000 + 180 * 3600000
+_MIN_LONGITUDE = 0x80000000 - 180 * 3600000
+
+
+def _exponent_of(what, desc):
+    if what == 0:
+        return 0
+    exp = None
+    for i, pow in enumerate(_pows):
+        if what < pow:
+            exp = i - 1
+            break
+    if exp is None or exp < 0:
+        raise dns.exception.SyntaxError(f"{desc} value out of bounds")
+    return exp
+
+
+def _float_to_tuple(what):
+    if what < 0:
+        sign = -1
+        what *= -1
+    else:
+        sign = 1
+    what = round(what * 3600000)
+    degrees = int(what // 3600000)
+    what -= degrees * 3600000
+    minutes = int(what // 60000)
+    what -= minutes * 60000
+    seconds = int(what // 1000)
+    what -= int(seconds * 1000)
+    what = int(what)
+    return (degrees, minutes, seconds, what, sign)
+
+
+def _tuple_to_float(what):
+    value = float(what[0])
+    value += float(what[1]) / 60.0
+    value += float(what[2]) / 3600.0
+    value += float(what[3]) / 3600000.0
+    return float(what[4]) * value
+
+
+def _encode_size(what, desc):
+    what = int(what)
+    exponent = _exponent_of(what, desc) & 0xF
+    base = what // pow(10, exponent) & 0xF
+    return base * 16 + exponent
+
+
+def _decode_size(what, desc):
+    exponent = what & 0x0F
+    if exponent > 9:
+        raise dns.exception.FormError(f"bad {desc} exponent")
+    base = (what & 0xF0) >> 4
+    if base > 9:
+        raise dns.exception.FormError(f"bad {desc} base")
+    return base * pow(10, exponent)
+
+
+def _check_coordinate_list(value, low, high):
+    if value[0] < low or value[0] > high:
+        raise ValueError(f"not in range [{low}, {high}]")
+    if value[1] < 0 or value[1] > 59:
+        raise ValueError("bad minutes value")
+    if value[2] < 0 or value[2] > 59:
+        raise ValueError("bad seconds value")
+    if value[3] < 0 or value[3] > 999:
+        raise ValueError("bad milliseconds value")
+    if value[4] != 1 and value[4] != -1:
+        raise ValueError("bad hemisphere value")
+
+
+@dns.immutable.immutable
+class LOC(dns.rdata.Rdata):
+    """LOC record"""
+
+    # see: RFC 1876
+
+    __slots__ = [
+        "latitude",
+        "longitude",
+        "altitude",
+        "size",
+        "horizontal_precision",
+        "vertical_precision",
+    ]
+
+    def __init__(
+        self,
+        rdclass,
+        rdtype,
+        latitude,
+        longitude,
+        altitude,
+        size=_default_size,
+        hprec=_default_hprec,
+        vprec=_default_vprec,
+    ):
+        """Initialize a LOC record instance.
+
+        The parameters I{latitude} and I{longitude} may be either a 4-tuple
+        of integers specifying (degrees, minutes, seconds, milliseconds),
+        or they may be floating point values specifying the number of
+        degrees. The other parameters are floats. Size, horizontal precision,
+        and vertical precision are specified in centimeters."""
+
+        super().__init__(rdclass, rdtype)
+        if isinstance(latitude, int):
+            latitude = float(latitude)
+        if isinstance(latitude, float):
+            latitude = _float_to_tuple(latitude)
+        _check_coordinate_list(latitude, -90, 90)
+        self.latitude = tuple(latitude)
+        if isinstance(longitude, int):
+            longitude = float(longitude)
+        if isinstance(longitude, float):
+            longitude = _float_to_tuple(longitude)
+        _check_coordinate_list(longitude, -180, 180)
+        self.longitude = tuple(longitude)
+        self.altitude = float(altitude)
+        self.size = float(size)
+        self.horizontal_precision = float(hprec)
+        self.vertical_precision = float(vprec)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        if self.latitude[4] > 0:
+            lat_hemisphere = "N"
+        else:
+            lat_hemisphere = "S"
+        if self.longitude[4] > 0:
+            long_hemisphere = "E"
+        else:
+            long_hemisphere = "W"
+        text = "%d %d %d.%03d %s %d %d %d.%03d %s %0.2fm" % (
+            self.latitude[0],
+            self.latitude[1],
+            self.latitude[2],
+            self.latitude[3],
+            lat_hemisphere,
+            self.longitude[0],
+            self.longitude[1],
+            self.longitude[2],
+            self.longitude[3],
+            long_hemisphere,
+            self.altitude / 100.0,
+        )
+
+        # do not print default values
+        if (
+            self.size != _default_size
+            or self.horizontal_precision != _default_hprec
+            or self.vertical_precision != _default_vprec
+        ):
+            text += (
+                f" {self.size / 100.0:0.2f}m {self.horizontal_precision / 100.0:0.2f}m"
+                f" {self.vertical_precision / 100.0:0.2f}m"
+            )
+        return text
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        latitude = [0, 0, 0, 0, 1]
+        longitude = [0, 0, 0, 0, 1]
+        size = _default_size
+        hprec = _default_hprec
+        vprec = _default_vprec
+
+        latitude[0] = tok.get_int()
+        t = tok.get_string()
+        if t.isdigit():
+            latitude[1] = int(t)
+            t = tok.get_string()
+            if "." in t:
+                (seconds, milliseconds) = t.split(".")
+                if not seconds.isdigit():
+                    raise dns.exception.SyntaxError("bad latitude seconds value")
+                latitude[2] = int(seconds)
+                l = len(milliseconds)
+                if l == 0 or l > 3 or not milliseconds.isdigit():
+                    raise dns.exception.SyntaxError("bad latitude milliseconds value")
+                if l == 1:
+                    m = 100
+                elif l == 2:
+                    m = 10
+                else:
+                    m = 1
+                latitude[3] = m * int(milliseconds)
+                t = tok.get_string()
+            elif t.isdigit():
+                latitude[2] = int(t)
+                t = tok.get_string()
+        if t == "S":
+            latitude[4] = -1
+        elif t != "N":
+            raise dns.exception.SyntaxError("bad latitude hemisphere value")
+
+        longitude[0] = tok.get_int()
+        t = tok.get_string()
+        if t.isdigit():
+            longitude[1] = int(t)
+            t = tok.get_string()
+            if "." in t:
+                (seconds, milliseconds) = t.split(".")
+                if not seconds.isdigit():
+                    raise dns.exception.SyntaxError("bad longitude seconds value")
+                longitude[2] = int(seconds)
+                l = len(milliseconds)
+                if l == 0 or l > 3 or not milliseconds.isdigit():
+                    raise dns.exception.SyntaxError("bad longitude milliseconds value")
+                if l == 1:
+                    m = 100
+                elif l == 2:
+                    m = 10
+                else:
+                    m = 1
+                longitude[3] = m * int(milliseconds)
+                t = tok.get_string()
+            elif t.isdigit():
+                longitude[2] = int(t)
+                t = tok.get_string()
+        if t == "W":
+            longitude[4] = -1
+        elif t != "E":
+            raise dns.exception.SyntaxError("bad longitude hemisphere value")
+
+        t = tok.get_string()
+        if t[-1] == "m":
+            t = t[0:-1]
+        altitude = float(t) * 100.0  # m -> cm
+
+        tokens = tok.get_remaining(max_tokens=3)
+        if len(tokens) >= 1:
+            value = tokens[0].unescape().value
+            if value[-1] == "m":
+                value = value[0:-1]
+            size = float(value) * 100.0  # m -> cm
+            if len(tokens) >= 2:
+                value = tokens[1].unescape().value
+                if value[-1] == "m":
+                    value = value[0:-1]
+                hprec = float(value) * 100.0  # m -> cm
+                if len(tokens) >= 3:
+                    value = tokens[2].unescape().value
+                    if value[-1] == "m":
+                        value = value[0:-1]
+                    vprec = float(value) * 100.0  # m -> cm
+
+        # Try encoding these now so we raise if they are bad
+        _encode_size(size, "size")
+        _encode_size(hprec, "horizontal precision")
+        _encode_size(vprec, "vertical precision")
+
+        return cls(rdclass, rdtype, latitude, longitude, altitude, size, hprec, vprec)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        milliseconds = (
+            self.latitude[0] * 3600000
+            + self.latitude[1] * 60000
+            + self.latitude[2] * 1000
+            + self.latitude[3]
+        ) * self.latitude[4]
+        latitude = 0x80000000 + milliseconds
+        milliseconds = (
+            self.longitude[0] * 3600000
+            + self.longitude[1] * 60000
+            + self.longitude[2] * 1000
+            + self.longitude[3]
+        ) * self.longitude[4]
+        longitude = 0x80000000 + milliseconds
+        altitude = int(self.altitude) + 10000000
+        size = _encode_size(self.size, "size")
+        hprec = _encode_size(self.horizontal_precision, "horizontal precision")
+        vprec = _encode_size(self.vertical_precision, "vertical precision")
+        wire = struct.pack(
+            "!BBBBIII", 0, size, hprec, vprec, latitude, longitude, altitude
+        )
+        file.write(wire)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        (
+            version,
+            size,
+            hprec,
+            vprec,
+            latitude,
+            longitude,
+            altitude,
+        ) = parser.get_struct("!BBBBIII")
+        if version != 0:
+            raise dns.exception.FormError("LOC version not zero")
+        if latitude < _MIN_LATITUDE or latitude > _MAX_LATITUDE:
+            raise dns.exception.FormError("bad latitude")
+        if latitude > 0x80000000:
+            latitude = (latitude - 0x80000000) / 3600000
+        else:
+            latitude = -1 * (0x80000000 - latitude) / 3600000
+        if longitude < _MIN_LONGITUDE or longitude > _MAX_LONGITUDE:
+            raise dns.exception.FormError("bad longitude")
+        if longitude > 0x80000000:
+            longitude = (longitude - 0x80000000) / 3600000
+        else:
+            longitude = -1 * (0x80000000 - longitude) / 3600000
+        altitude = float(altitude) - 10000000.0
+        size = _decode_size(size, "size")
+        hprec = _decode_size(hprec, "horizontal precision")
+        vprec = _decode_size(vprec, "vertical precision")
+        return cls(rdclass, rdtype, latitude, longitude, altitude, size, hprec, vprec)
+
+    @property
+    def float_latitude(self):
+        "latitude as a floating point value"
+        return _tuple_to_float(self.latitude)
+
+    @property
+    def float_longitude(self):
+        "longitude as a floating point value"
+        return _tuple_to_float(self.longitude)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/LP.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/LP.py
new file mode 100644
index 00000000..312663f1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/LP.py
@@ -0,0 +1,42 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+import struct
+
+import dns.immutable
+import dns.rdata
+
+
+@dns.immutable.immutable
+class LP(dns.rdata.Rdata):
+    """LP record"""
+
+    # see: rfc6742.txt
+
+    __slots__ = ["preference", "fqdn"]
+
+    def __init__(self, rdclass, rdtype, preference, fqdn):
+        super().__init__(rdclass, rdtype)
+        self.preference = self._as_uint16(preference)
+        self.fqdn = self._as_name(fqdn)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        fqdn = self.fqdn.choose_relativity(origin, relativize)
+        return "%d %s" % (self.preference, fqdn)
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        preference = tok.get_uint16()
+        fqdn = tok.get_name(origin, relativize, relativize_to)
+        return cls(rdclass, rdtype, preference, fqdn)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        file.write(struct.pack("!H", self.preference))
+        self.fqdn.to_wire(file, compress, origin, canonicalize)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        preference = parser.get_uint16()
+        fqdn = parser.get_name(origin)
+        return cls(rdclass, rdtype, preference, fqdn)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/MX.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/MX.py
new file mode 100644
index 00000000..0c300c5a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/MX.py
@@ -0,0 +1,24 @@
+# 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 dns.immutable
+import dns.rdtypes.mxbase
+
+
+@dns.immutable.immutable
+class MX(dns.rdtypes.mxbase.MXBase):
+    """MX record"""
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NID.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NID.py
new file mode 100644
index 00000000..2f649178
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NID.py
@@ -0,0 +1,47 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+import struct
+
+import dns.immutable
+import dns.rdtypes.util
+
+
+@dns.immutable.immutable
+class NID(dns.rdata.Rdata):
+    """NID record"""
+
+    # see: rfc6742.txt
+
+    __slots__ = ["preference", "nodeid"]
+
+    def __init__(self, rdclass, rdtype, preference, nodeid):
+        super().__init__(rdclass, rdtype)
+        self.preference = self._as_uint16(preference)
+        if isinstance(nodeid, bytes):
+            if len(nodeid) != 8:
+                raise ValueError("invalid nodeid")
+            self.nodeid = dns.rdata._hexify(nodeid, 4, b":")
+        else:
+            dns.rdtypes.util.parse_formatted_hex(nodeid, 4, 4, ":")
+            self.nodeid = nodeid
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return f"{self.preference} {self.nodeid}"
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        preference = tok.get_uint16()
+        nodeid = tok.get_identifier()
+        return cls(rdclass, rdtype, preference, nodeid)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        file.write(struct.pack("!H", self.preference))
+        file.write(dns.rdtypes.util.parse_formatted_hex(self.nodeid, 4, 4, ":"))
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        preference = parser.get_uint16()
+        nodeid = parser.get_remaining()
+        return cls(rdclass, rdtype, preference, nodeid)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NINFO.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NINFO.py
new file mode 100644
index 00000000..b177bddb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NINFO.py
@@ -0,0 +1,26 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2006, 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 dns.immutable
+import dns.rdtypes.txtbase
+
+
+@dns.immutable.immutable
+class NINFO(dns.rdtypes.txtbase.TXTBase):
+    """NINFO record"""
+
+    # see: draft-reid-dnsext-zs-01
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NS.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NS.py
new file mode 100644
index 00000000..c3f34ce9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NS.py
@@ -0,0 +1,24 @@
+# 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 dns.immutable
+import dns.rdtypes.nsbase
+
+
+@dns.immutable.immutable
+class NS(dns.rdtypes.nsbase.NSBase):
+    """NS record"""
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NSEC.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NSEC.py
new file mode 100644
index 00000000..3c78b722
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NSEC.py
@@ -0,0 +1,67 @@
+# 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 dns.exception
+import dns.immutable
+import dns.name
+import dns.rdata
+import dns.rdatatype
+import dns.rdtypes.util
+
+
+@dns.immutable.immutable
+class Bitmap(dns.rdtypes.util.Bitmap):
+    type_name = "NSEC"
+
+
+@dns.immutable.immutable
+class NSEC(dns.rdata.Rdata):
+    """NSEC record"""
+
+    __slots__ = ["next", "windows"]
+
+    def __init__(self, rdclass, rdtype, next, windows):
+        super().__init__(rdclass, rdtype)
+        self.next = self._as_name(next)
+        if not isinstance(windows, Bitmap):
+            windows = Bitmap(windows)
+        self.windows = tuple(windows.windows)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        next = self.next.choose_relativity(origin, relativize)
+        text = Bitmap(self.windows).to_text()
+        return f"{next}{text}"
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        next = tok.get_name(origin, relativize, relativize_to)
+        windows = Bitmap.from_text(tok)
+        return cls(rdclass, rdtype, next, windows)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        # Note that NSEC downcasing, originally mandated by RFC 4034
+        # section 6.2 was removed by RFC 6840 section 5.1.
+        self.next.to_wire(file, None, origin, False)
+        Bitmap(self.windows).to_wire(file)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        next = parser.get_name(origin)
+        bitmap = Bitmap.from_wire_parser(parser)
+        return cls(rdclass, rdtype, next, bitmap)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NSEC3.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NSEC3.py
new file mode 100644
index 00000000..d71302b7
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NSEC3.py
@@ -0,0 +1,126 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2004-2017 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 binascii
+import struct
+
+import dns.exception
+import dns.immutable
+import dns.rdata
+import dns.rdatatype
+import dns.rdtypes.util
+
+b32_hex_to_normal = bytes.maketrans(
+    b"0123456789ABCDEFGHIJKLMNOPQRSTUV", b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
+)
+b32_normal_to_hex = bytes.maketrans(
+    b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", b"0123456789ABCDEFGHIJKLMNOPQRSTUV"
+)
+
+# hash algorithm constants
+SHA1 = 1
+
+# flag constants
+OPTOUT = 1
+
+
+@dns.immutable.immutable
+class Bitmap(dns.rdtypes.util.Bitmap):
+    type_name = "NSEC3"
+
+
+@dns.immutable.immutable
+class NSEC3(dns.rdata.Rdata):
+    """NSEC3 record"""
+
+    __slots__ = ["algorithm", "flags", "iterations", "salt", "next", "windows"]
+
+    def __init__(
+        self, rdclass, rdtype, algorithm, flags, iterations, salt, next, windows
+    ):
+        super().__init__(rdclass, rdtype)
+        self.algorithm = self._as_uint8(algorithm)
+        self.flags = self._as_uint8(flags)
+        self.iterations = self._as_uint16(iterations)
+        self.salt = self._as_bytes(salt, True, 255)
+        self.next = self._as_bytes(next, True, 255)
+        if not isinstance(windows, Bitmap):
+            windows = Bitmap(windows)
+        self.windows = tuple(windows.windows)
+
+    def _next_text(self):
+        next = base64.b32encode(self.next).translate(b32_normal_to_hex).lower().decode()
+        next = next.rstrip("=")
+        return next
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        next = self._next_text()
+        if self.salt == b"":
+            salt = "-"
+        else:
+            salt = binascii.hexlify(self.salt).decode()
+        text = Bitmap(self.windows).to_text()
+        return "%u %u %u %s %s%s" % (
+            self.algorithm,
+            self.flags,
+            self.iterations,
+            salt,
+            next,
+            text,
+        )
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        algorithm = tok.get_uint8()
+        flags = tok.get_uint8()
+        iterations = tok.get_uint16()
+        salt = tok.get_string()
+        if salt == "-":
+            salt = b""
+        else:
+            salt = binascii.unhexlify(salt.encode("ascii"))
+        next = tok.get_string().encode("ascii").upper().translate(b32_hex_to_normal)
+        if next.endswith(b"="):
+            raise binascii.Error("Incorrect padding")
+        if len(next) % 8 != 0:
+            next += b"=" * (8 - len(next) % 8)
+        next = base64.b32decode(next)
+        bitmap = Bitmap.from_text(tok)
+        return cls(rdclass, rdtype, algorithm, flags, iterations, salt, next, bitmap)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        l = len(self.salt)
+        file.write(struct.pack("!BBHB", self.algorithm, self.flags, self.iterations, l))
+        file.write(self.salt)
+        l = len(self.next)
+        file.write(struct.pack("!B", l))
+        file.write(self.next)
+        Bitmap(self.windows).to_wire(file)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        (algorithm, flags, iterations) = parser.get_struct("!BBH")
+        salt = parser.get_counted_bytes()
+        next = parser.get_counted_bytes()
+        bitmap = Bitmap.from_wire_parser(parser)
+        return cls(rdclass, rdtype, algorithm, flags, iterations, salt, next, bitmap)
+
+    def next_name(self, origin=None):
+        return dns.name.from_text(self._next_text(), origin)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NSEC3PARAM.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NSEC3PARAM.py
new file mode 100644
index 00000000..d1e62ebc
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NSEC3PARAM.py
@@ -0,0 +1,69 @@
+# 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 binascii
+import struct
+
+import dns.exception
+import dns.immutable
+import dns.rdata
+
+
+@dns.immutable.immutable
+class NSEC3PARAM(dns.rdata.Rdata):
+    """NSEC3PARAM record"""
+
+    __slots__ = ["algorithm", "flags", "iterations", "salt"]
+
+    def __init__(self, rdclass, rdtype, algorithm, flags, iterations, salt):
+        super().__init__(rdclass, rdtype)
+        self.algorithm = self._as_uint8(algorithm)
+        self.flags = self._as_uint8(flags)
+        self.iterations = self._as_uint16(iterations)
+        self.salt = self._as_bytes(salt, True, 255)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        if self.salt == b"":
+            salt = "-"
+        else:
+            salt = binascii.hexlify(self.salt).decode()
+        return "%u %u %u %s" % (self.algorithm, self.flags, self.iterations, salt)
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        algorithm = tok.get_uint8()
+        flags = tok.get_uint8()
+        iterations = tok.get_uint16()
+        salt = tok.get_string()
+        if salt == "-":
+            salt = ""
+        else:
+            salt = binascii.unhexlify(salt.encode())
+        return cls(rdclass, rdtype, algorithm, flags, iterations, salt)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        l = len(self.salt)
+        file.write(struct.pack("!BBHB", self.algorithm, self.flags, self.iterations, l))
+        file.write(self.salt)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        (algorithm, flags, iterations) = parser.get_struct("!BBH")
+        salt = parser.get_counted_bytes()
+        return cls(rdclass, rdtype, algorithm, flags, iterations, salt)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/OPENPGPKEY.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/OPENPGPKEY.py
new file mode 100644
index 00000000..4d7a4b6c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/OPENPGPKEY.py
@@ -0,0 +1,53 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2016 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 dns.exception
+import dns.immutable
+import dns.rdata
+import dns.tokenizer
+
+
+@dns.immutable.immutable
+class OPENPGPKEY(dns.rdata.Rdata):
+    """OPENPGPKEY record"""
+
+    # see: RFC 7929
+
+    def __init__(self, rdclass, rdtype, key):
+        super().__init__(rdclass, rdtype)
+        self.key = self._as_bytes(key)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return dns.rdata._base64ify(self.key, chunksize=None, **kw)
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        b64 = tok.concatenate_remaining_identifiers().encode()
+        key = base64.b64decode(b64)
+        return cls(rdclass, rdtype, key)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        file.write(self.key)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        key = parser.get_remaining()
+        return cls(rdclass, rdtype, key)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/OPT.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/OPT.py
new file mode 100644
index 00000000..d343dfa5
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/OPT.py
@@ -0,0 +1,77 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2001-2017 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 struct
+
+import dns.edns
+import dns.exception
+import dns.immutable
+import dns.rdata
+
+# We don't implement from_text, and that's ok.
+# pylint: disable=abstract-method
+
+
+@dns.immutable.immutable
+class OPT(dns.rdata.Rdata):
+    """OPT record"""
+
+    __slots__ = ["options"]
+
+    def __init__(self, rdclass, rdtype, options):
+        """Initialize an OPT rdata.
+
+        *rdclass*, an ``int`` is the rdataclass of the Rdata,
+        which is also the payload size.
+
+        *rdtype*, an ``int`` is the rdatatype of the Rdata.
+
+        *options*, a tuple of ``bytes``
+        """
+
+        super().__init__(rdclass, rdtype)
+
+        def as_option(option):
+            if not isinstance(option, dns.edns.Option):
+                raise ValueError("option is not a dns.edns.option")
+            return option
+
+        self.options = self._as_tuple(options, as_option)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        for opt in self.options:
+            owire = opt.to_wire()
+            file.write(struct.pack("!HH", opt.otype, len(owire)))
+            file.write(owire)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return " ".join(opt.to_text() for opt in self.options)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        options = []
+        while parser.remaining() > 0:
+            (otype, olen) = parser.get_struct("!HH")
+            with parser.restrict_to(olen):
+                opt = dns.edns.option_from_wire_parser(otype, parser)
+            options.append(opt)
+        return cls(rdclass, rdtype, options)
+
+    @property
+    def payload(self):
+        "payload size"
+        return self.rdclass
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/PTR.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/PTR.py
new file mode 100644
index 00000000..98c36167
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/PTR.py
@@ -0,0 +1,24 @@
+# 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 dns.immutable
+import dns.rdtypes.nsbase
+
+
+@dns.immutable.immutable
+class PTR(dns.rdtypes.nsbase.NSBase):
+    """PTR record"""
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RESINFO.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RESINFO.py
new file mode 100644
index 00000000..76c8ea2a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RESINFO.py
@@ -0,0 +1,24 @@
+# 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 dns.immutable
+import dns.rdtypes.txtbase
+
+
+@dns.immutable.immutable
+class RESINFO(dns.rdtypes.txtbase.TXTBase):
+    """RESINFO record"""
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RP.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RP.py
new file mode 100644
index 00000000..a66cfc50
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RP.py
@@ -0,0 +1,58 @@
+# 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 dns.exception
+import dns.immutable
+import dns.name
+import dns.rdata
+
+
+@dns.immutable.immutable
+class RP(dns.rdata.Rdata):
+    """RP record"""
+
+    # see: RFC 1183
+
+    __slots__ = ["mbox", "txt"]
+
+    def __init__(self, rdclass, rdtype, mbox, txt):
+        super().__init__(rdclass, rdtype)
+        self.mbox = self._as_name(mbox)
+        self.txt = self._as_name(txt)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        mbox = self.mbox.choose_relativity(origin, relativize)
+        txt = self.txt.choose_relativity(origin, relativize)
+        return f"{str(mbox)} {str(txt)}"
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        mbox = tok.get_name(origin, relativize, relativize_to)
+        txt = tok.get_name(origin, relativize, relativize_to)
+        return cls(rdclass, rdtype, mbox, txt)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        self.mbox.to_wire(file, None, origin, canonicalize)
+        self.txt.to_wire(file, None, origin, canonicalize)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        mbox = parser.get_name(origin)
+        txt = parser.get_name(origin)
+        return cls(rdclass, rdtype, mbox, txt)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RRSIG.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RRSIG.py
new file mode 100644
index 00000000..8beb4237
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RRSIG.py
@@ -0,0 +1,157 @@
+# 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 calendar
+import struct
+import time
+
+import dns.dnssectypes
+import dns.exception
+import dns.immutable
+import dns.rdata
+import dns.rdatatype
+
+
+class BadSigTime(dns.exception.DNSException):
+    """Time in DNS SIG or RRSIG resource record cannot be parsed."""
+
+
+def sigtime_to_posixtime(what):
+    if len(what) <= 10 and what.isdigit():
+        return int(what)
+    if len(what) != 14:
+        raise BadSigTime
+    year = int(what[0:4])
+    month = int(what[4:6])
+    day = int(what[6:8])
+    hour = int(what[8:10])
+    minute = int(what[10:12])
+    second = int(what[12:14])
+    return calendar.timegm((year, month, day, hour, minute, second, 0, 0, 0))
+
+
+def posixtime_to_sigtime(what):
+    return time.strftime("%Y%m%d%H%M%S", time.gmtime(what))
+
+
+@dns.immutable.immutable
+class RRSIG(dns.rdata.Rdata):
+    """RRSIG record"""
+
+    __slots__ = [
+        "type_covered",
+        "algorithm",
+        "labels",
+        "original_ttl",
+        "expiration",
+        "inception",
+        "key_tag",
+        "signer",
+        "signature",
+    ]
+
+    def __init__(
+        self,
+        rdclass,
+        rdtype,
+        type_covered,
+        algorithm,
+        labels,
+        original_ttl,
+        expiration,
+        inception,
+        key_tag,
+        signer,
+        signature,
+    ):
+        super().__init__(rdclass, rdtype)
+        self.type_covered = self._as_rdatatype(type_covered)
+        self.algorithm = dns.dnssectypes.Algorithm.make(algorithm)
+        self.labels = self._as_uint8(labels)
+        self.original_ttl = self._as_ttl(original_ttl)
+        self.expiration = self._as_uint32(expiration)
+        self.inception = self._as_uint32(inception)
+        self.key_tag = self._as_uint16(key_tag)
+        self.signer = self._as_name(signer)
+        self.signature = self._as_bytes(signature)
+
+    def covers(self):
+        return self.type_covered
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return "%s %d %d %d %s %s %d %s %s" % (
+            dns.rdatatype.to_text(self.type_covered),
+            self.algorithm,
+            self.labels,
+            self.original_ttl,
+            posixtime_to_sigtime(self.expiration),
+            posixtime_to_sigtime(self.inception),
+            self.key_tag,
+            self.signer.choose_relativity(origin, relativize),
+            dns.rdata._base64ify(self.signature, **kw),
+        )
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        type_covered = dns.rdatatype.from_text(tok.get_string())
+        algorithm = dns.dnssectypes.Algorithm.from_text(tok.get_string())
+        labels = tok.get_int()
+        original_ttl = tok.get_ttl()
+        expiration = sigtime_to_posixtime(tok.get_string())
+        inception = sigtime_to_posixtime(tok.get_string())
+        key_tag = tok.get_int()
+        signer = tok.get_name(origin, relativize, relativize_to)
+        b64 = tok.concatenate_remaining_identifiers().encode()
+        signature = base64.b64decode(b64)
+        return cls(
+            rdclass,
+            rdtype,
+            type_covered,
+            algorithm,
+            labels,
+            original_ttl,
+            expiration,
+            inception,
+            key_tag,
+            signer,
+            signature,
+        )
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        header = struct.pack(
+            "!HBBIIIH",
+            self.type_covered,
+            self.algorithm,
+            self.labels,
+            self.original_ttl,
+            self.expiration,
+            self.inception,
+            self.key_tag,
+        )
+        file.write(header)
+        self.signer.to_wire(file, None, origin, canonicalize)
+        file.write(self.signature)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        header = parser.get_struct("!HBBIIIH")
+        signer = parser.get_name(origin)
+        signature = parser.get_remaining()
+        return cls(rdclass, rdtype, *header, signer, signature)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RT.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RT.py
new file mode 100644
index 00000000..5a4d45cf
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RT.py
@@ -0,0 +1,24 @@
+# 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 dns.immutable
+import dns.rdtypes.mxbase
+
+
+@dns.immutable.immutable
+class RT(dns.rdtypes.mxbase.UncompressedDowncasingMX):
+    """RT record"""
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SMIMEA.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SMIMEA.py
new file mode 100644
index 00000000..55d87bf8
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SMIMEA.py
@@ -0,0 +1,9 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+import dns.immutable
+import dns.rdtypes.tlsabase
+
+
+@dns.immutable.immutable
+class SMIMEA(dns.rdtypes.tlsabase.TLSABase):
+    """SMIMEA record"""
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SOA.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SOA.py
new file mode 100644
index 00000000..09aa8321
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SOA.py
@@ -0,0 +1,86 @@
+# 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 struct
+
+import dns.exception
+import dns.immutable
+import dns.name
+import dns.rdata
+
+
+@dns.immutable.immutable
+class SOA(dns.rdata.Rdata):
+    """SOA record"""
+
+    # see: RFC 1035
+
+    __slots__ = ["mname", "rname", "serial", "refresh", "retry", "expire", "minimum"]
+
+    def __init__(
+        self, rdclass, rdtype, mname, rname, serial, refresh, retry, expire, minimum
+    ):
+        super().__init__(rdclass, rdtype)
+        self.mname = self._as_name(mname)
+        self.rname = self._as_name(rname)
+        self.serial = self._as_uint32(serial)
+        self.refresh = self._as_ttl(refresh)
+        self.retry = self._as_ttl(retry)
+        self.expire = self._as_ttl(expire)
+        self.minimum = self._as_ttl(minimum)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        mname = self.mname.choose_relativity(origin, relativize)
+        rname = self.rname.choose_relativity(origin, relativize)
+        return "%s %s %d %d %d %d %d" % (
+            mname,
+            rname,
+            self.serial,
+            self.refresh,
+            self.retry,
+            self.expire,
+            self.minimum,
+        )
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        mname = tok.get_name(origin, relativize, relativize_to)
+        rname = tok.get_name(origin, relativize, relativize_to)
+        serial = tok.get_uint32()
+        refresh = tok.get_ttl()
+        retry = tok.get_ttl()
+        expire = tok.get_ttl()
+        minimum = tok.get_ttl()
+        return cls(
+            rdclass, rdtype, mname, rname, serial, refresh, retry, expire, minimum
+        )
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        self.mname.to_wire(file, compress, origin, canonicalize)
+        self.rname.to_wire(file, compress, origin, canonicalize)
+        five_ints = struct.pack(
+            "!IIIII", self.serial, self.refresh, self.retry, self.expire, self.minimum
+        )
+        file.write(five_ints)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        mname = parser.get_name(origin)
+        rname = parser.get_name(origin)
+        return cls(rdclass, rdtype, mname, rname, *parser.get_struct("!IIIII"))
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SPF.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SPF.py
new file mode 100644
index 00000000..1df3b705
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SPF.py
@@ -0,0 +1,26 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2006, 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 dns.immutable
+import dns.rdtypes.txtbase
+
+
+@dns.immutable.immutable
+class SPF(dns.rdtypes.txtbase.TXTBase):
+    """SPF record"""
+
+    # see: RFC 4408
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SSHFP.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SSHFP.py
new file mode 100644
index 00000000..d2c4b073
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SSHFP.py
@@ -0,0 +1,68 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2005-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 binascii
+import struct
+
+import dns.immutable
+import dns.rdata
+import dns.rdatatype
+
+
+@dns.immutable.immutable
+class SSHFP(dns.rdata.Rdata):
+    """SSHFP record"""
+
+    # See RFC 4255
+
+    __slots__ = ["algorithm", "fp_type", "fingerprint"]
+
+    def __init__(self, rdclass, rdtype, algorithm, fp_type, fingerprint):
+        super().__init__(rdclass, rdtype)
+        self.algorithm = self._as_uint8(algorithm)
+        self.fp_type = self._as_uint8(fp_type)
+        self.fingerprint = self._as_bytes(fingerprint, True)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        kw = kw.copy()
+        chunksize = kw.pop("chunksize", 128)
+        return "%d %d %s" % (
+            self.algorithm,
+            self.fp_type,
+            dns.rdata._hexify(self.fingerprint, chunksize=chunksize, **kw),
+        )
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        algorithm = tok.get_uint8()
+        fp_type = tok.get_uint8()
+        fingerprint = tok.concatenate_remaining_identifiers().encode()
+        fingerprint = binascii.unhexlify(fingerprint)
+        return cls(rdclass, rdtype, algorithm, fp_type, fingerprint)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        header = struct.pack("!BB", self.algorithm, self.fp_type)
+        file.write(header)
+        file.write(self.fingerprint)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        header = parser.get_struct("BB")
+        fingerprint = parser.get_remaining()
+        return cls(rdclass, rdtype, header[0], header[1], fingerprint)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TKEY.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TKEY.py
new file mode 100644
index 00000000..75f62249
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TKEY.py
@@ -0,0 +1,142 @@
+# 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.exception
+import dns.immutable
+import dns.rdata
+
+
+@dns.immutable.immutable
+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)
+        self.algorithm = self._as_name(algorithm)
+        self.inception = self._as_uint32(inception)
+        self.expiration = self._as_uint32(expiration)
+        self.mode = self._as_uint16(mode)
+        self.error = self._as_uint16(error)
+        self.key = self._as_bytes(key)
+        self.other = self._as_bytes(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) > 0:
+            text += f" {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(True).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) > 0:
+            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/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TLSA.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TLSA.py
new file mode 100644
index 00000000..4dffc553
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TLSA.py
@@ -0,0 +1,9 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+import dns.immutable
+import dns.rdtypes.tlsabase
+
+
+@dns.immutable.immutable
+class TLSA(dns.rdtypes.tlsabase.TLSABase):
+    """TLSA record"""
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TSIG.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TSIG.py
new file mode 100644
index 00000000..79423826
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TSIG.py
@@ -0,0 +1,160 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2001-2017 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.exception
+import dns.immutable
+import dns.rcode
+import dns.rdata
+
+
+@dns.immutable.immutable
+class TSIG(dns.rdata.Rdata):
+    """TSIG record"""
+
+    __slots__ = [
+        "algorithm",
+        "time_signed",
+        "fudge",
+        "mac",
+        "original_id",
+        "error",
+        "other",
+    ]
+
+    def __init__(
+        self,
+        rdclass,
+        rdtype,
+        algorithm,
+        time_signed,
+        fudge,
+        mac,
+        original_id,
+        error,
+        other,
+    ):
+        """Initialize a TSIG rdata.
+
+        *rdclass*, an ``int`` is the rdataclass of the Rdata.
+
+        *rdtype*, an ``int`` is the rdatatype of the Rdata.
+
+        *algorithm*, a ``dns.name.Name``.
+
+        *time_signed*, an ``int``.
+
+        *fudge*, an ``int`.
+
+        *mac*, a ``bytes``
+
+        *original_id*, an ``int``
+
+        *error*, an ``int``
+
+        *other*, a ``bytes``
+        """
+
+        super().__init__(rdclass, rdtype)
+        self.algorithm = self._as_name(algorithm)
+        self.time_signed = self._as_uint48(time_signed)
+        self.fudge = self._as_uint16(fudge)
+        self.mac = self._as_bytes(mac)
+        self.original_id = self._as_uint16(original_id)
+        self.error = dns.rcode.Rcode.make(error)
+        self.other = self._as_bytes(other)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        algorithm = self.algorithm.choose_relativity(origin, relativize)
+        error = dns.rcode.to_text(self.error, True)
+        text = (
+            f"{algorithm} {self.time_signed} {self.fudge} "
+            + f"{len(self.mac)} {dns.rdata._base64ify(self.mac, 0)} "
+            + f"{self.original_id} {error} {len(self.other)}"
+        )
+        if self.other:
+            text += f" {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)
+        time_signed = tok.get_uint48()
+        fudge = tok.get_uint16()
+        mac_len = tok.get_uint16()
+        mac = base64.b64decode(tok.get_string())
+        if len(mac) != mac_len:
+            raise SyntaxError("invalid MAC")
+        original_id = tok.get_uint16()
+        error = dns.rcode.from_text(tok.get_string())
+        other_len = tok.get_uint16()
+        if other_len > 0:
+            other = base64.b64decode(tok.get_string())
+            if len(other) != other_len:
+                raise SyntaxError("invalid other data")
+        else:
+            other = b""
+        return cls(
+            rdclass,
+            rdtype,
+            algorithm,
+            time_signed,
+            fudge,
+            mac,
+            original_id,
+            error,
+            other,
+        )
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        self.algorithm.to_wire(file, None, origin, False)
+        file.write(
+            struct.pack(
+                "!HIHH",
+                (self.time_signed >> 32) & 0xFFFF,
+                self.time_signed & 0xFFFFFFFF,
+                self.fudge,
+                len(self.mac),
+            )
+        )
+        file.write(self.mac)
+        file.write(struct.pack("!HHH", self.original_id, self.error, len(self.other)))
+        file.write(self.other)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        algorithm = parser.get_name()
+        time_signed = parser.get_uint48()
+        fudge = parser.get_uint16()
+        mac = parser.get_counted_bytes(2)
+        (original_id, error) = parser.get_struct("!HH")
+        other = parser.get_counted_bytes(2)
+        return cls(
+            rdclass,
+            rdtype,
+            algorithm,
+            time_signed,
+            fudge,
+            mac,
+            original_id,
+            error,
+            other,
+        )
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TXT.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TXT.py
new file mode 100644
index 00000000..6d4dae27
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TXT.py
@@ -0,0 +1,24 @@
+# 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 dns.immutable
+import dns.rdtypes.txtbase
+
+
+@dns.immutable.immutable
+class TXT(dns.rdtypes.txtbase.TXTBase):
+    """TXT record"""
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/URI.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/URI.py
new file mode 100644
index 00000000..2efbb305
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/URI.py
@@ -0,0 +1,79 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
+# Copyright (C) 2015 Red Hat, 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 struct
+
+import dns.exception
+import dns.immutable
+import dns.name
+import dns.rdata
+import dns.rdtypes.util
+
+
+@dns.immutable.immutable
+class URI(dns.rdata.Rdata):
+    """URI record"""
+
+    # see RFC 7553
+
+    __slots__ = ["priority", "weight", "target"]
+
+    def __init__(self, rdclass, rdtype, priority, weight, target):
+        super().__init__(rdclass, rdtype)
+        self.priority = self._as_uint16(priority)
+        self.weight = self._as_uint16(weight)
+        self.target = self._as_bytes(target, True)
+        if len(self.target) == 0:
+            raise dns.exception.SyntaxError("URI target cannot be empty")
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return '%d %d "%s"' % (self.priority, self.weight, self.target.decode())
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        priority = tok.get_uint16()
+        weight = tok.get_uint16()
+        target = tok.get().unescape()
+        if not (target.is_quoted_string() or target.is_identifier()):
+            raise dns.exception.SyntaxError("URI target must be a string")
+        return cls(rdclass, rdtype, priority, weight, target.value)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        two_ints = struct.pack("!HH", self.priority, self.weight)
+        file.write(two_ints)
+        file.write(self.target)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        (priority, weight) = parser.get_struct("!HH")
+        target = parser.get_remaining()
+        if len(target) == 0:
+            raise dns.exception.FormError("URI target may not be empty")
+        return cls(rdclass, rdtype, priority, weight, target)
+
+    def _processing_priority(self):
+        return self.priority
+
+    def _processing_weight(self):
+        return self.weight
+
+    @classmethod
+    def _processing_order(cls, iterable):
+        return dns.rdtypes.util.weighted_processing_order(iterable)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/WALLET.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/WALLET.py
new file mode 100644
index 00000000..ff464763
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/WALLET.py
@@ -0,0 +1,9 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+import dns.immutable
+import dns.rdtypes.txtbase
+
+
+@dns.immutable.immutable
+class WALLET(dns.rdtypes.txtbase.TXTBase):
+    """WALLET record"""
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/X25.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/X25.py
new file mode 100644
index 00000000..2436ddb6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/X25.py
@@ -0,0 +1,57 @@
+# 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 struct
+
+import dns.exception
+import dns.immutable
+import dns.rdata
+import dns.tokenizer
+
+
+@dns.immutable.immutable
+class X25(dns.rdata.Rdata):
+    """X25 record"""
+
+    # see RFC 1183
+
+    __slots__ = ["address"]
+
+    def __init__(self, rdclass, rdtype, address):
+        super().__init__(rdclass, rdtype)
+        self.address = self._as_bytes(address, True, 255)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return f'"{dns.rdata._escapify(self.address)}"'
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        address = tok.get_string()
+        return cls(rdclass, rdtype, address)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        l = len(self.address)
+        assert l < 256
+        file.write(struct.pack("!B", l))
+        file.write(self.address)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        address = parser.get_counted_bytes()
+        return cls(rdclass, rdtype, address)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/ZONEMD.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/ZONEMD.py
new file mode 100644
index 00000000..c90e3ee1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/ZONEMD.py
@@ -0,0 +1,66 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+import binascii
+import struct
+
+import dns.immutable
+import dns.rdata
+import dns.rdatatype
+import dns.zonetypes
+
+
+@dns.immutable.immutable
+class ZONEMD(dns.rdata.Rdata):
+    """ZONEMD record"""
+
+    # See RFC 8976
+
+    __slots__ = ["serial", "scheme", "hash_algorithm", "digest"]
+
+    def __init__(self, rdclass, rdtype, serial, scheme, hash_algorithm, digest):
+        super().__init__(rdclass, rdtype)
+        self.serial = self._as_uint32(serial)
+        self.scheme = dns.zonetypes.DigestScheme.make(scheme)
+        self.hash_algorithm = dns.zonetypes.DigestHashAlgorithm.make(hash_algorithm)
+        self.digest = self._as_bytes(digest)
+
+        if self.scheme == 0:  # reserved, RFC 8976 Sec. 5.2
+            raise ValueError("scheme 0 is reserved")
+        if self.hash_algorithm == 0:  # reserved, RFC 8976 Sec. 5.3
+            raise ValueError("hash_algorithm 0 is reserved")
+
+        hasher = dns.zonetypes._digest_hashers.get(self.hash_algorithm)
+        if hasher and hasher().digest_size != len(self.digest):
+            raise ValueError("digest length inconsistent with hash algorithm")
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        kw = kw.copy()
+        chunksize = kw.pop("chunksize", 128)
+        return "%d %d %d %s" % (
+            self.serial,
+            self.scheme,
+            self.hash_algorithm,
+            dns.rdata._hexify(self.digest, chunksize=chunksize, **kw),
+        )
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        serial = tok.get_uint32()
+        scheme = tok.get_uint8()
+        hash_algorithm = tok.get_uint8()
+        digest = tok.concatenate_remaining_identifiers().encode()
+        digest = binascii.unhexlify(digest)
+        return cls(rdclass, rdtype, serial, scheme, hash_algorithm, digest)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        header = struct.pack("!IBB", self.serial, self.scheme, self.hash_algorithm)
+        file.write(header)
+        file.write(self.digest)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        header = parser.get_struct("!IBB")
+        digest = parser.get_remaining()
+        return cls(rdclass, rdtype, header[0], header[1], header[2], digest)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__init__.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__init__.py
new file mode 100644
index 00000000..647b215b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__init__.py
@@ -0,0 +1,70 @@
+# 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.
+
+"""Class ANY (generic) rdata type classes."""
+
+__all__ = [
+    "AFSDB",
+    "AMTRELAY",
+    "AVC",
+    "CAA",
+    "CDNSKEY",
+    "CDS",
+    "CERT",
+    "CNAME",
+    "CSYNC",
+    "DLV",
+    "DNAME",
+    "DNSKEY",
+    "DS",
+    "EUI48",
+    "EUI64",
+    "GPOS",
+    "HINFO",
+    "HIP",
+    "ISDN",
+    "L32",
+    "L64",
+    "LOC",
+    "LP",
+    "MX",
+    "NID",
+    "NINFO",
+    "NS",
+    "NSEC",
+    "NSEC3",
+    "NSEC3PARAM",
+    "OPENPGPKEY",
+    "OPT",
+    "PTR",
+    "RESINFO",
+    "RP",
+    "RRSIG",
+    "RT",
+    "SMIMEA",
+    "SOA",
+    "SPF",
+    "SSHFP",
+    "TKEY",
+    "TLSA",
+    "TSIG",
+    "TXT",
+    "URI",
+    "WALLET",
+    "X25",
+    "ZONEMD",
+]
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/CH/A.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/CH/A.py
new file mode 100644
index 00000000..832e8d3a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/CH/A.py
@@ -0,0 +1,59 @@
+# 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 struct
+
+import dns.immutable
+import dns.rdtypes.mxbase
+
+
+@dns.immutable.immutable
+class A(dns.rdata.Rdata):
+    """A record for Chaosnet"""
+
+    # domain: the domain of the address
+    # address: the 16-bit address
+
+    __slots__ = ["domain", "address"]
+
+    def __init__(self, rdclass, rdtype, domain, address):
+        super().__init__(rdclass, rdtype)
+        self.domain = self._as_name(domain)
+        self.address = self._as_uint16(address)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        domain = self.domain.choose_relativity(origin, relativize)
+        return f"{domain} {self.address:o}"
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        domain = tok.get_name(origin, relativize, relativize_to)
+        address = tok.get_uint16(base=8)
+        return cls(rdclass, rdtype, domain, address)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        self.domain.to_wire(file, compress, origin, canonicalize)
+        pref = struct.pack("!H", self.address)
+        file.write(pref)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        domain = parser.get_name(origin)
+        address = parser.get_uint16()
+        return cls(rdclass, rdtype, domain, address)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/CH/__init__.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/CH/__init__.py
new file mode 100644
index 00000000..0760c26c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/CH/__init__.py
@@ -0,0 +1,22 @@
+# 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.
+
+"""Class CH rdata type classes."""
+
+__all__ = [
+    "A",
+]
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/A.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/A.py
new file mode 100644
index 00000000..e09d6110
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/A.py
@@ -0,0 +1,51 @@
+# 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 dns.exception
+import dns.immutable
+import dns.ipv4
+import dns.rdata
+import dns.tokenizer
+
+
+@dns.immutable.immutable
+class A(dns.rdata.Rdata):
+    """A record."""
+
+    __slots__ = ["address"]
+
+    def __init__(self, rdclass, rdtype, address):
+        super().__init__(rdclass, rdtype)
+        self.address = self._as_ipv4_address(address)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return self.address
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        address = tok.get_identifier()
+        return cls(rdclass, rdtype, address)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        file.write(dns.ipv4.inet_aton(self.address))
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        address = parser.get_remaining()
+        return cls(rdclass, rdtype, address)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/AAAA.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/AAAA.py
new file mode 100644
index 00000000..0cd139e7
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/AAAA.py
@@ -0,0 +1,51 @@
+# 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 dns.exception
+import dns.immutable
+import dns.ipv6
+import dns.rdata
+import dns.tokenizer
+
+
+@dns.immutable.immutable
+class AAAA(dns.rdata.Rdata):
+    """AAAA record."""
+
+    __slots__ = ["address"]
+
+    def __init__(self, rdclass, rdtype, address):
+        super().__init__(rdclass, rdtype)
+        self.address = self._as_ipv6_address(address)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return self.address
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        address = tok.get_identifier()
+        return cls(rdclass, rdtype, address)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        file.write(dns.ipv6.inet_aton(self.address))
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        address = parser.get_remaining()
+        return cls(rdclass, rdtype, address)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/APL.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/APL.py
new file mode 100644
index 00000000..44cb3fef
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/APL.py
@@ -0,0 +1,150 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2003-2017 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 binascii
+import codecs
+import struct
+
+import dns.exception
+import dns.immutable
+import dns.ipv4
+import dns.ipv6
+import dns.rdata
+import dns.tokenizer
+
+
+@dns.immutable.immutable
+class APLItem:
+    """An APL list item."""
+
+    __slots__ = ["family", "negation", "address", "prefix"]
+
+    def __init__(self, family, negation, address, prefix):
+        self.family = dns.rdata.Rdata._as_uint16(family)
+        self.negation = dns.rdata.Rdata._as_bool(negation)
+        if self.family == 1:
+            self.address = dns.rdata.Rdata._as_ipv4_address(address)
+            self.prefix = dns.rdata.Rdata._as_int(prefix, 0, 32)
+        elif self.family == 2:
+            self.address = dns.rdata.Rdata._as_ipv6_address(address)
+            self.prefix = dns.rdata.Rdata._as_int(prefix, 0, 128)
+        else:
+            self.address = dns.rdata.Rdata._as_bytes(address, max_length=127)
+            self.prefix = dns.rdata.Rdata._as_uint8(prefix)
+
+    def __str__(self):
+        if self.negation:
+            return "!%d:%s/%s" % (self.family, self.address, self.prefix)
+        else:
+            return "%d:%s/%s" % (self.family, self.address, self.prefix)
+
+    def to_wire(self, file):
+        if self.family == 1:
+            address = dns.ipv4.inet_aton(self.address)
+        elif self.family == 2:
+            address = dns.ipv6.inet_aton(self.address)
+        else:
+            address = binascii.unhexlify(self.address)
+        #
+        # Truncate least significant zero bytes.
+        #
+        last = 0
+        for i in range(len(address) - 1, -1, -1):
+            if address[i] != 0:
+                last = i + 1
+                break
+        address = address[0:last]
+        l = len(address)
+        assert l < 128
+        if self.negation:
+            l |= 0x80
+        header = struct.pack("!HBB", self.family, self.prefix, l)
+        file.write(header)
+        file.write(address)
+
+
+@dns.immutable.immutable
+class APL(dns.rdata.Rdata):
+    """APL record."""
+
+    # see: RFC 3123
+
+    __slots__ = ["items"]
+
+    def __init__(self, rdclass, rdtype, items):
+        super().__init__(rdclass, rdtype)
+        for item in items:
+            if not isinstance(item, APLItem):
+                raise ValueError("item not an APLItem")
+        self.items = tuple(items)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return " ".join(map(str, self.items))
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        items = []
+        for token in tok.get_remaining():
+            item = token.unescape().value
+            if item[0] == "!":
+                negation = True
+                item = item[1:]
+            else:
+                negation = False
+            (family, rest) = item.split(":", 1)
+            family = int(family)
+            (address, prefix) = rest.split("/", 1)
+            prefix = int(prefix)
+            item = APLItem(family, negation, address, prefix)
+            items.append(item)
+
+        return cls(rdclass, rdtype, items)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        for item in self.items:
+            item.to_wire(file)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        items = []
+        while parser.remaining() > 0:
+            header = parser.get_struct("!HBB")
+            afdlen = header[2]
+            if afdlen > 127:
+                negation = True
+                afdlen -= 128
+            else:
+                negation = False
+            address = parser.get_bytes(afdlen)
+            l = len(address)
+            if header[0] == 1:
+                if l < 4:
+                    address += b"\x00" * (4 - l)
+            elif header[0] == 2:
+                if l < 16:
+                    address += b"\x00" * (16 - l)
+            else:
+                #
+                # This isn't really right according to the RFC, but it
+                # seems better than throwing an exception
+                #
+                address = codecs.encode(address, "hex_codec")
+            item = APLItem(header[0], negation, address, header[1])
+            items.append(item)
+        return cls(rdclass, rdtype, items)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/DHCID.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/DHCID.py
new file mode 100644
index 00000000..723492fa
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/DHCID.py
@@ -0,0 +1,54 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2006, 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 dns.exception
+import dns.immutable
+import dns.rdata
+
+
+@dns.immutable.immutable
+class DHCID(dns.rdata.Rdata):
+    """DHCID record"""
+
+    # see: RFC 4701
+
+    __slots__ = ["data"]
+
+    def __init__(self, rdclass, rdtype, data):
+        super().__init__(rdclass, rdtype)
+        self.data = self._as_bytes(data)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return dns.rdata._base64ify(self.data, **kw)
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        b64 = tok.concatenate_remaining_identifiers().encode()
+        data = base64.b64decode(b64)
+        return cls(rdclass, rdtype, data)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        file.write(self.data)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        data = parser.get_remaining()
+        return cls(rdclass, rdtype, data)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/HTTPS.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/HTTPS.py
new file mode 100644
index 00000000..15464cbd
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/HTTPS.py
@@ -0,0 +1,9 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+import dns.immutable
+import dns.rdtypes.svcbbase
+
+
+@dns.immutable.immutable
+class HTTPS(dns.rdtypes.svcbbase.SVCBBase):
+    """HTTPS record"""
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/IPSECKEY.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/IPSECKEY.py
new file mode 100644
index 00000000..e3a66157
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/IPSECKEY.py
@@ -0,0 +1,91 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2006, 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.exception
+import dns.immutable
+import dns.rdtypes.util
+
+
+class Gateway(dns.rdtypes.util.Gateway):
+    name = "IPSECKEY gateway"
+
+
+@dns.immutable.immutable
+class IPSECKEY(dns.rdata.Rdata):
+    """IPSECKEY record"""
+
+    # see: RFC 4025
+
+    __slots__ = ["precedence", "gateway_type", "algorithm", "gateway", "key"]
+
+    def __init__(
+        self, rdclass, rdtype, precedence, gateway_type, algorithm, gateway, key
+    ):
+        super().__init__(rdclass, rdtype)
+        gateway = Gateway(gateway_type, gateway)
+        self.precedence = self._as_uint8(precedence)
+        self.gateway_type = gateway.type
+        self.algorithm = self._as_uint8(algorithm)
+        self.gateway = gateway.gateway
+        self.key = self._as_bytes(key)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        gateway = Gateway(self.gateway_type, self.gateway).to_text(origin, relativize)
+        return "%d %d %d %s %s" % (
+            self.precedence,
+            self.gateway_type,
+            self.algorithm,
+            gateway,
+            dns.rdata._base64ify(self.key, **kw),
+        )
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        precedence = tok.get_uint8()
+        gateway_type = tok.get_uint8()
+        algorithm = tok.get_uint8()
+        gateway = Gateway.from_text(
+            gateway_type, tok, origin, relativize, relativize_to
+        )
+        b64 = tok.concatenate_remaining_identifiers().encode()
+        key = base64.b64decode(b64)
+        return cls(
+            rdclass, rdtype, precedence, gateway_type, algorithm, gateway.gateway, key
+        )
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        header = struct.pack("!BBB", self.precedence, self.gateway_type, self.algorithm)
+        file.write(header)
+        Gateway(self.gateway_type, self.gateway).to_wire(
+            file, compress, origin, canonicalize
+        )
+        file.write(self.key)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        header = parser.get_struct("!BBB")
+        gateway_type = header[1]
+        gateway = Gateway.from_wire_parser(gateway_type, parser, origin)
+        key = parser.get_remaining()
+        return cls(
+            rdclass, rdtype, header[0], gateway_type, header[2], gateway.gateway, key
+        )
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/KX.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/KX.py
new file mode 100644
index 00000000..6073df47
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/KX.py
@@ -0,0 +1,24 @@
+# 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 dns.immutable
+import dns.rdtypes.mxbase
+
+
+@dns.immutable.immutable
+class KX(dns.rdtypes.mxbase.UncompressedDowncasingMX):
+    """KX record"""
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/NAPTR.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/NAPTR.py
new file mode 100644
index 00000000..195d1cba
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/NAPTR.py
@@ -0,0 +1,110 @@
+# 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 struct
+
+import dns.exception
+import dns.immutable
+import dns.name
+import dns.rdata
+import dns.rdtypes.util
+
+
+def _write_string(file, s):
+    l = len(s)
+    assert l < 256
+    file.write(struct.pack("!B", l))
+    file.write(s)
+
+
+@dns.immutable.immutable
+class NAPTR(dns.rdata.Rdata):
+    """NAPTR record"""
+
+    # see: RFC 3403
+
+    __slots__ = ["order", "preference", "flags", "service", "regexp", "replacement"]
+
+    def __init__(
+        self, rdclass, rdtype, order, preference, flags, service, regexp, replacement
+    ):
+        super().__init__(rdclass, rdtype)
+        self.flags = self._as_bytes(flags, True, 255)
+        self.service = self._as_bytes(service, True, 255)
+        self.regexp = self._as_bytes(regexp, True, 255)
+        self.order = self._as_uint16(order)
+        self.preference = self._as_uint16(preference)
+        self.replacement = self._as_name(replacement)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        replacement = self.replacement.choose_relativity(origin, relativize)
+        return '%d %d "%s" "%s" "%s" %s' % (
+            self.order,
+            self.preference,
+            dns.rdata._escapify(self.flags),
+            dns.rdata._escapify(self.service),
+            dns.rdata._escapify(self.regexp),
+            replacement,
+        )
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        order = tok.get_uint16()
+        preference = tok.get_uint16()
+        flags = tok.get_string()
+        service = tok.get_string()
+        regexp = tok.get_string()
+        replacement = tok.get_name(origin, relativize, relativize_to)
+        return cls(
+            rdclass, rdtype, order, preference, flags, service, regexp, replacement
+        )
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        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)
+        self.replacement.to_wire(file, compress, origin, canonicalize)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        (order, preference) = parser.get_struct("!HH")
+        strings = []
+        for _ in range(3):
+            s = parser.get_counted_bytes()
+            strings.append(s)
+        replacement = parser.get_name(origin)
+        return cls(
+            rdclass,
+            rdtype,
+            order,
+            preference,
+            strings[0],
+            strings[1],
+            strings[2],
+            replacement,
+        )
+
+    def _processing_priority(self):
+        return (self.order, self.preference)
+
+    @classmethod
+    def _processing_order(cls, iterable):
+        return dns.rdtypes.util.priority_processing_order(iterable)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/NSAP.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/NSAP.py
new file mode 100644
index 00000000..d55edb73
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/NSAP.py
@@ -0,0 +1,60 @@
+# 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 binascii
+
+import dns.exception
+import dns.immutable
+import dns.rdata
+import dns.tokenizer
+
+
+@dns.immutable.immutable
+class NSAP(dns.rdata.Rdata):
+    """NSAP record."""
+
+    # see: RFC 1706
+
+    __slots__ = ["address"]
+
+    def __init__(self, rdclass, rdtype, address):
+        super().__init__(rdclass, rdtype)
+        self.address = self._as_bytes(address)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return f"0x{binascii.hexlify(self.address).decode()}"
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        address = tok.get_string()
+        if address[0:2] != "0x":
+            raise dns.exception.SyntaxError("string does not start with 0x")
+        address = address[2:].replace(".", "")
+        if len(address) % 2 != 0:
+            raise dns.exception.SyntaxError("hexstring has odd length")
+        address = binascii.unhexlify(address.encode())
+        return cls(rdclass, rdtype, address)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        file.write(self.address)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        address = parser.get_remaining()
+        return cls(rdclass, rdtype, address)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/NSAP_PTR.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/NSAP_PTR.py
new file mode 100644
index 00000000..ce1c6632
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/NSAP_PTR.py
@@ -0,0 +1,24 @@
+# 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 dns.immutable
+import dns.rdtypes.nsbase
+
+
+@dns.immutable.immutable
+class NSAP_PTR(dns.rdtypes.nsbase.UncompressedNS):
+    """NSAP-PTR record"""
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/PX.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/PX.py
new file mode 100644
index 00000000..cdca1532
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/PX.py
@@ -0,0 +1,73 @@
+# 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 struct
+
+import dns.exception
+import dns.immutable
+import dns.name
+import dns.rdata
+import dns.rdtypes.util
+
+
+@dns.immutable.immutable
+class PX(dns.rdata.Rdata):
+    """PX record."""
+
+    # see: RFC 2163
+
+    __slots__ = ["preference", "map822", "mapx400"]
+
+    def __init__(self, rdclass, rdtype, preference, map822, mapx400):
+        super().__init__(rdclass, rdtype)
+        self.preference = self._as_uint16(preference)
+        self.map822 = self._as_name(map822)
+        self.mapx400 = self._as_name(mapx400)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        map822 = self.map822.choose_relativity(origin, relativize)
+        mapx400 = self.mapx400.choose_relativity(origin, relativize)
+        return "%d %s %s" % (self.preference, map822, mapx400)
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        preference = tok.get_uint16()
+        map822 = tok.get_name(origin, relativize, relativize_to)
+        mapx400 = tok.get_name(origin, relativize, relativize_to)
+        return cls(rdclass, rdtype, preference, map822, mapx400)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        pref = struct.pack("!H", self.preference)
+        file.write(pref)
+        self.map822.to_wire(file, None, origin, canonicalize)
+        self.mapx400.to_wire(file, None, origin, canonicalize)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        preference = parser.get_uint16()
+        map822 = parser.get_name(origin)
+        mapx400 = parser.get_name(origin)
+        return cls(rdclass, rdtype, preference, map822, mapx400)
+
+    def _processing_priority(self):
+        return self.preference
+
+    @classmethod
+    def _processing_order(cls, iterable):
+        return dns.rdtypes.util.priority_processing_order(iterable)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/SRV.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/SRV.py
new file mode 100644
index 00000000..5adef98f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/SRV.py
@@ -0,0 +1,75 @@
+# 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 struct
+
+import dns.exception
+import dns.immutable
+import dns.name
+import dns.rdata
+import dns.rdtypes.util
+
+
+@dns.immutable.immutable
+class SRV(dns.rdata.Rdata):
+    """SRV record"""
+
+    # see: RFC 2782
+
+    __slots__ = ["priority", "weight", "port", "target"]
+
+    def __init__(self, rdclass, rdtype, priority, weight, port, target):
+        super().__init__(rdclass, rdtype)
+        self.priority = self._as_uint16(priority)
+        self.weight = self._as_uint16(weight)
+        self.port = self._as_uint16(port)
+        self.target = self._as_name(target)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        target = self.target.choose_relativity(origin, relativize)
+        return "%d %d %d %s" % (self.priority, self.weight, self.port, target)
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        priority = tok.get_uint16()
+        weight = tok.get_uint16()
+        port = tok.get_uint16()
+        target = tok.get_name(origin, relativize, relativize_to)
+        return cls(rdclass, rdtype, priority, weight, port, target)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        three_ints = struct.pack("!HHH", self.priority, self.weight, self.port)
+        file.write(three_ints)
+        self.target.to_wire(file, compress, origin, canonicalize)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        (priority, weight, port) = parser.get_struct("!HHH")
+        target = parser.get_name(origin)
+        return cls(rdclass, rdtype, priority, weight, port, target)
+
+    def _processing_priority(self):
+        return self.priority
+
+    def _processing_weight(self):
+        return self.weight
+
+    @classmethod
+    def _processing_order(cls, iterable):
+        return dns.rdtypes.util.weighted_processing_order(iterable)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/SVCB.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/SVCB.py
new file mode 100644
index 00000000..ff3e9327
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/SVCB.py
@@ -0,0 +1,9 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+import dns.immutable
+import dns.rdtypes.svcbbase
+
+
+@dns.immutable.immutable
+class SVCB(dns.rdtypes.svcbbase.SVCBBase):
+    """SVCB record"""
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/WKS.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/WKS.py
new file mode 100644
index 00000000..881a7849
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/WKS.py
@@ -0,0 +1,100 @@
+# 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 socket
+import struct
+
+import dns.immutable
+import dns.ipv4
+import dns.rdata
+
+try:
+    _proto_tcp = socket.getprotobyname("tcp")
+    _proto_udp = socket.getprotobyname("udp")
+except OSError:
+    # Fall back to defaults in case /etc/protocols is unavailable.
+    _proto_tcp = 6
+    _proto_udp = 17
+
+
+@dns.immutable.immutable
+class WKS(dns.rdata.Rdata):
+    """WKS record"""
+
+    # see: RFC 1035
+
+    __slots__ = ["address", "protocol", "bitmap"]
+
+    def __init__(self, rdclass, rdtype, address, protocol, bitmap):
+        super().__init__(rdclass, rdtype)
+        self.address = self._as_ipv4_address(address)
+        self.protocol = self._as_uint8(protocol)
+        self.bitmap = self._as_bytes(bitmap)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        bits = []
+        for i, byte in enumerate(self.bitmap):
+            for j in range(0, 8):
+                if byte & (0x80 >> j):
+                    bits.append(str(i * 8 + j))
+        text = " ".join(bits)
+        return "%s %d %s" % (self.address, self.protocol, text)
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        address = tok.get_string()
+        protocol = tok.get_string()
+        if protocol.isdigit():
+            protocol = int(protocol)
+        else:
+            protocol = socket.getprotobyname(protocol)
+        bitmap = bytearray()
+        for token in tok.get_remaining():
+            value = token.unescape().value
+            if value.isdigit():
+                serv = int(value)
+            else:
+                if protocol != _proto_udp and protocol != _proto_tcp:
+                    raise NotImplementedError("protocol must be TCP or UDP")
+                if protocol == _proto_udp:
+                    protocol_text = "udp"
+                else:
+                    protocol_text = "tcp"
+                serv = socket.getservbyname(value, protocol_text)
+            i = serv // 8
+            l = len(bitmap)
+            if l < i + 1:
+                for _ in range(l, i + 1):
+                    bitmap.append(0)
+            bitmap[i] = bitmap[i] | (0x80 >> (serv % 8))
+        bitmap = dns.rdata._truncate_bitmap(bitmap)
+        return cls(rdclass, rdtype, address, protocol, bitmap)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        file.write(dns.ipv4.inet_aton(self.address))
+        protocol = struct.pack("!B", self.protocol)
+        file.write(protocol)
+        file.write(self.bitmap)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        address = parser.get_bytes(4)
+        protocol = parser.get_uint8()
+        bitmap = parser.get_remaining()
+        return cls(rdclass, rdtype, address, protocol, bitmap)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__init__.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__init__.py
new file mode 100644
index 00000000..dcec4dd2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__init__.py
@@ -0,0 +1,35 @@
+# 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.
+
+"""Class IN rdata type classes."""
+
+__all__ = [
+    "A",
+    "AAAA",
+    "APL",
+    "DHCID",
+    "HTTPS",
+    "IPSECKEY",
+    "KX",
+    "NAPTR",
+    "NSAP",
+    "NSAP_PTR",
+    "PX",
+    "SRV",
+    "SVCB",
+    "WKS",
+]
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/__init__.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/__init__.py
new file mode 100644
index 00000000..3997f84c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/__init__.py
@@ -0,0 +1,33 @@
+# 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.
+
+"""DNS rdata type classes"""
+
+__all__ = [
+    "ANY",
+    "IN",
+    "CH",
+    "dnskeybase",
+    "dsbase",
+    "euibase",
+    "mxbase",
+    "nsbase",
+    "svcbbase",
+    "tlsabase",
+    "txtbase",
+    "util",
+]
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/dnskeybase.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/dnskeybase.py
new file mode 100644
index 00000000..db300f8b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/dnskeybase.py
@@ -0,0 +1,87 @@
+# 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 enum
+import struct
+
+import dns.dnssectypes
+import dns.exception
+import dns.immutable
+import dns.rdata
+
+# wildcard import
+__all__ = ["SEP", "REVOKE", "ZONE"]  # noqa: F822
+
+
+class Flag(enum.IntFlag):
+    SEP = 0x0001
+    REVOKE = 0x0080
+    ZONE = 0x0100
+
+
+@dns.immutable.immutable
+class DNSKEYBase(dns.rdata.Rdata):
+    """Base class for rdata that is like a DNSKEY record"""
+
+    __slots__ = ["flags", "protocol", "algorithm", "key"]
+
+    def __init__(self, rdclass, rdtype, flags, protocol, algorithm, key):
+        super().__init__(rdclass, rdtype)
+        self.flags = Flag(self._as_uint16(flags))
+        self.protocol = self._as_uint8(protocol)
+        self.algorithm = dns.dnssectypes.Algorithm.make(algorithm)
+        self.key = self._as_bytes(key)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return "%d %d %d %s" % (
+            self.flags,
+            self.protocol,
+            self.algorithm,
+            dns.rdata._base64ify(self.key, **kw),
+        )
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        flags = tok.get_uint16()
+        protocol = tok.get_uint8()
+        algorithm = tok.get_string()
+        b64 = tok.concatenate_remaining_identifiers().encode()
+        key = base64.b64decode(b64)
+        return cls(rdclass, rdtype, flags, protocol, algorithm, key)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        header = struct.pack("!HBB", self.flags, self.protocol, self.algorithm)
+        file.write(header)
+        file.write(self.key)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        header = parser.get_struct("!HBB")
+        key = parser.get_remaining()
+        return cls(rdclass, rdtype, header[0], header[1], header[2], key)
+
+
+### BEGIN generated Flag constants
+
+SEP = Flag.SEP
+REVOKE = Flag.REVOKE
+ZONE = Flag.ZONE
+
+### END generated Flag constants
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/dsbase.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/dsbase.py
new file mode 100644
index 00000000..cd21f026
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/dsbase.py
@@ -0,0 +1,85 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2010, 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 binascii
+import struct
+
+import dns.dnssectypes
+import dns.immutable
+import dns.rdata
+import dns.rdatatype
+
+
+@dns.immutable.immutable
+class DSBase(dns.rdata.Rdata):
+    """Base class for rdata that is like a DS record"""
+
+    __slots__ = ["key_tag", "algorithm", "digest_type", "digest"]
+
+    # Digest types registry:
+    # https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml
+    _digest_length_by_type = {
+        1: 20,  # SHA-1, RFC 3658 Sec. 2.4
+        2: 32,  # SHA-256, RFC 4509 Sec. 2.2
+        3: 32,  # GOST R 34.11-94, RFC 5933 Sec. 4 in conjunction with RFC 4490 Sec. 2.1
+        4: 48,  # SHA-384, RFC 6605 Sec. 2
+    }
+
+    def __init__(self, rdclass, rdtype, key_tag, algorithm, digest_type, digest):
+        super().__init__(rdclass, rdtype)
+        self.key_tag = self._as_uint16(key_tag)
+        self.algorithm = dns.dnssectypes.Algorithm.make(algorithm)
+        self.digest_type = dns.dnssectypes.DSDigest.make(self._as_uint8(digest_type))
+        self.digest = self._as_bytes(digest)
+        try:
+            if len(self.digest) != self._digest_length_by_type[self.digest_type]:
+                raise ValueError("digest length inconsistent with digest type")
+        except KeyError:
+            if self.digest_type == 0:  # reserved, RFC 3658 Sec. 2.4
+                raise ValueError("digest type 0 is reserved")
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        kw = kw.copy()
+        chunksize = kw.pop("chunksize", 128)
+        return "%d %d %d %s" % (
+            self.key_tag,
+            self.algorithm,
+            self.digest_type,
+            dns.rdata._hexify(self.digest, chunksize=chunksize, **kw),
+        )
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        key_tag = tok.get_uint16()
+        algorithm = tok.get_string()
+        digest_type = tok.get_uint8()
+        digest = tok.concatenate_remaining_identifiers().encode()
+        digest = binascii.unhexlify(digest)
+        return cls(rdclass, rdtype, key_tag, algorithm, digest_type, digest)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        header = struct.pack("!HBB", self.key_tag, self.algorithm, self.digest_type)
+        file.write(header)
+        file.write(self.digest)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        header = parser.get_struct("!HBB")
+        digest = parser.get_remaining()
+        return cls(rdclass, rdtype, header[0], header[1], header[2], digest)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/euibase.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/euibase.py
new file mode 100644
index 00000000..a39c166b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/euibase.py
@@ -0,0 +1,70 @@
+# 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.immutable
+import dns.rdata
+
+
+@dns.immutable.immutable
+class EUIBase(dns.rdata.Rdata):
+    """EUIxx record"""
+
+    # 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().__init__(rdclass, rdtype)
+        self.eui = self._as_bytes(eui)
+        if len(self.eui) != self.byte_len:
+            raise dns.exception.FormError(
+                f"EUI{self.byte_len * 8} rdata has to have {self.byte_len} bytes"
+            )
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        return dns.rdata._hexify(self.eui, chunksize=2, separator=b"-", **kw)
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        text = tok.get_string()
+        if len(text) != cls.text_len:
+            raise dns.exception.SyntaxError(
+                f"Input text must have {cls.text_len} characters"
+            )
+        for i in range(2, cls.byte_len * 3 - 1, 3):
+            if text[i] != "-":
+                raise dns.exception.SyntaxError(f"Dash expected at position {i}")
+        text = text.replace("-", "")
+        try:
+            data = binascii.unhexlify(text.encode())
+        except (ValueError, TypeError) as ex:
+            raise dns.exception.SyntaxError(f"Hex decoding error: {str(ex)}")
+        return cls(rdclass, rdtype, data)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        file.write(self.eui)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        eui = parser.get_bytes(cls.byte_len)
+        return cls(rdclass, rdtype, eui)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/mxbase.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/mxbase.py
new file mode 100644
index 00000000..6d5e3d87
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/mxbase.py
@@ -0,0 +1,87 @@
+# 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.
+
+"""MX-like base classes."""
+
+import struct
+
+import dns.exception
+import dns.immutable
+import dns.name
+import dns.rdata
+import dns.rdtypes.util
+
+
+@dns.immutable.immutable
+class MXBase(dns.rdata.Rdata):
+    """Base class for rdata that is like an MX record."""
+
+    __slots__ = ["preference", "exchange"]
+
+    def __init__(self, rdclass, rdtype, preference, exchange):
+        super().__init__(rdclass, rdtype)
+        self.preference = self._as_uint16(preference)
+        self.exchange = self._as_name(exchange)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        exchange = self.exchange.choose_relativity(origin, relativize)
+        return "%d %s" % (self.preference, exchange)
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        preference = tok.get_uint16()
+        exchange = tok.get_name(origin, relativize, relativize_to)
+        return cls(rdclass, rdtype, preference, exchange)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        pref = struct.pack("!H", self.preference)
+        file.write(pref)
+        self.exchange.to_wire(file, compress, origin, canonicalize)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        preference = parser.get_uint16()
+        exchange = parser.get_name(origin)
+        return cls(rdclass, rdtype, preference, exchange)
+
+    def _processing_priority(self):
+        return self.preference
+
+    @classmethod
+    def _processing_order(cls, iterable):
+        return dns.rdtypes.util.priority_processing_order(iterable)
+
+
+@dns.immutable.immutable
+class UncompressedMX(MXBase):
+    """Base class for rdata that is like an MX record, but whose name
+    is not compressed when converted to DNS wire format, and whose
+    digestable form is not downcased."""
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        super()._to_wire(file, None, origin, False)
+
+
+@dns.immutable.immutable
+class UncompressedDowncasingMX(MXBase):
+    """Base class for rdata that is like an MX record, but whose name
+    is not compressed when convert to DNS wire format."""
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        super()._to_wire(file, None, origin, canonicalize)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/nsbase.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/nsbase.py
new file mode 100644
index 00000000..904224f0
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/nsbase.py
@@ -0,0 +1,63 @@
+# 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.
+
+"""NS-like base classes."""
+
+import dns.exception
+import dns.immutable
+import dns.name
+import dns.rdata
+
+
+@dns.immutable.immutable
+class NSBase(dns.rdata.Rdata):
+    """Base class for rdata that is like an NS record."""
+
+    __slots__ = ["target"]
+
+    def __init__(self, rdclass, rdtype, target):
+        super().__init__(rdclass, rdtype)
+        self.target = self._as_name(target)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        target = self.target.choose_relativity(origin, relativize)
+        return str(target)
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        target = tok.get_name(origin, relativize, relativize_to)
+        return cls(rdclass, rdtype, target)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        self.target.to_wire(file, compress, origin, canonicalize)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        target = parser.get_name(origin)
+        return cls(rdclass, rdtype, target)
+
+
+@dns.immutable.immutable
+class UncompressedNS(NSBase):
+    """Base class for rdata that is like an NS record, but whose name
+    is not compressed when convert to DNS wire format, and whose
+    digestable form is not downcased."""
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        self.target.to_wire(file, None, origin, False)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/svcbbase.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/svcbbase.py
new file mode 100644
index 00000000..a2b15b92
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/svcbbase.py
@@ -0,0 +1,585 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+import base64
+import enum
+import struct
+
+import dns.enum
+import dns.exception
+import dns.immutable
+import dns.ipv4
+import dns.ipv6
+import dns.name
+import dns.rdata
+import dns.rdtypes.util
+import dns.renderer
+import dns.tokenizer
+import dns.wire
+
+# Until there is an RFC, this module is experimental and may be changed in
+# incompatible ways.
+
+
+class UnknownParamKey(dns.exception.DNSException):
+    """Unknown SVCB ParamKey"""
+
+
+class ParamKey(dns.enum.IntEnum):
+    """SVCB ParamKey"""
+
+    MANDATORY = 0
+    ALPN = 1
+    NO_DEFAULT_ALPN = 2
+    PORT = 3
+    IPV4HINT = 4
+    ECH = 5
+    IPV6HINT = 6
+    DOHPATH = 7
+    OHTTP = 8
+
+    @classmethod
+    def _maximum(cls):
+        return 65535
+
+    @classmethod
+    def _short_name(cls):
+        return "SVCBParamKey"
+
+    @classmethod
+    def _prefix(cls):
+        return "KEY"
+
+    @classmethod
+    def _unknown_exception_class(cls):
+        return UnknownParamKey
+
+
+class Emptiness(enum.IntEnum):
+    NEVER = 0
+    ALWAYS = 1
+    ALLOWED = 2
+
+
+def _validate_key(key):
+    force_generic = False
+    if isinstance(key, bytes):
+        # We decode to latin-1 so we get 0-255 as valid and do NOT interpret
+        # UTF-8 sequences
+        key = key.decode("latin-1")
+    if isinstance(key, str):
+        if key.lower().startswith("key"):
+            force_generic = True
+            if key[3:].startswith("0") and len(key) != 4:
+                # key has leading zeros
+                raise ValueError("leading zeros in key")
+        key = key.replace("-", "_")
+    return (ParamKey.make(key), force_generic)
+
+
+def key_to_text(key):
+    return ParamKey.to_text(key).replace("_", "-").lower()
+
+
+# Like rdata escapify, but escapes ',' too.
+
+_escaped = b'",\\'
+
+
+def _escapify(qstring):
+    text = ""
+    for c in qstring:
+        if c in _escaped:
+            text += "\\" + chr(c)
+        elif c >= 0x20 and c < 0x7F:
+            text += chr(c)
+        else:
+            text += "\\%03d" % c
+    return text
+
+
+def _unescape(value):
+    if value == "":
+        return value
+    unescaped = b""
+    l = len(value)
+    i = 0
+    while i < l:
+        c = value[i]
+        i += 1
+        if c == "\\":
+            if i >= l:  # pragma: no cover   (can't happen via tokenizer get())
+                raise dns.exception.UnexpectedEnd
+            c = value[i]
+            i += 1
+            if c.isdigit():
+                if i >= l:
+                    raise dns.exception.UnexpectedEnd
+                c2 = value[i]
+                i += 1
+                if i >= l:
+                    raise dns.exception.UnexpectedEnd
+                c3 = value[i]
+                i += 1
+                if not (c2.isdigit() and c3.isdigit()):
+                    raise dns.exception.SyntaxError
+                codepoint = int(c) * 100 + int(c2) * 10 + int(c3)
+                if codepoint > 255:
+                    raise dns.exception.SyntaxError
+                unescaped += b"%c" % (codepoint)
+                continue
+        unescaped += c.encode()
+    return unescaped
+
+
+def _split(value):
+    l = len(value)
+    i = 0
+    items = []
+    unescaped = b""
+    while i < l:
+        c = value[i]
+        i += 1
+        if c == ord("\\"):
+            if i >= l:  # pragma: no cover   (can't happen via tokenizer get())
+                raise dns.exception.UnexpectedEnd
+            c = value[i]
+            i += 1
+            unescaped += b"%c" % (c)
+        elif c == ord(","):
+            items.append(unescaped)
+            unescaped = b""
+        else:
+            unescaped += b"%c" % (c)
+    items.append(unescaped)
+    return items
+
+
+@dns.immutable.immutable
+class Param:
+    """Abstract base class for SVCB parameters"""
+
+    @classmethod
+    def emptiness(cls):
+        return Emptiness.NEVER
+
+
+@dns.immutable.immutable
+class GenericParam(Param):
+    """Generic SVCB parameter"""
+
+    def __init__(self, value):
+        self.value = dns.rdata.Rdata._as_bytes(value, True)
+
+    @classmethod
+    def emptiness(cls):
+        return Emptiness.ALLOWED
+
+    @classmethod
+    def from_value(cls, value):
+        if value is None or len(value) == 0:
+            return None
+        else:
+            return cls(_unescape(value))
+
+    def to_text(self):
+        return '"' + dns.rdata._escapify(self.value) + '"'
+
+    @classmethod
+    def from_wire_parser(cls, parser, origin=None):  # pylint: disable=W0613
+        value = parser.get_bytes(parser.remaining())
+        if len(value) == 0:
+            return None
+        else:
+            return cls(value)
+
+    def to_wire(self, file, origin=None):  # pylint: disable=W0613
+        file.write(self.value)
+
+
+@dns.immutable.immutable
+class MandatoryParam(Param):
+    def __init__(self, keys):
+        # check for duplicates
+        keys = sorted([_validate_key(key)[0] for key in keys])
+        prior_k = None
+        for k in keys:
+            if k == prior_k:
+                raise ValueError(f"duplicate key {k:d}")
+            prior_k = k
+            if k == ParamKey.MANDATORY:
+                raise ValueError("listed the mandatory key as mandatory")
+        self.keys = tuple(keys)
+
+    @classmethod
+    def from_value(cls, value):
+        keys = [k.encode() for k in value.split(",")]
+        return cls(keys)
+
+    def to_text(self):
+        return '"' + ",".join([key_to_text(key) for key in self.keys]) + '"'
+
+    @classmethod
+    def from_wire_parser(cls, parser, origin=None):  # pylint: disable=W0613
+        keys = []
+        last_key = -1
+        while parser.remaining() > 0:
+            key = parser.get_uint16()
+            if key < last_key:
+                raise dns.exception.FormError("manadatory keys not ascending")
+            last_key = key
+            keys.append(key)
+        return cls(keys)
+
+    def to_wire(self, file, origin=None):  # pylint: disable=W0613
+        for key in self.keys:
+            file.write(struct.pack("!H", key))
+
+
+@dns.immutable.immutable
+class ALPNParam(Param):
+    def __init__(self, ids):
+        self.ids = dns.rdata.Rdata._as_tuple(
+            ids, lambda x: dns.rdata.Rdata._as_bytes(x, True, 255, False)
+        )
+
+    @classmethod
+    def from_value(cls, value):
+        return cls(_split(_unescape(value)))
+
+    def to_text(self):
+        value = ",".join([_escapify(id) for id in self.ids])
+        return '"' + dns.rdata._escapify(value.encode()) + '"'
+
+    @classmethod
+    def from_wire_parser(cls, parser, origin=None):  # pylint: disable=W0613
+        ids = []
+        while parser.remaining() > 0:
+            id = parser.get_counted_bytes()
+            ids.append(id)
+        return cls(ids)
+
+    def to_wire(self, file, origin=None):  # pylint: disable=W0613
+        for id in self.ids:
+            file.write(struct.pack("!B", len(id)))
+            file.write(id)
+
+
+@dns.immutable.immutable
+class NoDefaultALPNParam(Param):
+    # We don't ever expect to instantiate this class, but we need
+    # a from_value() and a from_wire_parser(), so we just return None
+    # from the class methods when things are OK.
+
+    @classmethod
+    def emptiness(cls):
+        return Emptiness.ALWAYS
+
+    @classmethod
+    def from_value(cls, value):
+        if value is None or value == "":
+            return None
+        else:
+            raise ValueError("no-default-alpn with non-empty value")
+
+    def to_text(self):
+        raise NotImplementedError  # pragma: no cover
+
+    @classmethod
+    def from_wire_parser(cls, parser, origin=None):  # pylint: disable=W0613
+        if parser.remaining() != 0:
+            raise dns.exception.FormError
+        return None
+
+    def to_wire(self, file, origin=None):  # pylint: disable=W0613
+        raise NotImplementedError  # pragma: no cover
+
+
+@dns.immutable.immutable
+class PortParam(Param):
+    def __init__(self, port):
+        self.port = dns.rdata.Rdata._as_uint16(port)
+
+    @classmethod
+    def from_value(cls, value):
+        value = int(value)
+        return cls(value)
+
+    def to_text(self):
+        return f'"{self.port}"'
+
+    @classmethod
+    def from_wire_parser(cls, parser, origin=None):  # pylint: disable=W0613
+        port = parser.get_uint16()
+        return cls(port)
+
+    def to_wire(self, file, origin=None):  # pylint: disable=W0613
+        file.write(struct.pack("!H", self.port))
+
+
+@dns.immutable.immutable
+class IPv4HintParam(Param):
+    def __init__(self, addresses):
+        self.addresses = dns.rdata.Rdata._as_tuple(
+            addresses, dns.rdata.Rdata._as_ipv4_address
+        )
+
+    @classmethod
+    def from_value(cls, value):
+        addresses = value.split(",")
+        return cls(addresses)
+
+    def to_text(self):
+        return '"' + ",".join(self.addresses) + '"'
+
+    @classmethod
+    def from_wire_parser(cls, parser, origin=None):  # pylint: disable=W0613
+        addresses = []
+        while parser.remaining() > 0:
+            ip = parser.get_bytes(4)
+            addresses.append(dns.ipv4.inet_ntoa(ip))
+        return cls(addresses)
+
+    def to_wire(self, file, origin=None):  # pylint: disable=W0613
+        for address in self.addresses:
+            file.write(dns.ipv4.inet_aton(address))
+
+
+@dns.immutable.immutable
+class IPv6HintParam(Param):
+    def __init__(self, addresses):
+        self.addresses = dns.rdata.Rdata._as_tuple(
+            addresses, dns.rdata.Rdata._as_ipv6_address
+        )
+
+    @classmethod
+    def from_value(cls, value):
+        addresses = value.split(",")
+        return cls(addresses)
+
+    def to_text(self):
+        return '"' + ",".join(self.addresses) + '"'
+
+    @classmethod
+    def from_wire_parser(cls, parser, origin=None):  # pylint: disable=W0613
+        addresses = []
+        while parser.remaining() > 0:
+            ip = parser.get_bytes(16)
+            addresses.append(dns.ipv6.inet_ntoa(ip))
+        return cls(addresses)
+
+    def to_wire(self, file, origin=None):  # pylint: disable=W0613
+        for address in self.addresses:
+            file.write(dns.ipv6.inet_aton(address))
+
+
+@dns.immutable.immutable
+class ECHParam(Param):
+    def __init__(self, ech):
+        self.ech = dns.rdata.Rdata._as_bytes(ech, True)
+
+    @classmethod
+    def from_value(cls, value):
+        if "\\" in value:
+            raise ValueError("escape in ECH value")
+        value = base64.b64decode(value.encode())
+        return cls(value)
+
+    def to_text(self):
+        b64 = base64.b64encode(self.ech).decode("ascii")
+        return f'"{b64}"'
+
+    @classmethod
+    def from_wire_parser(cls, parser, origin=None):  # pylint: disable=W0613
+        value = parser.get_bytes(parser.remaining())
+        return cls(value)
+
+    def to_wire(self, file, origin=None):  # pylint: disable=W0613
+        file.write(self.ech)
+
+
+@dns.immutable.immutable
+class OHTTPParam(Param):
+    # We don't ever expect to instantiate this class, but we need
+    # a from_value() and a from_wire_parser(), so we just return None
+    # from the class methods when things are OK.
+
+    @classmethod
+    def emptiness(cls):
+        return Emptiness.ALWAYS
+
+    @classmethod
+    def from_value(cls, value):
+        if value is None or value == "":
+            return None
+        else:
+            raise ValueError("ohttp with non-empty value")
+
+    def to_text(self):
+        raise NotImplementedError  # pragma: no cover
+
+    @classmethod
+    def from_wire_parser(cls, parser, origin=None):  # pylint: disable=W0613
+        if parser.remaining() != 0:
+            raise dns.exception.FormError
+        return None
+
+    def to_wire(self, file, origin=None):  # pylint: disable=W0613
+        raise NotImplementedError  # pragma: no cover
+
+
+_class_for_key = {
+    ParamKey.MANDATORY: MandatoryParam,
+    ParamKey.ALPN: ALPNParam,
+    ParamKey.NO_DEFAULT_ALPN: NoDefaultALPNParam,
+    ParamKey.PORT: PortParam,
+    ParamKey.IPV4HINT: IPv4HintParam,
+    ParamKey.ECH: ECHParam,
+    ParamKey.IPV6HINT: IPv6HintParam,
+    ParamKey.OHTTP: OHTTPParam,
+}
+
+
+def _validate_and_define(params, key, value):
+    (key, force_generic) = _validate_key(_unescape(key))
+    if key in params:
+        raise SyntaxError(f'duplicate key "{key:d}"')
+    cls = _class_for_key.get(key, GenericParam)
+    emptiness = cls.emptiness()
+    if value is None:
+        if emptiness == Emptiness.NEVER:
+            raise SyntaxError("value cannot be empty")
+        value = cls.from_value(value)
+    else:
+        if force_generic:
+            value = cls.from_wire_parser(dns.wire.Parser(_unescape(value)))
+        else:
+            value = cls.from_value(value)
+    params[key] = value
+
+
+@dns.immutable.immutable
+class SVCBBase(dns.rdata.Rdata):
+    """Base class for SVCB-like records"""
+
+    # see: draft-ietf-dnsop-svcb-https-11
+
+    __slots__ = ["priority", "target", "params"]
+
+    def __init__(self, rdclass, rdtype, priority, target, params):
+        super().__init__(rdclass, rdtype)
+        self.priority = self._as_uint16(priority)
+        self.target = self._as_name(target)
+        for k, v in params.items():
+            k = ParamKey.make(k)
+            if not isinstance(v, Param) and v is not None:
+                raise ValueError(f"{k:d} not a Param")
+        self.params = dns.immutable.Dict(params)
+        # Make sure any parameter listed as mandatory is present in the
+        # record.
+        mandatory = params.get(ParamKey.MANDATORY)
+        if mandatory:
+            for key in mandatory.keys:
+                # Note we have to say "not in" as we have None as a value
+                # so a get() and a not None test would be wrong.
+                if key not in params:
+                    raise ValueError(f"key {key:d} declared mandatory but not present")
+        # The no-default-alpn parameter requires the alpn parameter.
+        if ParamKey.NO_DEFAULT_ALPN in params:
+            if ParamKey.ALPN not in params:
+                raise ValueError("no-default-alpn present, but alpn missing")
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        target = self.target.choose_relativity(origin, relativize)
+        params = []
+        for key in sorted(self.params.keys()):
+            value = self.params[key]
+            if value is None:
+                params.append(key_to_text(key))
+            else:
+                kv = key_to_text(key) + "=" + value.to_text()
+                params.append(kv)
+        if len(params) > 0:
+            space = " "
+        else:
+            space = ""
+        return "%d %s%s%s" % (self.priority, target, space, " ".join(params))
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        priority = tok.get_uint16()
+        target = tok.get_name(origin, relativize, relativize_to)
+        if priority == 0:
+            token = tok.get()
+            if not token.is_eol_or_eof():
+                raise SyntaxError("parameters in AliasMode")
+            tok.unget(token)
+        params = {}
+        while True:
+            token = tok.get()
+            if token.is_eol_or_eof():
+                tok.unget(token)
+                break
+            if token.ttype != dns.tokenizer.IDENTIFIER:
+                raise SyntaxError("parameter is not an identifier")
+            equals = token.value.find("=")
+            if equals == len(token.value) - 1:
+                # 'key=', so next token should be a quoted string without
+                # any intervening whitespace.
+                key = token.value[:-1]
+                token = tok.get(want_leading=True)
+                if token.ttype != dns.tokenizer.QUOTED_STRING:
+                    raise SyntaxError("whitespace after =")
+                value = token.value
+            elif equals > 0:
+                # key=value
+                key = token.value[:equals]
+                value = token.value[equals + 1 :]
+            elif equals == 0:
+                # =key
+                raise SyntaxError('parameter cannot start with "="')
+            else:
+                # key
+                key = token.value
+                value = None
+            _validate_and_define(params, key, value)
+        return cls(rdclass, rdtype, priority, target, params)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        file.write(struct.pack("!H", self.priority))
+        self.target.to_wire(file, None, origin, False)
+        for key in sorted(self.params):
+            file.write(struct.pack("!H", key))
+            value = self.params[key]
+            with dns.renderer.prefixed_length(file, 2):
+                # Note that we're still writing a length of zero if the value is None
+                if value is not None:
+                    value.to_wire(file, origin)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        priority = parser.get_uint16()
+        target = parser.get_name(origin)
+        if priority == 0 and parser.remaining() != 0:
+            raise dns.exception.FormError("parameters in AliasMode")
+        params = {}
+        prior_key = -1
+        while parser.remaining() > 0:
+            key = parser.get_uint16()
+            if key < prior_key:
+                raise dns.exception.FormError("keys not in order")
+            prior_key = key
+            vlen = parser.get_uint16()
+            pcls = _class_for_key.get(key, GenericParam)
+            with parser.restrict_to(vlen):
+                value = pcls.from_wire_parser(parser, origin)
+            params[key] = value
+        return cls(rdclass, rdtype, priority, target, params)
+
+    def _processing_priority(self):
+        return self.priority
+
+    @classmethod
+    def _processing_order(cls, iterable):
+        return dns.rdtypes.util.priority_processing_order(iterable)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/tlsabase.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/tlsabase.py
new file mode 100644
index 00000000..a059d2c4
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/tlsabase.py
@@ -0,0 +1,71 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2005-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 binascii
+import struct
+
+import dns.immutable
+import dns.rdata
+import dns.rdatatype
+
+
+@dns.immutable.immutable
+class TLSABase(dns.rdata.Rdata):
+    """Base class for TLSA and SMIMEA records"""
+
+    # see: RFC 6698
+
+    __slots__ = ["usage", "selector", "mtype", "cert"]
+
+    def __init__(self, rdclass, rdtype, usage, selector, mtype, cert):
+        super().__init__(rdclass, rdtype)
+        self.usage = self._as_uint8(usage)
+        self.selector = self._as_uint8(selector)
+        self.mtype = self._as_uint8(mtype)
+        self.cert = self._as_bytes(cert)
+
+    def to_text(self, origin=None, relativize=True, **kw):
+        kw = kw.copy()
+        chunksize = kw.pop("chunksize", 128)
+        return "%d %d %d %s" % (
+            self.usage,
+            self.selector,
+            self.mtype,
+            dns.rdata._hexify(self.cert, chunksize=chunksize, **kw),
+        )
+
+    @classmethod
+    def from_text(
+        cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        usage = tok.get_uint8()
+        selector = tok.get_uint8()
+        mtype = tok.get_uint8()
+        cert = tok.concatenate_remaining_identifiers().encode()
+        cert = binascii.unhexlify(cert)
+        return cls(rdclass, rdtype, usage, selector, mtype, cert)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        header = struct.pack("!BBB", self.usage, self.selector, self.mtype)
+        file.write(header)
+        file.write(self.cert)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        header = parser.get_struct("BBB")
+        cert = parser.get_remaining()
+        return cls(rdclass, rdtype, header[0], header[1], header[2], cert)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/txtbase.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/txtbase.py
new file mode 100644
index 00000000..73db6d9e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/txtbase.py
@@ -0,0 +1,106 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2006-2017 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.
+
+"""TXT-like base class."""
+
+from typing import Any, Dict, Iterable, Optional, Tuple, Union
+
+import dns.exception
+import dns.immutable
+import dns.rdata
+import dns.renderer
+import dns.tokenizer
+
+
+@dns.immutable.immutable
+class TXTBase(dns.rdata.Rdata):
+    """Base class for rdata that is like a TXT record (see RFC 1035)."""
+
+    __slots__ = ["strings"]
+
+    def __init__(
+        self,
+        rdclass: dns.rdataclass.RdataClass,
+        rdtype: dns.rdatatype.RdataType,
+        strings: Iterable[Union[bytes, str]],
+    ):
+        """Initialize a TXT-like rdata.
+
+        *rdclass*, an ``int`` is the rdataclass of the Rdata.
+
+        *rdtype*, an ``int`` is the rdatatype of the Rdata.
+
+        *strings*, a tuple of ``bytes``
+        """
+        super().__init__(rdclass, rdtype)
+        self.strings: Tuple[bytes] = self._as_tuple(
+            strings, lambda x: self._as_bytes(x, True, 255)
+        )
+        if len(self.strings) == 0:
+            raise ValueError("the list of strings must not be empty")
+
+    def to_text(
+        self,
+        origin: Optional[dns.name.Name] = None,
+        relativize: bool = True,
+        **kw: Dict[str, Any],
+    ) -> str:
+        txt = ""
+        prefix = ""
+        for s in self.strings:
+            txt += f'{prefix}"{dns.rdata._escapify(s)}"'
+            prefix = " "
+        return txt
+
+    @classmethod
+    def from_text(
+        cls,
+        rdclass: dns.rdataclass.RdataClass,
+        rdtype: dns.rdatatype.RdataType,
+        tok: dns.tokenizer.Tokenizer,
+        origin: Optional[dns.name.Name] = None,
+        relativize: bool = True,
+        relativize_to: Optional[dns.name.Name] = None,
+    ) -> dns.rdata.Rdata:
+        strings = []
+        for token in tok.get_remaining():
+            token = token.unescape_to_bytes()
+            # The 'if' below is always true in the current code, but we
+            # are leaving this check in in case things change some day.
+            if not (
+                token.is_quoted_string() or token.is_identifier()
+            ):  # pragma: no cover
+                raise dns.exception.SyntaxError("expected a string")
+            if len(token.value) > 255:
+                raise dns.exception.SyntaxError("string too long")
+            strings.append(token.value)
+        if len(strings) == 0:
+            raise dns.exception.UnexpectedEnd
+        return cls(rdclass, rdtype, strings)
+
+    def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        for s in self.strings:
+            with dns.renderer.prefixed_length(file, 1):
+                file.write(s)
+
+    @classmethod
+    def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
+        strings = []
+        while parser.remaining() > 0:
+            s = parser.get_counted_bytes()
+            strings.append(s)
+        return cls(rdclass, rdtype, strings)
diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/util.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/util.py
new file mode 100644
index 00000000..653a0bf2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/util.py
@@ -0,0 +1,257 @@
+# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
+
+# Copyright (C) 2006, 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 collections
+import random
+import struct
+from typing import Any, List
+
+import dns.exception
+import dns.ipv4
+import dns.ipv6
+import dns.name
+import dns.rdata
+
+
+class Gateway:
+    """A helper class for the IPSECKEY gateway and AMTRELAY relay fields"""
+
+    name = ""
+
+    def __init__(self, type, gateway=None):
+        self.type = dns.rdata.Rdata._as_uint8(type)
+        self.gateway = gateway
+        self._check()
+
+    @classmethod
+    def _invalid_type(cls, gateway_type):
+        return f"invalid {cls.name} type: {gateway_type}"
+
+    def _check(self):
+        if self.type == 0:
+            if self.gateway not in (".", None):
+                raise SyntaxError(f"invalid {self.name} for type 0")
+            self.gateway = None
+        elif self.type == 1:
+            # check that it's OK
+            dns.ipv4.inet_aton(self.gateway)
+        elif self.type == 2:
+            # check that it's OK
+            dns.ipv6.inet_aton(self.gateway)
+        elif self.type == 3:
+            if not isinstance(self.gateway, dns.name.Name):
+                raise SyntaxError(f"invalid {self.name}; not a name")
+        else:
+            raise SyntaxError(self._invalid_type(self.type))
+
+    def to_text(self, origin=None, relativize=True):
+        if self.type == 0:
+            return "."
+        elif self.type in (1, 2):
+            return self.gateway
+        elif self.type == 3:
+            return str(self.gateway.choose_relativity(origin, relativize))
+        else:
+            raise ValueError(self._invalid_type(self.type))  # pragma: no cover
+
+    @classmethod
+    def from_text(
+        cls, gateway_type, tok, origin=None, relativize=True, relativize_to=None
+    ):
+        if gateway_type in (0, 1, 2):
+            gateway = tok.get_string()
+        elif gateway_type == 3:
+            gateway = tok.get_name(origin, relativize, relativize_to)
+        else:
+            raise dns.exception.SyntaxError(
+                cls._invalid_type(gateway_type)
+            )  # pragma: no cover
+        return cls(gateway_type, gateway)
+
+    # pylint: disable=unused-argument
+    def to_wire(self, file, compress=None, origin=None, canonicalize=False):
+        if self.type == 0:
+            pass
+        elif self.type == 1:
+            file.write(dns.ipv4.inet_aton(self.gateway))
+        elif self.type == 2:
+            file.write(dns.ipv6.inet_aton(self.gateway))
+        elif self.type == 3:
+            self.gateway.to_wire(file, None, origin, False)
+        else:
+            raise ValueError(self._invalid_type(self.type))  # pragma: no cover
+
+    # pylint: enable=unused-argument
+
+    @classmethod
+    def from_wire_parser(cls, gateway_type, parser, origin=None):
+        if gateway_type == 0:
+            gateway = None
+        elif gateway_type == 1:
+            gateway = dns.ipv4.inet_ntoa(parser.get_bytes(4))
+        elif gateway_type == 2:
+            gateway = dns.ipv6.inet_ntoa(parser.get_bytes(16))
+        elif gateway_type == 3:
+            gateway = parser.get_name(origin)
+        else:
+            raise dns.exception.FormError(cls._invalid_type(gateway_type))
+        return cls(gateway_type, gateway)
+
+
+class Bitmap:
+    """A helper class for the NSEC/NSEC3/CSYNC type bitmaps"""
+
+    type_name = ""
+
+    def __init__(self, windows=None):
+        last_window = -1
+        self.windows = windows
+        for window, bitmap in self.windows:
+            if not isinstance(window, int):
+                raise ValueError(f"bad {self.type_name} window type")
+            if window <= last_window:
+                raise ValueError(f"bad {self.type_name} window order")
+            if window > 256:
+                raise ValueError(f"bad {self.type_name} window number")
+            last_window = window
+            if not isinstance(bitmap, bytes):
+                raise ValueError(f"bad {self.type_name} octets type")
+            if len(bitmap) == 0 or len(bitmap) > 32:
+                raise ValueError(f"bad {self.type_name} octets")
+
+    def to_text(self) -> str:
+        text = ""
+        for window, bitmap in self.windows:
+            bits = []
+            for i, byte in enumerate(bitmap):
+                for j in range(0, 8):
+                    if byte & (0x80 >> j):
+                        rdtype = window * 256 + i * 8 + j
+                        bits.append(dns.rdatatype.to_text(rdtype))
+            text += " " + " ".join(bits)
+        return text
+
+    @classmethod
+    def from_text(cls, tok: "dns.tokenizer.Tokenizer") -> "Bitmap":
+        rdtypes = []
+        for token in tok.get_remaining():
+            rdtype = dns.rdatatype.from_text(token.unescape().value)
+            if rdtype == 0:
+                raise dns.exception.SyntaxError(f"{cls.type_name} with bit 0")
+            rdtypes.append(rdtype)
+        return cls.from_rdtypes(rdtypes)
+
+    @classmethod
+    def from_rdtypes(cls, rdtypes: List[dns.rdatatype.RdataType]) -> "Bitmap":
+        rdtypes = sorted(rdtypes)
+        window = 0
+        octets = 0
+        prior_rdtype = 0
+        bitmap = bytearray(b"\0" * 32)
+        windows = []
+        for rdtype in rdtypes:
+            if rdtype == prior_rdtype:
+                continue
+            prior_rdtype = rdtype
+            new_window = rdtype // 256
+            if new_window != window:
+                if octets != 0:
+                    windows.append((window, bytes(bitmap[0:octets])))
+                bitmap = bytearray(b"\0" * 32)
+                window = new_window
+            offset = rdtype % 256
+            byte = offset // 8
+            bit = offset % 8
+            octets = byte + 1
+            bitmap[byte] = bitmap[byte] | (0x80 >> bit)
+        if octets != 0:
+            windows.append((window, bytes(bitmap[0:octets])))
+        return cls(windows)
+
+    def to_wire(self, file: Any) -> None:
+        for window, bitmap in self.windows:
+            file.write(struct.pack("!BB", window, len(bitmap)))
+            file.write(bitmap)
+
+    @classmethod
+    def from_wire_parser(cls, parser: "dns.wire.Parser") -> "Bitmap":
+        windows = []
+        while parser.remaining() > 0:
+            window = parser.get_uint8()
+            bitmap = parser.get_counted_bytes()
+            windows.append((window, bitmap))
+        return cls(windows)
+
+
+def _priority_table(items):
+    by_priority = collections.defaultdict(list)
+    for rdata in items:
+        by_priority[rdata._processing_priority()].append(rdata)
+    return by_priority
+
+
+def priority_processing_order(iterable):
+    items = list(iterable)
+    if len(items) == 1:
+        return items
+    by_priority = _priority_table(items)
+    ordered = []
+    for k in sorted(by_priority.keys()):
+        rdatas = by_priority[k]
+        random.shuffle(rdatas)
+        ordered.extend(rdatas)
+    return ordered
+
+
+_no_weight = 0.1
+
+
+def weighted_processing_order(iterable):
+    items = list(iterable)
+    if len(items) == 1:
+        return items
+    by_priority = _priority_table(items)
+    ordered = []
+    for k in sorted(by_priority.keys()):
+        rdatas = by_priority[k]
+        total = sum(rdata._processing_weight() or _no_weight for rdata in rdatas)
+        while len(rdatas) > 1:
+            r = random.uniform(0, total)
+            for n, rdata in enumerate(rdatas):  # noqa: B007
+                weight = rdata._processing_weight() or _no_weight
+                if weight > r:
+                    break
+                r -= weight
+            total -= weight
+            ordered.append(rdata)  # pylint: disable=undefined-loop-variable
+            del rdatas[n]  # pylint: disable=undefined-loop-variable
+        ordered.append(rdatas[0])
+    return ordered
+
+
+def parse_formatted_hex(formatted, num_chunks, chunk_size, separator):
+    if len(formatted) != num_chunks * (chunk_size + 1) - 1:
+        raise ValueError("invalid formatted hex string")
+    value = b""
+    for _ in range(num_chunks):
+        chunk = formatted[0:chunk_size]
+        value += int(chunk, 16).to_bytes(chunk_size // 2, "big")
+        formatted = formatted[chunk_size:]
+        if len(formatted) > 0 and formatted[0] != separator:
+            raise ValueError("invalid formatted hex string")
+        formatted = formatted[1:]
+    return value