Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/dns/name.py: 60%
522 statements
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-25 07:09 +0000
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-25 07:09 +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 struct
24from typing import Any, Dict, Iterable, Optional, Tuple, Union
26try:
27 import idna # type: ignore
29 have_idna_2008 = True
30except ImportError: # pragma: no cover
31 have_idna_2008 = False
33import dns.enum
34import dns.exception
35import dns.immutable
36import dns.wire
38CompressType = Dict["Name", int]
41class NameRelation(dns.enum.IntEnum):
42 """Name relation result from fullcompare()."""
44 # This is an IntEnum for backwards compatibility in case anyone
45 # has hardwired the constants.
47 #: The compared names have no relationship to each other.
48 NONE = 0
49 #: the first name is a superdomain of the second.
50 SUPERDOMAIN = 1
51 #: The first name is a subdomain of the second.
52 SUBDOMAIN = 2
53 #: The compared names are equal.
54 EQUAL = 3
55 #: The compared names have a common ancestor.
56 COMMONANCESTOR = 4
58 @classmethod
59 def _maximum(cls):
60 return cls.COMMONANCESTOR
62 @classmethod
63 def _short_name(cls):
64 return cls.__name__
67# Backwards compatibility
68NAMERELN_NONE = NameRelation.NONE
69NAMERELN_SUPERDOMAIN = NameRelation.SUPERDOMAIN
70NAMERELN_SUBDOMAIN = NameRelation.SUBDOMAIN
71NAMERELN_EQUAL = NameRelation.EQUAL
72NAMERELN_COMMONANCESTOR = NameRelation.COMMONANCESTOR
75class EmptyLabel(dns.exception.SyntaxError):
76 """A DNS label is empty."""
79class BadEscape(dns.exception.SyntaxError):
80 """An escaped code in a text format of DNS name is invalid."""
83class BadPointer(dns.exception.FormError):
84 """A DNS compression pointer points forward instead of backward."""
87class BadLabelType(dns.exception.FormError):
88 """The label type in DNS name wire format is unknown."""
91class NeedAbsoluteNameOrOrigin(dns.exception.DNSException):
92 """An attempt was made to convert a non-absolute name to
93 wire when there was also a non-absolute (or missing) origin."""
96class NameTooLong(dns.exception.FormError):
97 """A DNS name is > 255 octets long."""
100class LabelTooLong(dns.exception.SyntaxError):
101 """A DNS label is > 63 octets long."""
104class AbsoluteConcatenation(dns.exception.DNSException):
105 """An attempt was made to append anything other than the
106 empty name to an absolute DNS name."""
109class NoParent(dns.exception.DNSException):
110 """An attempt was made to get the parent of the root name
111 or the empty name."""
114class NoIDNA2008(dns.exception.DNSException):
115 """IDNA 2008 processing was requested but the idna module is not
116 available."""
119class IDNAException(dns.exception.DNSException):
120 """IDNA processing raised an exception."""
122 supp_kwargs = {"idna_exception"}
123 fmt = "IDNA processing exception: {idna_exception}"
125 # We do this as otherwise mypy complains about unexpected keyword argument
126 # idna_exception
127 def __init__(self, *args, **kwargs):
128 super().__init__(*args, **kwargs)
131_escaped = b'"().;\\@$'
132_escaped_text = '"().;\\@$'
135def _escapify(label: Union[bytes, str]) -> str:
136 """Escape the characters in label which need it.
137 @returns: the escaped string
138 @rtype: string"""
139 if isinstance(label, bytes):
140 # Ordinary DNS label mode. Escape special characters and values
141 # < 0x20 or > 0x7f.
142 text = ""
143 for c in label:
144 if c in _escaped:
145 text += "\\" + chr(c)
146 elif c > 0x20 and c < 0x7F:
147 text += chr(c)
148 else:
149 text += "\\%03d" % c
150 return text
152 # Unicode label mode. Escape only special characters and values < 0x20
153 text = ""
154 for uc in label:
155 if uc in _escaped_text:
156 text += "\\" + uc
157 elif uc <= "\x20":
158 text += "\\%03d" % ord(uc)
159 else:
160 text += uc
161 return text
164class IDNACodec:
165 """Abstract base class for IDNA encoder/decoders."""
167 def __init__(self):
168 pass
170 def is_idna(self, label: bytes) -> bool:
171 return label.lower().startswith(b"xn--")
173 def encode(self, label: str) -> bytes:
174 raise NotImplementedError # pragma: no cover
176 def decode(self, label: bytes) -> str:
177 # We do not apply any IDNA policy on decode.
178 if self.is_idna(label):
179 try:
180 slabel = label[4:].decode("punycode")
181 return _escapify(slabel)
182 except Exception as e:
183 raise IDNAException(idna_exception=e)
184 else:
185 return _escapify(label)
188class IDNA2003Codec(IDNACodec):
189 """IDNA 2003 encoder/decoder."""
191 def __init__(self, strict_decode: bool = False):
192 """Initialize the IDNA 2003 encoder/decoder.
194 *strict_decode* is a ``bool``. If `True`, then IDNA2003 checking
195 is done when decoding. This can cause failures if the name
196 was encoded with IDNA2008. The default is `False`.
197 """
199 super().__init__()
200 self.strict_decode = strict_decode
202 def encode(self, label: str) -> bytes:
203 """Encode *label*."""
205 if label == "":
206 return b""
207 try:
208 return encodings.idna.ToASCII(label)
209 except UnicodeError:
210 raise LabelTooLong
212 def decode(self, label: bytes) -> str:
213 """Decode *label*."""
214 if not self.strict_decode:
215 return super().decode(label)
216 if label == b"":
217 return ""
218 try:
219 return _escapify(encodings.idna.ToUnicode(label))
220 except Exception as e:
221 raise IDNAException(idna_exception=e)
224class IDNA2008Codec(IDNACodec):
225 """IDNA 2008 encoder/decoder."""
227 def __init__(
228 self,
229 uts_46: bool = False,
230 transitional: bool = False,
231 allow_pure_ascii: bool = False,
232 strict_decode: bool = False,
233 ):
234 """Initialize the IDNA 2008 encoder/decoder.
236 *uts_46* is a ``bool``. If True, apply Unicode IDNA
237 compatibility processing as described in Unicode Technical
238 Standard #46 (https://unicode.org/reports/tr46/).
239 If False, do not apply the mapping. The default is False.
241 *transitional* is a ``bool``: If True, use the
242 "transitional" mode described in Unicode Technical Standard
243 #46. The default is False.
245 *allow_pure_ascii* is a ``bool``. If True, then a label which
246 consists of only ASCII characters is allowed. This is less
247 strict than regular IDNA 2008, but is also necessary for mixed
248 names, e.g. a name with starting with "_sip._tcp." and ending
249 in an IDN suffix which would otherwise be disallowed. The
250 default is False.
252 *strict_decode* is a ``bool``: If True, then IDNA2008 checking
253 is done when decoding. This can cause failures if the name
254 was encoded with IDNA2003. The default is False.
255 """
256 super().__init__()
257 self.uts_46 = uts_46
258 self.transitional = transitional
259 self.allow_pure_ascii = allow_pure_ascii
260 self.strict_decode = strict_decode
262 def encode(self, label: str) -> bytes:
263 if label == "":
264 return b""
265 if self.allow_pure_ascii and is_all_ascii(label):
266 encoded = label.encode("ascii")
267 if len(encoded) > 63:
268 raise LabelTooLong
269 return encoded
270 if not have_idna_2008:
271 raise NoIDNA2008
272 try:
273 if self.uts_46:
274 label = idna.uts46_remap(label, False, self.transitional)
275 return idna.alabel(label)
276 except idna.IDNAError as e:
277 if e.args[0] == "Label too long":
278 raise LabelTooLong
279 else:
280 raise IDNAException(idna_exception=e)
282 def decode(self, label: bytes) -> str:
283 if not self.strict_decode:
284 return super().decode(label)
285 if label == b"":
286 return ""
287 if not have_idna_2008:
288 raise NoIDNA2008
289 try:
290 ulabel = idna.ulabel(label)
291 if self.uts_46:
292 ulabel = idna.uts46_remap(ulabel, False, self.transitional)
293 return _escapify(ulabel)
294 except (idna.IDNAError, UnicodeError) as e:
295 raise IDNAException(idna_exception=e)
298IDNA_2003_Practical = IDNA2003Codec(False)
299IDNA_2003_Strict = IDNA2003Codec(True)
300IDNA_2003 = IDNA_2003_Practical
301IDNA_2008_Practical = IDNA2008Codec(True, False, True, False)
302IDNA_2008_UTS_46 = IDNA2008Codec(True, False, False, False)
303IDNA_2008_Strict = IDNA2008Codec(False, False, False, True)
304IDNA_2008_Transitional = IDNA2008Codec(True, True, False, False)
305IDNA_2008 = IDNA_2008_Practical
308def _validate_labels(labels: Tuple[bytes, ...]) -> None:
309 """Check for empty labels in the middle of a label sequence,
310 labels that are too long, and for too many labels.
312 Raises ``dns.name.NameTooLong`` if the name as a whole is too long.
314 Raises ``dns.name.EmptyLabel`` if a label is empty (i.e. the root
315 label) and appears in a position other than the end of the label
316 sequence
318 """
320 l = len(labels)
321 total = 0
322 i = -1
323 j = 0
324 for label in labels:
325 ll = len(label)
326 total += ll + 1
327 if ll > 63:
328 raise LabelTooLong
329 if i < 0 and label == b"":
330 i = j
331 j += 1
332 if total > 255:
333 raise NameTooLong
334 if i >= 0 and i != l - 1:
335 raise EmptyLabel
338def _maybe_convert_to_binary(label: Union[bytes, str]) -> bytes:
339 """If label is ``str``, convert it to ``bytes``. If it is already
340 ``bytes`` just return it.
342 """
344 if isinstance(label, bytes):
345 return label
346 if isinstance(label, str):
347 return label.encode()
348 raise ValueError # pragma: no cover
351@dns.immutable.immutable
352class Name:
354 """A DNS name.
356 The dns.name.Name class represents a DNS name as a tuple of
357 labels. Each label is a ``bytes`` in DNS wire format. Instances
358 of the class are immutable.
359 """
361 __slots__ = ["labels"]
363 def __init__(self, labels: Iterable[Union[bytes, str]]):
364 """*labels* is any iterable whose values are ``str`` or ``bytes``."""
366 blabels = [_maybe_convert_to_binary(x) for x in labels]
367 self.labels = tuple(blabels)
368 _validate_labels(self.labels)
370 def __copy__(self):
371 return Name(self.labels)
373 def __deepcopy__(self, memo):
374 return Name(copy.deepcopy(self.labels, memo))
376 def __getstate__(self):
377 # Names can be pickled
378 return {"labels": self.labels}
380 def __setstate__(self, state):
381 super().__setattr__("labels", state["labels"])
382 _validate_labels(self.labels)
384 def is_absolute(self) -> bool:
385 """Is the most significant label of this name the root label?
387 Returns a ``bool``.
388 """
390 return len(self.labels) > 0 and self.labels[-1] == b""
392 def is_wild(self) -> bool:
393 """Is this name wild? (I.e. Is the least significant label '*'?)
395 Returns a ``bool``.
396 """
398 return len(self.labels) > 0 and self.labels[0] == b"*"
400 def __hash__(self) -> int:
401 """Return a case-insensitive hash of the name.
403 Returns an ``int``.
404 """
406 h = 0
407 for label in self.labels:
408 for c in label.lower():
409 h += (h << 3) + c
410 return h
412 def fullcompare(self, other: "Name") -> Tuple[NameRelation, int, int]:
413 """Compare two names, returning a 3-tuple
414 ``(relation, order, nlabels)``.
416 *relation* describes the relation ship between the names,
417 and is one of: ``dns.name.NameRelation.NONE``,
418 ``dns.name.NameRelation.SUPERDOMAIN``, ``dns.name.NameRelation.SUBDOMAIN``,
419 ``dns.name.NameRelation.EQUAL``, or ``dns.name.NameRelation.COMMONANCESTOR``.
421 *order* is < 0 if *self* < *other*, > 0 if *self* > *other*, and ==
422 0 if *self* == *other*. A relative name is always less than an
423 absolute name. If both names have the same relativity, then
424 the DNSSEC order relation is used to order them.
426 *nlabels* is the number of significant labels that the two names
427 have in common.
429 Here are some examples. Names ending in "." are absolute names,
430 those not ending in "." are relative names.
432 ============= ============= =========== ===== =======
433 self other relation order nlabels
434 ============= ============= =========== ===== =======
435 www.example. www.example. equal 0 3
436 www.example. example. subdomain > 0 2
437 example. www.example. superdomain < 0 2
438 example1.com. example2.com. common anc. < 0 2
439 example1 example2. none < 0 0
440 example1. example2 none > 0 0
441 ============= ============= =========== ===== =======
442 """
444 sabs = self.is_absolute()
445 oabs = other.is_absolute()
446 if sabs != oabs:
447 if sabs:
448 return (NameRelation.NONE, 1, 0)
449 else:
450 return (NameRelation.NONE, -1, 0)
451 l1 = len(self.labels)
452 l2 = len(other.labels)
453 ldiff = l1 - l2
454 if ldiff < 0:
455 l = l1
456 else:
457 l = l2
459 order = 0
460 nlabels = 0
461 namereln = NameRelation.NONE
462 while l > 0:
463 l -= 1
464 l1 -= 1
465 l2 -= 1
466 label1 = self.labels[l1].lower()
467 label2 = other.labels[l2].lower()
468 if label1 < label2:
469 order = -1
470 if nlabels > 0:
471 namereln = NameRelation.COMMONANCESTOR
472 return (namereln, order, nlabels)
473 elif label1 > label2:
474 order = 1
475 if nlabels > 0:
476 namereln = NameRelation.COMMONANCESTOR
477 return (namereln, order, nlabels)
478 nlabels += 1
479 order = ldiff
480 if ldiff < 0:
481 namereln = NameRelation.SUPERDOMAIN
482 elif ldiff > 0:
483 namereln = NameRelation.SUBDOMAIN
484 else:
485 namereln = NameRelation.EQUAL
486 return (namereln, order, nlabels)
488 def is_subdomain(self, other: "Name") -> bool:
489 """Is self a subdomain of other?
491 Note that the notion of subdomain includes equality, e.g.
492 "dnspython.org" is a subdomain of itself.
494 Returns a ``bool``.
495 """
497 (nr, _, _) = self.fullcompare(other)
498 if nr == NameRelation.SUBDOMAIN or nr == NameRelation.EQUAL:
499 return True
500 return False
502 def is_superdomain(self, other: "Name") -> bool:
503 """Is self a superdomain of other?
505 Note that the notion of superdomain includes equality, e.g.
506 "dnspython.org" is a superdomain of itself.
508 Returns a ``bool``.
509 """
511 (nr, _, _) = self.fullcompare(other)
512 if nr == NameRelation.SUPERDOMAIN or nr == NameRelation.EQUAL:
513 return True
514 return False
516 def canonicalize(self) -> "Name":
517 """Return a name which is equal to the current name, but is in
518 DNSSEC canonical form.
519 """
521 return Name([x.lower() for x in self.labels])
523 def __eq__(self, other):
524 if isinstance(other, Name):
525 return self.fullcompare(other)[1] == 0
526 else:
527 return False
529 def __ne__(self, other):
530 if isinstance(other, Name):
531 return self.fullcompare(other)[1] != 0
532 else:
533 return True
535 def __lt__(self, other):
536 if isinstance(other, Name):
537 return self.fullcompare(other)[1] < 0
538 else:
539 return NotImplemented
541 def __le__(self, other):
542 if isinstance(other, Name):
543 return self.fullcompare(other)[1] <= 0
544 else:
545 return NotImplemented
547 def __ge__(self, other):
548 if isinstance(other, Name):
549 return self.fullcompare(other)[1] >= 0
550 else:
551 return NotImplemented
553 def __gt__(self, other):
554 if isinstance(other, Name):
555 return self.fullcompare(other)[1] > 0
556 else:
557 return NotImplemented
559 def __repr__(self):
560 return "<DNS name " + self.__str__() + ">"
562 def __str__(self):
563 return self.to_text(False)
565 def to_text(self, omit_final_dot: bool = False) -> str:
566 """Convert name to DNS text format.
568 *omit_final_dot* is a ``bool``. If True, don't emit the final
569 dot (denoting the root label) for absolute names. The default
570 is False.
572 Returns a ``str``.
573 """
575 if len(self.labels) == 0:
576 return "@"
577 if len(self.labels) == 1 and self.labels[0] == b"":
578 return "."
579 if omit_final_dot and self.is_absolute():
580 l = self.labels[:-1]
581 else:
582 l = self.labels
583 s = ".".join(map(_escapify, l))
584 return s
586 def to_unicode(
587 self, omit_final_dot: bool = False, idna_codec: Optional[IDNACodec] = None
588 ) -> str:
589 """Convert name to Unicode text format.
591 IDN ACE labels are converted to Unicode.
593 *omit_final_dot* is a ``bool``. If True, don't emit the final
594 dot (denoting the root label) for absolute names. The default
595 is False.
596 *idna_codec* specifies the IDNA encoder/decoder. If None, the
597 dns.name.IDNA_2003_Practical encoder/decoder is used.
598 The IDNA_2003_Practical decoder does
599 not impose any policy, it just decodes punycode, so if you
600 don't want checking for compliance, you can use this decoder
601 for IDNA2008 as well.
603 Returns a ``str``.
604 """
606 if len(self.labels) == 0:
607 return "@"
608 if len(self.labels) == 1 and self.labels[0] == b"":
609 return "."
610 if omit_final_dot and self.is_absolute():
611 l = self.labels[:-1]
612 else:
613 l = self.labels
614 if idna_codec is None:
615 idna_codec = IDNA_2003_Practical
616 return ".".join([idna_codec.decode(x) for x in l])
618 def to_digestable(self, origin: Optional["Name"] = None) -> bytes:
619 """Convert name to a format suitable for digesting in hashes.
621 The name is canonicalized and converted to uncompressed wire
622 format. All names in wire format are absolute. If the name
623 is a relative name, then an origin must be supplied.
625 *origin* is a ``dns.name.Name`` or ``None``. If the name is
626 relative and origin is not ``None``, then origin will be appended
627 to the name.
629 Raises ``dns.name.NeedAbsoluteNameOrOrigin`` if the name is
630 relative and no origin was provided.
632 Returns a ``bytes``.
633 """
635 digest = self.to_wire(origin=origin, canonicalize=True)
636 assert digest is not None
637 return digest
639 def to_wire(
640 self,
641 file: Optional[Any] = None,
642 compress: Optional[CompressType] = None,
643 origin: Optional["Name"] = None,
644 canonicalize: bool = False,
645 ) -> Optional[bytes]:
646 """Convert name to wire format, possibly compressing it.
648 *file* is the file where the name is emitted (typically an
649 io.BytesIO file). If ``None`` (the default), a ``bytes``
650 containing the wire name will be returned.
652 *compress*, a ``dict``, is the compression table to use. If
653 ``None`` (the default), names will not be compressed. Note that
654 the compression code assumes that compression offset 0 is the
655 start of *file*, and thus compression will not be correct
656 if this is not the case.
658 *origin* is a ``dns.name.Name`` or ``None``. If the name is
659 relative and origin is not ``None``, then *origin* will be appended
660 to it.
662 *canonicalize*, a ``bool``, indicates whether the name should
663 be canonicalized; that is, converted to a format suitable for
664 digesting in hashes.
666 Raises ``dns.name.NeedAbsoluteNameOrOrigin`` if the name is
667 relative and no origin was provided.
669 Returns a ``bytes`` or ``None``.
670 """
672 if file is None:
673 out = bytearray()
674 for label in self.labels:
675 out.append(len(label))
676 if canonicalize:
677 out += label.lower()
678 else:
679 out += label
680 if not self.is_absolute():
681 if origin is None or not origin.is_absolute():
682 raise NeedAbsoluteNameOrOrigin
683 for label in origin.labels:
684 out.append(len(label))
685 if canonicalize:
686 out += label.lower()
687 else:
688 out += label
689 return bytes(out)
691 labels: Iterable[bytes]
692 if not self.is_absolute():
693 if origin is None or not origin.is_absolute():
694 raise NeedAbsoluteNameOrOrigin
695 labels = list(self.labels)
696 labels.extend(list(origin.labels))
697 else:
698 labels = self.labels
699 i = 0
700 for label in labels:
701 n = Name(labels[i:])
702 i += 1
703 if compress is not None:
704 pos = compress.get(n)
705 else:
706 pos = None
707 if pos is not None:
708 value = 0xC000 + pos
709 s = struct.pack("!H", value)
710 file.write(s)
711 break
712 else:
713 if compress is not None and len(n) > 1:
714 pos = file.tell()
715 if pos <= 0x3FFF:
716 compress[n] = pos
717 l = len(label)
718 file.write(struct.pack("!B", l))
719 if l > 0:
720 if canonicalize:
721 file.write(label.lower())
722 else:
723 file.write(label)
724 return None
726 def __len__(self) -> int:
727 """The length of the name (in labels).
729 Returns an ``int``.
730 """
732 return len(self.labels)
734 def __getitem__(self, index):
735 return self.labels[index]
737 def __add__(self, other):
738 return self.concatenate(other)
740 def __sub__(self, other):
741 return self.relativize(other)
743 def split(self, depth: int) -> Tuple["Name", "Name"]:
744 """Split a name into a prefix and suffix names at the specified depth.
746 *depth* is an ``int`` specifying the number of labels in the suffix
748 Raises ``ValueError`` if *depth* was not >= 0 and <= the length of the
749 name.
751 Returns the tuple ``(prefix, suffix)``.
752 """
754 l = len(self.labels)
755 if depth == 0:
756 return (self, dns.name.empty)
757 elif depth == l:
758 return (dns.name.empty, self)
759 elif depth < 0 or depth > l:
760 raise ValueError("depth must be >= 0 and <= the length of the name")
761 return (Name(self[:-depth]), Name(self[-depth:]))
763 def concatenate(self, other: "Name") -> "Name":
764 """Return a new name which is the concatenation of self and other.
766 Raises ``dns.name.AbsoluteConcatenation`` if the name is
767 absolute and *other* is not the empty name.
769 Returns a ``dns.name.Name``.
770 """
772 if self.is_absolute() and len(other) > 0:
773 raise AbsoluteConcatenation
774 labels = list(self.labels)
775 labels.extend(list(other.labels))
776 return Name(labels)
778 def relativize(self, origin: "Name") -> "Name":
779 """If the name is a subdomain of *origin*, return a new name which is
780 the name relative to origin. Otherwise return the name.
782 For example, relativizing ``www.dnspython.org.`` to origin
783 ``dnspython.org.`` returns the name ``www``. Relativizing ``example.``
784 to origin ``dnspython.org.`` returns ``example.``.
786 Returns a ``dns.name.Name``.
787 """
789 if origin is not None and self.is_subdomain(origin):
790 return Name(self[: -len(origin)])
791 else:
792 return self
794 def derelativize(self, origin: "Name") -> "Name":
795 """If the name is a relative name, return a new name which is the
796 concatenation of the name and origin. Otherwise return the name.
798 For example, derelativizing ``www`` to origin ``dnspython.org.``
799 returns the name ``www.dnspython.org.``. Derelativizing ``example.``
800 to origin ``dnspython.org.`` returns ``example.``.
802 Returns a ``dns.name.Name``.
803 """
805 if not self.is_absolute():
806 return self.concatenate(origin)
807 else:
808 return self
810 def choose_relativity(
811 self, origin: Optional["Name"] = None, relativize: bool = True
812 ) -> "Name":
813 """Return a name with the relativity desired by the caller.
815 If *origin* is ``None``, then the name is returned.
816 Otherwise, if *relativize* is ``True`` the name is
817 relativized, and if *relativize* is ``False`` the name is
818 derelativized.
820 Returns a ``dns.name.Name``.
821 """
823 if origin:
824 if relativize:
825 return self.relativize(origin)
826 else:
827 return self.derelativize(origin)
828 else:
829 return self
831 def parent(self) -> "Name":
832 """Return the parent of the name.
834 For example, the parent of ``www.dnspython.org.`` is ``dnspython.org``.
836 Raises ``dns.name.NoParent`` if the name is either the root name or the
837 empty name, and thus has no parent.
839 Returns a ``dns.name.Name``.
840 """
842 if self == root or self == empty:
843 raise NoParent
844 return Name(self.labels[1:])
847#: The root name, '.'
848root = Name([b""])
850#: The empty name.
851empty = Name([])
854def from_unicode(
855 text: str, origin: Optional[Name] = root, idna_codec: Optional[IDNACodec] = None
856) -> Name:
857 """Convert unicode text into a Name object.
859 Labels are encoded in IDN ACE form according to rules specified by
860 the IDNA codec.
862 *text*, a ``str``, is the text to convert into a name.
864 *origin*, a ``dns.name.Name``, specifies the origin to
865 append to non-absolute names. The default is the root name.
867 *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA
868 encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder
869 is used.
871 Returns a ``dns.name.Name``.
872 """
874 if not isinstance(text, str):
875 raise ValueError("input to from_unicode() must be a unicode string")
876 if not (origin is None or isinstance(origin, Name)):
877 raise ValueError("origin must be a Name or None")
878 labels = []
879 label = ""
880 escaping = False
881 edigits = 0
882 total = 0
883 if idna_codec is None:
884 idna_codec = IDNA_2003
885 if text == "@":
886 text = ""
887 if text:
888 if text in [".", "\u3002", "\uff0e", "\uff61"]:
889 return Name([b""]) # no Unicode "u" on this constant!
890 for c in text:
891 if escaping:
892 if edigits == 0:
893 if c.isdigit():
894 total = int(c)
895 edigits += 1
896 else:
897 label += c
898 escaping = False
899 else:
900 if not c.isdigit():
901 raise BadEscape
902 total *= 10
903 total += int(c)
904 edigits += 1
905 if edigits == 3:
906 escaping = False
907 label += chr(total)
908 elif c in [".", "\u3002", "\uff0e", "\uff61"]:
909 if len(label) == 0:
910 raise EmptyLabel
911 labels.append(idna_codec.encode(label))
912 label = ""
913 elif c == "\\":
914 escaping = True
915 edigits = 0
916 total = 0
917 else:
918 label += c
919 if escaping:
920 raise BadEscape
921 if len(label) > 0:
922 labels.append(idna_codec.encode(label))
923 else:
924 labels.append(b"")
926 if (len(labels) == 0 or labels[-1] != b"") and origin is not None:
927 labels.extend(list(origin.labels))
928 return Name(labels)
931def is_all_ascii(text: str) -> bool:
932 for c in text:
933 if ord(c) > 0x7F:
934 return False
935 return True
938def from_text(
939 text: Union[bytes, str],
940 origin: Optional[Name] = root,
941 idna_codec: Optional[IDNACodec] = None,
942) -> Name:
943 """Convert text into a Name object.
945 *text*, a ``bytes`` or ``str``, is the text to convert into a name.
947 *origin*, a ``dns.name.Name``, specifies the origin to
948 append to non-absolute names. The default is the root name.
950 *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA
951 encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder
952 is used.
954 Returns a ``dns.name.Name``.
955 """
957 if isinstance(text, str):
958 if not is_all_ascii(text):
959 # Some codepoint in the input text is > 127, so IDNA applies.
960 return from_unicode(text, origin, idna_codec)
961 # The input is all ASCII, so treat this like an ordinary non-IDNA
962 # domain name. Note that "all ASCII" is about the input text,
963 # not the codepoints in the domain name. E.g. if text has value
964 #
965 # r'\150\151\152\153\154\155\156\157\158\159'
966 #
967 # then it's still "all ASCII" even though the domain name has
968 # codepoints > 127.
969 text = text.encode("ascii")
970 if not isinstance(text, bytes):
971 raise ValueError("input to from_text() must be a string")
972 if not (origin is None or isinstance(origin, Name)):
973 raise ValueError("origin must be a Name or None")
974 labels = []
975 label = b""
976 escaping = False
977 edigits = 0
978 total = 0
979 if text == b"@":
980 text = b""
981 if text:
982 if text == b".":
983 return Name([b""])
984 for c in text:
985 byte_ = struct.pack("!B", c)
986 if escaping:
987 if edigits == 0:
988 if byte_.isdigit():
989 total = int(byte_)
990 edigits += 1
991 else:
992 label += byte_
993 escaping = False
994 else:
995 if not byte_.isdigit():
996 raise BadEscape
997 total *= 10
998 total += int(byte_)
999 edigits += 1
1000 if edigits == 3:
1001 escaping = False
1002 label += struct.pack("!B", total)
1003 elif byte_ == b".":
1004 if len(label) == 0:
1005 raise EmptyLabel
1006 labels.append(label)
1007 label = b""
1008 elif byte_ == b"\\":
1009 escaping = True
1010 edigits = 0
1011 total = 0
1012 else:
1013 label += byte_
1014 if escaping:
1015 raise BadEscape
1016 if len(label) > 0:
1017 labels.append(label)
1018 else:
1019 labels.append(b"")
1020 if (len(labels) == 0 or labels[-1] != b"") and origin is not None:
1021 labels.extend(list(origin.labels))
1022 return Name(labels)
1025# we need 'dns.wire.Parser' quoted as dns.name and dns.wire depend on each other.
1028def from_wire_parser(parser: "dns.wire.Parser") -> Name:
1029 """Convert possibly compressed wire format into a Name.
1031 *parser* is a dns.wire.Parser.
1033 Raises ``dns.name.BadPointer`` if a compression pointer did not
1034 point backwards in the message.
1036 Raises ``dns.name.BadLabelType`` if an invalid label type was encountered.
1038 Returns a ``dns.name.Name``
1039 """
1041 labels = []
1042 biggest_pointer = parser.current
1043 with parser.restore_furthest():
1044 count = parser.get_uint8()
1045 while count != 0:
1046 if count < 64:
1047 labels.append(parser.get_bytes(count))
1048 elif count >= 192:
1049 current = (count & 0x3F) * 256 + parser.get_uint8()
1050 if current >= biggest_pointer:
1051 raise BadPointer
1052 biggest_pointer = current
1053 parser.seek(current)
1054 else:
1055 raise BadLabelType
1056 count = parser.get_uint8()
1057 labels.append(b"")
1058 return Name(labels)
1061def from_wire(message: bytes, current: int) -> Tuple[Name, int]:
1062 """Convert possibly compressed wire format into a Name.
1064 *message* is a ``bytes`` containing an entire DNS message in DNS
1065 wire form.
1067 *current*, an ``int``, is the offset of the beginning of the name
1068 from the start of the message
1070 Raises ``dns.name.BadPointer`` if a compression pointer did not
1071 point backwards in the message.
1073 Raises ``dns.name.BadLabelType`` if an invalid label type was encountered.
1075 Returns a ``(dns.name.Name, int)`` tuple consisting of the name
1076 that was read and the number of bytes of the wire format message
1077 which were consumed reading it.
1078 """
1080 if not isinstance(message, bytes):
1081 raise ValueError("input to from_wire() must be a byte string")
1082 parser = dns.wire.Parser(message, current)
1083 name = from_wire_parser(parser)
1084 return (name, parser.current - current)