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

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

634 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 dataclasses 

22import encodings.idna # pyright: ignore 

23import functools 

24import struct 

25from collections.abc import Callable, Iterable 

26from typing import Any 

27 

28import dns._features 

29import dns._text_util 

30import dns.enum 

31import dns.exception 

32import dns.immutable 

33import dns.wirebase 

34from dns.style import BaseStyle 

35 

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

37# that out, so... 

38# 

39# pyright: reportAttributeAccessIssue = false, reportPossiblyUnboundVariable = false 

40 

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

42 import idna # pyright: ignore 

43 

44 have_idna_2008 = True 

45else: # pragma: no cover 

46 have_idna_2008 = False 

47 

48 

49CompressType = dict["Name", int] 

50 

51 

52class NameRelation(dns.enum.IntEnum): 

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

54 

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

56 # has hardwired the constants. 

57 

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

59 NONE = 0 

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

61 SUPERDOMAIN = 1 

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

63 SUBDOMAIN = 2 

64 #: The compared names are equal. 

65 EQUAL = 3 

66 #: The compared names have a common ancestor. 

67 COMMONANCESTOR = 4 

68 

69 @classmethod 

70 def _maximum(cls): 

71 return cls.COMMONANCESTOR # pragma: no cover 

72 

73 @classmethod 

74 def _short_name(cls): 

75 return cls.__name__ # pragma: no cover 

76 

77 

78# Backwards compatibility 

79NAMERELN_NONE = NameRelation.NONE 

80NAMERELN_SUPERDOMAIN = NameRelation.SUPERDOMAIN 

81NAMERELN_SUBDOMAIN = NameRelation.SUBDOMAIN 

82NAMERELN_EQUAL = NameRelation.EQUAL 

83NAMERELN_COMMONANCESTOR = NameRelation.COMMONANCESTOR 

84 

85 

86class EmptyLabel(dns.exception.SyntaxError): 

87 """A DNS label is empty.""" 

88 

89 

90class BadEscape(dns.exception.SyntaxError): 

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

92 

93 

94class BadPointer(dns.exception.FormError): 

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

96 

97 

98class PointerChainTooLong(dns.exception.FormError): 

99 """A DNS compression pointer chain was too long.""" 

100 

101 

102class BadLabelType(dns.exception.FormError): 

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

104 

105 

106class NeedAbsoluteNameOrOrigin(dns.exception.DNSException): 

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

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

109 

110 

111class NameTooLong(dns.exception.FormError): 

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

113 

114 

115class LabelTooLong(dns.exception.SyntaxError): 

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

117 

118 

119class AbsoluteConcatenation(dns.exception.DNSException): 

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

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

122 

123 

124class NoParent(dns.exception.DNSException): 

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

126 or the empty name.""" 

127 

128 

129class NoIDNA2008(dns.exception.DNSException): 

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

131 available.""" 

132 

133 

134class IDNAException(dns.exception.DNSException): 

135 """IDNA processing raised an exception.""" 

136 

137 supp_kwargs = {"idna_exception"} 

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

139 

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

141 # idna_exception 

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

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

144 

145 

146class NeedSubdomainOfOrigin(dns.exception.DNSException): 

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

148 

149 

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

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

152 

153 

154def _escapify(label: bytes | str) -> str: 

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

156 

157 :returns: the escaped string 

158 :rtype: str 

159 """ 

160 if isinstance(label, bytes): 

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

162 # < 0x20 or > 0x7f. 

163 text = "" 

164 for c in label: 

165 if c in _escaped: 

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

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

168 text += chr(c) 

169 else: 

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

171 return text 

172 

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

174 text = "" 

175 for uc in label: 

176 if uc in _escaped_text: 

177 text += "\\" + uc 

178 elif uc <= "\x20": 

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

180 else: 

181 text += uc 

182 return text 

183 

184 

185class IDNACodec: 

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

187 

188 def __init__(self): 

189 pass 

190 

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

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

193 

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

195 raise NotImplementedError # pragma: no cover 

196 

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

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

199 if self.is_idna(label): 

200 try: 

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

202 return _escapify(slabel) 

203 except Exception as e: 

204 raise IDNAException(idna_exception=e) 

205 else: 

206 return _escapify(label) 

207 

208 

209class IDNA2003Codec(IDNACodec): 

210 """IDNA 2003 encoder/decoder.""" 

211 

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

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

214 

215 :param bool strict_decode: If ``True``, then IDNA2003 checking 

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

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

218 """ 

219 

220 super().__init__() 

221 self.strict_decode = strict_decode 

222 

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

224 """Encode *label*.""" 

225 

226 if label == "": 

227 return b"" 

228 try: 

229 return encodings.idna.ToASCII(label) 

230 except UnicodeError: 

231 raise LabelTooLong 

232 

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

234 """Decode *label*.""" 

235 if not self.strict_decode: 

236 return super().decode(label) 

237 if label == b"": 

238 return "" 

239 try: 

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

241 except Exception as e: 

242 raise IDNAException(idna_exception=e) 

243 

244 

245class IDNA2008Codec(IDNACodec): 

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

247 

248 def __init__( 

249 self, 

250 uts_46: bool = False, 

251 transitional: bool = False, 

252 allow_pure_ascii: bool = False, 

253 strict_decode: bool = False, 

254 ): 

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

256 

257 :param bool uts_46: If ``True``, apply Unicode IDNA compatibility 

258 processing as described in Unicode Technical Standard #46 

259 (https://unicode.org/reports/tr46/). If ``False``, do not 

260 apply the mapping. The default is ``False``. 

261 :param bool transitional: If ``True``, use the "transitional" mode 

262 described in Unicode Technical Standard #46. The default is 

263 ``False``. This setting has no effect in idna 3.11 and later 

264 as transitional support has been removed. 

265 :param bool allow_pure_ascii: If ``True``, then a label which 

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

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

268 names, e.g. a name starting with ``_sip._tcp.`` and ending 

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

270 default is ``False``. 

271 :param bool strict_decode: If ``True``, then IDNA2008 checking 

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

273 was encoded with IDNA2003. The default is ``False``. 

274 """ 

275 super().__init__() 

276 self.uts_46 = uts_46 

277 self.transitional = transitional 

278 self.allow_pure_ascii = allow_pure_ascii 

279 self.strict_decode = strict_decode 

280 

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

282 if label == "": 

283 return b"" 

284 if self.allow_pure_ascii and is_all_ascii(label): 

285 encoded = label.encode("ascii") 

286 if len(encoded) > 63: 

287 raise LabelTooLong 

288 return encoded 

289 if not have_idna_2008: 

290 raise NoIDNA2008 

291 try: 

292 if self.uts_46: 

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

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

295 return idna.alabel(label) 

296 except idna.IDNAError as e: 

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

298 raise LabelTooLong 

299 else: 

300 raise IDNAException(idna_exception=e) 

301 

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

303 if not self.strict_decode: 

304 return super().decode(label) 

305 if label == b"": 

306 return "" 

307 if not have_idna_2008: 

308 raise NoIDNA2008 

309 try: 

310 ulabel = idna.ulabel(label) 

311 if self.uts_46: 

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

313 return _escapify(ulabel) 

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

315 raise IDNAException(idna_exception=e) 

316 

317 

318IDNA_2003_Practical = IDNA2003Codec(False) 

319IDNA_2003_Strict = IDNA2003Codec(True) 

320IDNA_2003 = IDNA_2003_Practical 

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

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

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

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

325IDNA_2008 = IDNA_2008_Practical 

326if have_idna_2008: 

327 IDNA_DEFAULT = IDNA_2008_Practical 

328else: 

329 IDNA_DEFAULT = IDNA_2003_Practical 

330 

331 

332def set_default_idna_codec(idna_codec: IDNACodec): 

333 """Set the default IDNA codec.""" 

334 global IDNA_DEFAULT 

335 IDNA_DEFAULT = idna_codec 

336 

337 

338def _validate_labels(labels: tuple[bytes, ...]) -> None: 

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

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

341 

342 :raises dns.name.NameTooLong: if the name as a whole is too long. 

343 :raises dns.name.EmptyLabel: if a label is empty (i.e. the root 

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

345 sequence. 

346 """ 

347 

348 l = len(labels) 

349 total = 0 

350 i = -1 

351 j = 0 

352 for label in labels: 

353 ll = len(label) 

354 total += ll + 1 

355 if ll > 63: 

356 raise LabelTooLong 

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

358 i = j 

359 j += 1 

360 if total > 255: 

361 raise NameTooLong 

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

363 raise EmptyLabel 

364 

365 

366def _maybe_convert_to_binary(label: bytes | str) -> bytes: 

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

368 ``bytes`` just return it. 

369 

370 """ 

371 

372 if isinstance(label, bytes): 

373 return label 

374 else: 

375 return label.encode() 

376 

377 

378@dataclasses.dataclass(frozen=True) 

379class NameStyle(BaseStyle): 

380 """Name text styles. 

381 

382 :param bool omit_final_dot: If ``True``, don't emit the final dot 

383 (denoting the root label) for absolute names. The default is 

384 ``False``. 

385 :param idna_codec: The IDNA decoder to use. The default is ``None``, 

386 which means all text is in the standard DNS zonefile format, i.e. 

387 punycode will not be decoded. 

388 :type idna_codec: :py:class:`dns.name.IDNACodec` or ``None`` 

389 :param origin: If ``None`` (the default), the name's relativity is not 

390 altered before conversion to text. Otherwise, if *relativize* is 

391 ``True`` the name is relativized, and if *relativize* is ``False`` 

392 the name is derelativized. 

393 :type origin: :py:class:`dns.name.Name` or ``None`` 

394 :param bool relativize: Controls the direction of relativization when 

395 *origin* is set. 

396 """ 

397 

398 omit_final_dot: bool = False 

399 idna_codec: IDNACodec | None = None 

400 origin: "Name | None" = None 

401 relativize: bool = False 

402 

403 

404@dns.immutable.immutable 

405class Name: 

406 """A DNS name. 

407 

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

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

410 of the class are immutable. 

411 """ 

412 

413 __slots__ = ["labels"] 

414 

415 def __init__(self, labels: Iterable[bytes | str]): 

416 """Initialize a DNS name. 

417 

418 :param labels: An iterable whose values are ``str`` or ``bytes``. 

419 """ 

420 

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

422 self.labels = tuple(blabels) 

423 _validate_labels(self.labels) 

424 

425 def __copy__(self): 

426 return Name(self.labels) 

427 

428 def __deepcopy__(self, memo): 

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

430 

431 def __getstate__(self): 

432 # Names can be pickled 

433 return {"labels": self.labels} 

434 

435 def __setstate__(self, state): 

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

437 _validate_labels(self.labels) 

438 

439 def is_absolute(self) -> bool: 

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

441 

442 :rtype: bool 

443 """ 

444 

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

446 

447 def is_wild(self) -> bool: 

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

449 

450 :rtype: bool 

451 """ 

452 

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

454 

455 def __hash__(self) -> int: 

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

457 

458 :rtype: int 

459 """ 

460 

461 h = 0 

462 for label in self.labels: 

463 for c in label.lower(): 

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

465 return h 

466 

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

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

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

470 

471 *relation* describes the relationship between the names, and is one 

472 of: :py:attr:`dns.name.NameRelation.NONE`, 

473 :py:attr:`dns.name.NameRelation.SUPERDOMAIN`, 

474 :py:attr:`dns.name.NameRelation.SUBDOMAIN`, 

475 :py:attr:`dns.name.NameRelation.EQUAL`, or 

476 :py:attr:`dns.name.NameRelation.COMMONANCESTOR`. 

477 

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

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

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

481 the DNSSEC order relation is used to order them. 

482 

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

484 have in common. 

485 

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

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

488 

489 ============= ============= =========== ===== ======= 

490 self other relation order nlabels 

491 ============= ============= =========== ===== ======= 

492 www.example. www.example. equal 0 3 

493 www.example. example. subdomain > 0 2 

494 example. www.example. superdomain < 0 2 

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

496 example1 example2. none < 0 0 

497 example1. example2 none > 0 0 

498 ============= ============= =========== ===== ======= 

499 

500 :param other: The name to compare with. 

501 :type other: :py:class:`dns.name.Name` 

502 :returns: A 3-tuple ``(relation, order, nlabels)``. 

503 :rtype: tuple[:py:class:`dns.name.NameRelation`, int, int] 

504 """ 

505 

506 sabs = self.is_absolute() 

507 oabs = other.is_absolute() 

508 if sabs != oabs: 

509 if sabs: 

510 return (NameRelation.NONE, 1, 0) 

511 else: 

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

513 l1 = len(self.labels) 

514 l2 = len(other.labels) 

515 ldiff = l1 - l2 

516 if ldiff < 0: 

517 l = l1 

518 else: 

519 l = l2 

520 

521 order = 0 

522 nlabels = 0 

523 namereln = NameRelation.NONE 

524 while l > 0: 

525 l -= 1 

526 l1 -= 1 

527 l2 -= 1 

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

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

530 if label1 < label2: 

531 order = -1 

532 if nlabels > 0: 

533 namereln = NameRelation.COMMONANCESTOR 

534 return (namereln, order, nlabels) 

535 elif label1 > label2: 

536 order = 1 

537 if nlabels > 0: 

538 namereln = NameRelation.COMMONANCESTOR 

539 return (namereln, order, nlabels) 

540 nlabels += 1 

541 order = ldiff 

542 if ldiff < 0: 

543 namereln = NameRelation.SUPERDOMAIN 

544 elif ldiff > 0: 

545 namereln = NameRelation.SUBDOMAIN 

546 else: 

547 namereln = NameRelation.EQUAL 

548 return (namereln, order, nlabels) 

549 

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

551 """Is self a subdomain of other? 

552 

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

554 ``dnspython.org`` is a subdomain of itself. 

555 

556 :rtype: bool 

557 """ 

558 

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

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

561 return True 

562 return False 

563 

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

565 """Is self a superdomain of other? 

566 

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

568 ``dnspython.org`` is a superdomain of itself. 

569 

570 :rtype: bool 

571 """ 

572 

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

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

575 return True 

576 return False 

577 

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

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

580 DNSSEC canonical form. 

581 

582 :rtype: :py:class:`dns.name.Name` 

583 """ 

584 

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

586 

587 def __eq__(self, other): 

588 if isinstance(other, Name): 

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

590 else: 

591 return False 

592 

593 def __ne__(self, other): 

594 if isinstance(other, Name): 

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

596 else: 

597 return True 

598 

599 def __lt__(self, other): 

600 if isinstance(other, Name): 

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

602 else: 

603 return NotImplemented 

604 

605 def __le__(self, other): 

606 if isinstance(other, Name): 

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

608 else: 

609 return NotImplemented 

610 

611 def __ge__(self, other): 

612 if isinstance(other, Name): 

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

614 else: 

615 return NotImplemented 

616 

617 def __gt__(self, other): 

618 if isinstance(other, Name): 

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

620 else: 

621 return NotImplemented 

622 

623 def __repr__(self): 

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

625 

626 def __str__(self): 

627 return self.to_text(False) 

628 

629 def to_text( 

630 self, omit_final_dot: bool = False, style: NameStyle | None = None 

631 ) -> str: 

632 """Convert name to DNS text format. 

633 

634 :param bool omit_final_dot: If ``True``, don't emit the final dot 

635 (denoting the root label) for absolute names. The default is 

636 ``False``. 

637 :param style: If specified, the style overrides the other parameters. 

638 :type style: :py:class:`dns.name.NameStyle` or ``None`` 

639 :rtype: str 

640 """ 

641 if style is None: 

642 style = NameStyle(omit_final_dot=omit_final_dot) 

643 return self.to_styled_text(style) 

644 

645 def to_unicode( 

646 self, 

647 omit_final_dot: bool = False, 

648 idna_codec: IDNACodec | None = None, 

649 style: NameStyle | None = None, 

650 ) -> str: 

651 """Convert name to DNS text format. 

652 

653 IDN ACE labels are converted to Unicode using the specified codec. 

654 

655 :param bool omit_final_dot: If ``True``, don't emit the final dot 

656 (denoting the root label) for absolute names. The default is 

657 ``False``. 

658 :param idna_codec: The IDNA codec to use for decoding ACE labels. 

659 If ``None``, the default IDNA codec is used. 

660 :type idna_codec: :py:class:`dns.name.IDNACodec` or ``None`` 

661 :param style: If specified, the style overrides the other parameters. 

662 :type style: :py:class:`dns.name.NameStyle` or ``None`` 

663 :rtype: str 

664 """ 

665 if idna_codec is None: 

666 idna_codec = IDNA_DEFAULT 

667 if style is None: 

668 style = NameStyle(omit_final_dot=omit_final_dot, idna_codec=idna_codec) 

669 return self.to_styled_text(style) 

670 

671 def to_styled_text(self, style: NameStyle) -> str: 

672 """Convert name to text format, applying the style. 

673 

674 See the documentation for :py:class:`dns.name.NameStyle` for a description 

675 of the style parameters. 

676 

677 :param style: The style to apply. 

678 :type style: :py:class:`dns.name.NameStyle` 

679 :rtype: str 

680 """ 

681 

682 name = self.choose_relativity(style.origin, style.relativize) 

683 if len(name.labels) == 0: 

684 return "@" 

685 if len(name.labels) == 1 and name.labels[0] == b"": 

686 return "." 

687 if style.omit_final_dot and name.is_absolute(): 

688 l = name.labels[:-1] 

689 else: 

690 l = name.labels 

691 if style.idna_codec is None: 

692 return ".".join(map(_escapify, l)) 

693 else: 

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

695 

696 def to_digestable(self, origin: "Name | None" = None) -> bytes: 

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

698 

699 The name is canonicalized and converted to uncompressed wire 

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

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

702 

703 :param origin: If the name is relative and *origin* is not ``None``, 

704 then *origin* will be appended to the name. 

705 :type origin: :py:class:`dns.name.Name` or ``None`` 

706 :raises dns.name.NeedAbsoluteNameOrOrigin: if the name is relative 

707 and no origin was provided. 

708 :rtype: bytes 

709 """ 

710 

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

712 assert digest is not None 

713 return digest 

714 

715 def to_wire( 

716 self, 

717 file: Any | None = None, 

718 compress: CompressType | None = None, 

719 origin: "Name | None" = None, 

720 canonicalize: bool = False, 

721 ) -> bytes | None: 

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

723 

724 :param file: The file where the name is emitted (typically an 

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

726 containing the wire name will be returned. 

727 :param compress: The compression table to use. If ``None`` (the 

728 default), names will not be compressed. Note that the compression 

729 code assumes that compression offset 0 is the start of *file*, 

730 and thus compression will not be correct if this is not the case. 

731 :type compress: dict or ``None`` 

732 :param origin: If the name is relative and *origin* is not ``None``, 

733 then *origin* will be appended to it. 

734 :type origin: :py:class:`dns.name.Name` or ``None`` 

735 :param bool canonicalize: Whether the name should be canonicalized; 

736 that is, converted to a format suitable for digesting in hashes. 

737 :raises dns.name.NeedAbsoluteNameOrOrigin: if the name is relative 

738 and no origin was provided. 

739 :returns: ``None`` if *file* is provided, otherwise the wire format 

740 as ``bytes``. 

741 :rtype: bytes or ``None`` 

742 """ 

743 

744 if file is None: 

745 out = bytearray() 

746 for label in self.labels: 

747 out.append(len(label)) 

748 if canonicalize: 

749 out += label.lower() 

750 else: 

751 out += label 

752 if not self.is_absolute(): 

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

754 raise NeedAbsoluteNameOrOrigin 

755 for label in origin.labels: 

756 out.append(len(label)) 

757 if canonicalize: 

758 out += label.lower() 

759 else: 

760 out += label 

761 return bytes(out) 

762 

763 labels: Iterable[bytes] 

764 if not self.is_absolute(): 

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

766 raise NeedAbsoluteNameOrOrigin 

767 labels = list(self.labels) 

768 labels.extend(list(origin.labels)) 

769 else: 

770 labels = self.labels 

771 i = 0 

772 for label in labels: 

773 n = Name(labels[i:]) 

774 i += 1 

775 if compress is not None: 

776 pos = compress.get(n) 

777 else: 

778 pos = None 

779 if pos is not None: 

780 value = 0xC000 + pos 

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

782 file.write(s) 

783 break 

784 else: 

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

786 pos = file.tell() 

787 if pos <= 0x3FFF: 

788 compress[n] = pos 

789 l = len(label) 

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

791 if l > 0: 

792 if canonicalize: 

793 file.write(label.lower()) 

794 else: 

795 file.write(label) 

796 return None 

797 

798 def __len__(self) -> int: 

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

800 

801 :rtype: int 

802 """ 

803 

804 return len(self.labels) 

805 

806 def __getitem__(self, index: Any) -> Any: 

807 return self.labels[index] 

808 

809 def __add__(self, other): 

810 return self.concatenate(other) 

811 

812 def __sub__(self, other): 

813 return self.relativize(other) 

814 

815 def split(self, depth: int) -> tuple["Name", "Name"]: 

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

817 

818 :param int depth: The number of labels in the suffix. 

819 :raises ValueError: if *depth* is not >= 0 and <= the length of the 

820 name. 

821 :returns: A ``(prefix, suffix)`` tuple. 

822 :rtype: tuple[:py:class:`dns.name.Name`, :py:class:`dns.name.Name`] 

823 """ 

824 

825 l = len(self.labels) 

826 if depth == 0: 

827 return (self, empty) 

828 elif depth == l: 

829 return (empty, self) 

830 elif depth < 0 or depth > l: 

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

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

833 

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

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

836 

837 :param other: The name to concatenate. 

838 :type other: :py:class:`dns.name.Name` 

839 :raises dns.name.AbsoluteConcatenation: if the name is absolute and 

840 *other* is not the empty name. 

841 :rtype: :py:class:`dns.name.Name` 

842 """ 

843 

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

845 raise AbsoluteConcatenation 

846 labels = list(self.labels) 

847 labels.extend(list(other.labels)) 

848 return Name(labels) 

849 

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

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

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

853 

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

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

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

857 

858 :param origin: The origin to relativize against. 

859 :type origin: :py:class:`dns.name.Name` 

860 :rtype: :py:class:`dns.name.Name` 

861 """ 

862 

863 if self.is_subdomain(origin): 

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

865 else: 

866 return self 

867 

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

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

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

871 

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

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

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

875 

876 :param origin: The origin to append. 

877 :type origin: :py:class:`dns.name.Name` 

878 :rtype: :py:class:`dns.name.Name` 

879 """ 

880 

881 if not self.is_absolute(): 

882 return self.concatenate(origin) 

883 else: 

884 return self 

885 

886 def choose_relativity( 

887 self, origin: "Name | None" = None, relativize: bool = True 

888 ) -> "Name": 

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

890 

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

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

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

894 derelativized. 

895 

896 :param origin: If not ``None``, controls relativization. 

897 :type origin: :py:class:`dns.name.Name` or ``None`` 

898 :param bool relativize: If ``True``, relativize; if ``False``, 

899 derelativize. 

900 :rtype: :py:class:`dns.name.Name` 

901 """ 

902 

903 if origin: 

904 if relativize: 

905 return self.relativize(origin) 

906 else: 

907 return self.derelativize(origin) 

908 else: 

909 return self 

910 

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

912 """Return the parent of the name. 

913 

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

915 

916 :raises dns.name.NoParent: if the name is either the root name or the 

917 empty name, and thus has no parent. 

918 :rtype: :py:class:`dns.name.Name` 

919 """ 

920 

921 if self == root or self == empty: 

922 raise NoParent 

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

924 

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

926 """Return the maximal predecessor of the name in the DNSSEC ordering. 

927 

928 Returns the maximal predecessor in the zone whose origin is *origin*, 

929 or returns the longest name under *origin* if the name is *origin* 

930 (i.e. wraps around to the longest name, which may still be *origin* 

931 due to length considerations). 

932 

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

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

935 is absolute then the predecessor will be absolute. 

936 

937 :param origin: The zone origin. 

938 :type origin: :py:class:`dns.name.Name` 

939 :param bool prefix_ok: If ``True`` (the default), prefixing labels is 

940 allowed. Specify ``False`` when computing a maximal predecessor 

941 at a zone cut point. 

942 :rtype: :py:class:`dns.name.Name` 

943 """ 

944 return _handle_relativity_and_call( 

945 _absolute_predecessor, self, origin, prefix_ok 

946 ) 

947 

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

949 """Return the minimal successor of the name in the DNSSEC ordering. 

950 

951 Returns the minimal successor in the zone whose origin is *origin*, 

952 or returns *origin* if the successor cannot be computed due to name 

953 length limitations. 

954 

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

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

957 

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

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

960 is absolute then the successor will be absolute. 

961 

962 :param origin: The zone origin. 

963 :type origin: :py:class:`dns.name.Name` 

964 :param bool prefix_ok: If ``True`` (the default), prefixing a new 

965 minimal label is allowed. Specify ``False`` when computing a 

966 minimal successor at a zone cut point. 

967 :rtype: :py:class:`dns.name.Name` 

968 """ 

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

970 

971 

972#: The root name, '.' 

973root = Name([b""]) 

974 

975#: The empty name. 

976empty = Name([]) 

977 

978 

979def from_unicode( 

980 text: str, origin: Name | None = root, idna_codec: IDNACodec | None = None 

981) -> Name: 

982 """Convert unicode text into a :py:class:`dns.name.Name` object. 

983 

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

985 the IDNA codec. 

986 

987 :param str text: The text to convert into a name. 

988 :param origin: The origin to append to non-absolute names. The 

989 default is the root name. 

990 :type origin: :py:class:`dns.name.Name` or ``None`` 

991 :param idna_codec: The IDNA encoder/decoder. If ``None``, the default 

992 IDNA encoder/decoder is used. 

993 :type idna_codec: :py:class:`dns.name.IDNACodec` or ``None`` 

994 :rtype: :py:class:`dns.name.Name` 

995 """ 

996 

997 labels = [] 

998 label = "" 

999 escaping = False 

1000 edigits = 0 

1001 total = 0 

1002 if idna_codec is None: 

1003 idna_codec = IDNA_DEFAULT 

1004 if text == "@": 

1005 text = "" 

1006 if text: 

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

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

1009 for c in text: 

1010 if escaping: 

1011 if edigits == 0: 

1012 if dns._text_util.is_ascii_digit(c): 

1013 total = int(c) 

1014 edigits += 1 

1015 else: 

1016 label += c 

1017 escaping = False 

1018 else: 

1019 if not dns._text_util.is_ascii_digit(c): 

1020 raise BadEscape 

1021 total *= 10 

1022 total += int(c) 

1023 edigits += 1 

1024 if edigits == 3: 

1025 if total > 255: 

1026 raise BadEscape 

1027 escaping = False 

1028 label += chr(total) 

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

1030 if len(label) == 0: 

1031 raise EmptyLabel 

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

1033 label = "" 

1034 elif c == "\\": 

1035 escaping = True 

1036 edigits = 0 

1037 total = 0 

1038 else: 

1039 label += c 

1040 if escaping: 

1041 raise BadEscape 

1042 if len(label) > 0: 

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

1044 else: 

1045 labels.append(b"") 

1046 

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

1048 labels.extend(list(origin.labels)) 

1049 return Name(labels) 

1050 

1051 

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

1053 for c in text: 

1054 if ord(c) > 0x7F: 

1055 return False 

1056 return True 

1057 

1058 

1059def from_text( 

1060 text: bytes | str, 

1061 origin: Name | None = root, 

1062 idna_codec: IDNACodec | None = None, 

1063) -> Name: 

1064 """Convert text into a :py:class:`dns.name.Name` object. 

1065 

1066 :param text: The text to convert into a name. 

1067 :type text: bytes or str 

1068 :param origin: The origin to append to non-absolute names. The 

1069 default is the root name. 

1070 :type origin: :py:class:`dns.name.Name` or ``None`` 

1071 :param idna_codec: The IDNA encoder/decoder. If ``None``, the default 

1072 IDNA encoder/decoder is used. 

1073 :type idna_codec: :py:class:`dns.name.IDNACodec` or ``None`` 

1074 :rtype: :py:class:`dns.name.Name` 

1075 """ 

1076 

1077 if isinstance(text, str): 

1078 if not is_all_ascii(text): 

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

1080 return from_unicode(text, origin, idna_codec) 

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

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

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

1084 # 

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

1086 # 

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

1088 # codepoints > 127. 

1089 text = text.encode("ascii") 

1090 labels = [] 

1091 label = b"" 

1092 escaping = False 

1093 edigits = 0 

1094 total = 0 

1095 if text == b"@": 

1096 text = b"" 

1097 if text: 

1098 if text == b".": 

1099 return Name([b""]) 

1100 for c in text: 

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

1102 if escaping: 

1103 if edigits == 0: 

1104 if byte_.isdigit(): 

1105 total = int(byte_) 

1106 edigits += 1 

1107 else: 

1108 label += byte_ 

1109 escaping = False 

1110 else: 

1111 if not byte_.isdigit(): 

1112 raise BadEscape 

1113 total *= 10 

1114 total += int(byte_) 

1115 edigits += 1 

1116 if edigits == 3: 

1117 if total > 255: 

1118 raise BadEscape 

1119 escaping = False 

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

1121 elif byte_ == b".": 

1122 if len(label) == 0: 

1123 raise EmptyLabel 

1124 labels.append(label) 

1125 label = b"" 

1126 elif byte_ == b"\\": 

1127 escaping = True 

1128 edigits = 0 

1129 total = 0 

1130 else: 

1131 label += byte_ 

1132 if escaping: 

1133 raise BadEscape 

1134 if len(label) > 0: 

1135 labels.append(label) 

1136 else: 

1137 labels.append(b"") 

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

1139 labels.extend(list(origin.labels)) 

1140 return Name(labels) 

1141 

1142 

1143MAX_COMPRESSION_POINTER_CHAIN = 16 

1144 

1145 

1146def from_wire_parser(parser: dns.wirebase.Parser) -> Name: 

1147 """Convert possibly compressed wire format into a :py:class:`dns.name.Name`. 

1148 

1149 :param parser: The wire format parser. 

1150 :type parser: :py:class:`dns.wirebase.Parser` 

1151 :raises dns.name.BadPointer: if a compression pointer did not 

1152 point backwards in the message. 

1153 :raises dns.name.BadLabelType: if an invalid label type was encountered. 

1154 :rtype: :py:class:`dns.name.Name` 

1155 """ 

1156 

1157 labels = [] 

1158 biggest_pointer = parser.current 

1159 hops = 0 

1160 with parser.restore_furthest(): 

1161 count = parser.get_uint8() 

1162 while count != 0: 

1163 if count < 64: 

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

1165 elif count >= 192: 

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

1167 if current >= biggest_pointer: 

1168 raise BadPointer 

1169 biggest_pointer = current 

1170 hops += 1 

1171 if hops > MAX_COMPRESSION_POINTER_CHAIN: 

1172 raise PointerChainTooLong 

1173 parser.seek(current) 

1174 else: 

1175 raise BadLabelType 

1176 count = parser.get_uint8() 

1177 labels.append(b"") 

1178 return Name(labels) 

1179 

1180 

1181def from_wire(message: bytes, current: int) -> tuple[Name, int]: 

1182 """Convert possibly compressed wire format into a :py:class:`dns.name.Name`. 

1183 

1184 :param bytes message: A ``bytes`` containing an entire DNS message in DNS 

1185 wire form. 

1186 :param int current: The offset of the beginning of the name from the 

1187 start of the message. 

1188 :raises dns.name.BadPointer: if a compression pointer did not 

1189 point backwards in the message. 

1190 :raises dns.name.BadLabelType: if an invalid label type was encountered. 

1191 :returns: A tuple of the name that was read and the number of bytes of 

1192 the wire format message which were consumed reading it. 

1193 :rtype: tuple[:py:class:`dns.name.Name`, int] 

1194 """ 

1195 

1196 parser = dns.wirebase.Parser(message, current) 

1197 name = from_wire_parser(parser) 

1198 return (name, parser.current - current) 

1199 

1200 

1201# RFC 4471 Support 

1202 

1203_MINIMAL_OCTET = b"\x00" 

1204_MINIMAL_OCTET_VALUE = ord(_MINIMAL_OCTET) 

1205_SUCCESSOR_PREFIX = Name([_MINIMAL_OCTET]) 

1206_MAXIMAL_OCTET = b"\xff" 

1207_MAXIMAL_OCTET_VALUE = ord(_MAXIMAL_OCTET) 

1208_AT_SIGN_VALUE = ord("@") 

1209_LEFT_SQUARE_BRACKET_VALUE = ord("[") 

1210 

1211 

1212def _wire_length(labels): 

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

1214 

1215 

1216def _pad_to_max_name(name): 

1217 needed = 255 - _wire_length(name.labels) 

1218 new_labels = [] 

1219 while needed > 64: 

1220 new_labels.append(_MAXIMAL_OCTET * 63) 

1221 needed -= 64 

1222 if needed >= 2: 

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

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

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

1226 # label requires at least 2 bytes. 

1227 new_labels = list(reversed(new_labels)) 

1228 new_labels.extend(name.labels) 

1229 return Name(new_labels) 

1230 

1231 

1232def _pad_to_max_label(label, suffix_labels): 

1233 length = len(label) 

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

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

1236 if remaining <= 0: 

1237 # Shouldn't happen! 

1238 return label 

1239 needed = min(63 - length, remaining) 

1240 return label + _MAXIMAL_OCTET * needed 

1241 

1242 

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

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

1245 # 3.1.1. 

1246 # 

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

1248 # subdomain of origin. 

1249 if name == origin: 

1250 return _pad_to_max_name(name) 

1251 least_significant_label = name[0] 

1252 if least_significant_label == _MINIMAL_OCTET: 

1253 return name.parent() 

1254 least_octet = least_significant_label[-1] 

1255 suffix_labels = name.labels[1:] 

1256 if least_octet == _MINIMAL_OCTET_VALUE: 

1257 new_labels = [least_significant_label[:-1]] 

1258 else: 

1259 octets = bytearray(least_significant_label) 

1260 octet = octets[-1] 

1261 if octet == _LEFT_SQUARE_BRACKET_VALUE: 

1262 octet = _AT_SIGN_VALUE 

1263 else: 

1264 octet -= 1 

1265 octets[-1] = octet 

1266 least_significant_label = bytes(octets) 

1267 new_labels = [_pad_to_max_label(least_significant_label, suffix_labels)] 

1268 new_labels.extend(suffix_labels) 

1269 name = Name(new_labels) 

1270 if prefix_ok: 

1271 return _pad_to_max_name(name) 

1272 else: 

1273 return name 

1274 

1275 

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

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

1278 # 3.1.2. 

1279 # 

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

1281 # subdomain of origin. 

1282 if prefix_ok: 

1283 # Try prefixing \000 as new label 

1284 try: 

1285 return _SUCCESSOR_PREFIX.concatenate(name) 

1286 except NameTooLong: 

1287 pass 

1288 while name != origin: 

1289 # Try extending the least significant label. 

1290 least_significant_label = name[0] 

1291 if len(least_significant_label) < 63: 

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

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

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

1295 new_labels = [least_significant_label + _MINIMAL_OCTET] 

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

1297 try: 

1298 return Name(new_labels) 

1299 except NameTooLong: 

1300 pass 

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

1302 # signficant non-maximal byte in it. 

1303 octets = bytearray(least_significant_label) 

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

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

1306 # to the right of it. 

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

1308 octet = octets[i] 

1309 if octet == _MAXIMAL_OCTET_VALUE: 

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

1311 continue 

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

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

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

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

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

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

1318 if octet == _AT_SIGN_VALUE: 

1319 octet = _LEFT_SQUARE_BRACKET_VALUE 

1320 else: 

1321 octet += 1 

1322 octets[i] = octet 

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

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

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

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

1327 # always work. 

1328 return Name(new_labels) 

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

1330 # again. 

1331 name = name.parent() 

1332 

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

1334 # DNSSEC way. 

1335 return origin 

1336 

1337 

1338def _handle_relativity_and_call( 

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

1340 name: Name, 

1341 origin: Name, 

1342 prefix_ok: bool, 

1343) -> Name: 

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

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

1346 if not origin.is_absolute(): 

1347 raise NeedAbsoluteNameOrOrigin 

1348 relative = not name.is_absolute() 

1349 if relative: 

1350 name = name.derelativize(origin) 

1351 elif not name.is_subdomain(origin): 

1352 raise NeedSubdomainOfOrigin 

1353 result_name = function(name, origin, prefix_ok) 

1354 if relative: 

1355 result_name = result_name.relativize(origin) 

1356 return result_name