Загрузить файлы в «venv/Lib/site-packages/dns»
This commit is contained in:
168
venv/Lib/site-packages/dns/rcode.py
Normal file
168
venv/Lib/site-packages/dns/rcode.py
Normal file
@@ -0,0 +1,168 @@
|
||||
# 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.
|
||||
|
||||
"""DNS Result Codes."""
|
||||
|
||||
from typing import Tuple, Type
|
||||
|
||||
import dns.enum
|
||||
import dns.exception
|
||||
|
||||
|
||||
class Rcode(dns.enum.IntEnum):
|
||||
#: No error
|
||||
NOERROR = 0
|
||||
#: Format error
|
||||
FORMERR = 1
|
||||
#: Server failure
|
||||
SERVFAIL = 2
|
||||
#: Name does not exist ("Name Error" in RFC 1025 terminology).
|
||||
NXDOMAIN = 3
|
||||
#: Not implemented
|
||||
NOTIMP = 4
|
||||
#: Refused
|
||||
REFUSED = 5
|
||||
#: Name exists.
|
||||
YXDOMAIN = 6
|
||||
#: RRset exists.
|
||||
YXRRSET = 7
|
||||
#: RRset does not exist.
|
||||
NXRRSET = 8
|
||||
#: Not authoritative.
|
||||
NOTAUTH = 9
|
||||
#: Name not in zone.
|
||||
NOTZONE = 10
|
||||
#: DSO-TYPE Not Implemented
|
||||
DSOTYPENI = 11
|
||||
#: Bad EDNS version.
|
||||
BADVERS = 16
|
||||
#: TSIG Signature Failure
|
||||
BADSIG = 16
|
||||
#: Key not recognized.
|
||||
BADKEY = 17
|
||||
#: Signature out of time window.
|
||||
BADTIME = 18
|
||||
#: Bad TKEY Mode.
|
||||
BADMODE = 19
|
||||
#: Duplicate key name.
|
||||
BADNAME = 20
|
||||
#: Algorithm not supported.
|
||||
BADALG = 21
|
||||
#: Bad Truncation
|
||||
BADTRUNC = 22
|
||||
#: Bad/missing Server Cookie
|
||||
BADCOOKIE = 23
|
||||
|
||||
@classmethod
|
||||
def _maximum(cls):
|
||||
return 4095
|
||||
|
||||
@classmethod
|
||||
def _unknown_exception_class(cls) -> Type[Exception]:
|
||||
return UnknownRcode
|
||||
|
||||
|
||||
class UnknownRcode(dns.exception.DNSException):
|
||||
"""A DNS rcode is unknown."""
|
||||
|
||||
|
||||
def from_text(text: str) -> Rcode:
|
||||
"""Convert text into an rcode.
|
||||
|
||||
*text*, a ``str``, the textual rcode or an integer in textual form.
|
||||
|
||||
Raises ``dns.rcode.UnknownRcode`` if the rcode mnemonic is unknown.
|
||||
|
||||
Returns a ``dns.rcode.Rcode``.
|
||||
"""
|
||||
|
||||
return Rcode.from_text(text)
|
||||
|
||||
|
||||
def from_flags(flags: int, ednsflags: int) -> Rcode:
|
||||
"""Return the rcode value encoded by flags and ednsflags.
|
||||
|
||||
*flags*, an ``int``, the DNS flags field.
|
||||
|
||||
*ednsflags*, an ``int``, the EDNS flags field.
|
||||
|
||||
Raises ``ValueError`` if rcode is < 0 or > 4095
|
||||
|
||||
Returns a ``dns.rcode.Rcode``.
|
||||
"""
|
||||
|
||||
value = (flags & 0x000F) | ((ednsflags >> 20) & 0xFF0)
|
||||
return Rcode.make(value)
|
||||
|
||||
|
||||
def to_flags(value: Rcode) -> Tuple[int, int]:
|
||||
"""Return a (flags, ednsflags) tuple which encodes the rcode.
|
||||
|
||||
*value*, a ``dns.rcode.Rcode``, the rcode.
|
||||
|
||||
Raises ``ValueError`` if rcode is < 0 or > 4095.
|
||||
|
||||
Returns an ``(int, int)`` tuple.
|
||||
"""
|
||||
|
||||
if value < 0 or value > 4095:
|
||||
raise ValueError("rcode must be >= 0 and <= 4095")
|
||||
v = value & 0xF
|
||||
ev = (value & 0xFF0) << 20
|
||||
return (v, ev)
|
||||
|
||||
|
||||
def to_text(value: Rcode, tsig: bool = False) -> str:
|
||||
"""Convert rcode into text.
|
||||
|
||||
*value*, a ``dns.rcode.Rcode``, the rcode.
|
||||
|
||||
Raises ``ValueError`` if rcode is < 0 or > 4095.
|
||||
|
||||
Returns a ``str``.
|
||||
"""
|
||||
|
||||
if tsig and value == Rcode.BADVERS:
|
||||
return "BADSIG"
|
||||
return Rcode.to_text(value)
|
||||
|
||||
|
||||
### BEGIN generated Rcode constants
|
||||
|
||||
NOERROR = Rcode.NOERROR
|
||||
FORMERR = Rcode.FORMERR
|
||||
SERVFAIL = Rcode.SERVFAIL
|
||||
NXDOMAIN = Rcode.NXDOMAIN
|
||||
NOTIMP = Rcode.NOTIMP
|
||||
REFUSED = Rcode.REFUSED
|
||||
YXDOMAIN = Rcode.YXDOMAIN
|
||||
YXRRSET = Rcode.YXRRSET
|
||||
NXRRSET = Rcode.NXRRSET
|
||||
NOTAUTH = Rcode.NOTAUTH
|
||||
NOTZONE = Rcode.NOTZONE
|
||||
DSOTYPENI = Rcode.DSOTYPENI
|
||||
BADVERS = Rcode.BADVERS
|
||||
BADSIG = Rcode.BADSIG
|
||||
BADKEY = Rcode.BADKEY
|
||||
BADTIME = Rcode.BADTIME
|
||||
BADMODE = Rcode.BADMODE
|
||||
BADNAME = Rcode.BADNAME
|
||||
BADALG = Rcode.BADALG
|
||||
BADTRUNC = Rcode.BADTRUNC
|
||||
BADCOOKIE = Rcode.BADCOOKIE
|
||||
|
||||
### END generated Rcode constants
|
||||
935
venv/Lib/site-packages/dns/rdata.py
Normal file
935
venv/Lib/site-packages/dns/rdata.py
Normal file
@@ -0,0 +1,935 @@
|
||||
# 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.
|
||||
|
||||
"""DNS rdata."""
|
||||
|
||||
import base64
|
||||
import binascii
|
||||
import inspect
|
||||
import io
|
||||
import ipaddress
|
||||
import itertools
|
||||
import random
|
||||
from importlib import import_module
|
||||
from typing import Any, Dict, Tuple
|
||||
|
||||
import dns.exception
|
||||
import dns.immutable
|
||||
import dns.ipv4
|
||||
import dns.ipv6
|
||||
import dns.name
|
||||
import dns.rdataclass
|
||||
import dns.rdatatype
|
||||
import dns.tokenizer
|
||||
import dns.ttl
|
||||
import dns.wire
|
||||
|
||||
_chunksize = 32
|
||||
|
||||
# We currently allow comparisons for rdata with relative names for backwards
|
||||
# compatibility, but in the future we will not, as these kinds of comparisons
|
||||
# can lead to subtle bugs if code is not carefully written.
|
||||
#
|
||||
# This switch allows the future behavior to be turned on so code can be
|
||||
# tested with it.
|
||||
_allow_relative_comparisons = True
|
||||
|
||||
|
||||
class NoRelativeRdataOrdering(dns.exception.DNSException):
|
||||
"""An attempt was made to do an ordered comparison of one or more
|
||||
rdata with relative names. The only reliable way of sorting rdata
|
||||
is to use non-relativized rdata.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def _wordbreak(data, chunksize=_chunksize, separator=b" "):
|
||||
"""Break a binary string into chunks of chunksize characters separated by
|
||||
a space.
|
||||
"""
|
||||
|
||||
if not chunksize:
|
||||
return data.decode()
|
||||
return separator.join(
|
||||
[data[i : i + chunksize] for i in range(0, len(data), chunksize)]
|
||||
).decode()
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
|
||||
|
||||
def _hexify(data, chunksize=_chunksize, separator=b" ", **kw):
|
||||
"""Convert a binary string into its hex encoding, broken up into chunks
|
||||
of chunksize characters separated by a separator.
|
||||
"""
|
||||
|
||||
return _wordbreak(binascii.hexlify(data), chunksize, separator)
|
||||
|
||||
|
||||
def _base64ify(data, chunksize=_chunksize, separator=b" ", **kw):
|
||||
"""Convert a binary string into its base64 encoding, broken up into chunks
|
||||
of chunksize characters separated by a separator.
|
||||
"""
|
||||
|
||||
return _wordbreak(base64.b64encode(data), chunksize, separator)
|
||||
|
||||
|
||||
# pylint: enable=unused-argument
|
||||
|
||||
__escaped = b'"\\'
|
||||
|
||||
|
||||
def _escapify(qstring):
|
||||
"""Escape the characters in a quoted string which need it."""
|
||||
|
||||
if isinstance(qstring, str):
|
||||
qstring = qstring.encode()
|
||||
if not isinstance(qstring, bytearray):
|
||||
qstring = bytearray(qstring)
|
||||
|
||||
text = ""
|
||||
for c in qstring:
|
||||
if c in __escaped:
|
||||
text += "\\" + chr(c)
|
||||
elif c >= 0x20 and c < 0x7F:
|
||||
text += chr(c)
|
||||
else:
|
||||
text += f"\\{c:03d}"
|
||||
return text
|
||||
|
||||
|
||||
def _truncate_bitmap(what):
|
||||
"""Determine the index of greatest byte that isn't all zeros, and
|
||||
return the bitmap that contains all the bytes less than that index.
|
||||
"""
|
||||
|
||||
for i in range(len(what) - 1, -1, -1):
|
||||
if what[i] != 0:
|
||||
return what[0 : i + 1]
|
||||
return what[0:1]
|
||||
|
||||
|
||||
# So we don't have to edit all the rdata classes...
|
||||
_constify = dns.immutable.constify
|
||||
|
||||
|
||||
@dns.immutable.immutable
|
||||
class Rdata:
|
||||
"""Base class for all DNS rdata types."""
|
||||
|
||||
__slots__ = ["rdclass", "rdtype", "rdcomment"]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
rdclass: dns.rdataclass.RdataClass,
|
||||
rdtype: dns.rdatatype.RdataType,
|
||||
) -> None:
|
||||
"""Initialize an rdata.
|
||||
|
||||
*rdclass*, an ``int`` is the rdataclass of the Rdata.
|
||||
|
||||
*rdtype*, an ``int`` is the rdatatype of the Rdata.
|
||||
"""
|
||||
|
||||
self.rdclass = self._as_rdataclass(rdclass)
|
||||
self.rdtype = self._as_rdatatype(rdtype)
|
||||
self.rdcomment = None
|
||||
|
||||
def _get_all_slots(self):
|
||||
return itertools.chain.from_iterable(
|
||||
getattr(cls, "__slots__", []) for cls in self.__class__.__mro__
|
||||
)
|
||||
|
||||
def __getstate__(self):
|
||||
# We used to try to do a tuple of all slots here, but it
|
||||
# doesn't work as self._all_slots isn't available at
|
||||
# __setstate__() time. Before that we tried to store a tuple
|
||||
# of __slots__, but that didn't work as it didn't store the
|
||||
# slots defined by ancestors. This older way didn't fail
|
||||
# outright, but ended up with partially broken objects, e.g.
|
||||
# if you unpickled an A RR it wouldn't have rdclass and rdtype
|
||||
# attributes, and would compare badly.
|
||||
state = {}
|
||||
for slot in self._get_all_slots():
|
||||
state[slot] = getattr(self, slot)
|
||||
return state
|
||||
|
||||
def __setstate__(self, state):
|
||||
for slot, val in state.items():
|
||||
object.__setattr__(self, slot, val)
|
||||
if not hasattr(self, "rdcomment"):
|
||||
# Pickled rdata from 2.0.x might not have a rdcomment, so add
|
||||
# it if needed.
|
||||
object.__setattr__(self, "rdcomment", None)
|
||||
|
||||
def covers(self) -> dns.rdatatype.RdataType:
|
||||
"""Return the type a Rdata covers.
|
||||
|
||||
DNS SIG/RRSIG rdatas apply to a specific type; this type is
|
||||
returned by the covers() function. If the rdata type is not
|
||||
SIG or RRSIG, dns.rdatatype.NONE is returned. This is useful when
|
||||
creating rdatasets, allowing the rdataset to contain only RRSIGs
|
||||
of a particular type, e.g. RRSIG(NS).
|
||||
|
||||
Returns a ``dns.rdatatype.RdataType``.
|
||||
"""
|
||||
|
||||
return dns.rdatatype.NONE
|
||||
|
||||
def extended_rdatatype(self) -> int:
|
||||
"""Return a 32-bit type value, the least significant 16 bits of
|
||||
which are the ordinary DNS type, and the upper 16 bits of which are
|
||||
the "covered" type, if any.
|
||||
|
||||
Returns an ``int``.
|
||||
"""
|
||||
|
||||
return self.covers() << 16 | self.rdtype
|
||||
|
||||
def to_text(
|
||||
self,
|
||||
origin: dns.name.Name | None = None,
|
||||
relativize: bool = True,
|
||||
**kw: Dict[str, Any],
|
||||
) -> str:
|
||||
"""Convert an rdata to text format.
|
||||
|
||||
Returns a ``str``.
|
||||
"""
|
||||
|
||||
raise NotImplementedError # pragma: no cover
|
||||
|
||||
def _to_wire(
|
||||
self,
|
||||
file: Any,
|
||||
compress: dns.name.CompressType | None = None,
|
||||
origin: dns.name.Name | None = None,
|
||||
canonicalize: bool = False,
|
||||
) -> None:
|
||||
raise NotImplementedError # pragma: no cover
|
||||
|
||||
def to_wire(
|
||||
self,
|
||||
file: Any | None = None,
|
||||
compress: dns.name.CompressType | None = None,
|
||||
origin: dns.name.Name | None = None,
|
||||
canonicalize: bool = False,
|
||||
) -> bytes | None:
|
||||
"""Convert an rdata to wire format.
|
||||
|
||||
Returns a ``bytes`` if no output file was specified, or ``None`` otherwise.
|
||||
"""
|
||||
|
||||
if file:
|
||||
# We call _to_wire() and then return None explicitly instead of
|
||||
# of just returning the None from _to_wire() as mypy's func-returns-value
|
||||
# unhelpfully errors out with "error: "_to_wire" of "Rdata" does not return
|
||||
# a value (it only ever returns None)"
|
||||
self._to_wire(file, compress, origin, canonicalize)
|
||||
return None
|
||||
else:
|
||||
f = io.BytesIO()
|
||||
self._to_wire(f, compress, origin, canonicalize)
|
||||
return f.getvalue()
|
||||
|
||||
def to_generic(self, origin: dns.name.Name | None = None) -> "GenericRdata":
|
||||
"""Creates a dns.rdata.GenericRdata equivalent of this rdata.
|
||||
|
||||
Returns a ``dns.rdata.GenericRdata``.
|
||||
"""
|
||||
wire = self.to_wire(origin=origin)
|
||||
assert wire is not None # for type checkers
|
||||
return GenericRdata(self.rdclass, self.rdtype, wire)
|
||||
|
||||
def to_digestable(self, origin: dns.name.Name | None = None) -> bytes:
|
||||
"""Convert rdata to a format suitable for digesting in hashes. This
|
||||
is also the DNSSEC canonical form.
|
||||
|
||||
Returns a ``bytes``.
|
||||
"""
|
||||
wire = self.to_wire(origin=origin, canonicalize=True)
|
||||
assert wire is not None # for mypy
|
||||
return wire
|
||||
|
||||
def __repr__(self):
|
||||
covers = self.covers()
|
||||
if covers == dns.rdatatype.NONE:
|
||||
ctext = ""
|
||||
else:
|
||||
ctext = "(" + dns.rdatatype.to_text(covers) + ")"
|
||||
return (
|
||||
"<DNS "
|
||||
+ dns.rdataclass.to_text(self.rdclass)
|
||||
+ " "
|
||||
+ dns.rdatatype.to_text(self.rdtype)
|
||||
+ ctext
|
||||
+ " rdata: "
|
||||
+ str(self)
|
||||
+ ">"
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.to_text()
|
||||
|
||||
def _cmp(self, other):
|
||||
"""Compare an rdata with another rdata of the same rdtype and
|
||||
rdclass.
|
||||
|
||||
For rdata with only absolute names:
|
||||
Return < 0 if self < other in the DNSSEC ordering, 0 if self
|
||||
== other, and > 0 if self > other.
|
||||
For rdata with at least one relative names:
|
||||
The rdata sorts before any rdata with only absolute names.
|
||||
When compared with another relative rdata, all names are
|
||||
made absolute as if they were relative to the root, as the
|
||||
proper origin is not available. While this creates a stable
|
||||
ordering, it is NOT guaranteed to be the DNSSEC ordering.
|
||||
In the future, all ordering comparisons for rdata with
|
||||
relative names will be disallowed.
|
||||
"""
|
||||
# the next two lines are for type checkers, so they are bound
|
||||
our = b""
|
||||
their = b""
|
||||
try:
|
||||
our = self.to_digestable()
|
||||
our_relative = False
|
||||
except dns.name.NeedAbsoluteNameOrOrigin:
|
||||
if _allow_relative_comparisons:
|
||||
our = self.to_digestable(dns.name.root)
|
||||
our_relative = True
|
||||
try:
|
||||
their = other.to_digestable()
|
||||
their_relative = False
|
||||
except dns.name.NeedAbsoluteNameOrOrigin:
|
||||
if _allow_relative_comparisons:
|
||||
their = other.to_digestable(dns.name.root)
|
||||
their_relative = True
|
||||
if _allow_relative_comparisons:
|
||||
if our_relative != their_relative:
|
||||
# For the purpose of comparison, all rdata with at least one
|
||||
# relative name is less than an rdata with only absolute names.
|
||||
if our_relative:
|
||||
return -1
|
||||
else:
|
||||
return 1
|
||||
elif our_relative or their_relative:
|
||||
raise NoRelativeRdataOrdering
|
||||
if our == their:
|
||||
return 0
|
||||
elif our > their:
|
||||
return 1
|
||||
else:
|
||||
return -1
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, Rdata):
|
||||
return False
|
||||
if self.rdclass != other.rdclass or self.rdtype != other.rdtype:
|
||||
return False
|
||||
our_relative = False
|
||||
their_relative = False
|
||||
try:
|
||||
our = self.to_digestable()
|
||||
except dns.name.NeedAbsoluteNameOrOrigin:
|
||||
our = self.to_digestable(dns.name.root)
|
||||
our_relative = True
|
||||
try:
|
||||
their = other.to_digestable()
|
||||
except dns.name.NeedAbsoluteNameOrOrigin:
|
||||
their = other.to_digestable(dns.name.root)
|
||||
their_relative = True
|
||||
if our_relative != their_relative:
|
||||
return False
|
||||
return our == their
|
||||
|
||||
def __ne__(self, other):
|
||||
if not isinstance(other, Rdata):
|
||||
return True
|
||||
if self.rdclass != other.rdclass or self.rdtype != other.rdtype:
|
||||
return True
|
||||
return not self.__eq__(other)
|
||||
|
||||
def __lt__(self, other):
|
||||
if (
|
||||
not isinstance(other, Rdata)
|
||||
or self.rdclass != other.rdclass
|
||||
or self.rdtype != other.rdtype
|
||||
):
|
||||
return NotImplemented
|
||||
return self._cmp(other) < 0
|
||||
|
||||
def __le__(self, other):
|
||||
if (
|
||||
not isinstance(other, Rdata)
|
||||
or self.rdclass != other.rdclass
|
||||
or self.rdtype != other.rdtype
|
||||
):
|
||||
return NotImplemented
|
||||
return self._cmp(other) <= 0
|
||||
|
||||
def __ge__(self, other):
|
||||
if (
|
||||
not isinstance(other, Rdata)
|
||||
or self.rdclass != other.rdclass
|
||||
or self.rdtype != other.rdtype
|
||||
):
|
||||
return NotImplemented
|
||||
return self._cmp(other) >= 0
|
||||
|
||||
def __gt__(self, other):
|
||||
if (
|
||||
not isinstance(other, Rdata)
|
||||
or self.rdclass != other.rdclass
|
||||
or self.rdtype != other.rdtype
|
||||
):
|
||||
return NotImplemented
|
||||
return self._cmp(other) > 0
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.to_digestable(dns.name.root))
|
||||
|
||||
@classmethod
|
||||
def from_text(
|
||||
cls,
|
||||
rdclass: dns.rdataclass.RdataClass,
|
||||
rdtype: dns.rdatatype.RdataType,
|
||||
tok: dns.tokenizer.Tokenizer,
|
||||
origin: dns.name.Name | None = None,
|
||||
relativize: bool = True,
|
||||
relativize_to: dns.name.Name | None = None,
|
||||
) -> "Rdata":
|
||||
raise NotImplementedError # pragma: no cover
|
||||
|
||||
@classmethod
|
||||
def from_wire_parser(
|
||||
cls,
|
||||
rdclass: dns.rdataclass.RdataClass,
|
||||
rdtype: dns.rdatatype.RdataType,
|
||||
parser: dns.wire.Parser,
|
||||
origin: dns.name.Name | None = None,
|
||||
) -> "Rdata":
|
||||
raise NotImplementedError # pragma: no cover
|
||||
|
||||
def replace(self, **kwargs: Any) -> "Rdata":
|
||||
"""
|
||||
Create a new Rdata instance based on the instance replace was
|
||||
invoked on. It is possible to pass different parameters to
|
||||
override the corresponding properties of the base Rdata.
|
||||
|
||||
Any field specific to the Rdata type can be replaced, but the
|
||||
*rdtype* and *rdclass* fields cannot.
|
||||
|
||||
Returns an instance of the same Rdata subclass as *self*.
|
||||
"""
|
||||
|
||||
# Get the constructor parameters.
|
||||
parameters = inspect.signature(self.__init__).parameters # type: ignore
|
||||
|
||||
# Ensure that all of the arguments correspond to valid fields.
|
||||
# Don't allow rdclass or rdtype to be changed, though.
|
||||
for key in kwargs:
|
||||
if key == "rdcomment":
|
||||
continue
|
||||
if key not in parameters:
|
||||
raise AttributeError(
|
||||
f"'{self.__class__.__name__}' object has no attribute '{key}'"
|
||||
)
|
||||
if key in ("rdclass", "rdtype"):
|
||||
raise AttributeError(
|
||||
f"Cannot overwrite '{self.__class__.__name__}' attribute '{key}'"
|
||||
)
|
||||
|
||||
# Construct the parameter list. For each field, use the value in
|
||||
# kwargs if present, and the current value otherwise.
|
||||
args = (kwargs.get(key, getattr(self, key)) for key in parameters)
|
||||
|
||||
# Create, validate, and return the new object.
|
||||
rd = self.__class__(*args)
|
||||
# The comment is not set in the constructor, so give it special
|
||||
# handling.
|
||||
rdcomment = kwargs.get("rdcomment", self.rdcomment)
|
||||
if rdcomment is not None:
|
||||
object.__setattr__(rd, "rdcomment", rdcomment)
|
||||
return rd
|
||||
|
||||
# Type checking and conversion helpers. These are class methods as
|
||||
# they don't touch object state and may be useful to others.
|
||||
|
||||
@classmethod
|
||||
def _as_rdataclass(cls, value):
|
||||
return dns.rdataclass.RdataClass.make(value)
|
||||
|
||||
@classmethod
|
||||
def _as_rdatatype(cls, value):
|
||||
return dns.rdatatype.RdataType.make(value)
|
||||
|
||||
@classmethod
|
||||
def _as_bytes(
|
||||
cls,
|
||||
value: Any,
|
||||
encode: bool = False,
|
||||
max_length: int | None = None,
|
||||
empty_ok: bool = True,
|
||||
) -> bytes:
|
||||
if encode and isinstance(value, str):
|
||||
bvalue = value.encode()
|
||||
elif isinstance(value, bytearray):
|
||||
bvalue = bytes(value)
|
||||
elif isinstance(value, bytes):
|
||||
bvalue = value
|
||||
else:
|
||||
raise ValueError("not bytes")
|
||||
if max_length is not None and len(bvalue) > max_length:
|
||||
raise ValueError("too long")
|
||||
if not empty_ok and len(bvalue) == 0:
|
||||
raise ValueError("empty bytes not allowed")
|
||||
return bvalue
|
||||
|
||||
@classmethod
|
||||
def _as_name(cls, value):
|
||||
# Note that proper name conversion (e.g. with origin and IDNA
|
||||
# awareness) is expected to be done via from_text. This is just
|
||||
# a simple thing for people invoking the constructor directly.
|
||||
if isinstance(value, str):
|
||||
return dns.name.from_text(value)
|
||||
elif not isinstance(value, dns.name.Name):
|
||||
raise ValueError("not a name")
|
||||
return value
|
||||
|
||||
@classmethod
|
||||
def _as_uint8(cls, value):
|
||||
if not isinstance(value, int):
|
||||
raise ValueError("not an integer")
|
||||
if value < 0 or value > 255:
|
||||
raise ValueError("not a uint8")
|
||||
return value
|
||||
|
||||
@classmethod
|
||||
def _as_uint16(cls, value):
|
||||
if not isinstance(value, int):
|
||||
raise ValueError("not an integer")
|
||||
if value < 0 or value > 65535:
|
||||
raise ValueError("not a uint16")
|
||||
return value
|
||||
|
||||
@classmethod
|
||||
def _as_uint32(cls, value):
|
||||
if not isinstance(value, int):
|
||||
raise ValueError("not an integer")
|
||||
if value < 0 or value > 4294967295:
|
||||
raise ValueError("not a uint32")
|
||||
return value
|
||||
|
||||
@classmethod
|
||||
def _as_uint48(cls, value):
|
||||
if not isinstance(value, int):
|
||||
raise ValueError("not an integer")
|
||||
if value < 0 or value > 281474976710655:
|
||||
raise ValueError("not a uint48")
|
||||
return value
|
||||
|
||||
@classmethod
|
||||
def _as_int(cls, value, low=None, high=None):
|
||||
if not isinstance(value, int):
|
||||
raise ValueError("not an integer")
|
||||
if low is not None and value < low:
|
||||
raise ValueError("value too small")
|
||||
if high is not None and value > high:
|
||||
raise ValueError("value too large")
|
||||
return value
|
||||
|
||||
@classmethod
|
||||
def _as_ipv4_address(cls, value):
|
||||
if isinstance(value, str):
|
||||
return dns.ipv4.canonicalize(value)
|
||||
elif isinstance(value, bytes):
|
||||
return dns.ipv4.inet_ntoa(value)
|
||||
elif isinstance(value, ipaddress.IPv4Address):
|
||||
return dns.ipv4.inet_ntoa(value.packed)
|
||||
else:
|
||||
raise ValueError("not an IPv4 address")
|
||||
|
||||
@classmethod
|
||||
def _as_ipv6_address(cls, value):
|
||||
if isinstance(value, str):
|
||||
return dns.ipv6.canonicalize(value)
|
||||
elif isinstance(value, bytes):
|
||||
return dns.ipv6.inet_ntoa(value)
|
||||
elif isinstance(value, ipaddress.IPv6Address):
|
||||
return dns.ipv6.inet_ntoa(value.packed)
|
||||
else:
|
||||
raise ValueError("not an IPv6 address")
|
||||
|
||||
@classmethod
|
||||
def _as_bool(cls, value):
|
||||
if isinstance(value, bool):
|
||||
return value
|
||||
else:
|
||||
raise ValueError("not a boolean")
|
||||
|
||||
@classmethod
|
||||
def _as_ttl(cls, value):
|
||||
if isinstance(value, int):
|
||||
return cls._as_int(value, 0, dns.ttl.MAX_TTL)
|
||||
elif isinstance(value, str):
|
||||
return dns.ttl.from_text(value)
|
||||
else:
|
||||
raise ValueError("not a TTL")
|
||||
|
||||
@classmethod
|
||||
def _as_tuple(cls, value, as_value):
|
||||
try:
|
||||
# For user convenience, if value is a singleton of the list
|
||||
# element type, wrap it in a tuple.
|
||||
return (as_value(value),)
|
||||
except Exception:
|
||||
# Otherwise, check each element of the iterable *value*
|
||||
# against *as_value*.
|
||||
return tuple(as_value(v) for v in value)
|
||||
|
||||
# Processing order
|
||||
|
||||
@classmethod
|
||||
def _processing_order(cls, iterable):
|
||||
items = list(iterable)
|
||||
random.shuffle(items)
|
||||
return items
|
||||
|
||||
|
||||
@dns.immutable.immutable
|
||||
class GenericRdata(Rdata):
|
||||
"""Generic Rdata Class
|
||||
|
||||
This class is used for rdata types for which we have no better
|
||||
implementation. It implements the DNS "unknown RRs" scheme.
|
||||
"""
|
||||
|
||||
__slots__ = ["data"]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
rdclass: dns.rdataclass.RdataClass,
|
||||
rdtype: dns.rdatatype.RdataType,
|
||||
data: bytes,
|
||||
) -> None:
|
||||
super().__init__(rdclass, rdtype)
|
||||
self.data = data
|
||||
|
||||
def to_text(
|
||||
self,
|
||||
origin: dns.name.Name | None = None,
|
||||
relativize: bool = True,
|
||||
**kw: Dict[str, Any],
|
||||
) -> str:
|
||||
return rf"\# {len(self.data)} " + _hexify(self.data, **kw) # pyright: ignore
|
||||
|
||||
@classmethod
|
||||
def from_text(
|
||||
cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
|
||||
):
|
||||
token = tok.get()
|
||||
if not token.is_identifier() or token.value != r"\#":
|
||||
raise dns.exception.SyntaxError(r"generic rdata does not start with \#")
|
||||
length = tok.get_int()
|
||||
hex = tok.concatenate_remaining_identifiers(True).encode()
|
||||
data = binascii.unhexlify(hex)
|
||||
if len(data) != length:
|
||||
raise dns.exception.SyntaxError("generic rdata hex data has wrong length")
|
||||
return cls(rdclass, rdtype, data)
|
||||
|
||||
def _to_wire(self, file, compress=None, origin=None, canonicalize=False):
|
||||
file.write(self.data)
|
||||
|
||||
def to_generic(self, origin: dns.name.Name | None = None) -> "GenericRdata":
|
||||
return self
|
||||
|
||||
@classmethod
|
||||
def from_wire_parser(cls, rdclass, rdtype, parser, origin=None):
|
||||
return cls(rdclass, rdtype, parser.get_remaining())
|
||||
|
||||
|
||||
_rdata_classes: Dict[Tuple[dns.rdataclass.RdataClass, dns.rdatatype.RdataType], Any] = (
|
||||
{}
|
||||
)
|
||||
_module_prefix = "dns.rdtypes"
|
||||
_dynamic_load_allowed = True
|
||||
|
||||
|
||||
def get_rdata_class(rdclass, rdtype, use_generic=True):
|
||||
cls = _rdata_classes.get((rdclass, rdtype))
|
||||
if not cls:
|
||||
cls = _rdata_classes.get((dns.rdataclass.ANY, rdtype))
|
||||
if not cls and _dynamic_load_allowed:
|
||||
rdclass_text = dns.rdataclass.to_text(rdclass)
|
||||
rdtype_text = dns.rdatatype.to_text(rdtype)
|
||||
rdtype_text = rdtype_text.replace("-", "_")
|
||||
try:
|
||||
mod = import_module(
|
||||
".".join([_module_prefix, rdclass_text, rdtype_text])
|
||||
)
|
||||
cls = getattr(mod, rdtype_text)
|
||||
_rdata_classes[(rdclass, rdtype)] = cls
|
||||
except ImportError:
|
||||
try:
|
||||
mod = import_module(".".join([_module_prefix, "ANY", rdtype_text]))
|
||||
cls = getattr(mod, rdtype_text)
|
||||
_rdata_classes[(dns.rdataclass.ANY, rdtype)] = cls
|
||||
_rdata_classes[(rdclass, rdtype)] = cls
|
||||
except ImportError:
|
||||
pass
|
||||
if not cls and use_generic:
|
||||
cls = GenericRdata
|
||||
_rdata_classes[(rdclass, rdtype)] = cls
|
||||
return cls
|
||||
|
||||
|
||||
def load_all_types(disable_dynamic_load=True):
|
||||
"""Load all rdata types for which dnspython has a non-generic implementation.
|
||||
|
||||
Normally dnspython loads DNS rdatatype implementations on demand, but in some
|
||||
specialized cases loading all types at an application-controlled time is preferred.
|
||||
|
||||
If *disable_dynamic_load*, a ``bool``, is ``True`` then dnspython will not attempt
|
||||
to use its dynamic loading mechanism if an unknown type is subsequently encountered,
|
||||
and will simply use the ``GenericRdata`` class.
|
||||
"""
|
||||
# Load class IN and ANY types.
|
||||
for rdtype in dns.rdatatype.RdataType:
|
||||
get_rdata_class(dns.rdataclass.IN, rdtype, False)
|
||||
# Load the one non-ANY implementation we have in CH. Everything
|
||||
# else in CH is an ANY type, and we'll discover those on demand but won't
|
||||
# have to import anything.
|
||||
get_rdata_class(dns.rdataclass.CH, dns.rdatatype.A, False)
|
||||
if disable_dynamic_load:
|
||||
# Now disable dynamic loading so any subsequent unknown type immediately becomes
|
||||
# GenericRdata without a load attempt.
|
||||
global _dynamic_load_allowed
|
||||
_dynamic_load_allowed = False
|
||||
|
||||
|
||||
def from_text(
|
||||
rdclass: dns.rdataclass.RdataClass | str,
|
||||
rdtype: dns.rdatatype.RdataType | str,
|
||||
tok: dns.tokenizer.Tokenizer | str,
|
||||
origin: dns.name.Name | None = None,
|
||||
relativize: bool = True,
|
||||
relativize_to: dns.name.Name | None = None,
|
||||
idna_codec: dns.name.IDNACodec | None = None,
|
||||
) -> Rdata:
|
||||
"""Build an rdata object from text format.
|
||||
|
||||
This function attempts to dynamically load a class which
|
||||
implements the specified rdata class and type. If there is no
|
||||
class-and-type-specific implementation, the GenericRdata class
|
||||
is used.
|
||||
|
||||
Once a class is chosen, its from_text() class method is called
|
||||
with the parameters to this function.
|
||||
|
||||
If *tok* is a ``str``, then a tokenizer is created and the string
|
||||
is used as its input.
|
||||
|
||||
*rdclass*, a ``dns.rdataclass.RdataClass`` or ``str``, the rdataclass.
|
||||
|
||||
*rdtype*, a ``dns.rdatatype.RdataType`` or ``str``, the rdatatype.
|
||||
|
||||
*tok*, a ``dns.tokenizer.Tokenizer`` or a ``str``.
|
||||
|
||||
*origin*, a ``dns.name.Name`` (or ``None``), the
|
||||
origin to use for relative names.
|
||||
|
||||
*relativize*, a ``bool``. If true, name will be relativized.
|
||||
|
||||
*relativize_to*, a ``dns.name.Name`` (or ``None``), the origin to use
|
||||
when relativizing names. If not set, the *origin* value will be used.
|
||||
|
||||
*idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA
|
||||
encoder/decoder to use if a tokenizer needs to be created. If
|
||||
``None``, the default IDNA 2003 encoder/decoder is used. If a
|
||||
tokenizer is not created, then the codec associated with the tokenizer
|
||||
is the one that is used.
|
||||
|
||||
Returns an instance of the chosen Rdata subclass.
|
||||
|
||||
"""
|
||||
if isinstance(tok, str):
|
||||
tok = dns.tokenizer.Tokenizer(tok, idna_codec=idna_codec)
|
||||
if not isinstance(tok, dns.tokenizer.Tokenizer):
|
||||
raise ValueError("tok must be a string or a Tokenizer")
|
||||
rdclass = dns.rdataclass.RdataClass.make(rdclass)
|
||||
rdtype = dns.rdatatype.RdataType.make(rdtype)
|
||||
cls = get_rdata_class(rdclass, rdtype)
|
||||
assert cls is not None # for type checkers
|
||||
with dns.exception.ExceptionWrapper(dns.exception.SyntaxError):
|
||||
rdata = None
|
||||
if cls != GenericRdata:
|
||||
# peek at first token
|
||||
token = tok.get()
|
||||
tok.unget(token)
|
||||
if token.is_identifier() and token.value == r"\#":
|
||||
#
|
||||
# Known type using the generic syntax. Extract the
|
||||
# wire form from the generic syntax, and then run
|
||||
# from_wire on it.
|
||||
#
|
||||
grdata = GenericRdata.from_text(
|
||||
rdclass, rdtype, tok, origin, relativize, relativize_to
|
||||
)
|
||||
rdata = from_wire(
|
||||
rdclass, rdtype, grdata.data, 0, len(grdata.data), origin
|
||||
)
|
||||
#
|
||||
# If this comparison isn't equal, then there must have been
|
||||
# compressed names in the wire format, which is an error,
|
||||
# there being no reasonable context to decompress with.
|
||||
#
|
||||
rwire = rdata.to_wire()
|
||||
if rwire != grdata.data:
|
||||
raise dns.exception.SyntaxError(
|
||||
"compressed data in "
|
||||
"generic syntax form "
|
||||
"of known rdatatype"
|
||||
)
|
||||
if rdata is None:
|
||||
rdata = cls.from_text(
|
||||
rdclass, rdtype, tok, origin, relativize, relativize_to
|
||||
)
|
||||
token = tok.get_eol_as_token()
|
||||
if token.comment is not None:
|
||||
object.__setattr__(rdata, "rdcomment", token.comment)
|
||||
return rdata
|
||||
|
||||
|
||||
def from_wire_parser(
|
||||
rdclass: dns.rdataclass.RdataClass | str,
|
||||
rdtype: dns.rdatatype.RdataType | str,
|
||||
parser: dns.wire.Parser,
|
||||
origin: dns.name.Name | None = None,
|
||||
) -> Rdata:
|
||||
"""Build an rdata object from wire format
|
||||
|
||||
This function attempts to dynamically load a class which
|
||||
implements the specified rdata class and type. If there is no
|
||||
class-and-type-specific implementation, the GenericRdata class
|
||||
is used.
|
||||
|
||||
Once a class is chosen, its from_wire() class method is called
|
||||
with the parameters to this function.
|
||||
|
||||
*rdclass*, a ``dns.rdataclass.RdataClass`` or ``str``, the rdataclass.
|
||||
|
||||
*rdtype*, a ``dns.rdatatype.RdataType`` or ``str``, the rdatatype.
|
||||
|
||||
*parser*, a ``dns.wire.Parser``, the parser, which should be
|
||||
restricted to the rdata length.
|
||||
|
||||
*origin*, a ``dns.name.Name`` (or ``None``). If not ``None``,
|
||||
then names will be relativized to this origin.
|
||||
|
||||
Returns an instance of the chosen Rdata subclass.
|
||||
"""
|
||||
|
||||
rdclass = dns.rdataclass.RdataClass.make(rdclass)
|
||||
rdtype = dns.rdatatype.RdataType.make(rdtype)
|
||||
cls = get_rdata_class(rdclass, rdtype)
|
||||
assert cls is not None # for type checkers
|
||||
with dns.exception.ExceptionWrapper(dns.exception.FormError):
|
||||
return cls.from_wire_parser(rdclass, rdtype, parser, origin)
|
||||
|
||||
|
||||
def from_wire(
|
||||
rdclass: dns.rdataclass.RdataClass | str,
|
||||
rdtype: dns.rdatatype.RdataType | str,
|
||||
wire: bytes,
|
||||
current: int,
|
||||
rdlen: int,
|
||||
origin: dns.name.Name | None = None,
|
||||
) -> Rdata:
|
||||
"""Build an rdata object from wire format
|
||||
|
||||
This function attempts to dynamically load a class which
|
||||
implements the specified rdata class and type. If there is no
|
||||
class-and-type-specific implementation, the GenericRdata class
|
||||
is used.
|
||||
|
||||
Once a class is chosen, its from_wire() class method is called
|
||||
with the parameters to this function.
|
||||
|
||||
*rdclass*, an ``int``, the rdataclass.
|
||||
|
||||
*rdtype*, an ``int``, the rdatatype.
|
||||
|
||||
*wire*, a ``bytes``, the wire-format message.
|
||||
|
||||
*current*, an ``int``, the offset in wire of the beginning of
|
||||
the rdata.
|
||||
|
||||
*rdlen*, an ``int``, the length of the wire-format rdata
|
||||
|
||||
*origin*, a ``dns.name.Name`` (or ``None``). If not ``None``,
|
||||
then names will be relativized to this origin.
|
||||
|
||||
Returns an instance of the chosen Rdata subclass.
|
||||
"""
|
||||
parser = dns.wire.Parser(wire, current)
|
||||
with parser.restrict_to(rdlen):
|
||||
return from_wire_parser(rdclass, rdtype, parser, origin)
|
||||
|
||||
|
||||
class RdatatypeExists(dns.exception.DNSException):
|
||||
"""DNS rdatatype already exists."""
|
||||
|
||||
supp_kwargs = {"rdclass", "rdtype"}
|
||||
fmt = (
|
||||
"The rdata type with class {rdclass:d} and rdtype {rdtype:d} "
|
||||
+ "already exists."
|
||||
)
|
||||
|
||||
|
||||
def register_type(
|
||||
implementation: Any,
|
||||
rdtype: int,
|
||||
rdtype_text: str,
|
||||
is_singleton: bool = False,
|
||||
rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN,
|
||||
) -> None:
|
||||
"""Dynamically register a module to handle an rdatatype.
|
||||
|
||||
*implementation*, a subclass of ``dns.rdata.Rdata`` implementing the type,
|
||||
or a module containing such a class named by its text form.
|
||||
|
||||
*rdtype*, an ``int``, the rdatatype to register.
|
||||
|
||||
*rdtype_text*, a ``str``, the textual form of the rdatatype.
|
||||
|
||||
*is_singleton*, a ``bool``, indicating if the type is a singleton (i.e.
|
||||
RRsets of the type can have only one member.)
|
||||
|
||||
*rdclass*, the rdataclass of the type, or ``dns.rdataclass.ANY`` if
|
||||
it applies to all classes.
|
||||
"""
|
||||
|
||||
rdtype = dns.rdatatype.RdataType.make(rdtype)
|
||||
existing_cls = get_rdata_class(rdclass, rdtype)
|
||||
if existing_cls != GenericRdata or dns.rdatatype.is_metatype(rdtype):
|
||||
raise RdatatypeExists(rdclass=rdclass, rdtype=rdtype)
|
||||
if isinstance(implementation, type) and issubclass(implementation, Rdata):
|
||||
impclass = implementation
|
||||
else:
|
||||
impclass = getattr(implementation, rdtype_text.replace("-", "_"))
|
||||
_rdata_classes[(rdclass, rdtype)] = impclass
|
||||
dns.rdatatype.register_type(rdtype, rdtype_text, is_singleton)
|
||||
118
venv/Lib/site-packages/dns/rdataclass.py
Normal file
118
venv/Lib/site-packages/dns/rdataclass.py
Normal file
@@ -0,0 +1,118 @@
|
||||
# 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.
|
||||
|
||||
"""DNS Rdata Classes."""
|
||||
|
||||
import dns.enum
|
||||
import dns.exception
|
||||
|
||||
|
||||
class RdataClass(dns.enum.IntEnum):
|
||||
"""DNS Rdata Class"""
|
||||
|
||||
RESERVED0 = 0
|
||||
IN = 1
|
||||
INTERNET = IN
|
||||
CH = 3
|
||||
CHAOS = CH
|
||||
HS = 4
|
||||
HESIOD = HS
|
||||
NONE = 254
|
||||
ANY = 255
|
||||
|
||||
@classmethod
|
||||
def _maximum(cls):
|
||||
return 65535
|
||||
|
||||
@classmethod
|
||||
def _short_name(cls):
|
||||
return "class"
|
||||
|
||||
@classmethod
|
||||
def _prefix(cls):
|
||||
return "CLASS"
|
||||
|
||||
@classmethod
|
||||
def _unknown_exception_class(cls):
|
||||
return UnknownRdataclass
|
||||
|
||||
|
||||
_metaclasses = {RdataClass.NONE, RdataClass.ANY}
|
||||
|
||||
|
||||
class UnknownRdataclass(dns.exception.DNSException):
|
||||
"""A DNS class is unknown."""
|
||||
|
||||
|
||||
def from_text(text: str) -> RdataClass:
|
||||
"""Convert text into a DNS rdata class value.
|
||||
|
||||
The input text can be a defined DNS RR class mnemonic or
|
||||
instance of the DNS generic class syntax.
|
||||
|
||||
For example, "IN" and "CLASS1" will both result in a value of 1.
|
||||
|
||||
Raises ``dns.rdatatype.UnknownRdataclass`` if the class is unknown.
|
||||
|
||||
Raises ``ValueError`` if the rdata class value is not >= 0 and <= 65535.
|
||||
|
||||
Returns a ``dns.rdataclass.RdataClass``.
|
||||
"""
|
||||
|
||||
return RdataClass.from_text(text)
|
||||
|
||||
|
||||
def to_text(value: RdataClass) -> str:
|
||||
"""Convert a DNS rdata class value to text.
|
||||
|
||||
If the value has a known mnemonic, it will be used, otherwise the
|
||||
DNS generic class syntax will be used.
|
||||
|
||||
Raises ``ValueError`` if the rdata class value is not >= 0 and <= 65535.
|
||||
|
||||
Returns a ``str``.
|
||||
"""
|
||||
|
||||
return RdataClass.to_text(value)
|
||||
|
||||
|
||||
def is_metaclass(rdclass: RdataClass) -> bool:
|
||||
"""True if the specified class is a metaclass.
|
||||
|
||||
The currently defined metaclasses are ANY and NONE.
|
||||
|
||||
*rdclass* is a ``dns.rdataclass.RdataClass``.
|
||||
"""
|
||||
|
||||
if rdclass in _metaclasses:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
### BEGIN generated RdataClass constants
|
||||
|
||||
RESERVED0 = RdataClass.RESERVED0
|
||||
IN = RdataClass.IN
|
||||
INTERNET = RdataClass.INTERNET
|
||||
CH = RdataClass.CH
|
||||
CHAOS = RdataClass.CHAOS
|
||||
HS = RdataClass.HS
|
||||
HESIOD = RdataClass.HESIOD
|
||||
NONE = RdataClass.NONE
|
||||
ANY = RdataClass.ANY
|
||||
|
||||
### END generated RdataClass constants
|
||||
508
venv/Lib/site-packages/dns/rdataset.py
Normal file
508
venv/Lib/site-packages/dns/rdataset.py
Normal file
@@ -0,0 +1,508 @@
|
||||
# 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.
|
||||
|
||||
"""DNS rdatasets (an rdataset is a set of rdatas of a given type and class)"""
|
||||
|
||||
import io
|
||||
import random
|
||||
import struct
|
||||
from typing import Any, Collection, Dict, List, cast
|
||||
|
||||
import dns.exception
|
||||
import dns.immutable
|
||||
import dns.name
|
||||
import dns.rdata
|
||||
import dns.rdataclass
|
||||
import dns.rdatatype
|
||||
import dns.renderer
|
||||
import dns.set
|
||||
import dns.ttl
|
||||
|
||||
# define SimpleSet here for backwards compatibility
|
||||
SimpleSet = dns.set.Set
|
||||
|
||||
|
||||
class DifferingCovers(dns.exception.DNSException):
|
||||
"""An attempt was made to add a DNS SIG/RRSIG whose covered type
|
||||
is not the same as that of the other rdatas in the rdataset."""
|
||||
|
||||
|
||||
class IncompatibleTypes(dns.exception.DNSException):
|
||||
"""An attempt was made to add DNS RR data of an incompatible type."""
|
||||
|
||||
|
||||
class Rdataset(dns.set.Set):
|
||||
"""A DNS rdataset."""
|
||||
|
||||
__slots__ = ["rdclass", "rdtype", "covers", "ttl"]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
rdclass: dns.rdataclass.RdataClass,
|
||||
rdtype: dns.rdatatype.RdataType,
|
||||
covers: dns.rdatatype.RdataType = dns.rdatatype.NONE,
|
||||
ttl: int = 0,
|
||||
):
|
||||
"""Create a new rdataset of the specified class and type.
|
||||
|
||||
*rdclass*, a ``dns.rdataclass.RdataClass``, the rdataclass.
|
||||
|
||||
*rdtype*, an ``dns.rdatatype.RdataType``, the rdatatype.
|
||||
|
||||
*covers*, an ``dns.rdatatype.RdataType``, the covered rdatatype.
|
||||
|
||||
*ttl*, an ``int``, the TTL.
|
||||
"""
|
||||
|
||||
super().__init__()
|
||||
self.rdclass = rdclass
|
||||
self.rdtype: dns.rdatatype.RdataType = rdtype
|
||||
self.covers: dns.rdatatype.RdataType = covers
|
||||
self.ttl = ttl
|
||||
|
||||
def _clone(self):
|
||||
obj = cast(Rdataset, super()._clone())
|
||||
obj.rdclass = self.rdclass
|
||||
obj.rdtype = self.rdtype
|
||||
obj.covers = self.covers
|
||||
obj.ttl = self.ttl
|
||||
return obj
|
||||
|
||||
def update_ttl(self, ttl: int) -> None:
|
||||
"""Perform TTL minimization.
|
||||
|
||||
Set the TTL of the rdataset to be the lesser of the set's current
|
||||
TTL or the specified TTL. If the set contains no rdatas, set the TTL
|
||||
to the specified TTL.
|
||||
|
||||
*ttl*, an ``int`` or ``str``.
|
||||
"""
|
||||
ttl = dns.ttl.make(ttl)
|
||||
if len(self) == 0:
|
||||
self.ttl = ttl
|
||||
elif ttl < self.ttl:
|
||||
self.ttl = ttl
|
||||
|
||||
# pylint: disable=arguments-differ,arguments-renamed
|
||||
def add( # pyright: ignore
|
||||
self, rd: dns.rdata.Rdata, ttl: int | None = None
|
||||
) -> None:
|
||||
"""Add the specified rdata to the rdataset.
|
||||
|
||||
If the optional *ttl* parameter is supplied, then
|
||||
``self.update_ttl(ttl)`` will be called prior to adding the rdata.
|
||||
|
||||
*rd*, a ``dns.rdata.Rdata``, the rdata
|
||||
|
||||
*ttl*, an ``int``, the TTL.
|
||||
|
||||
Raises ``dns.rdataset.IncompatibleTypes`` if the type and class
|
||||
do not match the type and class of the rdataset.
|
||||
|
||||
Raises ``dns.rdataset.DifferingCovers`` if the type is a signature
|
||||
type and the covered type does not match that of the rdataset.
|
||||
"""
|
||||
|
||||
#
|
||||
# If we're adding a signature, do some special handling to
|
||||
# check that the signature covers the same type as the
|
||||
# other rdatas in this rdataset. If this is the first rdata
|
||||
# in the set, initialize the covers field.
|
||||
#
|
||||
if self.rdclass != rd.rdclass or self.rdtype != rd.rdtype:
|
||||
raise IncompatibleTypes
|
||||
if ttl is not None:
|
||||
self.update_ttl(ttl)
|
||||
if self.rdtype == dns.rdatatype.RRSIG or self.rdtype == dns.rdatatype.SIG:
|
||||
covers = rd.covers()
|
||||
if len(self) == 0 and self.covers == dns.rdatatype.NONE:
|
||||
self.covers = covers
|
||||
elif self.covers != covers:
|
||||
raise DifferingCovers
|
||||
if dns.rdatatype.is_singleton(rd.rdtype) and len(self) > 0:
|
||||
self.clear()
|
||||
super().add(rd)
|
||||
|
||||
def union_update(self, other):
|
||||
self.update_ttl(other.ttl)
|
||||
super().union_update(other)
|
||||
|
||||
def intersection_update(self, other):
|
||||
self.update_ttl(other.ttl)
|
||||
super().intersection_update(other)
|
||||
|
||||
def update(self, other):
|
||||
"""Add all rdatas in other to self.
|
||||
|
||||
*other*, a ``dns.rdataset.Rdataset``, the rdataset from which
|
||||
to update.
|
||||
"""
|
||||
|
||||
self.update_ttl(other.ttl)
|
||||
super().update(other)
|
||||
|
||||
def _rdata_repr(self):
|
||||
def maybe_truncate(s):
|
||||
if len(s) > 100:
|
||||
return s[:100] + "..."
|
||||
return s
|
||||
|
||||
return "[" + ", ".join(f"<{maybe_truncate(str(rr))}>" for rr in self) + "]"
|
||||
|
||||
def __repr__(self):
|
||||
if self.covers == 0:
|
||||
ctext = ""
|
||||
else:
|
||||
ctext = "(" + dns.rdatatype.to_text(self.covers) + ")"
|
||||
return (
|
||||
"<DNS "
|
||||
+ dns.rdataclass.to_text(self.rdclass)
|
||||
+ " "
|
||||
+ dns.rdatatype.to_text(self.rdtype)
|
||||
+ ctext
|
||||
+ " rdataset: "
|
||||
+ self._rdata_repr()
|
||||
+ ">"
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.to_text()
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, Rdataset):
|
||||
return False
|
||||
if (
|
||||
self.rdclass != other.rdclass
|
||||
or self.rdtype != other.rdtype
|
||||
or self.covers != other.covers
|
||||
):
|
||||
return False
|
||||
return super().__eq__(other)
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
|
||||
def to_text(
|
||||
self,
|
||||
name: dns.name.Name | None = None,
|
||||
origin: dns.name.Name | None = None,
|
||||
relativize: bool = True,
|
||||
override_rdclass: dns.rdataclass.RdataClass | None = None,
|
||||
want_comments: bool = False,
|
||||
**kw: Dict[str, Any],
|
||||
) -> str:
|
||||
"""Convert the rdataset into DNS zone file format.
|
||||
|
||||
See ``dns.name.Name.choose_relativity`` for more information
|
||||
on how *origin* and *relativize* determine the way names
|
||||
are emitted.
|
||||
|
||||
Any additional keyword arguments are passed on to the rdata
|
||||
``to_text()`` method.
|
||||
|
||||
*name*, a ``dns.name.Name``. If name is not ``None``, emit RRs with
|
||||
*name* as the owner name.
|
||||
|
||||
*origin*, a ``dns.name.Name`` or ``None``, the origin for relative
|
||||
names.
|
||||
|
||||
*relativize*, a ``bool``. If ``True``, names will be relativized
|
||||
to *origin*.
|
||||
|
||||
*override_rdclass*, a ``dns.rdataclass.RdataClass`` or ``None``.
|
||||
If not ``None``, use this class instead of the Rdataset's class.
|
||||
|
||||
*want_comments*, a ``bool``. If ``True``, emit comments for rdata
|
||||
which have them. The default is ``False``.
|
||||
"""
|
||||
|
||||
if name is not None:
|
||||
name = name.choose_relativity(origin, relativize)
|
||||
ntext = str(name)
|
||||
pad = " "
|
||||
else:
|
||||
ntext = ""
|
||||
pad = ""
|
||||
s = io.StringIO()
|
||||
if override_rdclass is not None:
|
||||
rdclass = override_rdclass
|
||||
else:
|
||||
rdclass = self.rdclass
|
||||
if len(self) == 0:
|
||||
#
|
||||
# Empty rdatasets are used for the question section, and in
|
||||
# some dynamic updates, so we don't need to print out the TTL
|
||||
# (which is meaningless anyway).
|
||||
#
|
||||
s.write(
|
||||
f"{ntext}{pad}{dns.rdataclass.to_text(rdclass)} "
|
||||
f"{dns.rdatatype.to_text(self.rdtype)}\n"
|
||||
)
|
||||
else:
|
||||
for rd in self:
|
||||
extra = ""
|
||||
if want_comments:
|
||||
if rd.rdcomment:
|
||||
extra = f" ;{rd.rdcomment}"
|
||||
s.write(
|
||||
f"{ntext}{pad}{self.ttl} "
|
||||
f"{dns.rdataclass.to_text(rdclass)} "
|
||||
f"{dns.rdatatype.to_text(self.rdtype)} "
|
||||
f"{rd.to_text(origin=origin, relativize=relativize, **kw)}"
|
||||
f"{extra}\n"
|
||||
)
|
||||
#
|
||||
# We strip off the final \n for the caller's convenience in printing
|
||||
#
|
||||
return s.getvalue()[:-1]
|
||||
|
||||
def to_wire(
|
||||
self,
|
||||
name: dns.name.Name,
|
||||
file: Any,
|
||||
compress: dns.name.CompressType | None = None,
|
||||
origin: dns.name.Name | None = None,
|
||||
override_rdclass: dns.rdataclass.RdataClass | None = None,
|
||||
want_shuffle: bool = True,
|
||||
) -> int:
|
||||
"""Convert the rdataset to wire format.
|
||||
|
||||
*name*, a ``dns.name.Name`` is the owner name to use.
|
||||
|
||||
*file* is the file where the name is emitted (typically a
|
||||
BytesIO file).
|
||||
|
||||
*compress*, a ``dict``, is the compression table to use. If
|
||||
``None`` (the default), names will not be compressed.
|
||||
|
||||
*origin* is a ``dns.name.Name`` or ``None``. If the name is
|
||||
relative and origin is not ``None``, then *origin* will be appended
|
||||
to it.
|
||||
|
||||
*override_rdclass*, an ``int``, is used as the class instead of the
|
||||
class of the rdataset. This is useful when rendering rdatasets
|
||||
associated with dynamic updates.
|
||||
|
||||
*want_shuffle*, a ``bool``. If ``True``, then the order of the
|
||||
Rdatas within the Rdataset will be shuffled before rendering.
|
||||
|
||||
Returns an ``int``, the number of records emitted.
|
||||
"""
|
||||
|
||||
if override_rdclass is not None:
|
||||
rdclass = override_rdclass
|
||||
want_shuffle = False
|
||||
else:
|
||||
rdclass = self.rdclass
|
||||
if len(self) == 0:
|
||||
name.to_wire(file, compress, origin)
|
||||
file.write(struct.pack("!HHIH", self.rdtype, rdclass, 0, 0))
|
||||
return 1
|
||||
else:
|
||||
l: Rdataset | List[dns.rdata.Rdata]
|
||||
if want_shuffle:
|
||||
l = list(self)
|
||||
random.shuffle(l)
|
||||
else:
|
||||
l = self
|
||||
for rd in l:
|
||||
name.to_wire(file, compress, origin)
|
||||
file.write(struct.pack("!HHI", self.rdtype, rdclass, self.ttl))
|
||||
with dns.renderer.prefixed_length(file, 2):
|
||||
rd.to_wire(file, compress, origin)
|
||||
return len(self)
|
||||
|
||||
def match(
|
||||
self,
|
||||
rdclass: dns.rdataclass.RdataClass,
|
||||
rdtype: dns.rdatatype.RdataType,
|
||||
covers: dns.rdatatype.RdataType,
|
||||
) -> bool:
|
||||
"""Returns ``True`` if this rdataset matches the specified class,
|
||||
type, and covers.
|
||||
"""
|
||||
if self.rdclass == rdclass and self.rdtype == rdtype and self.covers == covers:
|
||||
return True
|
||||
return False
|
||||
|
||||
def processing_order(self) -> List[dns.rdata.Rdata]:
|
||||
"""Return rdatas in a valid processing order according to the type's
|
||||
specification. For example, MX records are in preference order from
|
||||
lowest to highest preferences, with items of the same preference
|
||||
shuffled.
|
||||
|
||||
For types that do not define a processing order, the rdatas are
|
||||
simply shuffled.
|
||||
"""
|
||||
if len(self) == 0:
|
||||
return []
|
||||
else:
|
||||
return self[0]._processing_order(iter(self)) # pyright: ignore
|
||||
|
||||
|
||||
@dns.immutable.immutable
|
||||
class ImmutableRdataset(Rdataset): # lgtm[py/missing-equals]
|
||||
"""An immutable DNS rdataset."""
|
||||
|
||||
_clone_class = Rdataset
|
||||
|
||||
def __init__(self, rdataset: Rdataset):
|
||||
"""Create an immutable rdataset from the specified rdataset."""
|
||||
|
||||
super().__init__(
|
||||
rdataset.rdclass, rdataset.rdtype, rdataset.covers, rdataset.ttl
|
||||
)
|
||||
self.items = dns.immutable.Dict(rdataset.items)
|
||||
|
||||
def update_ttl(self, ttl):
|
||||
raise TypeError("immutable")
|
||||
|
||||
def add(self, rd, ttl=None):
|
||||
raise TypeError("immutable")
|
||||
|
||||
def union_update(self, other):
|
||||
raise TypeError("immutable")
|
||||
|
||||
def intersection_update(self, other):
|
||||
raise TypeError("immutable")
|
||||
|
||||
def update(self, other):
|
||||
raise TypeError("immutable")
|
||||
|
||||
def __delitem__(self, i):
|
||||
raise TypeError("immutable")
|
||||
|
||||
# lgtm complains about these not raising ArithmeticError, but there is
|
||||
# precedent for overrides of these methods in other classes to raise
|
||||
# TypeError, and it seems like the better exception.
|
||||
|
||||
def __ior__(self, other): # lgtm[py/unexpected-raise-in-special-method]
|
||||
raise TypeError("immutable")
|
||||
|
||||
def __iand__(self, other): # lgtm[py/unexpected-raise-in-special-method]
|
||||
raise TypeError("immutable")
|
||||
|
||||
def __iadd__(self, other): # lgtm[py/unexpected-raise-in-special-method]
|
||||
raise TypeError("immutable")
|
||||
|
||||
def __isub__(self, other): # lgtm[py/unexpected-raise-in-special-method]
|
||||
raise TypeError("immutable")
|
||||
|
||||
def clear(self):
|
||||
raise TypeError("immutable")
|
||||
|
||||
def __copy__(self):
|
||||
return ImmutableRdataset(super().copy()) # pyright: ignore
|
||||
|
||||
def copy(self):
|
||||
return ImmutableRdataset(super().copy()) # pyright: ignore
|
||||
|
||||
def union(self, other):
|
||||
return ImmutableRdataset(super().union(other)) # pyright: ignore
|
||||
|
||||
def intersection(self, other):
|
||||
return ImmutableRdataset(super().intersection(other)) # pyright: ignore
|
||||
|
||||
def difference(self, other):
|
||||
return ImmutableRdataset(super().difference(other)) # pyright: ignore
|
||||
|
||||
def symmetric_difference(self, other):
|
||||
return ImmutableRdataset(super().symmetric_difference(other)) # pyright: ignore
|
||||
|
||||
|
||||
def from_text_list(
|
||||
rdclass: dns.rdataclass.RdataClass | str,
|
||||
rdtype: dns.rdatatype.RdataType | str,
|
||||
ttl: int,
|
||||
text_rdatas: Collection[str],
|
||||
idna_codec: dns.name.IDNACodec | None = None,
|
||||
origin: dns.name.Name | None = None,
|
||||
relativize: bool = True,
|
||||
relativize_to: dns.name.Name | None = None,
|
||||
) -> Rdataset:
|
||||
"""Create an rdataset with the specified class, type, and TTL, and with
|
||||
the specified list of rdatas in text format.
|
||||
|
||||
*idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA
|
||||
encoder/decoder to use; if ``None``, the default IDNA 2003
|
||||
encoder/decoder is used.
|
||||
|
||||
*origin*, a ``dns.name.Name`` (or ``None``), the
|
||||
origin to use for relative names.
|
||||
|
||||
*relativize*, a ``bool``. If true, name will be relativized.
|
||||
|
||||
*relativize_to*, a ``dns.name.Name`` (or ``None``), the origin to use
|
||||
when relativizing names. If not set, the *origin* value will be used.
|
||||
|
||||
Returns a ``dns.rdataset.Rdataset`` object.
|
||||
"""
|
||||
|
||||
rdclass = dns.rdataclass.RdataClass.make(rdclass)
|
||||
rdtype = dns.rdatatype.RdataType.make(rdtype)
|
||||
r = Rdataset(rdclass, rdtype)
|
||||
r.update_ttl(ttl)
|
||||
for t in text_rdatas:
|
||||
rd = dns.rdata.from_text(
|
||||
r.rdclass, r.rdtype, t, origin, relativize, relativize_to, idna_codec
|
||||
)
|
||||
r.add(rd)
|
||||
return r
|
||||
|
||||
|
||||
def from_text(
|
||||
rdclass: dns.rdataclass.RdataClass | str,
|
||||
rdtype: dns.rdatatype.RdataType | str,
|
||||
ttl: int,
|
||||
*text_rdatas: Any,
|
||||
) -> Rdataset:
|
||||
"""Create an rdataset with the specified class, type, and TTL, and with
|
||||
the specified rdatas in text format.
|
||||
|
||||
Returns a ``dns.rdataset.Rdataset`` object.
|
||||
"""
|
||||
|
||||
return from_text_list(rdclass, rdtype, ttl, cast(Collection[str], text_rdatas))
|
||||
|
||||
|
||||
def from_rdata_list(ttl: int, rdatas: Collection[dns.rdata.Rdata]) -> Rdataset:
|
||||
"""Create an rdataset with the specified TTL, and with
|
||||
the specified list of rdata objects.
|
||||
|
||||
Returns a ``dns.rdataset.Rdataset`` object.
|
||||
"""
|
||||
|
||||
if len(rdatas) == 0:
|
||||
raise ValueError("rdata list must not be empty")
|
||||
r = None
|
||||
for rd in rdatas:
|
||||
if r is None:
|
||||
r = Rdataset(rd.rdclass, rd.rdtype)
|
||||
r.update_ttl(ttl)
|
||||
r.add(rd)
|
||||
assert r is not None
|
||||
return r
|
||||
|
||||
|
||||
def from_rdata(ttl: int, *rdatas: Any) -> Rdataset:
|
||||
"""Create an rdataset with the specified TTL, and with
|
||||
the specified rdata objects.
|
||||
|
||||
Returns a ``dns.rdataset.Rdataset`` object.
|
||||
"""
|
||||
|
||||
return from_rdata_list(ttl, cast(Collection[dns.rdata.Rdata], rdatas))
|
||||
338
venv/Lib/site-packages/dns/rdatatype.py
Normal file
338
venv/Lib/site-packages/dns/rdatatype.py
Normal file
@@ -0,0 +1,338 @@
|
||||
# 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.
|
||||
|
||||
"""DNS Rdata Types."""
|
||||
|
||||
from typing import Dict
|
||||
|
||||
import dns.enum
|
||||
import dns.exception
|
||||
|
||||
|
||||
class RdataType(dns.enum.IntEnum):
|
||||
"""DNS Rdata Type"""
|
||||
|
||||
TYPE0 = 0
|
||||
NONE = 0
|
||||
A = 1
|
||||
NS = 2
|
||||
MD = 3
|
||||
MF = 4
|
||||
CNAME = 5
|
||||
SOA = 6
|
||||
MB = 7
|
||||
MG = 8
|
||||
MR = 9
|
||||
NULL = 10
|
||||
WKS = 11
|
||||
PTR = 12
|
||||
HINFO = 13
|
||||
MINFO = 14
|
||||
MX = 15
|
||||
TXT = 16
|
||||
RP = 17
|
||||
AFSDB = 18
|
||||
X25 = 19
|
||||
ISDN = 20
|
||||
RT = 21
|
||||
NSAP = 22
|
||||
NSAP_PTR = 23
|
||||
SIG = 24
|
||||
KEY = 25
|
||||
PX = 26
|
||||
GPOS = 27
|
||||
AAAA = 28
|
||||
LOC = 29
|
||||
NXT = 30
|
||||
SRV = 33
|
||||
NAPTR = 35
|
||||
KX = 36
|
||||
CERT = 37
|
||||
A6 = 38
|
||||
DNAME = 39
|
||||
OPT = 41
|
||||
APL = 42
|
||||
DS = 43
|
||||
SSHFP = 44
|
||||
IPSECKEY = 45
|
||||
RRSIG = 46
|
||||
NSEC = 47
|
||||
DNSKEY = 48
|
||||
DHCID = 49
|
||||
NSEC3 = 50
|
||||
NSEC3PARAM = 51
|
||||
TLSA = 52
|
||||
SMIMEA = 53
|
||||
HIP = 55
|
||||
NINFO = 56
|
||||
CDS = 59
|
||||
CDNSKEY = 60
|
||||
OPENPGPKEY = 61
|
||||
CSYNC = 62
|
||||
ZONEMD = 63
|
||||
SVCB = 64
|
||||
HTTPS = 65
|
||||
DSYNC = 66
|
||||
SPF = 99
|
||||
UNSPEC = 103
|
||||
NID = 104
|
||||
L32 = 105
|
||||
L64 = 106
|
||||
LP = 107
|
||||
EUI48 = 108
|
||||
EUI64 = 109
|
||||
TKEY = 249
|
||||
TSIG = 250
|
||||
IXFR = 251
|
||||
AXFR = 252
|
||||
MAILB = 253
|
||||
MAILA = 254
|
||||
ANY = 255
|
||||
URI = 256
|
||||
CAA = 257
|
||||
AVC = 258
|
||||
AMTRELAY = 260
|
||||
RESINFO = 261
|
||||
WALLET = 262
|
||||
TA = 32768
|
||||
DLV = 32769
|
||||
|
||||
@classmethod
|
||||
def _maximum(cls):
|
||||
return 65535
|
||||
|
||||
@classmethod
|
||||
def _short_name(cls):
|
||||
return "type"
|
||||
|
||||
@classmethod
|
||||
def _prefix(cls):
|
||||
return "TYPE"
|
||||
|
||||
@classmethod
|
||||
def _extra_from_text(cls, text):
|
||||
if text.find("-") >= 0:
|
||||
try:
|
||||
return cls[text.replace("-", "_")]
|
||||
except KeyError: # pragma: no cover
|
||||
pass
|
||||
return _registered_by_text.get(text)
|
||||
|
||||
@classmethod
|
||||
def _extra_to_text(cls, value, current_text):
|
||||
if current_text is None:
|
||||
return _registered_by_value.get(value)
|
||||
if current_text.find("_") >= 0:
|
||||
return current_text.replace("_", "-")
|
||||
return current_text
|
||||
|
||||
@classmethod
|
||||
def _unknown_exception_class(cls):
|
||||
return UnknownRdatatype
|
||||
|
||||
|
||||
_registered_by_text: Dict[str, RdataType] = {}
|
||||
_registered_by_value: Dict[RdataType, str] = {}
|
||||
|
||||
_metatypes = {RdataType.OPT}
|
||||
|
||||
_singletons = {
|
||||
RdataType.SOA,
|
||||
RdataType.NXT,
|
||||
RdataType.DNAME,
|
||||
RdataType.NSEC,
|
||||
RdataType.CNAME,
|
||||
}
|
||||
|
||||
|
||||
class UnknownRdatatype(dns.exception.DNSException):
|
||||
"""DNS resource record type is unknown."""
|
||||
|
||||
|
||||
def from_text(text: str) -> RdataType:
|
||||
"""Convert text into a DNS rdata type value.
|
||||
|
||||
The input text can be a defined DNS RR type mnemonic or
|
||||
instance of the DNS generic type syntax.
|
||||
|
||||
For example, "NS" and "TYPE2" will both result in a value of 2.
|
||||
|
||||
Raises ``dns.rdatatype.UnknownRdatatype`` if the type is unknown.
|
||||
|
||||
Raises ``ValueError`` if the rdata type value is not >= 0 and <= 65535.
|
||||
|
||||
Returns a ``dns.rdatatype.RdataType``.
|
||||
"""
|
||||
|
||||
return RdataType.from_text(text)
|
||||
|
||||
|
||||
def to_text(value: RdataType) -> str:
|
||||
"""Convert a DNS rdata type value to text.
|
||||
|
||||
If the value has a known mnemonic, it will be used, otherwise the
|
||||
DNS generic type syntax will be used.
|
||||
|
||||
Raises ``ValueError`` if the rdata type value is not >= 0 and <= 65535.
|
||||
|
||||
Returns a ``str``.
|
||||
"""
|
||||
|
||||
return RdataType.to_text(value)
|
||||
|
||||
|
||||
def is_metatype(rdtype: RdataType) -> bool:
|
||||
"""True if the specified type is a metatype.
|
||||
|
||||
*rdtype* is a ``dns.rdatatype.RdataType``.
|
||||
|
||||
The currently defined metatypes are TKEY, TSIG, IXFR, AXFR, MAILA,
|
||||
MAILB, ANY, and OPT.
|
||||
|
||||
Returns a ``bool``.
|
||||
"""
|
||||
|
||||
return (256 > rdtype >= 128) or rdtype in _metatypes
|
||||
|
||||
|
||||
def is_singleton(rdtype: RdataType) -> bool:
|
||||
"""Is the specified type a singleton type?
|
||||
|
||||
Singleton types can only have a single rdata in an rdataset, or a single
|
||||
RR in an RRset.
|
||||
|
||||
The currently defined singleton types are CNAME, DNAME, NSEC, NXT, and
|
||||
SOA.
|
||||
|
||||
*rdtype* is an ``int``.
|
||||
|
||||
Returns a ``bool``.
|
||||
"""
|
||||
|
||||
if rdtype in _singletons:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# pylint: disable=redefined-outer-name
|
||||
def register_type(
|
||||
rdtype: RdataType, rdtype_text: str, is_singleton: bool = False
|
||||
) -> None:
|
||||
"""Dynamically register an rdatatype.
|
||||
|
||||
*rdtype*, a ``dns.rdatatype.RdataType``, the rdatatype to register.
|
||||
|
||||
*rdtype_text*, a ``str``, the textual form of the rdatatype.
|
||||
|
||||
*is_singleton*, a ``bool``, indicating if the type is a singleton (i.e.
|
||||
RRsets of the type can have only one member.)
|
||||
"""
|
||||
|
||||
_registered_by_text[rdtype_text] = rdtype
|
||||
_registered_by_value[rdtype] = rdtype_text
|
||||
if is_singleton:
|
||||
_singletons.add(rdtype)
|
||||
|
||||
|
||||
### BEGIN generated RdataType constants
|
||||
|
||||
TYPE0 = RdataType.TYPE0
|
||||
NONE = RdataType.NONE
|
||||
A = RdataType.A
|
||||
NS = RdataType.NS
|
||||
MD = RdataType.MD
|
||||
MF = RdataType.MF
|
||||
CNAME = RdataType.CNAME
|
||||
SOA = RdataType.SOA
|
||||
MB = RdataType.MB
|
||||
MG = RdataType.MG
|
||||
MR = RdataType.MR
|
||||
NULL = RdataType.NULL
|
||||
WKS = RdataType.WKS
|
||||
PTR = RdataType.PTR
|
||||
HINFO = RdataType.HINFO
|
||||
MINFO = RdataType.MINFO
|
||||
MX = RdataType.MX
|
||||
TXT = RdataType.TXT
|
||||
RP = RdataType.RP
|
||||
AFSDB = RdataType.AFSDB
|
||||
X25 = RdataType.X25
|
||||
ISDN = RdataType.ISDN
|
||||
RT = RdataType.RT
|
||||
NSAP = RdataType.NSAP
|
||||
NSAP_PTR = RdataType.NSAP_PTR
|
||||
SIG = RdataType.SIG
|
||||
KEY = RdataType.KEY
|
||||
PX = RdataType.PX
|
||||
GPOS = RdataType.GPOS
|
||||
AAAA = RdataType.AAAA
|
||||
LOC = RdataType.LOC
|
||||
NXT = RdataType.NXT
|
||||
SRV = RdataType.SRV
|
||||
NAPTR = RdataType.NAPTR
|
||||
KX = RdataType.KX
|
||||
CERT = RdataType.CERT
|
||||
A6 = RdataType.A6
|
||||
DNAME = RdataType.DNAME
|
||||
OPT = RdataType.OPT
|
||||
APL = RdataType.APL
|
||||
DS = RdataType.DS
|
||||
SSHFP = RdataType.SSHFP
|
||||
IPSECKEY = RdataType.IPSECKEY
|
||||
RRSIG = RdataType.RRSIG
|
||||
NSEC = RdataType.NSEC
|
||||
DNSKEY = RdataType.DNSKEY
|
||||
DHCID = RdataType.DHCID
|
||||
NSEC3 = RdataType.NSEC3
|
||||
NSEC3PARAM = RdataType.NSEC3PARAM
|
||||
TLSA = RdataType.TLSA
|
||||
SMIMEA = RdataType.SMIMEA
|
||||
HIP = RdataType.HIP
|
||||
NINFO = RdataType.NINFO
|
||||
CDS = RdataType.CDS
|
||||
CDNSKEY = RdataType.CDNSKEY
|
||||
OPENPGPKEY = RdataType.OPENPGPKEY
|
||||
CSYNC = RdataType.CSYNC
|
||||
ZONEMD = RdataType.ZONEMD
|
||||
SVCB = RdataType.SVCB
|
||||
HTTPS = RdataType.HTTPS
|
||||
DSYNC = RdataType.DSYNC
|
||||
SPF = RdataType.SPF
|
||||
UNSPEC = RdataType.UNSPEC
|
||||
NID = RdataType.NID
|
||||
L32 = RdataType.L32
|
||||
L64 = RdataType.L64
|
||||
LP = RdataType.LP
|
||||
EUI48 = RdataType.EUI48
|
||||
EUI64 = RdataType.EUI64
|
||||
TKEY = RdataType.TKEY
|
||||
TSIG = RdataType.TSIG
|
||||
IXFR = RdataType.IXFR
|
||||
AXFR = RdataType.AXFR
|
||||
MAILB = RdataType.MAILB
|
||||
MAILA = RdataType.MAILA
|
||||
ANY = RdataType.ANY
|
||||
URI = RdataType.URI
|
||||
CAA = RdataType.CAA
|
||||
AVC = RdataType.AVC
|
||||
AMTRELAY = RdataType.AMTRELAY
|
||||
RESINFO = RdataType.RESINFO
|
||||
WALLET = RdataType.WALLET
|
||||
TA = RdataType.TA
|
||||
DLV = RdataType.DLV
|
||||
|
||||
### END generated RdataType constants
|
||||
Reference in New Issue
Block a user