Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/dns/name.py: 25%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

619 statements  

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 

20import copy 

21import encodings.idna # type: ignore 

22import functools 

23import struct 

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

25 

26import dns._features 

27import dns.enum 

28import dns.exception 

29import dns.immutable 

30import dns.wire 

31 

32# Dnspython will never access idna if the import fails, but pyright can't figure 

33# that out, so... 

34# 

35# pyright: reportAttributeAccessIssue = false, reportPossiblyUnboundVariable = false 

36 

37if dns._features.have("idna"): 

38 import idna # type: ignore 

39 

40 have_idna_2008 = True 

41else: # pragma: no cover 

42 have_idna_2008 = False 

43 

44 

45CompressType = Dict["Name", int] 

46 

47 

48class NameRelation(dns.enum.IntEnum): 

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

50 

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

52 # has hardwired the constants. 

53 

54 #: The compared names have no relationship to each other. 

55 NONE = 0 

56 #: the first name is a superdomain of the second. 

57 SUPERDOMAIN = 1 

58 #: The first name is a subdomain of the second. 

59 SUBDOMAIN = 2 

60 #: The compared names are equal. 

61 EQUAL = 3 

62 #: The compared names have a common ancestor. 

63 COMMONANCESTOR = 4 

64 

65 @classmethod 

66 def _maximum(cls): 

67 return cls.COMMONANCESTOR # pragma: no cover 

68 

69 @classmethod 

70 def _short_name(cls): 

71 return cls.__name__ # pragma: no cover 

72 

73 

74# Backwards compatibility 

75NAMERELN_NONE = NameRelation.NONE 

76NAMERELN_SUPERDOMAIN = NameRelation.SUPERDOMAIN 

77NAMERELN_SUBDOMAIN = NameRelation.SUBDOMAIN 

78NAMERELN_EQUAL = NameRelation.EQUAL 

79NAMERELN_COMMONANCESTOR = NameRelation.COMMONANCESTOR 

80 

81 

82class EmptyLabel(dns.exception.SyntaxError): 

83 """A DNS label is empty.""" 

84 

85 

86class BadEscape(dns.exception.SyntaxError): 

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

88 

89 

90class BadPointer(dns.exception.FormError): 

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

92 

93 

94class BadLabelType(dns.exception.FormError): 

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

96 

97 

98class NeedAbsoluteNameOrOrigin(dns.exception.DNSException): 

99 """An attempt was made to convert a non-absolute name to 

100 wire when there was also a non-absolute (or missing) origin.""" 

101 

102 

103class NameTooLong(dns.exception.FormError): 

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

105 

106 

107class LabelTooLong(dns.exception.SyntaxError): 

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

109 

110 

111class AbsoluteConcatenation(dns.exception.DNSException): 

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

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

114 

115 

116class NoParent(dns.exception.DNSException): 

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

118 or the empty name.""" 

119 

120 

121class NoIDNA2008(dns.exception.DNSException): 

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

123 available.""" 

124 

125 

126class IDNAException(dns.exception.DNSException): 

127 """IDNA processing raised an exception.""" 

128 

129 supp_kwargs = {"idna_exception"} 

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

131 

132 # We do this as otherwise mypy complains about unexpected keyword argument 

133 # idna_exception 

134 def __init__(self, *args, **kwargs): 

135 super().__init__(*args, **kwargs) 

136 

137 

138class NeedSubdomainOfOrigin(dns.exception.DNSException): 

139 """An absolute name was provided that is not a subdomain of the specified origin.""" 

140 

141 

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

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

144 

145 

146def _escapify(label: Union[bytes, str]) -> str: 

147 """Escape the characters in label which need it. 

148 @returns: the escaped string 

149 @rtype: string""" 

150 if isinstance(label, bytes): 

151 # Ordinary DNS label mode. Escape special characters and values 

152 # < 0x20 or > 0x7f. 

153 text = "" 

154 for c in label: 

155 if c in _escaped: 

156 text += "\\" + chr(c) 

157 elif c > 0x20 and c < 0x7F: 

158 text += chr(c) 

159 else: 

160 text += f"\\{c:03d}" 

161 return text 

162 

163 # Unicode label mode. Escape only special characters and values < 0x20 

164 text = "" 

165 for uc in label: 

166 if uc in _escaped_text: 

167 text += "\\" + uc 

168 elif uc <= "\x20": 

169 text += f"\\{ord(uc):03d}" 

170 else: 

171 text += uc 

172 return text 

173 

174 

175class IDNACodec: 

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

177 

178 def __init__(self): 

179 pass 

180 

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

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

183 

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

185 raise NotImplementedError # pragma: no cover 

186 

187 def decode(self, label: bytes) -> str: 

188 # We do not apply any IDNA policy on decode. 

189 if self.is_idna(label): 

190 try: 

191 slabel = label[4:].decode("punycode") 

192 return _escapify(slabel) 

193 except Exception as e: 

194 raise IDNAException(idna_exception=e) 

195 else: 

196 return _escapify(label) 

197 

198 

199class IDNA2003Codec(IDNACodec): 

200 """IDNA 2003 encoder/decoder.""" 

201 

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

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

204 

205 *strict_decode* is a ``bool``. If `True`, then IDNA2003 checking 

206 is done when decoding. This can cause failures if the name 

207 was encoded with IDNA2008. The default is `False`. 

208 """ 

209 

210 super().__init__() 

211 self.strict_decode = strict_decode 

212 

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

214 """Encode *label*.""" 

215 

216 if label == "": 

217 return b"" 

218 try: 

219 return encodings.idna.ToASCII(label) 

220 except UnicodeError: 

221 raise LabelTooLong 

222 

223 def decode(self, label: bytes) -> str: 

224 """Decode *label*.""" 

225 if not self.strict_decode: 

226 return super().decode(label) 

227 if label == b"": 

228 return "" 

229 try: 

230 return _escapify(encodings.idna.ToUnicode(label)) 

231 except Exception as e: 

232 raise IDNAException(idna_exception=e) 

233 

234 

235class IDNA2008Codec(IDNACodec): 

236 """IDNA 2008 encoder/decoder.""" 

237 

238 def __init__( 

239 self, 

240 uts_46: bool = False, 

241 transitional: bool = False, 

242 allow_pure_ascii: bool = False, 

243 strict_decode: bool = False, 

244 ): 

245 """Initialize the IDNA 2008 encoder/decoder. 

246 

247 *uts_46* is a ``bool``. If True, apply Unicode IDNA 

248 compatibility processing as described in Unicode Technical 

249 Standard #46 (https://unicode.org/reports/tr46/). 

250 If False, do not apply the mapping. The default is False. 

251 

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

253 "transitional" mode described in Unicode Technical Standard 

254 #46. The default is False. 

255 

256 *allow_pure_ascii* is a ``bool``. If True, then a label which 

257 consists of only ASCII characters is allowed. This is less 

258 strict than regular IDNA 2008, but is also necessary for mixed 

259 names, e.g. a name with starting with "_sip._tcp." and ending 

260 in an IDN suffix which would otherwise be disallowed. The 

261 default is False. 

262 

263 *strict_decode* is a ``bool``: If True, then IDNA2008 checking 

264 is done when decoding. This can cause failures if the name 

265 was encoded with IDNA2003. The default is False. 

266 """ 

267 super().__init__() 

268 self.uts_46 = uts_46 

269 self.transitional = transitional 

270 self.allow_pure_ascii = allow_pure_ascii 

271 self.strict_decode = strict_decode 

272 

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

274 if label == "": 

275 return b"" 

276 if self.allow_pure_ascii and is_all_ascii(label): 

277 encoded = label.encode("ascii") 

278 if len(encoded) > 63: 

279 raise LabelTooLong 

280 return encoded 

281 if not have_idna_2008: 

282 raise NoIDNA2008 

283 try: 

284 if self.uts_46: 

285 # pylint: disable=possibly-used-before-assignment 

286 label = idna.uts46_remap(label, False, self.transitional) 

287 return idna.alabel(label) 

288 except idna.IDNAError as e: 

289 if e.args[0] == "Label too long": 

290 raise LabelTooLong 

291 else: 

292 raise IDNAException(idna_exception=e) 

293 

294 def decode(self, label: bytes) -> str: 

295 if not self.strict_decode: 

296 return super().decode(label) 

297 if label == b"": 

298 return "" 

299 if not have_idna_2008: 

300 raise NoIDNA2008 

301 try: 

302 ulabel = idna.ulabel(label) 

303 if self.uts_46: 

304 ulabel = idna.uts46_remap(ulabel, False, self.transitional) 

305 return _escapify(ulabel) 

306 except (idna.IDNAError, UnicodeError) as e: 

307 raise IDNAException(idna_exception=e) 

308 

309 

310IDNA_2003_Practical = IDNA2003Codec(False) 

311IDNA_2003_Strict = IDNA2003Codec(True) 

312IDNA_2003 = IDNA_2003_Practical 

313IDNA_2008_Practical = IDNA2008Codec(True, False, True, False) 

314IDNA_2008_UTS_46 = IDNA2008Codec(True, False, False, False) 

315IDNA_2008_Strict = IDNA2008Codec(False, False, False, True) 

316IDNA_2008_Transitional = IDNA2008Codec(True, True, False, False) 

317IDNA_2008 = IDNA_2008_Practical 

318 

319 

320def _validate_labels(labels: Tuple[bytes, ...]) -> None: 

321 """Check for empty labels in the middle of a label sequence, 

322 labels that are too long, and for too many labels. 

323 

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

325 

326 Raises ``dns.name.EmptyLabel`` if a label is empty (i.e. the root 

327 label) and appears in a position other than the end of the label 

328 sequence 

329 

330 """ 

331 

332 l = len(labels) 

333 total = 0 

334 i = -1 

335 j = 0 

336 for label in labels: 

337 ll = len(label) 

338 total += ll + 1 

339 if ll > 63: 

340 raise LabelTooLong 

341 if i < 0 and label == b"": 

342 i = j 

343 j += 1 

344 if total > 255: 

345 raise NameTooLong 

346 if i >= 0 and i != l - 1: 

347 raise EmptyLabel 

348 

349 

350def _maybe_convert_to_binary(label: Union[bytes, str]) -> bytes: 

351 """If label is ``str``, convert it to ``bytes``. If it is already 

352 ``bytes`` just return it. 

353 

354 """ 

355 

356 if isinstance(label, bytes): 

357 return label 

358 if isinstance(label, str): 

359 return label.encode() 

360 raise ValueError # pragma: no cover 

361 

362 

363@dns.immutable.immutable 

364class Name: 

365 """A DNS name. 

366 

367 The dns.name.Name class represents a DNS name as a tuple of 

368 labels. Each label is a ``bytes`` in DNS wire format. Instances 

369 of the class are immutable. 

370 """ 

371 

372 __slots__ = ["labels"] 

373 

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

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

376 

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

378 self.labels = tuple(blabels) 

379 _validate_labels(self.labels) 

380 

381 def __copy__(self): 

382 return Name(self.labels) 

383 

384 def __deepcopy__(self, memo): 

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

386 

387 def __getstate__(self): 

388 # Names can be pickled 

389 return {"labels": self.labels} 

390 

391 def __setstate__(self, state): 

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

393 _validate_labels(self.labels) 

394 

395 def is_absolute(self) -> bool: 

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

397 

398 Returns a ``bool``. 

399 """ 

400 

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

402 

403 def is_wild(self) -> bool: 

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

405 

406 Returns a ``bool``. 

407 """ 

408 

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

410 

411 def __hash__(self) -> int: 

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

413 

414 Returns an ``int``. 

415 """ 

416 

417 h = 0 

418 for label in self.labels: 

419 for c in label.lower(): 

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

421 return h 

422 

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

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

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

426 

427 *relation* describes the relation ship between the names, 

428 and is one of: ``dns.name.NameRelation.NONE``, 

429 ``dns.name.NameRelation.SUPERDOMAIN``, ``dns.name.NameRelation.SUBDOMAIN``, 

430 ``dns.name.NameRelation.EQUAL``, or ``dns.name.NameRelation.COMMONANCESTOR``. 

431 

432 *order* is < 0 if *self* < *other*, > 0 if *self* > *other*, and == 

433 0 if *self* == *other*. A relative name is always less than an 

434 absolute name. If both names have the same relativity, then 

435 the DNSSEC order relation is used to order them. 

436 

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

438 have in common. 

439 

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

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

442 

443 ============= ============= =========== ===== ======= 

444 self other relation order nlabels 

445 ============= ============= =========== ===== ======= 

446 www.example. www.example. equal 0 3 

447 www.example. example. subdomain > 0 2 

448 example. www.example. superdomain < 0 2 

449 example1.com. example2.com. common anc. < 0 2 

450 example1 example2. none < 0 0 

451 example1. example2 none > 0 0 

452 ============= ============= =========== ===== ======= 

453 """ 

454 

455 sabs = self.is_absolute() 

456 oabs = other.is_absolute() 

457 if sabs != oabs: 

458 if sabs: 

459 return (NameRelation.NONE, 1, 0) 

460 else: 

461 return (NameRelation.NONE, -1, 0) 

462 l1 = len(self.labels) 

463 l2 = len(other.labels) 

464 ldiff = l1 - l2 

465 if ldiff < 0: 

466 l = l1 

467 else: 

468 l = l2 

469 

470 order = 0 

471 nlabels = 0 

472 namereln = NameRelation.NONE 

473 while l > 0: 

474 l -= 1 

475 l1 -= 1 

476 l2 -= 1 

477 label1 = self.labels[l1].lower() 

478 label2 = other.labels[l2].lower() 

479 if label1 < label2: 

480 order = -1 

481 if nlabels > 0: 

482 namereln = NameRelation.COMMONANCESTOR 

483 return (namereln, order, nlabels) 

484 elif label1 > label2: 

485 order = 1 

486 if nlabels > 0: 

487 namereln = NameRelation.COMMONANCESTOR 

488 return (namereln, order, nlabels) 

489 nlabels += 1 

490 order = ldiff 

491 if ldiff < 0: 

492 namereln = NameRelation.SUPERDOMAIN 

493 elif ldiff > 0: 

494 namereln = NameRelation.SUBDOMAIN 

495 else: 

496 namereln = NameRelation.EQUAL 

497 return (namereln, order, nlabels) 

498 

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

500 """Is self a subdomain of other? 

501 

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

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

504 

505 Returns a ``bool``. 

506 """ 

507 

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

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

510 return True 

511 return False 

512 

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

514 """Is self a superdomain of other? 

515 

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

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

518 

519 Returns a ``bool``. 

520 """ 

521 

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

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

524 return True 

525 return False 

526 

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

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

529 DNSSEC canonical form. 

530 """ 

531 

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

533 

534 def __eq__(self, other): 

535 if isinstance(other, Name): 

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

537 else: 

538 return False 

539 

540 def __ne__(self, other): 

541 if isinstance(other, Name): 

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

543 else: 

544 return True 

545 

546 def __lt__(self, other): 

547 if isinstance(other, Name): 

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

549 else: 

550 return NotImplemented 

551 

552 def __le__(self, other): 

553 if isinstance(other, Name): 

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

555 else: 

556 return NotImplemented 

557 

558 def __ge__(self, other): 

559 if isinstance(other, Name): 

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

561 else: 

562 return NotImplemented 

563 

564 def __gt__(self, other): 

565 if isinstance(other, Name): 

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

567 else: 

568 return NotImplemented 

569 

570 def __repr__(self): 

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

572 

573 def __str__(self): 

574 return self.to_text(False) 

575 

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

577 """Convert name to DNS text format. 

578 

579 *omit_final_dot* is a ``bool``. If True, don't emit the final 

580 dot (denoting the root label) for absolute names. The default 

581 is False. 

582 

583 Returns a ``str``. 

584 """ 

585 

586 if len(self.labels) == 0: 

587 return "@" 

588 if len(self.labels) == 1 and self.labels[0] == b"": 

589 return "." 

590 if omit_final_dot and self.is_absolute(): 

591 l = self.labels[:-1] 

592 else: 

593 l = self.labels 

594 s = ".".join(map(_escapify, l)) 

595 return s 

596 

597 def to_unicode( 

598 self, omit_final_dot: bool = False, idna_codec: Optional[IDNACodec] = None 

599 ) -> str: 

600 """Convert name to Unicode text format. 

601 

602 IDN ACE labels are converted to Unicode. 

603 

604 *omit_final_dot* is a ``bool``. If True, don't emit the final 

605 dot (denoting the root label) for absolute names. The default 

606 is False. 

607 *idna_codec* specifies the IDNA encoder/decoder. If None, the 

608 dns.name.IDNA_2003_Practical encoder/decoder is used. 

609 The IDNA_2003_Practical decoder does 

610 not impose any policy, it just decodes punycode, so if you 

611 don't want checking for compliance, you can use this decoder 

612 for IDNA2008 as well. 

613 

614 Returns a ``str``. 

615 """ 

616 

617 if len(self.labels) == 0: 

618 return "@" 

619 if len(self.labels) == 1 and self.labels[0] == b"": 

620 return "." 

621 if omit_final_dot and self.is_absolute(): 

622 l = self.labels[:-1] 

623 else: 

624 l = self.labels 

625 if idna_codec is None: 

626 idna_codec = IDNA_2003_Practical 

627 return ".".join([idna_codec.decode(x) for x in l]) 

628 

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

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

631 

632 The name is canonicalized and converted to uncompressed wire 

633 format. All names in wire format are absolute. If the name 

634 is a relative name, then an origin must be supplied. 

635 

636 *origin* is a ``dns.name.Name`` or ``None``. If the name is 

637 relative and origin is not ``None``, then origin will be appended 

638 to the name. 

639 

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

641 relative and no origin was provided. 

642 

643 Returns a ``bytes``. 

644 """ 

645 

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

647 assert digest is not None 

648 return digest 

649 

650 def to_wire( 

651 self, 

652 file: Optional[Any] = None, 

653 compress: Optional[CompressType] = None, 

654 origin: Optional["Name"] = None, 

655 canonicalize: bool = False, 

656 ) -> Optional[bytes]: 

657 """Convert name to wire format, possibly compressing it. 

658 

659 *file* is the file where the name is emitted (typically an 

660 io.BytesIO file). If ``None`` (the default), a ``bytes`` 

661 containing the wire name will be returned. 

662 

663 *compress*, a ``dict``, is the compression table to use. If 

664 ``None`` (the default), names will not be compressed. Note that 

665 the compression code assumes that compression offset 0 is the 

666 start of *file*, and thus compression will not be correct 

667 if this is not the case. 

668 

669 *origin* is a ``dns.name.Name`` or ``None``. If the name is 

670 relative and origin is not ``None``, then *origin* will be appended 

671 to it. 

672 

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

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

675 digesting in hashes. 

676 

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

678 relative and no origin was provided. 

679 

680 Returns a ``bytes`` or ``None``. 

681 """ 

682 

683 if file is None: 

684 out = bytearray() 

685 for label in self.labels: 

686 out.append(len(label)) 

687 if canonicalize: 

688 out += label.lower() 

689 else: 

690 out += label 

691 if not self.is_absolute(): 

692 if origin is None or not origin.is_absolute(): 

693 raise NeedAbsoluteNameOrOrigin 

694 for label in origin.labels: 

695 out.append(len(label)) 

696 if canonicalize: 

697 out += label.lower() 

698 else: 

699 out += label 

700 return bytes(out) 

701 

702 labels: Iterable[bytes] 

703 if not self.is_absolute(): 

704 if origin is None or not origin.is_absolute(): 

705 raise NeedAbsoluteNameOrOrigin 

706 labels = list(self.labels) 

707 labels.extend(list(origin.labels)) 

708 else: 

709 labels = self.labels 

710 i = 0 

711 for label in labels: 

712 n = Name(labels[i:]) 

713 i += 1 

714 if compress is not None: 

715 pos = compress.get(n) 

716 else: 

717 pos = None 

718 if pos is not None: 

719 value = 0xC000 + pos 

720 s = struct.pack("!H", value) 

721 file.write(s) 

722 break 

723 else: 

724 if compress is not None and len(n) > 1: 

725 pos = file.tell() 

726 if pos <= 0x3FFF: 

727 compress[n] = pos 

728 l = len(label) 

729 file.write(struct.pack("!B", l)) 

730 if l > 0: 

731 if canonicalize: 

732 file.write(label.lower()) 

733 else: 

734 file.write(label) 

735 return None 

736 

737 def __len__(self) -> int: 

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

739 

740 Returns an ``int``. 

741 """ 

742 

743 return len(self.labels) 

744 

745 def __getitem__(self, index): 

746 return self.labels[index] 

747 

748 def __add__(self, other): 

749 return self.concatenate(other) 

750 

751 def __sub__(self, other): 

752 return self.relativize(other) 

753 

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

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

756 

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

758 

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

760 name. 

761 

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

763 """ 

764 

765 l = len(self.labels) 

766 if depth == 0: 

767 return (self, dns.name.empty) 

768 elif depth == l: 

769 return (dns.name.empty, self) 

770 elif depth < 0 or depth > l: 

771 raise ValueError("depth must be >= 0 and <= the length of the name") 

772 return (Name(self[:-depth]), Name(self[-depth:])) 

773 

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

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

776 

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

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

779 

780 Returns a ``dns.name.Name``. 

781 """ 

782 

783 if self.is_absolute() and len(other) > 0: 

784 raise AbsoluteConcatenation 

785 labels = list(self.labels) 

786 labels.extend(list(other.labels)) 

787 return Name(labels) 

788 

789 def relativize(self, origin: "Name") -> "Name": 

790 """If the name is a subdomain of *origin*, return a new name which is 

791 the name relative to origin. Otherwise return the name. 

792 

793 For example, relativizing ``www.dnspython.org.`` to origin 

794 ``dnspython.org.`` returns the name ``www``. Relativizing ``example.`` 

795 to origin ``dnspython.org.`` returns ``example.``. 

796 

797 Returns a ``dns.name.Name``. 

798 """ 

799 

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

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

802 else: 

803 return self 

804 

805 def derelativize(self, origin: "Name") -> "Name": 

806 """If the name is a relative name, return a new name which is the 

807 concatenation of the name and origin. Otherwise return the name. 

808 

809 For example, derelativizing ``www`` to origin ``dnspython.org.`` 

810 returns the name ``www.dnspython.org.``. Derelativizing ``example.`` 

811 to origin ``dnspython.org.`` returns ``example.``. 

812 

813 Returns a ``dns.name.Name``. 

814 """ 

815 

816 if not self.is_absolute(): 

817 return self.concatenate(origin) 

818 else: 

819 return self 

820 

821 def choose_relativity( 

822 self, origin: Optional["Name"] = None, relativize: bool = True 

823 ) -> "Name": 

824 """Return a name with the relativity desired by the caller. 

825 

826 If *origin* is ``None``, then the name is returned. 

827 Otherwise, if *relativize* is ``True`` the name is 

828 relativized, and if *relativize* is ``False`` the name is 

829 derelativized. 

830 

831 Returns a ``dns.name.Name``. 

832 """ 

833 

834 if origin: 

835 if relativize: 

836 return self.relativize(origin) 

837 else: 

838 return self.derelativize(origin) 

839 else: 

840 return self 

841 

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

843 """Return the parent of the name. 

844 

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

846 

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

848 empty name, and thus has no parent. 

849 

850 Returns a ``dns.name.Name``. 

851 """ 

852 

853 if self == root or self == empty: 

854 raise NoParent 

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

856 

857 def predecessor(self, origin: "Name", prefix_ok: bool = True) -> "Name": 

858 """Return the maximal predecessor of *name* in the DNSSEC ordering in the zone 

859 whose origin is *origin*, or return the longest name under *origin* if the 

860 name is origin (i.e. wrap around to the longest name, which may still be 

861 *origin* due to length considerations. 

862 

863 The relativity of the name is preserved, so if this name is relative 

864 then the method will return a relative name, and likewise if this name 

865 is absolute then the predecessor will be absolute. 

866 

867 *prefix_ok* indicates if prefixing labels is allowed, and 

868 defaults to ``True``. Normally it is good to allow this, but if computing 

869 a maximal predecessor at a zone cut point then ``False`` must be specified. 

870 """ 

871 return _handle_relativity_and_call( 

872 _absolute_predecessor, self, origin, prefix_ok 

873 ) 

874 

875 def successor(self, origin: "Name", prefix_ok: bool = True) -> "Name": 

876 """Return the minimal successor of *name* in the DNSSEC ordering in the zone 

877 whose origin is *origin*, or return *origin* if the successor cannot be 

878 computed due to name length limitations. 

879 

880 Note that *origin* is returned in the "too long" cases because wrapping 

881 around to the origin is how NSEC records express "end of the zone". 

882 

883 The relativity of the name is preserved, so if this name is relative 

884 then the method will return a relative name, and likewise if this name 

885 is absolute then the successor will be absolute. 

886 

887 *prefix_ok* indicates if prefixing a new minimal label is allowed, and 

888 defaults to ``True``. Normally it is good to allow this, but if computing 

889 a minimal successor at a zone cut point then ``False`` must be specified. 

890 """ 

891 return _handle_relativity_and_call(_absolute_successor, self, origin, prefix_ok) 

892 

893 

894#: The root name, '.' 

895root = Name([b""]) 

896 

897#: The empty name. 

898empty = Name([]) 

899 

900 

901def from_unicode( 

902 text: str, origin: Optional[Name] = root, idna_codec: Optional[IDNACodec] = None 

903) -> Name: 

904 """Convert unicode text into a Name object. 

905 

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

907 the IDNA codec. 

908 

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

910 

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

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

913 

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

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

916 is used. 

917 

918 Returns a ``dns.name.Name``. 

919 """ 

920 

921 if not isinstance(text, str): 

922 raise ValueError("input to from_unicode() must be a unicode string") 

923 if not (origin is None or isinstance(origin, Name)): 

924 raise ValueError("origin must be a Name or None") 

925 labels = [] 

926 label = "" 

927 escaping = False 

928 edigits = 0 

929 total = 0 

930 if idna_codec is None: 

931 idna_codec = IDNA_2003 

932 if text == "@": 

933 text = "" 

934 if text: 

935 if text in [".", "\u3002", "\uff0e", "\uff61"]: 

936 return Name([b""]) # no Unicode "u" on this constant! 

937 for c in text: 

938 if escaping: 

939 if edigits == 0: 

940 if c.isdigit(): 

941 total = int(c) 

942 edigits += 1 

943 else: 

944 label += c 

945 escaping = False 

946 else: 

947 if not c.isdigit(): 

948 raise BadEscape 

949 total *= 10 

950 total += int(c) 

951 edigits += 1 

952 if edigits == 3: 

953 escaping = False 

954 label += chr(total) 

955 elif c in [".", "\u3002", "\uff0e", "\uff61"]: 

956 if len(label) == 0: 

957 raise EmptyLabel 

958 labels.append(idna_codec.encode(label)) 

959 label = "" 

960 elif c == "\\": 

961 escaping = True 

962 edigits = 0 

963 total = 0 

964 else: 

965 label += c 

966 if escaping: 

967 raise BadEscape 

968 if len(label) > 0: 

969 labels.append(idna_codec.encode(label)) 

970 else: 

971 labels.append(b"") 

972 

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

974 labels.extend(list(origin.labels)) 

975 return Name(labels) 

976 

977 

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

979 for c in text: 

980 if ord(c) > 0x7F: 

981 return False 

982 return True 

983 

984 

985def from_text( 

986 text: Union[bytes, str], 

987 origin: Optional[Name] = root, 

988 idna_codec: Optional[IDNACodec] = None, 

989) -> Name: 

990 """Convert text into a Name object. 

991 

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

993 

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

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

996 

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

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

999 is used. 

1000 

1001 Returns a ``dns.name.Name``. 

1002 """ 

1003 

1004 if isinstance(text, str): 

1005 if not is_all_ascii(text): 

1006 # Some codepoint in the input text is > 127, so IDNA applies. 

1007 return from_unicode(text, origin, idna_codec) 

1008 # The input is all ASCII, so treat this like an ordinary non-IDNA 

1009 # domain name. Note that "all ASCII" is about the input text, 

1010 # not the codepoints in the domain name. E.g. if text has value 

1011 # 

1012 # r'\150\151\152\153\154\155\156\157\158\159' 

1013 # 

1014 # then it's still "all ASCII" even though the domain name has 

1015 # codepoints > 127. 

1016 text = text.encode("ascii") 

1017 if not isinstance(text, bytes): 

1018 raise ValueError("input to from_text() must be a string") 

1019 if not (origin is None or isinstance(origin, Name)): 

1020 raise ValueError("origin must be a Name or None") 

1021 labels = [] 

1022 label = b"" 

1023 escaping = False 

1024 edigits = 0 

1025 total = 0 

1026 if text == b"@": 

1027 text = b"" 

1028 if text: 

1029 if text == b".": 

1030 return Name([b""]) 

1031 for c in text: 

1032 byte_ = struct.pack("!B", c) 

1033 if escaping: 

1034 if edigits == 0: 

1035 if byte_.isdigit(): 

1036 total = int(byte_) 

1037 edigits += 1 

1038 else: 

1039 label += byte_ 

1040 escaping = False 

1041 else: 

1042 if not byte_.isdigit(): 

1043 raise BadEscape 

1044 total *= 10 

1045 total += int(byte_) 

1046 edigits += 1 

1047 if edigits == 3: 

1048 escaping = False 

1049 label += struct.pack("!B", total) 

1050 elif byte_ == b".": 

1051 if len(label) == 0: 

1052 raise EmptyLabel 

1053 labels.append(label) 

1054 label = b"" 

1055 elif byte_ == b"\\": 

1056 escaping = True 

1057 edigits = 0 

1058 total = 0 

1059 else: 

1060 label += byte_ 

1061 if escaping: 

1062 raise BadEscape 

1063 if len(label) > 0: 

1064 labels.append(label) 

1065 else: 

1066 labels.append(b"") 

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

1068 labels.extend(list(origin.labels)) 

1069 return Name(labels) 

1070 

1071 

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

1073 

1074 

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

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

1077 

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

1079 

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

1081 point backwards in the message. 

1082 

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

1084 

1085 Returns a ``dns.name.Name`` 

1086 """ 

1087 

1088 labels = [] 

1089 biggest_pointer = parser.current 

1090 with parser.restore_furthest(): 

1091 count = parser.get_uint8() 

1092 while count != 0: 

1093 if count < 64: 

1094 labels.append(parser.get_bytes(count)) 

1095 elif count >= 192: 

1096 current = (count & 0x3F) * 256 + parser.get_uint8() 

1097 if current >= biggest_pointer: 

1098 raise BadPointer 

1099 biggest_pointer = current 

1100 parser.seek(current) 

1101 else: 

1102 raise BadLabelType 

1103 count = parser.get_uint8() 

1104 labels.append(b"") 

1105 return Name(labels) 

1106 

1107 

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

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

1110 

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

1112 wire form. 

1113 

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

1115 from the start of the message 

1116 

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

1118 point backwards in the message. 

1119 

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

1121 

1122 Returns a ``(dns.name.Name, int)`` tuple consisting of the name 

1123 that was read and the number of bytes of the wire format message 

1124 which were consumed reading it. 

1125 """ 

1126 

1127 if not isinstance(message, bytes): 

1128 raise ValueError("input to from_wire() must be a byte string") 

1129 parser = dns.wire.Parser(message, current) 

1130 name = from_wire_parser(parser) 

1131 return (name, parser.current - current) 

1132 

1133 

1134# RFC 4471 Support 

1135 

1136_MINIMAL_OCTET = b"\x00" 

1137_MINIMAL_OCTET_VALUE = ord(_MINIMAL_OCTET) 

1138_SUCCESSOR_PREFIX = Name([_MINIMAL_OCTET]) 

1139_MAXIMAL_OCTET = b"\xff" 

1140_MAXIMAL_OCTET_VALUE = ord(_MAXIMAL_OCTET) 

1141_AT_SIGN_VALUE = ord("@") 

1142_LEFT_SQUARE_BRACKET_VALUE = ord("[") 

1143 

1144 

1145def _wire_length(labels): 

1146 return functools.reduce(lambda v, x: v + len(x) + 1, labels, 0) 

1147 

1148 

1149def _pad_to_max_name(name): 

1150 needed = 255 - _wire_length(name.labels) 

1151 new_labels = [] 

1152 while needed > 64: 

1153 new_labels.append(_MAXIMAL_OCTET * 63) 

1154 needed -= 64 

1155 if needed >= 2: 

1156 new_labels.append(_MAXIMAL_OCTET * (needed - 1)) 

1157 # Note we're already maximal in the needed == 1 case as while we'd like 

1158 # to add one more byte as a new label, we can't, as adding a new non-empty 

1159 # label requires at least 2 bytes. 

1160 new_labels = list(reversed(new_labels)) 

1161 new_labels.extend(name.labels) 

1162 return Name(new_labels) 

1163 

1164 

1165def _pad_to_max_label(label, suffix_labels): 

1166 length = len(label) 

1167 # We have to subtract one here to account for the length byte of label. 

1168 remaining = 255 - _wire_length(suffix_labels) - length - 1 

1169 if remaining <= 0: 

1170 # Shouldn't happen! 

1171 return label 

1172 needed = min(63 - length, remaining) 

1173 return label + _MAXIMAL_OCTET * needed 

1174 

1175 

1176def _absolute_predecessor(name: Name, origin: Name, prefix_ok: bool) -> Name: 

1177 # This is the RFC 4471 predecessor algorithm using the "absolute method" of section 

1178 # 3.1.1. 

1179 # 

1180 # Our caller must ensure that the name and origin are absolute, and that name is a 

1181 # subdomain of origin. 

1182 if name == origin: 

1183 return _pad_to_max_name(name) 

1184 least_significant_label = name[0] 

1185 if least_significant_label == _MINIMAL_OCTET: 

1186 return name.parent() 

1187 least_octet = least_significant_label[-1] 

1188 suffix_labels = name.labels[1:] 

1189 if least_octet == _MINIMAL_OCTET_VALUE: 

1190 new_labels = [least_significant_label[:-1]] 

1191 else: 

1192 octets = bytearray(least_significant_label) 

1193 octet = octets[-1] 

1194 if octet == _LEFT_SQUARE_BRACKET_VALUE: 

1195 octet = _AT_SIGN_VALUE 

1196 else: 

1197 octet -= 1 

1198 octets[-1] = octet 

1199 least_significant_label = bytes(octets) 

1200 new_labels = [_pad_to_max_label(least_significant_label, suffix_labels)] 

1201 new_labels.extend(suffix_labels) 

1202 name = Name(new_labels) 

1203 if prefix_ok: 

1204 return _pad_to_max_name(name) 

1205 else: 

1206 return name 

1207 

1208 

1209def _absolute_successor(name: Name, origin: Name, prefix_ok: bool) -> Name: 

1210 # This is the RFC 4471 successor algorithm using the "absolute method" of section 

1211 # 3.1.2. 

1212 # 

1213 # Our caller must ensure that the name and origin are absolute, and that name is a 

1214 # subdomain of origin. 

1215 if prefix_ok: 

1216 # Try prefixing \000 as new label 

1217 try: 

1218 return _SUCCESSOR_PREFIX.concatenate(name) 

1219 except NameTooLong: 

1220 pass 

1221 while name != origin: 

1222 # Try extending the least significant label. 

1223 least_significant_label = name[0] 

1224 if len(least_significant_label) < 63: 

1225 # We may be able to extend the least label with a minimal additional byte. 

1226 # This is only "may" because we could have a maximal length name even though 

1227 # the least significant label isn't maximally long. 

1228 new_labels = [least_significant_label + _MINIMAL_OCTET] 

1229 new_labels.extend(name.labels[1:]) 

1230 try: 

1231 return dns.name.Name(new_labels) 

1232 except dns.name.NameTooLong: 

1233 pass 

1234 # We can't extend the label either, so we'll try to increment the least 

1235 # signficant non-maximal byte in it. 

1236 octets = bytearray(least_significant_label) 

1237 # We do this reversed iteration with an explicit indexing variable because 

1238 # if we find something to increment, we're going to want to truncate everything 

1239 # to the right of it. 

1240 for i in range(len(octets) - 1, -1, -1): 

1241 octet = octets[i] 

1242 if octet == _MAXIMAL_OCTET_VALUE: 

1243 # We can't increment this, so keep looking. 

1244 continue 

1245 # Finally, something we can increment. We have to apply a special rule for 

1246 # incrementing "@", sending it to "[", because RFC 4034 6.1 says that when 

1247 # comparing names, uppercase letters compare as if they were their 

1248 # lower-case equivalents. If we increment "@" to "A", then it would compare 

1249 # as "a", which is after "[", "\", "]", "^", "_", and "`", so we would have 

1250 # skipped the most minimal successor, namely "[". 

1251 if octet == _AT_SIGN_VALUE: 

1252 octet = _LEFT_SQUARE_BRACKET_VALUE 

1253 else: 

1254 octet += 1 

1255 octets[i] = octet 

1256 # We can now truncate all of the maximal values we skipped (if any) 

1257 new_labels = [bytes(octets[: i + 1])] 

1258 new_labels.extend(name.labels[1:]) 

1259 # We haven't changed the length of the name, so the Name constructor will 

1260 # always work. 

1261 return Name(new_labels) 

1262 # We couldn't increment, so chop off the least significant label and try 

1263 # again. 

1264 name = name.parent() 

1265 

1266 # We couldn't increment at all, so return the origin, as wrapping around is the 

1267 # DNSSEC way. 

1268 return origin 

1269 

1270 

1271def _handle_relativity_and_call( 

1272 function: Callable[[Name, Name, bool], Name], 

1273 name: Name, 

1274 origin: Name, 

1275 prefix_ok: bool, 

1276) -> Name: 

1277 # Make "name" absolute if needed, ensure that the origin is absolute, 

1278 # call function(), and then relativize the result if needed. 

1279 if not origin.is_absolute(): 

1280 raise NeedAbsoluteNameOrOrigin 

1281 relative = not name.is_absolute() 

1282 if relative: 

1283 name = name.derelativize(origin) 

1284 elif not name.is_subdomain(origin): 

1285 raise NeedSubdomainOfOrigin 

1286 result_name = function(name, origin, prefix_ok) 

1287 if relative: 

1288 result_name = result_name.relativize(origin) 

1289 return result_name