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