Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
name.py1284 linesDownload Raw Back to dns
1# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license2 3# Copyright (C) 2001-2017 Nominum, Inc.4#5# Permission to use, copy, modify, and distribute this software and its6# documentation for any purpose with or without fee is hereby granted,7# provided that the above copyright notice and this permission notice8# appear in all copies.9#10# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES11# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF12# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR13# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES14# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN15# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT16# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.17 18"""DNS Names.19"""20 21import copy22import encodings.idna  # type: ignore23import functools24import struct25from typing import Any, Callable, Dict, Iterable, Optional, Tuple, Union26 27import dns._features28import dns.enum29import dns.exception30import dns.immutable31import dns.wire32 33if dns._features.have("idna"):34    import idna  # type: ignore35 36    have_idna_2008 = True37else:  # pragma: no cover38    have_idna_2008 = False39 40CompressType = Dict["Name", int]41 42 43class NameRelation(dns.enum.IntEnum):44    """Name relation result from fullcompare()."""45 46    # This is an IntEnum for backwards compatibility in case anyone47    # has hardwired the constants.48 49    #: The compared names have no relationship to each other.50    NONE = 051    #: the first name is a superdomain of the second.52    SUPERDOMAIN = 153    #: The first name is a subdomain of the second.54    SUBDOMAIN = 255    #: The compared names are equal.56    EQUAL = 357    #: The compared names have a common ancestor.58    COMMONANCESTOR = 459 60    @classmethod61    def _maximum(cls):62        return cls.COMMONANCESTOR63 64    @classmethod65    def _short_name(cls):66        return cls.__name__67 68 69# Backwards compatibility70NAMERELN_NONE = NameRelation.NONE71NAMERELN_SUPERDOMAIN = NameRelation.SUPERDOMAIN72NAMERELN_SUBDOMAIN = NameRelation.SUBDOMAIN73NAMERELN_EQUAL = NameRelation.EQUAL74NAMERELN_COMMONANCESTOR = NameRelation.COMMONANCESTOR75 76 77class EmptyLabel(dns.exception.SyntaxError):78    """A DNS label is empty."""79 80 81class BadEscape(dns.exception.SyntaxError):82    """An escaped code in a text format of DNS name is invalid."""83 84 85class BadPointer(dns.exception.FormError):86    """A DNS compression pointer points forward instead of backward."""87 88 89class BadLabelType(dns.exception.FormError):90    """The label type in DNS name wire format is unknown."""91 92 93class NeedAbsoluteNameOrOrigin(dns.exception.DNSException):94    """An attempt was made to convert a non-absolute name to95    wire when there was also a non-absolute (or missing) origin."""96 97 98class NameTooLong(dns.exception.FormError):99    """A DNS name is > 255 octets long."""100 101 102class LabelTooLong(dns.exception.SyntaxError):103    """A DNS label is > 63 octets long."""104 105 106class AbsoluteConcatenation(dns.exception.DNSException):107    """An attempt was made to append anything other than the108    empty name to an absolute DNS name."""109 110 111class NoParent(dns.exception.DNSException):112    """An attempt was made to get the parent of the root name113    or the empty name."""114 115 116class NoIDNA2008(dns.exception.DNSException):117    """IDNA 2008 processing was requested but the idna module is not118    available."""119 120 121class IDNAException(dns.exception.DNSException):122    """IDNA processing raised an exception."""123 124    supp_kwargs = {"idna_exception"}125    fmt = "IDNA processing exception: {idna_exception}"126 127    # We do this as otherwise mypy complains about unexpected keyword argument128    # idna_exception129    def __init__(self, *args, **kwargs):130        super().__init__(*args, **kwargs)131 132 133class NeedSubdomainOfOrigin(dns.exception.DNSException):134    """An absolute name was provided that is not a subdomain of the specified origin."""135 136 137_escaped = b'"().;\\@$'138_escaped_text = '"().;\\@$'139 140 141def _escapify(label: Union[bytes, str]) -> str:142    """Escape the characters in label which need it.143    @returns: the escaped string144    @rtype: string"""145    if isinstance(label, bytes):146        # Ordinary DNS label mode.  Escape special characters and values147        # < 0x20 or > 0x7f.148        text = ""149        for c in label:150            if c in _escaped:151                text += "\\" + chr(c)152            elif c > 0x20 and c < 0x7F:153                text += chr(c)154            else:155                text += "\\%03d" % c156        return text157 158    # Unicode label mode.  Escape only special characters and values < 0x20159    text = ""160    for uc in label:161        if uc in _escaped_text:162            text += "\\" + uc163        elif uc <= "\x20":164            text += "\\%03d" % ord(uc)165        else:166            text += uc167    return text168 169 170class IDNACodec:171    """Abstract base class for IDNA encoder/decoders."""172 173    def __init__(self):174        pass175 176    def is_idna(self, label: bytes) -> bool:177        return label.lower().startswith(b"xn--")178 179    def encode(self, label: str) -> bytes:180        raise NotImplementedError  # pragma: no cover181 182    def decode(self, label: bytes) -> str:183        # We do not apply any IDNA policy on decode.184        if self.is_idna(label):185            try:186                slabel = label[4:].decode("punycode")187                return _escapify(slabel)188            except Exception as e:189                raise IDNAException(idna_exception=e)190        else:191            return _escapify(label)192 193 194class IDNA2003Codec(IDNACodec):195    """IDNA 2003 encoder/decoder."""196 197    def __init__(self, strict_decode: bool = False):198        """Initialize the IDNA 2003 encoder/decoder.199 200        *strict_decode* is a ``bool``. If `True`, then IDNA2003 checking201        is done when decoding.  This can cause failures if the name202        was encoded with IDNA2008.  The default is `False`.203        """204 205        super().__init__()206        self.strict_decode = strict_decode207 208    def encode(self, label: str) -> bytes:209        """Encode *label*."""210 211        if label == "":212            return b""213        try:214            return encodings.idna.ToASCII(label)215        except UnicodeError:216            raise LabelTooLong217 218    def decode(self, label: bytes) -> str:219        """Decode *label*."""220        if not self.strict_decode:221            return super().decode(label)222        if label == b"":223            return ""224        try:225            return _escapify(encodings.idna.ToUnicode(label))226        except Exception as e:227            raise IDNAException(idna_exception=e)228 229 230class IDNA2008Codec(IDNACodec):231    """IDNA 2008 encoder/decoder."""232 233    def __init__(234        self,235        uts_46: bool = False,236        transitional: bool = False,237        allow_pure_ascii: bool = False,238        strict_decode: bool = False,239    ):240        """Initialize the IDNA 2008 encoder/decoder.241 242        *uts_46* is a ``bool``.  If True, apply Unicode IDNA243        compatibility processing as described in Unicode Technical244        Standard #46 (https://unicode.org/reports/tr46/).245        If False, do not apply the mapping.  The default is False.246 247        *transitional* is a ``bool``: If True, use the248        "transitional" mode described in Unicode Technical Standard249        #46.  The default is False.250 251        *allow_pure_ascii* is a ``bool``.  If True, then a label which252        consists of only ASCII characters is allowed.  This is less253        strict than regular IDNA 2008, but is also necessary for mixed254        names, e.g. a name with starting with "_sip._tcp." and ending255        in an IDN suffix which would otherwise be disallowed.  The256        default is False.257 258        *strict_decode* is a ``bool``: If True, then IDNA2008 checking259        is done when decoding.  This can cause failures if the name260        was encoded with IDNA2003.  The default is False.261        """262        super().__init__()263        self.uts_46 = uts_46264        self.transitional = transitional265        self.allow_pure_ascii = allow_pure_ascii266        self.strict_decode = strict_decode267 268    def encode(self, label: str) -> bytes:269        if label == "":270            return b""271        if self.allow_pure_ascii and is_all_ascii(label):272            encoded = label.encode("ascii")273            if len(encoded) > 63:274                raise LabelTooLong275            return encoded276        if not have_idna_2008:277            raise NoIDNA2008278        try:279            if self.uts_46:280                label = idna.uts46_remap(label, False, self.transitional)281            return idna.alabel(label)282        except idna.IDNAError as e:283            if e.args[0] == "Label too long":284                raise LabelTooLong285            else:286                raise IDNAException(idna_exception=e)287 288    def decode(self, label: bytes) -> str:289        if not self.strict_decode:290            return super().decode(label)291        if label == b"":292            return ""293        if not have_idna_2008:294            raise NoIDNA2008295        try:296            ulabel = idna.ulabel(label)297            if self.uts_46:298                ulabel = idna.uts46_remap(ulabel, False, self.transitional)299            return _escapify(ulabel)300        except (idna.IDNAError, UnicodeError) as e:301            raise IDNAException(idna_exception=e)302 303 304IDNA_2003_Practical = IDNA2003Codec(False)305IDNA_2003_Strict = IDNA2003Codec(True)306IDNA_2003 = IDNA_2003_Practical307IDNA_2008_Practical = IDNA2008Codec(True, False, True, False)308IDNA_2008_UTS_46 = IDNA2008Codec(True, False, False, False)309IDNA_2008_Strict = IDNA2008Codec(False, False, False, True)310IDNA_2008_Transitional = IDNA2008Codec(True, True, False, False)311IDNA_2008 = IDNA_2008_Practical312 313 314def _validate_labels(labels: Tuple[bytes, ...]) -> None:315    """Check for empty labels in the middle of a label sequence,316    labels that are too long, and for too many labels.317 318    Raises ``dns.name.NameTooLong`` if the name as a whole is too long.319 320    Raises ``dns.name.EmptyLabel`` if a label is empty (i.e. the root321    label) and appears in a position other than the end of the label322    sequence323 324    """325 326    l = len(labels)327    total = 0328    i = -1329    j = 0330    for label in labels:331        ll = len(label)332        total += ll + 1333        if ll > 63:334            raise LabelTooLong335        if i < 0 and label == b"":336            i = j337        j += 1338    if total > 255:339        raise NameTooLong340    if i >= 0 and i != l - 1:341        raise EmptyLabel342 343 344def _maybe_convert_to_binary(label: Union[bytes, str]) -> bytes:345    """If label is ``str``, convert it to ``bytes``.  If it is already346    ``bytes`` just return it.347 348    """349 350    if isinstance(label, bytes):351        return label352    if isinstance(label, str):353        return label.encode()354    raise ValueError  # pragma: no cover355 356 357@dns.immutable.immutable358class Name:359    """A DNS name.360 361    The dns.name.Name class represents a DNS name as a tuple of362    labels.  Each label is a ``bytes`` in DNS wire format.  Instances363    of the class are immutable.364    """365 366    __slots__ = ["labels"]367 368    def __init__(self, labels: Iterable[Union[bytes, str]]):369        """*labels* is any iterable whose values are ``str`` or ``bytes``."""370 371        blabels = [_maybe_convert_to_binary(x) for x in labels]372        self.labels = tuple(blabels)373        _validate_labels(self.labels)374 375    def __copy__(self):376        return Name(self.labels)377 378    def __deepcopy__(self, memo):379        return Name(copy.deepcopy(self.labels, memo))380 381    def __getstate__(self):382        # Names can be pickled383        return {"labels": self.labels}384 385    def __setstate__(self, state):386        super().__setattr__("labels", state["labels"])387        _validate_labels(self.labels)388 389    def is_absolute(self) -> bool:390        """Is the most significant label of this name the root label?391 392        Returns a ``bool``.393        """394 395        return len(self.labels) > 0 and self.labels[-1] == b""396 397    def is_wild(self) -> bool:398        """Is this name wild?  (I.e. Is the least significant label '*'?)399 400        Returns a ``bool``.401        """402 403        return len(self.labels) > 0 and self.labels[0] == b"*"404 405    def __hash__(self) -> int:406        """Return a case-insensitive hash of the name.407 408        Returns an ``int``.409        """410 411        h = 0412        for label in self.labels:413            for c in label.lower():414                h += (h << 3) + c415        return h416 417    def fullcompare(self, other: "Name") -> Tuple[NameRelation, int, int]:418        """Compare two names, returning a 3-tuple419        ``(relation, order, nlabels)``.420 421        *relation* describes the relation ship between the names,422        and is one of: ``dns.name.NameRelation.NONE``,423        ``dns.name.NameRelation.SUPERDOMAIN``, ``dns.name.NameRelation.SUBDOMAIN``,424        ``dns.name.NameRelation.EQUAL``, or ``dns.name.NameRelation.COMMONANCESTOR``.425 426        *order* is < 0 if *self* < *other*, > 0 if *self* > *other*, and ==427        0 if *self* == *other*.  A relative name is always less than an428        absolute name.  If both names have the same relativity, then429        the DNSSEC order relation is used to order them.430 431        *nlabels* is the number of significant labels that the two names432        have in common.433 434        Here are some examples.  Names ending in "." are absolute names,435        those not ending in "." are relative names.436 437        =============  =============  ===========  =====  =======438        self           other          relation     order  nlabels439        =============  =============  ===========  =====  =======440        www.example.   www.example.   equal        0      3441        www.example.   example.       subdomain    > 0    2442        example.       www.example.   superdomain  < 0    2443        example1.com.  example2.com.  common anc.  < 0    2444        example1       example2.      none         < 0    0445        example1.      example2       none         > 0    0446        =============  =============  ===========  =====  =======447        """448 449        sabs = self.is_absolute()450        oabs = other.is_absolute()451        if sabs != oabs:452            if sabs:453                return (NameRelation.NONE, 1, 0)454            else:455                return (NameRelation.NONE, -1, 0)456        l1 = len(self.labels)457        l2 = len(other.labels)458        ldiff = l1 - l2459        if ldiff < 0:460            l = l1461        else:462            l = l2463 464        order = 0465        nlabels = 0466        namereln = NameRelation.NONE467        while l > 0:468            l -= 1469            l1 -= 1470            l2 -= 1471            label1 = self.labels[l1].lower()472            label2 = other.labels[l2].lower()473            if label1 < label2:474                order = -1475                if nlabels > 0:476                    namereln = NameRelation.COMMONANCESTOR477                return (namereln, order, nlabels)478            elif label1 > label2:479                order = 1480                if nlabels > 0:481                    namereln = NameRelation.COMMONANCESTOR482                return (namereln, order, nlabels)483            nlabels += 1484        order = ldiff485        if ldiff < 0:486            namereln = NameRelation.SUPERDOMAIN487        elif ldiff > 0:488            namereln = NameRelation.SUBDOMAIN489        else:490            namereln = NameRelation.EQUAL491        return (namereln, order, nlabels)492 493    def is_subdomain(self, other: "Name") -> bool:494        """Is self a subdomain of other?495 496        Note that the notion of subdomain includes equality, e.g.497        "dnspython.org" is a subdomain of itself.498 499        Returns a ``bool``.500        """501 502        (nr, _, _) = self.fullcompare(other)503        if nr == NameRelation.SUBDOMAIN or nr == NameRelation.EQUAL:504            return True505        return False506 507    def is_superdomain(self, other: "Name") -> bool:508        """Is self a superdomain of other?509 510        Note that the notion of superdomain includes equality, e.g.511        "dnspython.org" is a superdomain of itself.512 513        Returns a ``bool``.514        """515 516        (nr, _, _) = self.fullcompare(other)517        if nr == NameRelation.SUPERDOMAIN or nr == NameRelation.EQUAL:518            return True519        return False520 521    def canonicalize(self) -> "Name":522        """Return a name which is equal to the current name, but is in523        DNSSEC canonical form.524        """525 526        return Name([x.lower() for x in self.labels])527 528    def __eq__(self, other):529        if isinstance(other, Name):530            return self.fullcompare(other)[1] == 0531        else:532            return False533 534    def __ne__(self, other):535        if isinstance(other, Name):536            return self.fullcompare(other)[1] != 0537        else:538            return True539 540    def __lt__(self, other):541        if isinstance(other, Name):542            return self.fullcompare(other)[1] < 0543        else:544            return NotImplemented545 546    def __le__(self, other):547        if isinstance(other, Name):548            return self.fullcompare(other)[1] <= 0549        else:550            return NotImplemented551 552    def __ge__(self, other):553        if isinstance(other, Name):554            return self.fullcompare(other)[1] >= 0555        else:556            return NotImplemented557 558    def __gt__(self, other):559        if isinstance(other, Name):560            return self.fullcompare(other)[1] > 0561        else:562            return NotImplemented563 564    def __repr__(self):565        return "<DNS name " + self.__str__() + ">"566 567    def __str__(self):568        return self.to_text(False)569 570    def to_text(self, omit_final_dot: bool = False) -> str:571        """Convert name to DNS text format.572 573        *omit_final_dot* is a ``bool``.  If True, don't emit the final574        dot (denoting the root label) for absolute names.  The default575        is False.576 577        Returns a ``str``.578        """579 580        if len(self.labels) == 0:581            return "@"582        if len(self.labels) == 1 and self.labels[0] == b"":583            return "."584        if omit_final_dot and self.is_absolute():585            l = self.labels[:-1]586        else:587            l = self.labels588        s = ".".join(map(_escapify, l))589        return s590 591    def to_unicode(592        self, omit_final_dot: bool = False, idna_codec: Optional[IDNACodec] = None593    ) -> str:594        """Convert name to Unicode text format.595 596        IDN ACE labels are converted to Unicode.597 598        *omit_final_dot* is a ``bool``.  If True, don't emit the final599        dot (denoting the root label) for absolute names.  The default600        is False.601        *idna_codec* specifies the IDNA encoder/decoder.  If None, the602        dns.name.IDNA_2003_Practical encoder/decoder is used.603        The IDNA_2003_Practical decoder does604        not impose any policy, it just decodes punycode, so if you605        don't want checking for compliance, you can use this decoder606        for IDNA2008 as well.607 608        Returns a ``str``.609        """610 611        if len(self.labels) == 0:612            return "@"613        if len(self.labels) == 1 and self.labels[0] == b"":614            return "."615        if omit_final_dot and self.is_absolute():616            l = self.labels[:-1]617        else:618            l = self.labels619        if idna_codec is None:620            idna_codec = IDNA_2003_Practical621        return ".".join([idna_codec.decode(x) for x in l])622 623    def to_digestable(self, origin: Optional["Name"] = None) -> bytes:624        """Convert name to a format suitable for digesting in hashes.625 626        The name is canonicalized and converted to uncompressed wire627        format.  All names in wire format are absolute.  If the name628        is a relative name, then an origin must be supplied.629 630        *origin* is a ``dns.name.Name`` or ``None``.  If the name is631        relative and origin is not ``None``, then origin will be appended632        to the name.633 634        Raises ``dns.name.NeedAbsoluteNameOrOrigin`` if the name is635        relative and no origin was provided.636 637        Returns a ``bytes``.638        """639 640        digest = self.to_wire(origin=origin, canonicalize=True)641        assert digest is not None642        return digest643 644    def to_wire(645        self,646        file: Optional[Any] = None,647        compress: Optional[CompressType] = None,648        origin: Optional["Name"] = None,649        canonicalize: bool = False,650    ) -> Optional[bytes]:651        """Convert name to wire format, possibly compressing it.652 653        *file* is the file where the name is emitted (typically an654        io.BytesIO file).  If ``None`` (the default), a ``bytes``655        containing the wire name will be returned.656 657        *compress*, a ``dict``, is the compression table to use.  If658        ``None`` (the default), names will not be compressed.  Note that659        the compression code assumes that compression offset 0 is the660        start of *file*, and thus compression will not be correct661        if this is not the case.662 663        *origin* is a ``dns.name.Name`` or ``None``.  If the name is664        relative and origin is not ``None``, then *origin* will be appended665        to it.666 667        *canonicalize*, a ``bool``, indicates whether the name should668        be canonicalized; that is, converted to a format suitable for669        digesting in hashes.670 671        Raises ``dns.name.NeedAbsoluteNameOrOrigin`` if the name is672        relative and no origin was provided.673 674        Returns a ``bytes`` or ``None``.675        """676 677        if file is None:678            out = bytearray()679            for label in self.labels:680                out.append(len(label))681                if canonicalize:682                    out += label.lower()683                else:684                    out += label685            if not self.is_absolute():686                if origin is None or not origin.is_absolute():687                    raise NeedAbsoluteNameOrOrigin688                for label in origin.labels:689                    out.append(len(label))690                    if canonicalize:691                        out += label.lower()692                    else:693                        out += label694            return bytes(out)695 696        labels: Iterable[bytes]697        if not self.is_absolute():698            if origin is None or not origin.is_absolute():699                raise NeedAbsoluteNameOrOrigin700            labels = list(self.labels)701            labels.extend(list(origin.labels))702        else:703            labels = self.labels704        i = 0705        for label in labels:706            n = Name(labels[i:])707            i += 1708            if compress is not None:709                pos = compress.get(n)710            else:711                pos = None712            if pos is not None:713                value = 0xC000 + pos714                s = struct.pack("!H", value)715                file.write(s)716                break717            else:718                if compress is not None and len(n) > 1:719                    pos = file.tell()720                    if pos <= 0x3FFF:721                        compress[n] = pos722                l = len(label)723                file.write(struct.pack("!B", l))724                if l > 0:725                    if canonicalize:726                        file.write(label.lower())727                    else:728                        file.write(label)729        return None730 731    def __len__(self) -> int:732        """The length of the name (in labels).733 734        Returns an ``int``.735        """736 737        return len(self.labels)738 739    def __getitem__(self, index):740        return self.labels[index]741 742    def __add__(self, other):743        return self.concatenate(other)744 745    def __sub__(self, other):746        return self.relativize(other)747 748    def split(self, depth: int) -> Tuple["Name", "Name"]:749        """Split a name into a prefix and suffix names at the specified depth.750 751        *depth* is an ``int`` specifying the number of labels in the suffix752 753        Raises ``ValueError`` if *depth* was not >= 0 and <= the length of the754        name.755 756        Returns the tuple ``(prefix, suffix)``.757        """758 759        l = len(self.labels)760        if depth == 0:761            return (self, dns.name.empty)762        elif depth == l:763            return (dns.name.empty, self)764        elif depth < 0 or depth > l:765            raise ValueError("depth must be >= 0 and <= the length of the name")766        return (Name(self[:-depth]), Name(self[-depth:]))767 768    def concatenate(self, other: "Name") -> "Name":769        """Return a new name which is the concatenation of self and other.770 771        Raises ``dns.name.AbsoluteConcatenation`` if the name is772        absolute and *other* is not the empty name.773 774        Returns a ``dns.name.Name``.775        """776 777        if self.is_absolute() and len(other) > 0:778            raise AbsoluteConcatenation779        labels = list(self.labels)780        labels.extend(list(other.labels))781        return Name(labels)782 783    def relativize(self, origin: "Name") -> "Name":784        """If the name is a subdomain of *origin*, return a new name which is785        the name relative to origin.  Otherwise return the name.786 787        For example, relativizing ``www.dnspython.org.`` to origin788        ``dnspython.org.`` returns the name ``www``.  Relativizing ``example.``789        to origin ``dnspython.org.`` returns ``example.``.790 791        Returns a ``dns.name.Name``.792        """793 794        if origin is not None and self.is_subdomain(origin):795            return Name(self[: -len(origin)])796        else:797            return self798 799    def derelativize(self, origin: "Name") -> "Name":800        """If the name is a relative name, return a new name which is the801        concatenation of the name and origin.  Otherwise return the name.802 803        For example, derelativizing ``www`` to origin ``dnspython.org.``804        returns the name ``www.dnspython.org.``.  Derelativizing ``example.``805        to origin ``dnspython.org.`` returns ``example.``.806 807        Returns a ``dns.name.Name``.808        """809 810        if not self.is_absolute():811            return self.concatenate(origin)812        else:813            return self814 815    def choose_relativity(816        self, origin: Optional["Name"] = None, relativize: bool = True817    ) -> "Name":818        """Return a name with the relativity desired by the caller.819 820        If *origin* is ``None``, then the name is returned.821        Otherwise, if *relativize* is ``True`` the name is822        relativized, and if *relativize* is ``False`` the name is823        derelativized.824 825        Returns a ``dns.name.Name``.826        """827 828        if origin:829            if relativize:830                return self.relativize(origin)831            else:832                return self.derelativize(origin)833        else:834            return self835 836    def parent(self) -> "Name":837        """Return the parent of the name.838 839        For example, the parent of ``www.dnspython.org.`` is ``dnspython.org``.840 841        Raises ``dns.name.NoParent`` if the name is either the root name or the842        empty name, and thus has no parent.843 844        Returns a ``dns.name.Name``.845        """846 847        if self == root or self == empty:848            raise NoParent849        return Name(self.labels[1:])850 851    def predecessor(self, origin: "Name", prefix_ok: bool = True) -> "Name":852        """Return the maximal predecessor of *name* in the DNSSEC ordering in the zone853        whose origin is *origin*, or return the longest name under *origin* if the854        name is origin (i.e. wrap around to the longest name, which may still be855        *origin* due to length considerations.856 857        The relativity of the name is preserved, so if this name is relative858        then the method will return a relative name, and likewise if this name859        is absolute then the predecessor will be absolute.860 861        *prefix_ok* indicates if prefixing labels is allowed, and862        defaults to ``True``.  Normally it is good to allow this, but if computing863        a maximal predecessor at a zone cut point then ``False`` must be specified.864        """865        return _handle_relativity_and_call(866            _absolute_predecessor, self, origin, prefix_ok867        )868 869    def successor(self, origin: "Name", prefix_ok: bool = True) -> "Name":870        """Return the minimal successor of *name* in the DNSSEC ordering in the zone871        whose origin is *origin*, or return *origin* if the successor cannot be872        computed due to name length limitations.873 874        Note that *origin* is returned in the "too long" cases because wrapping875        around to the origin is how NSEC records express "end of the zone".876 877        The relativity of the name is preserved, so if this name is relative878        then the method will return a relative name, and likewise if this name879        is absolute then the successor will be absolute.880 881        *prefix_ok* indicates if prefixing a new minimal label is allowed, and882        defaults to ``True``.  Normally it is good to allow this, but if computing883        a minimal successor at a zone cut point then ``False`` must be specified.884        """885        return _handle_relativity_and_call(_absolute_successor, self, origin, prefix_ok)886 887 888#: The root name, '.'889root = Name([b""])890 891#: The empty name.892empty = Name([])893 894 895def from_unicode(896    text: str, origin: Optional[Name] = root, idna_codec: Optional[IDNACodec] = None897) -> Name:898    """Convert unicode text into a Name object.899 900    Labels are encoded in IDN ACE form according to rules specified by901    the IDNA codec.902 903    *text*, a ``str``, is the text to convert into a name.904 905    *origin*, a ``dns.name.Name``, specifies the origin to906    append to non-absolute names.  The default is the root name.907 908    *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA909    encoder/decoder.  If ``None``, the default IDNA 2003 encoder/decoder910    is used.911 912    Returns a ``dns.name.Name``.913    """914 915    if not isinstance(text, str):916        raise ValueError("input to from_unicode() must be a unicode string")917    if not (origin is None or isinstance(origin, Name)):918        raise ValueError("origin must be a Name or None")919    labels = []920    label = ""921    escaping = False922    edigits = 0923    total = 0924    if idna_codec is None:925        idna_codec = IDNA_2003926    if text == "@":927        text = ""928    if text:929        if text in [".", "\u3002", "\uff0e", "\uff61"]:930            return Name([b""])  # no Unicode "u" on this constant!931        for c in text:932            if escaping:933                if edigits == 0:934                    if c.isdigit():935                        total = int(c)936                        edigits += 1937                    else:938                        label += c939                        escaping = False940                else:941                    if not c.isdigit():942                        raise BadEscape943                    total *= 10944                    total += int(c)945                    edigits += 1946                    if edigits == 3:947                        escaping = False948                        label += chr(total)949            elif c in [".", "\u3002", "\uff0e", "\uff61"]:950                if len(label) == 0:951                    raise EmptyLabel952                labels.append(idna_codec.encode(label))953                label = ""954            elif c == "\\":955                escaping = True956                edigits = 0957                total = 0958            else:959                label += c960        if escaping:961            raise BadEscape962        if len(label) > 0:963            labels.append(idna_codec.encode(label))964        else:965            labels.append(b"")966 967    if (len(labels) == 0 or labels[-1] != b"") and origin is not None:968        labels.extend(list(origin.labels))969    return Name(labels)970 971 972def is_all_ascii(text: str) -> bool:973    for c in text:974        if ord(c) > 0x7F:975            return False976    return True977 978 979def from_text(980    text: Union[bytes, str],981    origin: Optional[Name] = root,982    idna_codec: Optional[IDNACodec] = None,983) -> Name:984    """Convert text into a Name object.985 986    *text*, a ``bytes`` or ``str``, is the text to convert into a name.987 988    *origin*, a ``dns.name.Name``, specifies the origin to989    append to non-absolute names.  The default is the root name.990 991    *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA992    encoder/decoder.  If ``None``, the default IDNA 2003 encoder/decoder993    is used.994 995    Returns a ``dns.name.Name``.996    """997 998    if isinstance(text, str):999        if not is_all_ascii(text):1000            # Some codepoint in the input text is > 127, so IDNA applies.1001            return from_unicode(text, origin, idna_codec)1002        # The input is all ASCII, so treat this like an ordinary non-IDNA1003        # domain name.  Note that "all ASCII" is about the input text,1004        # not the codepoints in the domain name.  E.g. if text has value1005        #1006        # r'\150\151\152\153\154\155\156\157\158\159'1007        #1008        # then it's still "all ASCII" even though the domain name has1009        # codepoints > 127.1010        text = text.encode("ascii")1011    if not isinstance(text, bytes):1012        raise ValueError("input to from_text() must be a string")1013    if not (origin is None or isinstance(origin, Name)):1014        raise ValueError("origin must be a Name or None")1015    labels = []1016    label = b""1017    escaping = False1018    edigits = 01019    total = 01020    if text == b"@":1021        text = b""1022    if text:1023        if text == b".":1024            return Name([b""])1025        for c in text:1026            byte_ = struct.pack("!B", c)1027            if escaping:1028                if edigits == 0:1029                    if byte_.isdigit():1030                        total = int(byte_)1031                        edigits += 11032                    else:1033                        label += byte_1034                        escaping = False1035                else:1036                    if not byte_.isdigit():1037                        raise BadEscape1038                    total *= 101039                    total += int(byte_)1040                    edigits += 11041                    if edigits == 3:1042                        escaping = False1043                        label += struct.pack("!B", total)1044            elif byte_ == b".":1045                if len(label) == 0:1046                    raise EmptyLabel1047                labels.append(label)1048                label = b""1049            elif byte_ == b"\\":1050                escaping = True1051                edigits = 01052                total = 01053            else:1054                label += byte_1055        if escaping:1056            raise BadEscape1057        if len(label) > 0:1058            labels.append(label)1059        else:1060            labels.append(b"")1061    if (len(labels) == 0 or labels[-1] != b"") and origin is not None:1062        labels.extend(list(origin.labels))1063    return Name(labels)1064 1065 1066# we need 'dns.wire.Parser' quoted as dns.name and dns.wire depend on each other.1067 1068 1069def from_wire_parser(parser: "dns.wire.Parser") -> Name:1070    """Convert possibly compressed wire format into a Name.1071 1072    *parser* is a dns.wire.Parser.1073 1074    Raises ``dns.name.BadPointer`` if a compression pointer did not1075    point backwards in the message.1076 1077    Raises ``dns.name.BadLabelType`` if an invalid label type was encountered.1078 1079    Returns a ``dns.name.Name``1080    """1081 1082    labels = []1083    biggest_pointer = parser.current1084    with parser.restore_furthest():1085        count = parser.get_uint8()1086        while count != 0:1087            if count < 64:1088                labels.append(parser.get_bytes(count))1089            elif count >= 192:1090                current = (count & 0x3F) * 256 + parser.get_uint8()1091                if current >= biggest_pointer:1092                    raise BadPointer1093                biggest_pointer = current1094                parser.seek(current)1095            else:1096                raise BadLabelType1097            count = parser.get_uint8()1098        labels.append(b"")1099    return Name(labels)1100 1101 1102def from_wire(message: bytes, current: int) -> Tuple[Name, int]:1103    """Convert possibly compressed wire format into a Name.1104 1105    *message* is a ``bytes`` containing an entire DNS message in DNS1106    wire form.1107 1108    *current*, an ``int``, is the offset of the beginning of the name1109    from the start of the message1110 1111    Raises ``dns.name.BadPointer`` if a compression pointer did not1112    point backwards in the message.1113 1114    Raises ``dns.name.BadLabelType`` if an invalid label type was encountered.1115 1116    Returns a ``(dns.name.Name, int)`` tuple consisting of the name1117    that was read and the number of bytes of the wire format message1118    which were consumed reading it.1119    """1120 1121    if not isinstance(message, bytes):1122        raise ValueError("input to from_wire() must be a byte string")1123    parser = dns.wire.Parser(message, current)1124    name = from_wire_parser(parser)1125    return (name, parser.current - current)1126 1127 1128# RFC 4471 Support1129 1130_MINIMAL_OCTET = b"\x00"1131_MINIMAL_OCTET_VALUE = ord(_MINIMAL_OCTET)1132_SUCCESSOR_PREFIX = Name([_MINIMAL_OCTET])1133_MAXIMAL_OCTET = b"\xff"1134_MAXIMAL_OCTET_VALUE = ord(_MAXIMAL_OCTET)1135_AT_SIGN_VALUE = ord("@")1136_LEFT_SQUARE_BRACKET_VALUE = ord("[")1137 1138 1139def _wire_length(labels):1140    return functools.reduce(lambda v, x: v + len(x) + 1, labels, 0)1141 1142 1143def _pad_to_max_name(name):1144    needed = 255 - _wire_length(name.labels)1145    new_labels = []1146    while needed > 64:1147        new_labels.append(_MAXIMAL_OCTET * 63)1148        needed -= 641149    if needed >= 2:1150        new_labels.append(_MAXIMAL_OCTET * (needed - 1))1151    # Note we're already maximal in the needed == 1 case as while we'd like1152    # to add one more byte as a new label, we can't, as adding a new non-empty1153    # label requires at least 2 bytes.1154    new_labels = list(reversed(new_labels))1155    new_labels.extend(name.labels)1156    return Name(new_labels)1157 1158 1159def _pad_to_max_label(label, suffix_labels):1160    length = len(label)1161    # We have to subtract one here to account for the length byte of label.1162    remaining = 255 - _wire_length(suffix_labels) - length - 11163    if remaining <= 0:1164        # Shouldn't happen!1165        return label1166    needed = min(63 - length, remaining)1167    return label + _MAXIMAL_OCTET * needed1168 1169 1170def _absolute_predecessor(name: Name, origin: Name, prefix_ok: bool) -> Name:1171    # This is the RFC 4471 predecessor algorithm using the "absolute method" of section1172    # 3.1.1.1173    #1174    # Our caller must ensure that the name and origin are absolute, and that name is a1175    # subdomain of origin.1176    if name == origin:1177        return _pad_to_max_name(name)1178    least_significant_label = name[0]1179    if least_significant_label == _MINIMAL_OCTET:1180        return name.parent()1181    least_octet = least_significant_label[-1]1182    suffix_labels = name.labels[1:]1183    if least_octet == _MINIMAL_OCTET_VALUE:1184        new_labels = [least_significant_label[:-1]]1185    else:1186        octets = bytearray(least_significant_label)1187        octet = octets[-1]1188        if octet == _LEFT_SQUARE_BRACKET_VALUE:1189            octet = _AT_SIGN_VALUE1190        else:1191            octet -= 11192        octets[-1] = octet1193        least_significant_label = bytes(octets)1194        new_labels = [_pad_to_max_label(least_significant_label, suffix_labels)]1195    new_labels.extend(suffix_labels)1196    name = Name(new_labels)1197    if prefix_ok:1198        return _pad_to_max_name(name)1199    else:1200        return name

Showing the first 1,200 of 1284 lines. Download the file for the rest.

codekingpro/portable-devtools · Team Ai