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

20 

21import copy 

22import encodings.idna # type: ignore 

23import functools 

24import struct 

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

26 

27import dns._features 

28import dns.enum 

29import dns.exception 

30import dns.immutable 

31import dns.wire 

32 

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

34# that out, so... 

35# 

36# pyright: reportAttributeAccessIssue = false, reportPossiblyUnboundVariable = false 

37 

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

39 import idna # type: ignore 

40 

41 have_idna_2008 = True 

42else: # pragma: no cover 

43 have_idna_2008 = False 

44 

45 

46CompressType = Dict["Name", int] 

47 

48 

49class NameRelation(dns.enum.IntEnum): 

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

51 

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

53 # has hardwired the constants. 

54 

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

56 NONE = 0 

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

58 SUPERDOMAIN = 1 

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

60 SUBDOMAIN = 2 

61 #: The compared names are equal. 

62 EQUAL = 3 

63 #: The compared names have a common ancestor. 

64 COMMONANCESTOR = 4 

65 

66 @classmethod 

67 def _maximum(cls): 

68 return cls.COMMONANCESTOR # pragma: no cover 

69 

70 @classmethod 

71 def _short_name(cls): 

72 return cls.__name__ # pragma: no cover 

73 

74 

75# Backwards compatibility 

76NAMERELN_NONE = NameRelation.NONE 

77NAMERELN_SUPERDOMAIN = NameRelation.SUPERDOMAIN 

78NAMERELN_SUBDOMAIN = NameRelation.SUBDOMAIN 

79NAMERELN_EQUAL = NameRelation.EQUAL 

80NAMERELN_COMMONANCESTOR = NameRelation.COMMONANCESTOR 

81 

82 

83class EmptyLabel(dns.exception.SyntaxError): 

84 """A DNS label is empty.""" 

85 

86 

87class BadEscape(dns.exception.SyntaxError): 

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

89 

90 

91class BadPointer(dns.exception.FormError): 

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

93 

94 

95class BadLabelType(dns.exception.FormError): 

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

97 

98 

99class NeedAbsoluteNameOrOrigin(dns.exception.DNSException): 

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

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

102 

103 

104class NameTooLong(dns.exception.FormError): 

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

106 

107 

108class LabelTooLong(dns.exception.SyntaxError): 

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

110 

111 

112class AbsoluteConcatenation(dns.exception.DNSException): 

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

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

115 

116 

117class NoParent(dns.exception.DNSException): 

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

119 or the empty name.""" 

120 

121 

122class NoIDNA2008(dns.exception.DNSException): 

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

124 available.""" 

125 

126 

127class IDNAException(dns.exception.DNSException): 

128 """IDNA processing raised an exception.""" 

129 

130 supp_kwargs = {"idna_exception"} 

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

132 

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

134 # idna_exception 

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

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

137 

138 

139class NeedSubdomainOfOrigin(dns.exception.DNSException): 

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

141 

142 

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

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

145 

146 

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

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

149 @returns: the escaped string 

150 @rtype: string""" 

151 if isinstance(label, bytes): 

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

153 # < 0x20 or > 0x7f. 

154 text = "" 

155 for c in label: 

156 if c in _escaped: 

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

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

159 text += chr(c) 

160 else: 

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

162 return text 

163 

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

165 text = "" 

166 for uc in label: 

167 if uc in _escaped_text: 

168 text += "\\" + uc 

169 elif uc <= "\x20": 

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

171 else: 

172 text += uc 

173 return text 

174 

175 

176class IDNACodec: 

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

178 

179 def __init__(self): 

180 pass 

181 

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

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

184 

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

186 raise NotImplementedError # pragma: no cover 

187 

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

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

190 if self.is_idna(label): 

191 try: 

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

193 return _escapify(slabel) 

194 except Exception as e: 

195 raise IDNAException(idna_exception=e) 

196 else: 

197 return _escapify(label) 

198 

199 

200class IDNA2003Codec(IDNACodec): 

201 """IDNA 2003 encoder/decoder.""" 

202 

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

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

205 

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

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

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

209 """ 

210 

211 super().__init__() 

212 self.strict_decode = strict_decode 

213 

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

215 """Encode *label*.""" 

216 

217 if label == "": 

218 return b"" 

219 try: 

220 return encodings.idna.ToASCII(label) 

221 except UnicodeError: 

222 raise LabelTooLong 

223 

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

225 """Decode *label*.""" 

226 if not self.strict_decode: 

227 return super().decode(label) 

228 if label == b"": 

229 return "" 

230 try: 

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

232 except Exception as e: 

233 raise IDNAException(idna_exception=e) 

234 

235 

236class IDNA2008Codec(IDNACodec): 

237 """IDNA 2008 encoder/decoder.""" 

238 

239 def __init__( 

240 self, 

241 uts_46: bool = False, 

242 transitional: bool = False, 

243 allow_pure_ascii: bool = False, 

244 strict_decode: bool = False, 

245 ): 

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

247 

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

249 compatibility processing as described in Unicode Technical 

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

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

252 

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

254 "transitional" mode described in Unicode Technical Standard 

255 #46. The default is False. 

256 

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

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

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

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

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

262 default is False. 

263 

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

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

266 was encoded with IDNA2003. The default is False. 

267 """ 

268 super().__init__() 

269 self.uts_46 = uts_46 

270 self.transitional = transitional 

271 self.allow_pure_ascii = allow_pure_ascii 

272 self.strict_decode = strict_decode 

273 

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

275 if label == "": 

276 return b"" 

277 if self.allow_pure_ascii and is_all_ascii(label): 

278 encoded = label.encode("ascii") 

279 if len(encoded) > 63: 

280 raise LabelTooLong 

281 return encoded 

282 if not have_idna_2008: 

283 raise NoIDNA2008 

284 try: 

285 if self.uts_46: 

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

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

288 return idna.alabel(label) 

289 except idna.IDNAError as e: 

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

291 raise LabelTooLong 

292 else: 

293 raise IDNAException(idna_exception=e) 

294 

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

296 if not self.strict_decode: 

297 return super().decode(label) 

298 if label == b"": 

299 return "" 

300 if not have_idna_2008: 

301 raise NoIDNA2008 

302 try: 

303 ulabel = idna.ulabel(label) 

304 if self.uts_46: 

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

306 return _escapify(ulabel) 

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

308 raise IDNAException(idna_exception=e) 

309 

310 

311IDNA_2003_Practical = IDNA2003Codec(False) 

312IDNA_2003_Strict = IDNA2003Codec(True) 

313IDNA_2003 = IDNA_2003_Practical 

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

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

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

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

318IDNA_2008 = IDNA_2008_Practical 

319 

320 

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

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

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

324 

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

326 

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

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

329 sequence 

330 

331 """ 

332 

333 l = len(labels) 

334 total = 0 

335 i = -1 

336 j = 0 

337 for label in labels: 

338 ll = len(label) 

339 total += ll + 1 

340 if ll > 63: 

341 raise LabelTooLong 

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

343 i = j 

344 j += 1 

345 if total > 255: 

346 raise NameTooLong 

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

348 raise EmptyLabel 

349 

350 

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

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

353 ``bytes`` just return it. 

354 

355 """ 

356 

357 if isinstance(label, bytes): 

358 return label 

359 if isinstance(label, str): 

360 return label.encode() 

361 raise ValueError # pragma: no cover 

362 

363 

364@dns.immutable.immutable 

365class Name: 

366 """A DNS name. 

367 

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

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

370 of the class are immutable. 

371 """ 

372 

373 __slots__ = ["labels"] 

374 

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

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

377 

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

379 self.labels = tuple(blabels) 

380 _validate_labels(self.labels) 

381 

382 def __copy__(self): 

383 return Name(self.labels) 

384 

385 def __deepcopy__(self, memo): 

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

387 

388 def __getstate__(self): 

389 # Names can be pickled 

390 return {"labels": self.labels} 

391 

392 def __setstate__(self, state): 

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

394 _validate_labels(self.labels) 

395 

396 def is_absolute(self) -> bool: 

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

398 

399 Returns a ``bool``. 

400 """ 

401 

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

403 

404 def is_wild(self) -> bool: 

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

406 

407 Returns a ``bool``. 

408 """ 

409 

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

411 

412 def __hash__(self) -> int: 

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

414 

415 Returns an ``int``. 

416 """ 

417 

418 h = 0 

419 for label in self.labels: 

420 for c in label.lower(): 

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

422 return h 

423 

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

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

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

427 

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

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

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

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

432 

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

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

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

436 the DNSSEC order relation is used to order them. 

437 

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

439 have in common. 

440 

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

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

443 

444 ============= ============= =========== ===== ======= 

445 self other relation order nlabels 

446 ============= ============= =========== ===== ======= 

447 www.example. www.example. equal 0 3 

448 www.example. example. subdomain > 0 2 

449 example. www.example. superdomain < 0 2 

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

451 example1 example2. none < 0 0 

452 example1. example2 none > 0 0 

453 ============= ============= =========== ===== ======= 

454 """ 

455 

456 sabs = self.is_absolute() 

457 oabs = other.is_absolute() 

458 if sabs != oabs: 

459 if sabs: 

460 return (NameRelation.NONE, 1, 0) 

461 else: 

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

463 l1 = len(self.labels) 

464 l2 = len(other.labels) 

465 ldiff = l1 - l2 

466 if ldiff < 0: 

467 l = l1 

468 else: 

469 l = l2 

470 

471 order = 0 

472 nlabels = 0 

473 namereln = NameRelation.NONE 

474 while l > 0: 

475 l -= 1 

476 l1 -= 1 

477 l2 -= 1 

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

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

480 if label1 < label2: 

481 order = -1 

482 if nlabels > 0: 

483 namereln = NameRelation.COMMONANCESTOR 

484 return (namereln, order, nlabels) 

485 elif label1 > label2: 

486 order = 1 

487 if nlabels > 0: 

488 namereln = NameRelation.COMMONANCESTOR 

489 return (namereln, order, nlabels) 

490 nlabels += 1 

491 order = ldiff 

492 if ldiff < 0: 

493 namereln = NameRelation.SUPERDOMAIN 

494 elif ldiff > 0: 

495 namereln = NameRelation.SUBDOMAIN 

496 else: 

497 namereln = NameRelation.EQUAL 

498 return (namereln, order, nlabels) 

499 

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

501 """Is self a subdomain of other? 

502 

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

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

505 

506 Returns a ``bool``. 

507 """ 

508 

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

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

511 return True 

512 return False 

513 

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

515 """Is self a superdomain of other? 

516 

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

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

519 

520 Returns a ``bool``. 

521 """ 

522 

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

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

525 return True 

526 return False 

527 

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

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

530 DNSSEC canonical form. 

531 """ 

532 

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

534 

535 def __eq__(self, other): 

536 if isinstance(other, Name): 

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

538 else: 

539 return False 

540 

541 def __ne__(self, other): 

542 if isinstance(other, Name): 

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

544 else: 

545 return True 

546 

547 def __lt__(self, other): 

548 if isinstance(other, Name): 

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

550 else: 

551 return NotImplemented 

552 

553 def __le__(self, other): 

554 if isinstance(other, Name): 

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

556 else: 

557 return NotImplemented 

558 

559 def __ge__(self, other): 

560 if isinstance(other, Name): 

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

562 else: 

563 return NotImplemented 

564 

565 def __gt__(self, other): 

566 if isinstance(other, Name): 

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

568 else: 

569 return NotImplemented 

570 

571 def __repr__(self): 

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

573 

574 def __str__(self): 

575 return self.to_text(False) 

576 

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

578 """Convert name to DNS text format. 

579 

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

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

582 is False. 

583 

584 Returns a ``str``. 

585 """ 

586 

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

588 return "@" 

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

590 return "." 

591 if omit_final_dot and self.is_absolute(): 

592 l = self.labels[:-1] 

593 else: 

594 l = self.labels 

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

596 return s 

597 

598 def to_unicode( 

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

600 ) -> str: 

601 """Convert name to Unicode text format. 

602 

603 IDN ACE labels are converted to Unicode. 

604 

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

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

607 is False. 

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

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

610 The IDNA_2003_Practical decoder does 

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

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

613 for IDNA2008 as well. 

614 

615 Returns a ``str``. 

616 """ 

617 

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

619 return "@" 

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

621 return "." 

622 if omit_final_dot and self.is_absolute(): 

623 l = self.labels[:-1] 

624 else: 

625 l = self.labels 

626 if idna_codec is None: 

627 idna_codec = IDNA_2003_Practical 

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

629 

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

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

632 

633 The name is canonicalized and converted to uncompressed wire 

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

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

636 

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

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

639 to the name. 

640 

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

642 relative and no origin was provided. 

643 

644 Returns a ``bytes``. 

645 """ 

646 

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

648 assert digest is not None 

649 return digest 

650 

651 def to_wire( 

652 self, 

653 file: Optional[Any] = None, 

654 compress: Optional[CompressType] = None, 

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

656 canonicalize: bool = False, 

657 ) -> Optional[bytes]: 

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

659 

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

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

662 containing the wire name will be returned. 

663 

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

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

666 the compression code assumes that compression offset 0 is the 

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

668 if this is not the case. 

669 

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

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

672 to it. 

673 

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

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

676 digesting in hashes. 

677 

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

679 relative and no origin was provided. 

680 

681 Returns a ``bytes`` or ``None``. 

682 """ 

683 

684 if file is None: 

685 out = bytearray() 

686 for label in self.labels: 

687 out.append(len(label)) 

688 if canonicalize: 

689 out += label.lower() 

690 else: 

691 out += label 

692 if not self.is_absolute(): 

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

694 raise NeedAbsoluteNameOrOrigin 

695 for label in origin.labels: 

696 out.append(len(label)) 

697 if canonicalize: 

698 out += label.lower() 

699 else: 

700 out += label 

701 return bytes(out) 

702 

703 labels: Iterable[bytes] 

704 if not self.is_absolute(): 

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

706 raise NeedAbsoluteNameOrOrigin 

707 labels = list(self.labels) 

708 labels.extend(list(origin.labels)) 

709 else: 

710 labels = self.labels 

711 i = 0 

712 for label in labels: 

713 n = Name(labels[i:]) 

714 i += 1 

715 if compress is not None: 

716 pos = compress.get(n) 

717 else: 

718 pos = None 

719 if pos is not None: 

720 value = 0xC000 + pos 

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

722 file.write(s) 

723 break 

724 else: 

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

726 pos = file.tell() 

727 if pos <= 0x3FFF: 

728 compress[n] = pos 

729 l = len(label) 

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

731 if l > 0: 

732 if canonicalize: 

733 file.write(label.lower()) 

734 else: 

735 file.write(label) 

736 return None 

737 

738 def __len__(self) -> int: 

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

740 

741 Returns an ``int``. 

742 """ 

743 

744 return len(self.labels) 

745 

746 def __getitem__(self, index): 

747 return self.labels[index] 

748 

749 def __add__(self, other): 

750 return self.concatenate(other) 

751 

752 def __sub__(self, other): 

753 return self.relativize(other) 

754 

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

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

757 

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

759 

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

761 name. 

762 

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

764 """ 

765 

766 l = len(self.labels) 

767 if depth == 0: 

768 return (self, dns.name.empty) 

769 elif depth == l: 

770 return (dns.name.empty, self) 

771 elif depth < 0 or depth > l: 

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

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

774 

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

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

777 

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

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

780 

781 Returns a ``dns.name.Name``. 

782 """ 

783 

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

785 raise AbsoluteConcatenation 

786 labels = list(self.labels) 

787 labels.extend(list(other.labels)) 

788 return Name(labels) 

789 

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

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

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

793 

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

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

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

797 

798 Returns a ``dns.name.Name``. 

799 """ 

800 

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

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

803 else: 

804 return self 

805 

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

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

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

809 

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

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

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

813 

814 Returns a ``dns.name.Name``. 

815 """ 

816 

817 if not self.is_absolute(): 

818 return self.concatenate(origin) 

819 else: 

820 return self 

821 

822 def choose_relativity( 

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

824 ) -> "Name": 

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

826 

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

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

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

830 derelativized. 

831 

832 Returns a ``dns.name.Name``. 

833 """ 

834 

835 if origin: 

836 if relativize: 

837 return self.relativize(origin) 

838 else: 

839 return self.derelativize(origin) 

840 else: 

841 return self 

842 

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

844 """Return the parent of the name. 

845 

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

847 

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

849 empty name, and thus has no parent. 

850 

851 Returns a ``dns.name.Name``. 

852 """ 

853 

854 if self == root or self == empty: 

855 raise NoParent 

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

857 

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

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

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

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

862 *origin* due to length considerations. 

863 

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

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

866 is absolute then the predecessor will be absolute. 

867 

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

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

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

871 """ 

872 return _handle_relativity_and_call( 

873 _absolute_predecessor, self, origin, prefix_ok 

874 ) 

875 

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

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

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

879 computed due to name length limitations. 

880 

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

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

883 

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

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

886 is absolute then the successor will be absolute. 

887 

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

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

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

891 """ 

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

893 

894 

895#: The root name, '.' 

896root = Name([b""]) 

897 

898#: The empty name. 

899empty = Name([]) 

900 

901 

902def from_unicode( 

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

904) -> Name: 

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

906 

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

908 the IDNA codec. 

909 

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

911 

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

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

914 

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

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

917 is used. 

918 

919 Returns a ``dns.name.Name``. 

920 """ 

921 

922 if not isinstance(text, str): 

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

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

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

926 labels = [] 

927 label = "" 

928 escaping = False 

929 edigits = 0 

930 total = 0 

931 if idna_codec is None: 

932 idna_codec = IDNA_2003 

933 if text == "@": 

934 text = "" 

935 if text: 

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

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

938 for c in text: 

939 if escaping: 

940 if edigits == 0: 

941 if c.isdigit(): 

942 total = int(c) 

943 edigits += 1 

944 else: 

945 label += c 

946 escaping = False 

947 else: 

948 if not c.isdigit(): 

949 raise BadEscape 

950 total *= 10 

951 total += int(c) 

952 edigits += 1 

953 if edigits == 3: 

954 escaping = False 

955 label += chr(total) 

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

957 if len(label) == 0: 

958 raise EmptyLabel 

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

960 label = "" 

961 elif c == "\\": 

962 escaping = True 

963 edigits = 0 

964 total = 0 

965 else: 

966 label += c 

967 if escaping: 

968 raise BadEscape 

969 if len(label) > 0: 

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

971 else: 

972 labels.append(b"") 

973 

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

975 labels.extend(list(origin.labels)) 

976 return Name(labels) 

977 

978 

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

980 for c in text: 

981 if ord(c) > 0x7F: 

982 return False 

983 return True 

984 

985 

986def from_text( 

987 text: Union[bytes, str], 

988 origin: Optional[Name] = root, 

989 idna_codec: Optional[IDNACodec] = None, 

990) -> Name: 

991 """Convert text into a Name object. 

992 

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

994 

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

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

997 

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

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

1000 is used. 

1001 

1002 Returns a ``dns.name.Name``. 

1003 """ 

1004 

1005 if isinstance(text, str): 

1006 if not is_all_ascii(text): 

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

1008 return from_unicode(text, origin, idna_codec) 

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

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

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

1012 # 

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

1014 # 

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

1016 # codepoints > 127. 

1017 text = text.encode("ascii") 

1018 if not isinstance(text, bytes): 

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

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

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

1022 labels = [] 

1023 label = b"" 

1024 escaping = False 

1025 edigits = 0 

1026 total = 0 

1027 if text == b"@": 

1028 text = b"" 

1029 if text: 

1030 if text == b".": 

1031 return Name([b""]) 

1032 for c in text: 

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

1034 if escaping: 

1035 if edigits == 0: 

1036 if byte_.isdigit(): 

1037 total = int(byte_) 

1038 edigits += 1 

1039 else: 

1040 label += byte_ 

1041 escaping = False 

1042 else: 

1043 if not byte_.isdigit(): 

1044 raise BadEscape 

1045 total *= 10 

1046 total += int(byte_) 

1047 edigits += 1 

1048 if edigits == 3: 

1049 escaping = False 

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

1051 elif byte_ == b".": 

1052 if len(label) == 0: 

1053 raise EmptyLabel 

1054 labels.append(label) 

1055 label = b"" 

1056 elif byte_ == b"\\": 

1057 escaping = True 

1058 edigits = 0 

1059 total = 0 

1060 else: 

1061 label += byte_ 

1062 if escaping: 

1063 raise BadEscape 

1064 if len(label) > 0: 

1065 labels.append(label) 

1066 else: 

1067 labels.append(b"") 

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

1069 labels.extend(list(origin.labels)) 

1070 return Name(labels) 

1071 

1072 

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

1074 

1075 

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

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

1078 

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

1080 

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

1082 point backwards in the message. 

1083 

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

1085 

1086 Returns a ``dns.name.Name`` 

1087 """ 

1088 

1089 labels = [] 

1090 biggest_pointer = parser.current 

1091 with parser.restore_furthest(): 

1092 count = parser.get_uint8() 

1093 while count != 0: 

1094 if count < 64: 

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

1096 elif count >= 192: 

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

1098 if current >= biggest_pointer: 

1099 raise BadPointer 

1100 biggest_pointer = current 

1101 parser.seek(current) 

1102 else: 

1103 raise BadLabelType 

1104 count = parser.get_uint8() 

1105 labels.append(b"") 

1106 return Name(labels) 

1107 

1108 

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

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

1111 

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

1113 wire form. 

1114 

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

1116 from the start of the message 

1117 

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

1119 point backwards in the message. 

1120 

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

1122 

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

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

1125 which were consumed reading it. 

1126 """ 

1127 

1128 if not isinstance(message, bytes): 

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

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

1131 name = from_wire_parser(parser) 

1132 return (name, parser.current - current) 

1133 

1134 

1135# RFC 4471 Support 

1136 

1137_MINIMAL_OCTET = b"\x00" 

1138_MINIMAL_OCTET_VALUE = ord(_MINIMAL_OCTET) 

1139_SUCCESSOR_PREFIX = Name([_MINIMAL_OCTET]) 

1140_MAXIMAL_OCTET = b"\xff" 

1141_MAXIMAL_OCTET_VALUE = ord(_MAXIMAL_OCTET) 

1142_AT_SIGN_VALUE = ord("@") 

1143_LEFT_SQUARE_BRACKET_VALUE = ord("[") 

1144 

1145 

1146def _wire_length(labels): 

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

1148 

1149 

1150def _pad_to_max_name(name): 

1151 needed = 255 - _wire_length(name.labels) 

1152 new_labels = [] 

1153 while needed > 64: 

1154 new_labels.append(_MAXIMAL_OCTET * 63) 

1155 needed -= 64 

1156 if needed >= 2: 

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

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

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

1160 # label requires at least 2 bytes. 

1161 new_labels = list(reversed(new_labels)) 

1162 new_labels.extend(name.labels) 

1163 return Name(new_labels) 

1164 

1165 

1166def _pad_to_max_label(label, suffix_labels): 

1167 length = len(label) 

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

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

1170 if remaining <= 0: 

1171 # Shouldn't happen! 

1172 return label 

1173 needed = min(63 - length, remaining) 

1174 return label + _MAXIMAL_OCTET * needed 

1175 

1176 

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

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

1179 # 3.1.1. 

1180 # 

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

1182 # subdomain of origin. 

1183 if name == origin: 

1184 return _pad_to_max_name(name) 

1185 least_significant_label = name[0] 

1186 if least_significant_label == _MINIMAL_OCTET: 

1187 return name.parent() 

1188 least_octet = least_significant_label[-1] 

1189 suffix_labels = name.labels[1:] 

1190 if least_octet == _MINIMAL_OCTET_VALUE: 

1191 new_labels = [least_significant_label[:-1]] 

1192 else: 

1193 octets = bytearray(least_significant_label) 

1194 octet = octets[-1] 

1195 if octet == _LEFT_SQUARE_BRACKET_VALUE: 

1196 octet = _AT_SIGN_VALUE 

1197 else: 

1198 octet -= 1 

1199 octets[-1] = octet 

1200 least_significant_label = bytes(octets) 

1201 new_labels = [_pad_to_max_label(least_significant_label, suffix_labels)] 

1202 new_labels.extend(suffix_labels) 

1203 name = Name(new_labels) 

1204 if prefix_ok: 

1205 return _pad_to_max_name(name) 

1206 else: 

1207 return name 

1208 

1209 

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

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

1212 # 3.1.2. 

1213 # 

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

1215 # subdomain of origin. 

1216 if prefix_ok: 

1217 # Try prefixing \000 as new label 

1218 try: 

1219 return _SUCCESSOR_PREFIX.concatenate(name) 

1220 except NameTooLong: 

1221 pass 

1222 while name != origin: 

1223 # Try extending the least significant label. 

1224 least_significant_label = name[0] 

1225 if len(least_significant_label) < 63: 

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

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

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

1229 new_labels = [least_significant_label + _MINIMAL_OCTET] 

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

1231 try: 

1232 return dns.name.Name(new_labels) 

1233 except dns.name.NameTooLong: 

1234 pass 

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

1236 # signficant non-maximal byte in it. 

1237 octets = bytearray(least_significant_label) 

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

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

1240 # to the right of it. 

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

1242 octet = octets[i] 

1243 if octet == _MAXIMAL_OCTET_VALUE: 

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

1245 continue 

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

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

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

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

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

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

1252 if octet == _AT_SIGN_VALUE: 

1253 octet = _LEFT_SQUARE_BRACKET_VALUE 

1254 else: 

1255 octet += 1 

1256 octets[i] = octet 

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

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

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

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

1261 # always work. 

1262 return Name(new_labels) 

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

1264 # again. 

1265 name = name.parent() 

1266 

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

1268 # DNSSEC way. 

1269 return origin 

1270 

1271 

1272def _handle_relativity_and_call( 

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

1274 name: Name, 

1275 origin: Name, 

1276 prefix_ok: bool, 

1277) -> Name: 

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

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

1280 if not origin.is_absolute(): 

1281 raise NeedAbsoluteNameOrOrigin 

1282 relative = not name.is_absolute() 

1283 if relative: 

1284 name = name.derelativize(origin) 

1285 elif not name.is_subdomain(origin): 

1286 raise NeedSubdomainOfOrigin 

1287 result_name = function(name, origin, prefix_ok) 

1288 if relative: 

1289 result_name = result_name.relativize(origin) 

1290 return result_name