Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/dns/name.py: 50%
619 statements
« prev ^ index » next coverage.py v7.4.1, created at 2024-02-02 06:07 +0000
« prev ^ index » next coverage.py v7.4.1, created at 2024-02-02 06:07 +0000
1# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
3# Copyright (C) 2001-2017 Nominum, Inc.
4#
5# Permission to use, copy, modify, and distribute this software and its
6# documentation for any purpose with or without fee is hereby granted,
7# provided that the above copyright notice and this permission notice
8# appear in all copies.
9#
10# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
11# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
13# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
16# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18"""DNS Names.
19"""
21import copy
22import encodings.idna # type: ignore
23import functools
24import struct
25from typing import Any, Callable, Dict, Iterable, Optional, Tuple, Union
27try:
28 import idna # type: ignore
30 have_idna_2008 = True
31except ImportError: # pragma: no cover
32 have_idna_2008 = False
34import dns.enum
35import dns.exception
36import dns.immutable
37import dns.wire
39CompressType = Dict["Name", int]
42class NameRelation(dns.enum.IntEnum):
43 """Name relation result from fullcompare()."""
45 # This is an IntEnum for backwards compatibility in case anyone
46 # has hardwired the constants.
48 #: The compared names have no relationship to each other.
49 NONE = 0
50 #: the first name is a superdomain of the second.
51 SUPERDOMAIN = 1
52 #: The first name is a subdomain of the second.
53 SUBDOMAIN = 2
54 #: The compared names are equal.
55 EQUAL = 3
56 #: The compared names have a common ancestor.
57 COMMONANCESTOR = 4
59 @classmethod
60 def _maximum(cls):
61 return cls.COMMONANCESTOR
63 @classmethod
64 def _short_name(cls):
65 return cls.__name__
68# Backwards compatibility
69NAMERELN_NONE = NameRelation.NONE
70NAMERELN_SUPERDOMAIN = NameRelation.SUPERDOMAIN
71NAMERELN_SUBDOMAIN = NameRelation.SUBDOMAIN
72NAMERELN_EQUAL = NameRelation.EQUAL
73NAMERELN_COMMONANCESTOR = NameRelation.COMMONANCESTOR
76class EmptyLabel(dns.exception.SyntaxError):
77 """A DNS label is empty."""
80class BadEscape(dns.exception.SyntaxError):
81 """An escaped code in a text format of DNS name is invalid."""
84class BadPointer(dns.exception.FormError):
85 """A DNS compression pointer points forward instead of backward."""
88class BadLabelType(dns.exception.FormError):
89 """The label type in DNS name wire format is unknown."""
92class NeedAbsoluteNameOrOrigin(dns.exception.DNSException):
93 """An attempt was made to convert a non-absolute name to
94 wire when there was also a non-absolute (or missing) origin."""
97class NameTooLong(dns.exception.FormError):
98 """A DNS name is > 255 octets long."""
101class LabelTooLong(dns.exception.SyntaxError):
102 """A DNS label is > 63 octets long."""
105class AbsoluteConcatenation(dns.exception.DNSException):
106 """An attempt was made to append anything other than the
107 empty name to an absolute DNS name."""
110class NoParent(dns.exception.DNSException):
111 """An attempt was made to get the parent of the root name
112 or the empty name."""
115class NoIDNA2008(dns.exception.DNSException):
116 """IDNA 2008 processing was requested but the idna module is not
117 available."""
120class IDNAException(dns.exception.DNSException):
121 """IDNA processing raised an exception."""
123 supp_kwargs = {"idna_exception"}
124 fmt = "IDNA processing exception: {idna_exception}"
126 # We do this as otherwise mypy complains about unexpected keyword argument
127 # idna_exception
128 def __init__(self, *args, **kwargs):
129 super().__init__(*args, **kwargs)
132class NeedSubdomainOfOrigin(dns.exception.DNSException):
133 """An absolute name was provided that is not a subdomain of the specified origin."""
136_escaped = b'"().;\\@$'
137_escaped_text = '"().;\\@$'
140def _escapify(label: Union[bytes, str]) -> str:
141 """Escape the characters in label which need it.
142 @returns: the escaped string
143 @rtype: string"""
144 if isinstance(label, bytes):
145 # Ordinary DNS label mode. Escape special characters and values
146 # < 0x20 or > 0x7f.
147 text = ""
148 for c in label:
149 if c in _escaped:
150 text += "\\" + chr(c)
151 elif c > 0x20 and c < 0x7F:
152 text += chr(c)
153 else:
154 text += "\\%03d" % c
155 return text
157 # Unicode label mode. Escape only special characters and values < 0x20
158 text = ""
159 for uc in label:
160 if uc in _escaped_text:
161 text += "\\" + uc
162 elif uc <= "\x20":
163 text += "\\%03d" % ord(uc)
164 else:
165 text += uc
166 return text
169class IDNACodec:
170 """Abstract base class for IDNA encoder/decoders."""
172 def __init__(self):
173 pass
175 def is_idna(self, label: bytes) -> bool:
176 return label.lower().startswith(b"xn--")
178 def encode(self, label: str) -> bytes:
179 raise NotImplementedError # pragma: no cover
181 def decode(self, label: bytes) -> str:
182 # We do not apply any IDNA policy on decode.
183 if self.is_idna(label):
184 try:
185 slabel = label[4:].decode("punycode")
186 return _escapify(slabel)
187 except Exception as e:
188 raise IDNAException(idna_exception=e)
189 else:
190 return _escapify(label)
193class IDNA2003Codec(IDNACodec):
194 """IDNA 2003 encoder/decoder."""
196 def __init__(self, strict_decode: bool = False):
197 """Initialize the IDNA 2003 encoder/decoder.
199 *strict_decode* is a ``bool``. If `True`, then IDNA2003 checking
200 is done when decoding. This can cause failures if the name
201 was encoded with IDNA2008. The default is `False`.
202 """
204 super().__init__()
205 self.strict_decode = strict_decode
207 def encode(self, label: str) -> bytes:
208 """Encode *label*."""
210 if label == "":
211 return b""
212 try:
213 return encodings.idna.ToASCII(label)
214 except UnicodeError:
215 raise LabelTooLong
217 def decode(self, label: bytes) -> str:
218 """Decode *label*."""
219 if not self.strict_decode:
220 return super().decode(label)
221 if label == b"":
222 return ""
223 try:
224 return _escapify(encodings.idna.ToUnicode(label))
225 except Exception as e:
226 raise IDNAException(idna_exception=e)
229class IDNA2008Codec(IDNACodec):
230 """IDNA 2008 encoder/decoder."""
232 def __init__(
233 self,
234 uts_46: bool = False,
235 transitional: bool = False,
236 allow_pure_ascii: bool = False,
237 strict_decode: bool = False,
238 ):
239 """Initialize the IDNA 2008 encoder/decoder.
241 *uts_46* is a ``bool``. If True, apply Unicode IDNA
242 compatibility processing as described in Unicode Technical
243 Standard #46 (https://unicode.org/reports/tr46/).
244 If False, do not apply the mapping. The default is False.
246 *transitional* is a ``bool``: If True, use the
247 "transitional" mode described in Unicode Technical Standard
248 #46. The default is False.
250 *allow_pure_ascii* is a ``bool``. If True, then a label which
251 consists of only ASCII characters is allowed. This is less
252 strict than regular IDNA 2008, but is also necessary for mixed
253 names, e.g. a name with starting with "_sip._tcp." and ending
254 in an IDN suffix which would otherwise be disallowed. The
255 default is False.
257 *strict_decode* is a ``bool``: If True, then IDNA2008 checking
258 is done when decoding. This can cause failures if the name
259 was encoded with IDNA2003. The default is False.
260 """
261 super().__init__()
262 self.uts_46 = uts_46
263 self.transitional = transitional
264 self.allow_pure_ascii = allow_pure_ascii
265 self.strict_decode = strict_decode
267 def encode(self, label: str) -> bytes:
268 if label == "":
269 return b""
270 if self.allow_pure_ascii and is_all_ascii(label):
271 encoded = label.encode("ascii")
272 if len(encoded) > 63:
273 raise LabelTooLong
274 return encoded
275 if not have_idna_2008:
276 raise NoIDNA2008
277 try:
278 if self.uts_46:
279 label = idna.uts46_remap(label, False, self.transitional)
280 return idna.alabel(label)
281 except idna.IDNAError as e:
282 if e.args[0] == "Label too long":
283 raise LabelTooLong
284 else:
285 raise IDNAException(idna_exception=e)
287 def decode(self, label: bytes) -> str:
288 if not self.strict_decode:
289 return super().decode(label)
290 if label == b"":
291 return ""
292 if not have_idna_2008:
293 raise NoIDNA2008
294 try:
295 ulabel = idna.ulabel(label)
296 if self.uts_46:
297 ulabel = idna.uts46_remap(ulabel, False, self.transitional)
298 return _escapify(ulabel)
299 except (idna.IDNAError, UnicodeError) as e:
300 raise IDNAException(idna_exception=e)
303IDNA_2003_Practical = IDNA2003Codec(False)
304IDNA_2003_Strict = IDNA2003Codec(True)
305IDNA_2003 = IDNA_2003_Practical
306IDNA_2008_Practical = IDNA2008Codec(True, False, True, False)
307IDNA_2008_UTS_46 = IDNA2008Codec(True, False, False, False)
308IDNA_2008_Strict = IDNA2008Codec(False, False, False, True)
309IDNA_2008_Transitional = IDNA2008Codec(True, True, False, False)
310IDNA_2008 = IDNA_2008_Practical
313def _validate_labels(labels: Tuple[bytes, ...]) -> None:
314 """Check for empty labels in the middle of a label sequence,
315 labels that are too long, and for too many labels.
317 Raises ``dns.name.NameTooLong`` if the name as a whole is too long.
319 Raises ``dns.name.EmptyLabel`` if a label is empty (i.e. the root
320 label) and appears in a position other than the end of the label
321 sequence
323 """
325 l = len(labels)
326 total = 0
327 i = -1
328 j = 0
329 for label in labels:
330 ll = len(label)
331 total += ll + 1
332 if ll > 63:
333 raise LabelTooLong
334 if i < 0 and label == b"":
335 i = j
336 j += 1
337 if total > 255:
338 raise NameTooLong
339 if i >= 0 and i != l - 1:
340 raise EmptyLabel
343def _maybe_convert_to_binary(label: Union[bytes, str]) -> bytes:
344 """If label is ``str``, convert it to ``bytes``. If it is already
345 ``bytes`` just return it.
347 """
349 if isinstance(label, bytes):
350 return label
351 if isinstance(label, str):
352 return label.encode()
353 raise ValueError # pragma: no cover
356@dns.immutable.immutable
357class Name:
359 """A DNS name.
361 The dns.name.Name class represents a DNS name as a tuple of
362 labels. Each label is a ``bytes`` in DNS wire format. Instances
363 of the class are immutable.
364 """
366 __slots__ = ["labels"]
368 def __init__(self, labels: Iterable[Union[bytes, str]]):
369 """*labels* is any iterable whose values are ``str`` or ``bytes``."""
371 blabels = [_maybe_convert_to_binary(x) for x in labels]
372 self.labels = tuple(blabels)
373 _validate_labels(self.labels)
375 def __copy__(self):
376 return Name(self.labels)
378 def __deepcopy__(self, memo):
379 return Name(copy.deepcopy(self.labels, memo))
381 def __getstate__(self):
382 # Names can be pickled
383 return {"labels": self.labels}
385 def __setstate__(self, state):
386 super().__setattr__("labels", state["labels"])
387 _validate_labels(self.labels)
389 def is_absolute(self) -> bool:
390 """Is the most significant label of this name the root label?
392 Returns a ``bool``.
393 """
395 return len(self.labels) > 0 and self.labels[-1] == b""
397 def is_wild(self) -> bool:
398 """Is this name wild? (I.e. Is the least significant label '*'?)
400 Returns a ``bool``.
401 """
403 return len(self.labels) > 0 and self.labels[0] == b"*"
405 def __hash__(self) -> int:
406 """Return a case-insensitive hash of the name.
408 Returns an ``int``.
409 """
411 h = 0
412 for label in self.labels:
413 for c in label.lower():
414 h += (h << 3) + c
415 return h
417 def fullcompare(self, other: "Name") -> Tuple[NameRelation, int, int]:
418 """Compare two names, returning a 3-tuple
419 ``(relation, order, nlabels)``.
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``.
426 *order* is < 0 if *self* < *other*, > 0 if *self* > *other*, and ==
427 0 if *self* == *other*. A relative name is always less than an
428 absolute name. If both names have the same relativity, then
429 the DNSSEC order relation is used to order them.
431 *nlabels* is the number of significant labels that the two names
432 have in common.
434 Here are some examples. Names ending in "." are absolute names,
435 those not ending in "." are relative names.
437 ============= ============= =========== ===== =======
438 self other relation order nlabels
439 ============= ============= =========== ===== =======
440 www.example. www.example. equal 0 3
441 www.example. example. subdomain > 0 2
442 example. www.example. superdomain < 0 2
443 example1.com. example2.com. common anc. < 0 2
444 example1 example2. none < 0 0
445 example1. example2 none > 0 0
446 ============= ============= =========== ===== =======
447 """
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 - l2
459 if ldiff < 0:
460 l = l1
461 else:
462 l = l2
464 order = 0
465 nlabels = 0
466 namereln = NameRelation.NONE
467 while l > 0:
468 l -= 1
469 l1 -= 1
470 l2 -= 1
471 label1 = self.labels[l1].lower()
472 label2 = other.labels[l2].lower()
473 if label1 < label2:
474 order = -1
475 if nlabels > 0:
476 namereln = NameRelation.COMMONANCESTOR
477 return (namereln, order, nlabels)
478 elif label1 > label2:
479 order = 1
480 if nlabels > 0:
481 namereln = NameRelation.COMMONANCESTOR
482 return (namereln, order, nlabels)
483 nlabels += 1
484 order = ldiff
485 if ldiff < 0:
486 namereln = NameRelation.SUPERDOMAIN
487 elif ldiff > 0:
488 namereln = NameRelation.SUBDOMAIN
489 else:
490 namereln = NameRelation.EQUAL
491 return (namereln, order, nlabels)
493 def is_subdomain(self, other: "Name") -> bool:
494 """Is self a subdomain of other?
496 Note that the notion of subdomain includes equality, e.g.
497 "dnspython.org" is a subdomain of itself.
499 Returns a ``bool``.
500 """
502 (nr, _, _) = self.fullcompare(other)
503 if nr == NameRelation.SUBDOMAIN or nr == NameRelation.EQUAL:
504 return True
505 return False
507 def is_superdomain(self, other: "Name") -> bool:
508 """Is self a superdomain of other?
510 Note that the notion of superdomain includes equality, e.g.
511 "dnspython.org" is a superdomain of itself.
513 Returns a ``bool``.
514 """
516 (nr, _, _) = self.fullcompare(other)
517 if nr == NameRelation.SUPERDOMAIN or nr == NameRelation.EQUAL:
518 return True
519 return False
521 def canonicalize(self) -> "Name":
522 """Return a name which is equal to the current name, but is in
523 DNSSEC canonical form.
524 """
526 return Name([x.lower() for x in self.labels])
528 def __eq__(self, other):
529 if isinstance(other, Name):
530 return self.fullcompare(other)[1] == 0
531 else:
532 return False
534 def __ne__(self, other):
535 if isinstance(other, Name):
536 return self.fullcompare(other)[1] != 0
537 else:
538 return True
540 def __lt__(self, other):
541 if isinstance(other, Name):
542 return self.fullcompare(other)[1] < 0
543 else:
544 return NotImplemented
546 def __le__(self, other):
547 if isinstance(other, Name):
548 return self.fullcompare(other)[1] <= 0
549 else:
550 return NotImplemented
552 def __ge__(self, other):
553 if isinstance(other, Name):
554 return self.fullcompare(other)[1] >= 0
555 else:
556 return NotImplemented
558 def __gt__(self, other):
559 if isinstance(other, Name):
560 return self.fullcompare(other)[1] > 0
561 else:
562 return NotImplemented
564 def __repr__(self):
565 return "<DNS name " + self.__str__() + ">"
567 def __str__(self):
568 return self.to_text(False)
570 def to_text(self, omit_final_dot: bool = False) -> str:
571 """Convert name to DNS text format.
573 *omit_final_dot* is a ``bool``. If True, don't emit the final
574 dot (denoting the root label) for absolute names. The default
575 is False.
577 Returns a ``str``.
578 """
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.labels
588 s = ".".join(map(_escapify, l))
589 return s
591 def to_unicode(
592 self, omit_final_dot: bool = False, idna_codec: Optional[IDNACodec] = None
593 ) -> str:
594 """Convert name to Unicode text format.
596 IDN ACE labels are converted to Unicode.
598 *omit_final_dot* is a ``bool``. If True, don't emit the final
599 dot (denoting the root label) for absolute names. The default
600 is False.
601 *idna_codec* specifies the IDNA encoder/decoder. If None, the
602 dns.name.IDNA_2003_Practical encoder/decoder is used.
603 The IDNA_2003_Practical decoder does
604 not impose any policy, it just decodes punycode, so if you
605 don't want checking for compliance, you can use this decoder
606 for IDNA2008 as well.
608 Returns a ``str``.
609 """
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.labels
619 if idna_codec is None:
620 idna_codec = IDNA_2003_Practical
621 return ".".join([idna_codec.decode(x) for x in l])
623 def to_digestable(self, origin: Optional["Name"] = None) -> bytes:
624 """Convert name to a format suitable for digesting in hashes.
626 The name is canonicalized and converted to uncompressed wire
627 format. All names in wire format are absolute. If the name
628 is a relative name, then an origin must be supplied.
630 *origin* is a ``dns.name.Name`` or ``None``. If the name is
631 relative and origin is not ``None``, then origin will be appended
632 to the name.
634 Raises ``dns.name.NeedAbsoluteNameOrOrigin`` if the name is
635 relative and no origin was provided.
637 Returns a ``bytes``.
638 """
640 digest = self.to_wire(origin=origin, canonicalize=True)
641 assert digest is not None
642 return digest
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.
653 *file* is the file where the name is emitted (typically an
654 io.BytesIO file). If ``None`` (the default), a ``bytes``
655 containing the wire name will be returned.
657 *compress*, a ``dict``, is the compression table to use. If
658 ``None`` (the default), names will not be compressed. Note that
659 the compression code assumes that compression offset 0 is the
660 start of *file*, and thus compression will not be correct
661 if this is not the case.
663 *origin* is a ``dns.name.Name`` or ``None``. If the name is
664 relative and origin is not ``None``, then *origin* will be appended
665 to it.
667 *canonicalize*, a ``bool``, indicates whether the name should
668 be canonicalized; that is, converted to a format suitable for
669 digesting in hashes.
671 Raises ``dns.name.NeedAbsoluteNameOrOrigin`` if the name is
672 relative and no origin was provided.
674 Returns a ``bytes`` or ``None``.
675 """
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 += label
685 if not self.is_absolute():
686 if origin is None or not origin.is_absolute():
687 raise NeedAbsoluteNameOrOrigin
688 for label in origin.labels:
689 out.append(len(label))
690 if canonicalize:
691 out += label.lower()
692 else:
693 out += label
694 return bytes(out)
696 labels: Iterable[bytes]
697 if not self.is_absolute():
698 if origin is None or not origin.is_absolute():
699 raise NeedAbsoluteNameOrOrigin
700 labels = list(self.labels)
701 labels.extend(list(origin.labels))
702 else:
703 labels = self.labels
704 i = 0
705 for label in labels:
706 n = Name(labels[i:])
707 i += 1
708 if compress is not None:
709 pos = compress.get(n)
710 else:
711 pos = None
712 if pos is not None:
713 value = 0xC000 + pos
714 s = struct.pack("!H", value)
715 file.write(s)
716 break
717 else:
718 if compress is not None and len(n) > 1:
719 pos = file.tell()
720 if pos <= 0x3FFF:
721 compress[n] = pos
722 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 None
731 def __len__(self) -> int:
732 """The length of the name (in labels).
734 Returns an ``int``.
735 """
737 return len(self.labels)
739 def __getitem__(self, index):
740 return self.labels[index]
742 def __add__(self, other):
743 return self.concatenate(other)
745 def __sub__(self, other):
746 return self.relativize(other)
748 def split(self, depth: int) -> Tuple["Name", "Name"]:
749 """Split a name into a prefix and suffix names at the specified depth.
751 *depth* is an ``int`` specifying the number of labels in the suffix
753 Raises ``ValueError`` if *depth* was not >= 0 and <= the length of the
754 name.
756 Returns the tuple ``(prefix, suffix)``.
757 """
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:]))
768 def concatenate(self, other: "Name") -> "Name":
769 """Return a new name which is the concatenation of self and other.
771 Raises ``dns.name.AbsoluteConcatenation`` if the name is
772 absolute and *other* is not the empty name.
774 Returns a ``dns.name.Name``.
775 """
777 if self.is_absolute() and len(other) > 0:
778 raise AbsoluteConcatenation
779 labels = list(self.labels)
780 labels.extend(list(other.labels))
781 return Name(labels)
783 def relativize(self, origin: "Name") -> "Name":
784 """If the name is a subdomain of *origin*, return a new name which is
785 the name relative to origin. Otherwise return the name.
787 For example, relativizing ``www.dnspython.org.`` to origin
788 ``dnspython.org.`` returns the name ``www``. Relativizing ``example.``
789 to origin ``dnspython.org.`` returns ``example.``.
791 Returns a ``dns.name.Name``.
792 """
794 if origin is not None and self.is_subdomain(origin):
795 return Name(self[: -len(origin)])
796 else:
797 return self
799 def derelativize(self, origin: "Name") -> "Name":
800 """If the name is a relative name, return a new name which is the
801 concatenation of the name and origin. Otherwise return the name.
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.``.
807 Returns a ``dns.name.Name``.
808 """
810 if not self.is_absolute():
811 return self.concatenate(origin)
812 else:
813 return self
815 def choose_relativity(
816 self, origin: Optional["Name"] = None, relativize: bool = True
817 ) -> "Name":
818 """Return a name with the relativity desired by the caller.
820 If *origin* is ``None``, then the name is returned.
821 Otherwise, if *relativize* is ``True`` the name is
822 relativized, and if *relativize* is ``False`` the name is
823 derelativized.
825 Returns a ``dns.name.Name``.
826 """
828 if origin:
829 if relativize:
830 return self.relativize(origin)
831 else:
832 return self.derelativize(origin)
833 else:
834 return self
836 def parent(self) -> "Name":
837 """Return the parent of the name.
839 For example, the parent of ``www.dnspython.org.`` is ``dnspython.org``.
841 Raises ``dns.name.NoParent`` if the name is either the root name or the
842 empty name, and thus has no parent.
844 Returns a ``dns.name.Name``.
845 """
847 if self == root or self == empty:
848 raise NoParent
849 return Name(self.labels[1:])
851 def predecessor(self, origin: "Name", prefix_ok: bool = True) -> "Name":
852 """Return the maximal predecessor of *name* in the DNSSEC ordering in the zone
853 whose origin is *origin*, or return the longest name under *origin* if the
854 name is origin (i.e. wrap around to the longest name, which may still be
855 *origin* due to length considerations.
857 The relativity of the name is preserved, so if this name is relative
858 then the method will return a relative name, and likewise if this name
859 is absolute then the predecessor will be absolute.
861 *prefix_ok* indicates if prefixing labels is allowed, and
862 defaults to ``True``. Normally it is good to allow this, but if computing
863 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_ok
867 )
869 def successor(self, origin: "Name", prefix_ok: bool = True) -> "Name":
870 """Return the minimal successor of *name* in the DNSSEC ordering in the zone
871 whose origin is *origin*, or return *origin* if the successor cannot be
872 computed due to name length limitations.
874 Note that *origin* is returned in the "too long" cases because wrapping
875 around to the origin is how NSEC records express "end of the zone".
877 The relativity of the name is preserved, so if this name is relative
878 then the method will return a relative name, and likewise if this name
879 is absolute then the successor will be absolute.
881 *prefix_ok* indicates if prefixing a new minimal label is allowed, and
882 defaults to ``True``. Normally it is good to allow this, but if computing
883 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)
888#: The root name, '.'
889root = Name([b""])
891#: The empty name.
892empty = Name([])
895def from_unicode(
896 text: str, origin: Optional[Name] = root, idna_codec: Optional[IDNACodec] = None
897) -> Name:
898 """Convert unicode text into a Name object.
900 Labels are encoded in IDN ACE form according to rules specified by
901 the IDNA codec.
903 *text*, a ``str``, is the text to convert into a name.
905 *origin*, a ``dns.name.Name``, specifies the origin to
906 append to non-absolute names. The default is the root name.
908 *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA
909 encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder
910 is used.
912 Returns a ``dns.name.Name``.
913 """
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 = False
922 edigits = 0
923 total = 0
924 if idna_codec is None:
925 idna_codec = IDNA_2003
926 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 += 1
937 else:
938 label += c
939 escaping = False
940 else:
941 if not c.isdigit():
942 raise BadEscape
943 total *= 10
944 total += int(c)
945 edigits += 1
946 if edigits == 3:
947 escaping = False
948 label += chr(total)
949 elif c in [".", "\u3002", "\uff0e", "\uff61"]:
950 if len(label) == 0:
951 raise EmptyLabel
952 labels.append(idna_codec.encode(label))
953 label = ""
954 elif c == "\\":
955 escaping = True
956 edigits = 0
957 total = 0
958 else:
959 label += c
960 if escaping:
961 raise BadEscape
962 if len(label) > 0:
963 labels.append(idna_codec.encode(label))
964 else:
965 labels.append(b"")
967 if (len(labels) == 0 or labels[-1] != b"") and origin is not None:
968 labels.extend(list(origin.labels))
969 return Name(labels)
972def is_all_ascii(text: str) -> bool:
973 for c in text:
974 if ord(c) > 0x7F:
975 return False
976 return True
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.
986 *text*, a ``bytes`` or ``str``, is the text to convert into a name.
988 *origin*, a ``dns.name.Name``, specifies the origin to
989 append to non-absolute names. The default is the root name.
991 *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA
992 encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder
993 is used.
995 Returns a ``dns.name.Name``.
996 """
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-IDNA
1003 # domain name. Note that "all ASCII" is about the input text,
1004 # not the codepoints in the domain name. E.g. if text has value
1005 #
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 has
1009 # 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 = False
1018 edigits = 0
1019 total = 0
1020 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 += 1
1032 else:
1033 label += byte_
1034 escaping = False
1035 else:
1036 if not byte_.isdigit():
1037 raise BadEscape
1038 total *= 10
1039 total += int(byte_)
1040 edigits += 1
1041 if edigits == 3:
1042 escaping = False
1043 label += struct.pack("!B", total)
1044 elif byte_ == b".":
1045 if len(label) == 0:
1046 raise EmptyLabel
1047 labels.append(label)
1048 label = b""
1049 elif byte_ == b"\\":
1050 escaping = True
1051 edigits = 0
1052 total = 0
1053 else:
1054 label += byte_
1055 if escaping:
1056 raise BadEscape
1057 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)
1066# we need 'dns.wire.Parser' quoted as dns.name and dns.wire depend on each other.
1069def from_wire_parser(parser: "dns.wire.Parser") -> Name:
1070 """Convert possibly compressed wire format into a Name.
1072 *parser* is a dns.wire.Parser.
1074 Raises ``dns.name.BadPointer`` if a compression pointer did not
1075 point backwards in the message.
1077 Raises ``dns.name.BadLabelType`` if an invalid label type was encountered.
1079 Returns a ``dns.name.Name``
1080 """
1082 labels = []
1083 biggest_pointer = parser.current
1084 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 BadPointer
1093 biggest_pointer = current
1094 parser.seek(current)
1095 else:
1096 raise BadLabelType
1097 count = parser.get_uint8()
1098 labels.append(b"")
1099 return Name(labels)
1102def from_wire(message: bytes, current: int) -> Tuple[Name, int]:
1103 """Convert possibly compressed wire format into a Name.
1105 *message* is a ``bytes`` containing an entire DNS message in DNS
1106 wire form.
1108 *current*, an ``int``, is the offset of the beginning of the name
1109 from the start of the message
1111 Raises ``dns.name.BadPointer`` if a compression pointer did not
1112 point backwards in the message.
1114 Raises ``dns.name.BadLabelType`` if an invalid label type was encountered.
1116 Returns a ``(dns.name.Name, int)`` tuple consisting of the name
1117 that was read and the number of bytes of the wire format message
1118 which were consumed reading it.
1119 """
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)
1128# RFC 4471 Support
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("[")
1139def _wire_length(labels):
1140 return functools.reduce(lambda v, x: v + len(x) + 1, labels, 0)
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 -= 64
1149 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 like
1152 # to add one more byte as a new label, we can't, as adding a new non-empty
1153 # label requires at least 2 bytes.
1154 new_labels = list(reversed(new_labels))
1155 new_labels.extend(name.labels)
1156 return Name(new_labels)
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 - 1
1163 if remaining <= 0:
1164 # Shouldn't happen!
1165 return label
1166 needed = min(63 - length, remaining)
1167 return label + _MAXIMAL_OCTET * needed
1170def _absolute_predecessor(name: Name, origin: Name, prefix_ok: bool) -> Name:
1171 # This is the RFC 4471 predecessor algorithm using the "absolute method" of section
1172 # 3.1.1.
1173 #
1174 # Our caller must ensure that the name and origin are absolute, and that name is a
1175 # 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_VALUE
1190 else:
1191 octet -= 1
1192 octets[-1] = octet
1193 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
1203def _absolute_successor(name: Name, origin: Name, prefix_ok: bool) -> Name:
1204 # This is the RFC 4471 successor algorithm using the "absolute method" of section
1205 # 3.1.2.
1206 #
1207 # Our caller must ensure that the name and origin are absolute, and that name is a
1208 # subdomain of origin.
1209 if prefix_ok:
1210 # Try prefixing \000 as new label
1211 try:
1212 return _SUCCESSOR_PREFIX.concatenate(name)
1213 except NameTooLong:
1214 pass
1215 while name != origin:
1216 # Try extending the least significant label.
1217 least_significant_label = name[0]
1218 if len(least_significant_label) < 63:
1219 # We may be able to extend the least label with a minimal additional byte.
1220 # This is only "may" because we could have a maximal length name even though
1221 # the least significant label isn't maximally long.
1222 new_labels = [least_significant_label + _MINIMAL_OCTET]
1223 new_labels.extend(name.labels[1:])
1224 try:
1225 return dns.name.Name(new_labels)
1226 except dns.name.NameTooLong:
1227 pass
1228 # We can't extend the label either, so we'll try to increment the least
1229 # signficant non-maximal byte in it.
1230 octets = bytearray(least_significant_label)
1231 # We do this reversed iteration with an explicit indexing variable because
1232 # if we find something to increment, we're going to want to truncate everything
1233 # to the right of it.
1234 for i in range(len(octets) - 1, -1, -1):
1235 octet = octets[i]
1236 if octet == _MAXIMAL_OCTET_VALUE:
1237 # We can't increment this, so keep looking.
1238 continue
1239 # Finally, something we can increment. We have to apply a special rule for
1240 # incrementing "@", sending it to "[", because RFC 4034 6.1 says that when
1241 # comparing names, uppercase letters compare as if they were their
1242 # lower-case equivalents. If we increment "@" to "A", then it would compare
1243 # as "a", which is after "[", "\", "]", "^", "_", and "`", so we would have
1244 # skipped the most minimal successor, namely "[".
1245 if octet == _AT_SIGN_VALUE:
1246 octet = _LEFT_SQUARE_BRACKET_VALUE
1247 else:
1248 octet += 1
1249 octets[i] = octet
1250 # We can now truncate all of the maximal values we skipped (if any)
1251 new_labels = [bytes(octets[: i + 1])]
1252 new_labels.extend(name.labels[1:])
1253 # We haven't changed the length of the name, so the Name constructor will
1254 # always work.
1255 return Name(new_labels)
1256 # We couldn't increment, so chop off the least significant label and try
1257 # again.
1258 name = name.parent()
1260 # We couldn't increment at all, so return the origin, as wrapping around is the
1261 # DNSSEC way.
1262 return origin
1265def _handle_relativity_and_call(
1266 function: Callable[[Name, Name, bool], Name],
1267 name: Name,
1268 origin: Name,
1269 prefix_ok: bool,
1270) -> Name:
1271 # Make "name" absolute if needed, ensure that the origin is absolute,
1272 # call function(), and then relativize the result if needed.
1273 if not origin.is_absolute():
1274 raise NeedAbsoluteNameOrOrigin
1275 relative = not name.is_absolute()
1276 if relative:
1277 name = name.derelativize(origin)
1278 elif not name.is_subdomain(origin):
1279 raise NeedSubdomainOfOrigin
1280 result_name = function(name, origin, prefix_ok)
1281 if relative:
1282 result_name = result_name.relativize(origin)
1283 return result_name