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

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 encodings.idna # type: ignore 

23import struct 

24from typing import Any, Dict, Iterable, Optional, Tuple, Union 

25 

26try: 

27 import idna # type: ignore 

28 

29 have_idna_2008 = True 

30except ImportError: # pragma: no cover 

31 have_idna_2008 = False 

32 

33import dns.enum 

34import dns.exception 

35import dns.immutable 

36import dns.wire 

37 

38CompressType = Dict["Name", int] 

39 

40 

41class NameRelation(dns.enum.IntEnum): 

42 """Name relation result from fullcompare().""" 

43 

44 # This is an IntEnum for backwards compatibility in case anyone 

45 # has hardwired the constants. 

46 

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 

57 

58 @classmethod 

59 def _maximum(cls): 

60 return cls.COMMONANCESTOR 

61 

62 @classmethod 

63 def _short_name(cls): 

64 return cls.__name__ 

65 

66 

67# Backwards compatibility 

68NAMERELN_NONE = NameRelation.NONE 

69NAMERELN_SUPERDOMAIN = NameRelation.SUPERDOMAIN 

70NAMERELN_SUBDOMAIN = NameRelation.SUBDOMAIN 

71NAMERELN_EQUAL = NameRelation.EQUAL 

72NAMERELN_COMMONANCESTOR = NameRelation.COMMONANCESTOR 

73 

74 

75class EmptyLabel(dns.exception.SyntaxError): 

76 """A DNS label is empty.""" 

77 

78 

79class BadEscape(dns.exception.SyntaxError): 

80 """An escaped code in a text format of DNS name is invalid.""" 

81 

82 

83class BadPointer(dns.exception.FormError): 

84 """A DNS compression pointer points forward instead of backward.""" 

85 

86 

87class BadLabelType(dns.exception.FormError): 

88 """The label type in DNS name wire format is unknown.""" 

89 

90 

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.""" 

94 

95 

96class NameTooLong(dns.exception.FormError): 

97 """A DNS name is > 255 octets long.""" 

98 

99 

100class LabelTooLong(dns.exception.SyntaxError): 

101 """A DNS label is > 63 octets long.""" 

102 

103 

104class AbsoluteConcatenation(dns.exception.DNSException): 

105 """An attempt was made to append anything other than the 

106 empty name to an absolute DNS name.""" 

107 

108 

109class NoParent(dns.exception.DNSException): 

110 """An attempt was made to get the parent of the root name 

111 or the empty name.""" 

112 

113 

114class NoIDNA2008(dns.exception.DNSException): 

115 """IDNA 2008 processing was requested but the idna module is not 

116 available.""" 

117 

118 

119class IDNAException(dns.exception.DNSException): 

120 """IDNA processing raised an exception.""" 

121 

122 supp_kwargs = {"idna_exception"} 

123 fmt = "IDNA processing exception: {idna_exception}" 

124 

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) 

129 

130 

131_escaped = b'"().;\\@$' 

132_escaped_text = '"().;\\@$' 

133 

134 

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 

151 

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 

162 

163 

164class IDNACodec: 

165 """Abstract base class for IDNA encoder/decoders.""" 

166 

167 def __init__(self): 

168 pass 

169 

170 def is_idna(self, label: bytes) -> bool: 

171 return label.lower().startswith(b"xn--") 

172 

173 def encode(self, label: str) -> bytes: 

174 raise NotImplementedError # pragma: no cover 

175 

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) 

186 

187 

188class IDNA2003Codec(IDNACodec): 

189 """IDNA 2003 encoder/decoder.""" 

190 

191 def __init__(self, strict_decode: bool = False): 

192 """Initialize the IDNA 2003 encoder/decoder. 

193 

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 """ 

198 

199 super().__init__() 

200 self.strict_decode = strict_decode 

201 

202 def encode(self, label: str) -> bytes: 

203 """Encode *label*.""" 

204 

205 if label == "": 

206 return b"" 

207 try: 

208 return encodings.idna.ToASCII(label) 

209 except UnicodeError: 

210 raise LabelTooLong 

211 

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) 

222 

223 

224class IDNA2008Codec(IDNACodec): 

225 """IDNA 2008 encoder/decoder.""" 

226 

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. 

235 

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. 

240 

241 *transitional* is a ``bool``: If True, use the 

242 "transitional" mode described in Unicode Technical Standard 

243 #46. The default is False. 

244 

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. 

251 

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 

261 

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) 

281 

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) 

296 

297 

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 

306 

307 

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. 

311 

312 Raises ``dns.name.NameTooLong`` if the name as a whole is too long. 

313 

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 

317 

318 """ 

319 

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 

336 

337 

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. 

341 

342 """ 

343 

344 if isinstance(label, bytes): 

345 return label 

346 if isinstance(label, str): 

347 return label.encode() 

348 raise ValueError # pragma: no cover 

349 

350 

351@dns.immutable.immutable 

352class Name: 

353 

354 """A DNS name. 

355 

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 """ 

360 

361 __slots__ = ["labels"] 

362 

363 def __init__(self, labels: Iterable[Union[bytes, str]]): 

364 """*labels* is any iterable whose values are ``str`` or ``bytes``.""" 

365 

366 blabels = [_maybe_convert_to_binary(x) for x in labels] 

367 self.labels = tuple(blabels) 

368 _validate_labels(self.labels) 

369 

370 def __copy__(self): 

371 return Name(self.labels) 

372 

373 def __deepcopy__(self, memo): 

374 return Name(copy.deepcopy(self.labels, memo)) 

375 

376 def __getstate__(self): 

377 # Names can be pickled 

378 return {"labels": self.labels} 

379 

380 def __setstate__(self, state): 

381 super().__setattr__("labels", state["labels"]) 

382 _validate_labels(self.labels) 

383 

384 def is_absolute(self) -> bool: 

385 """Is the most significant label of this name the root label? 

386 

387 Returns a ``bool``. 

388 """ 

389 

390 return len(self.labels) > 0 and self.labels[-1] == b"" 

391 

392 def is_wild(self) -> bool: 

393 """Is this name wild? (I.e. Is the least significant label '*'?) 

394 

395 Returns a ``bool``. 

396 """ 

397 

398 return len(self.labels) > 0 and self.labels[0] == b"*" 

399 

400 def __hash__(self) -> int: 

401 """Return a case-insensitive hash of the name. 

402 

403 Returns an ``int``. 

404 """ 

405 

406 h = 0 

407 for label in self.labels: 

408 for c in label.lower(): 

409 h += (h << 3) + c 

410 return h 

411 

412 def fullcompare(self, other: "Name") -> Tuple[NameRelation, int, int]: 

413 """Compare two names, returning a 3-tuple 

414 ``(relation, order, nlabels)``. 

415 

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``. 

420 

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. 

425 

426 *nlabels* is the number of significant labels that the two names 

427 have in common. 

428 

429 Here are some examples. Names ending in "." are absolute names, 

430 those not ending in "." are relative names. 

431 

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 """ 

443 

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 

458 

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) 

487 

488 def is_subdomain(self, other: "Name") -> bool: 

489 """Is self a subdomain of other? 

490 

491 Note that the notion of subdomain includes equality, e.g. 

492 "dnspython.org" is a subdomain of itself. 

493 

494 Returns a ``bool``. 

495 """ 

496 

497 (nr, _, _) = self.fullcompare(other) 

498 if nr == NameRelation.SUBDOMAIN or nr == NameRelation.EQUAL: 

499 return True 

500 return False 

501 

502 def is_superdomain(self, other: "Name") -> bool: 

503 """Is self a superdomain of other? 

504 

505 Note that the notion of superdomain includes equality, e.g. 

506 "dnspython.org" is a superdomain of itself. 

507 

508 Returns a ``bool``. 

509 """ 

510 

511 (nr, _, _) = self.fullcompare(other) 

512 if nr == NameRelation.SUPERDOMAIN or nr == NameRelation.EQUAL: 

513 return True 

514 return False 

515 

516 def canonicalize(self) -> "Name": 

517 """Return a name which is equal to the current name, but is in 

518 DNSSEC canonical form. 

519 """ 

520 

521 return Name([x.lower() for x in self.labels]) 

522 

523 def __eq__(self, other): 

524 if isinstance(other, Name): 

525 return self.fullcompare(other)[1] == 0 

526 else: 

527 return False 

528 

529 def __ne__(self, other): 

530 if isinstance(other, Name): 

531 return self.fullcompare(other)[1] != 0 

532 else: 

533 return True 

534 

535 def __lt__(self, other): 

536 if isinstance(other, Name): 

537 return self.fullcompare(other)[1] < 0 

538 else: 

539 return NotImplemented 

540 

541 def __le__(self, other): 

542 if isinstance(other, Name): 

543 return self.fullcompare(other)[1] <= 0 

544 else: 

545 return NotImplemented 

546 

547 def __ge__(self, other): 

548 if isinstance(other, Name): 

549 return self.fullcompare(other)[1] >= 0 

550 else: 

551 return NotImplemented 

552 

553 def __gt__(self, other): 

554 if isinstance(other, Name): 

555 return self.fullcompare(other)[1] > 0 

556 else: 

557 return NotImplemented 

558 

559 def __repr__(self): 

560 return "<DNS name " + self.__str__() + ">" 

561 

562 def __str__(self): 

563 return self.to_text(False) 

564 

565 def to_text(self, omit_final_dot: bool = False) -> str: 

566 """Convert name to DNS text format. 

567 

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. 

571 

572 Returns a ``str``. 

573 """ 

574 

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 

585 

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. 

590 

591 IDN ACE labels are converted to Unicode. 

592 

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. 

602 

603 Returns a ``str``. 

604 """ 

605 

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]) 

617 

618 def to_digestable(self, origin: Optional["Name"] = None) -> bytes: 

619 """Convert name to a format suitable for digesting in hashes. 

620 

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. 

624 

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. 

628 

629 Raises ``dns.name.NeedAbsoluteNameOrOrigin`` if the name is 

630 relative and no origin was provided. 

631 

632 Returns a ``bytes``. 

633 """ 

634 

635 digest = self.to_wire(origin=origin, canonicalize=True) 

636 assert digest is not None 

637 return digest 

638 

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. 

647 

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. 

651 

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. 

657 

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. 

661 

662 *canonicalize*, a ``bool``, indicates whether the name should 

663 be canonicalized; that is, converted to a format suitable for 

664 digesting in hashes. 

665 

666 Raises ``dns.name.NeedAbsoluteNameOrOrigin`` if the name is 

667 relative and no origin was provided. 

668 

669 Returns a ``bytes`` or ``None``. 

670 """ 

671 

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) 

690 

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 

725 

726 def __len__(self) -> int: 

727 """The length of the name (in labels). 

728 

729 Returns an ``int``. 

730 """ 

731 

732 return len(self.labels) 

733 

734 def __getitem__(self, index): 

735 return self.labels[index] 

736 

737 def __add__(self, other): 

738 return self.concatenate(other) 

739 

740 def __sub__(self, other): 

741 return self.relativize(other) 

742 

743 def split(self, depth: int) -> Tuple["Name", "Name"]: 

744 """Split a name into a prefix and suffix names at the specified depth. 

745 

746 *depth* is an ``int`` specifying the number of labels in the suffix 

747 

748 Raises ``ValueError`` if *depth* was not >= 0 and <= the length of the 

749 name. 

750 

751 Returns the tuple ``(prefix, suffix)``. 

752 """ 

753 

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:])) 

762 

763 def concatenate(self, other: "Name") -> "Name": 

764 """Return a new name which is the concatenation of self and other. 

765 

766 Raises ``dns.name.AbsoluteConcatenation`` if the name is 

767 absolute and *other* is not the empty name. 

768 

769 Returns a ``dns.name.Name``. 

770 """ 

771 

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) 

777 

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. 

781 

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.``. 

785 

786 Returns a ``dns.name.Name``. 

787 """ 

788 

789 if origin is not None and self.is_subdomain(origin): 

790 return Name(self[: -len(origin)]) 

791 else: 

792 return self 

793 

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. 

797 

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.``. 

801 

802 Returns a ``dns.name.Name``. 

803 """ 

804 

805 if not self.is_absolute(): 

806 return self.concatenate(origin) 

807 else: 

808 return self 

809 

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. 

814 

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. 

819 

820 Returns a ``dns.name.Name``. 

821 """ 

822 

823 if origin: 

824 if relativize: 

825 return self.relativize(origin) 

826 else: 

827 return self.derelativize(origin) 

828 else: 

829 return self 

830 

831 def parent(self) -> "Name": 

832 """Return the parent of the name. 

833 

834 For example, the parent of ``www.dnspython.org.`` is ``dnspython.org``. 

835 

836 Raises ``dns.name.NoParent`` if the name is either the root name or the 

837 empty name, and thus has no parent. 

838 

839 Returns a ``dns.name.Name``. 

840 """ 

841 

842 if self == root or self == empty: 

843 raise NoParent 

844 return Name(self.labels[1:]) 

845 

846 

847#: The root name, '.' 

848root = Name([b""]) 

849 

850#: The empty name. 

851empty = Name([]) 

852 

853 

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. 

858 

859 Labels are encoded in IDN ACE form according to rules specified by 

860 the IDNA codec. 

861 

862 *text*, a ``str``, is the text to convert into a name. 

863 

864 *origin*, a ``dns.name.Name``, specifies the origin to 

865 append to non-absolute names. The default is the root name. 

866 

867 *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA 

868 encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder 

869 is used. 

870 

871 Returns a ``dns.name.Name``. 

872 """ 

873 

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"") 

925 

926 if (len(labels) == 0 or labels[-1] != b"") and origin is not None: 

927 labels.extend(list(origin.labels)) 

928 return Name(labels) 

929 

930 

931def is_all_ascii(text: str) -> bool: 

932 for c in text: 

933 if ord(c) > 0x7F: 

934 return False 

935 return True 

936 

937 

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. 

944 

945 *text*, a ``bytes`` or ``str``, is the text to convert into a name. 

946 

947 *origin*, a ``dns.name.Name``, specifies the origin to 

948 append to non-absolute names. The default is the root name. 

949 

950 *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA 

951 encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder 

952 is used. 

953 

954 Returns a ``dns.name.Name``. 

955 """ 

956 

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) 

1023 

1024 

1025# we need 'dns.wire.Parser' quoted as dns.name and dns.wire depend on each other. 

1026 

1027 

1028def from_wire_parser(parser: "dns.wire.Parser") -> Name: 

1029 """Convert possibly compressed wire format into a Name. 

1030 

1031 *parser* is a dns.wire.Parser. 

1032 

1033 Raises ``dns.name.BadPointer`` if a compression pointer did not 

1034 point backwards in the message. 

1035 

1036 Raises ``dns.name.BadLabelType`` if an invalid label type was encountered. 

1037 

1038 Returns a ``dns.name.Name`` 

1039 """ 

1040 

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) 

1059 

1060 

1061def from_wire(message: bytes, current: int) -> Tuple[Name, int]: 

1062 """Convert possibly compressed wire format into a Name. 

1063 

1064 *message* is a ``bytes`` containing an entire DNS message in DNS 

1065 wire form. 

1066 

1067 *current*, an ``int``, is the offset of the beginning of the name 

1068 from the start of the message 

1069 

1070 Raises ``dns.name.BadPointer`` if a compression pointer did not 

1071 point backwards in the message. 

1072 

1073 Raises ``dns.name.BadLabelType`` if an invalid label type was encountered. 

1074 

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 """ 

1079 

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)