1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """DNS Names.
17
18 @var root: The DNS root name.
19 @type root: dns.name.Name object
20 @var empty: The empty DNS name.
21 @type empty: dns.name.Name object
22 """
23
24 from io import BytesIO
25 import struct
26 import sys
27 import copy
28 import encodings.idna
29 try:
30 import idna
31 have_idna_2008 = True
32 except ImportError:
33 have_idna_2008 = False
34
35 import dns.exception
36 import dns.wiredata
37
38 from ._compat import long, binary_type, text_type, unichr, maybe_decode
39
40 try:
41 maxint = sys.maxint
42 except AttributeError:
43 maxint = (1 << (8 * struct.calcsize("P"))) // 2 - 1
44
45 NAMERELN_NONE = 0
46 NAMERELN_SUPERDOMAIN = 1
47 NAMERELN_SUBDOMAIN = 2
48 NAMERELN_EQUAL = 3
49 NAMERELN_COMMONANCESTOR = 4
50
51
53
54 """A DNS label is empty."""
55
56
58
59 """An escaped code in a text format of DNS name is invalid."""
60
61
63
64 """A DNS compression pointer points forward instead of backward."""
65
66
68
69 """The label type in DNS name wire format is unknown."""
70
71
73
74 """An attempt was made to convert a non-absolute name to
75 wire when there was also a non-absolute (or missing) origin."""
76
77
81
82
86
87
89
90 """An attempt was made to append anything other than the
91 empty name to an absolute DNS name."""
92
93
94 -class NoParent(dns.exception.DNSException):
95
96 """An attempt was made to get the parent of the root name
97 or the empty name."""
98
100
101 """IDNA 2008 processing was requested but the idna module is not
102 available."""
103
104
106
107 """IDNA processing raised an exception."""
108
109 supp_kwargs = set(['idna_exception'])
110 fmt = "IDNA processing exception: {idna_exception}"
111
113
114 """Abstract base class for IDNA encoder/decoders."""
115
118
120 raise NotImplementedError
121
123
124 downcased = label.lower()
125 if downcased.startswith(b'xn--'):
126 try:
127 label = downcased[4:].decode('punycode')
128 except Exception as e:
129 raise IDNAException(idna_exception=e)
130 else:
131 label = maybe_decode(label)
132 return _escapify(label, True)
133
135
136 """IDNA 2003 encoder/decoder."""
137
138 - def __init__(self, strict_decode=False):
139 """Initialize the IDNA 2003 encoder/decoder.
140 @param strict_decode: If True, then IDNA2003 checking is done when
141 decoding. This can cause failures if the name was encoded with
142 IDNA2008. The default is False.
143 @type strict_decode: bool
144 """
145 super(IDNA2003Codec, self).__init__()
146 self.strict_decode = strict_decode
147
149 if label == '':
150 return b''
151 try:
152 return encodings.idna.ToASCII(label)
153 except UnicodeError:
154 raise LabelTooLong
155
157 if not self.strict_decode:
158 return super(IDNA2003Codec, self).decode(label)
159 if label == b'':
160 return u''
161 try:
162 return _escapify(encodings.idna.ToUnicode(label), True)
163 except Exception as e:
164 raise IDNAException(idna_exception=e)
165
167
168 """IDNA 2008 encoder/decoder."""
169
170 - def __init__(self, uts_46=False, transitional=False,
171 allow_pure_ascii=False, strict_decode=False):
172 """Initialize the IDNA 2008 encoder/decoder.
173 @param uts_46: If True, apply Unicode IDNA compatibility processing
174 as described in Unicode Technical Standard #46
175 (U{http://unicode.org/reports/tr46/}). This parameter is only
176 meaningful if IDNA 2008 is in use. If False, do not apply
177 the mapping. The default is False
178 @type uts_46: bool
179 @param transitional: If True, use the "transitional" mode described
180 in Unicode Technical Standard #46. This parameter is only
181 meaningful if IDNA 2008 is in use. The default is False.
182 @type transitional: bool
183 @param allow_pure_ascii: If True, then a label which
184 consists of only ASCII characters is allowed. This is less strict
185 than regular IDNA 2008, but is also necessary for mixed names,
186 e.g. a name with starting with "_sip._tcp." and ending in an IDN
187 suffixm which would otherwise be disallowed. The default is False
188 @type allow_pure_ascii: bool
189 @param strict_decode: If True, then IDNA2008 checking is done when
190 decoding. This can cause failures if the name was encoded with
191 IDNA2003. The default is False.
192 @type strict_decode: bool
193 """
194 super(IDNA2008Codec, self).__init__()
195 self.uts_46 = uts_46
196 self.transitional = transitional
197 self.allow_pure_ascii = allow_pure_ascii
198 self.strict_decode = strict_decode
199
201 for c in label:
202 if ord(c) > 0x7f:
203 return False
204 return True
205
207 if label == '':
208 return b''
209 if self.allow_pure_ascii and self.is_all_ascii(label):
210 return label.encode('ascii')
211 if not have_idna_2008:
212 raise NoIDNA2008
213 try:
214 if self.uts_46:
215 label = idna.uts46_remap(label, False, self.transitional)
216 return idna.alabel(label)
217 except idna.IDNAError as e:
218 raise IDNAException(idna_exception=e)
219
221 if not self.strict_decode:
222 return super(IDNA2008Codec, self).decode(label)
223 if label == b'':
224 return u''
225 if not have_idna_2008:
226 raise NoIDNA2008
227 try:
228 if self.uts_46:
229 label = idna.uts46_remap(label, False, False)
230 return _escapify(idna.ulabel(label), True)
231 except idna.IDNAError as e:
232 raise IDNAException(idna_exception=e)
233
234 _escaped = bytearray(b'"().;\\@$')
235
236 IDNA_2003_Practical = IDNA2003Codec(False)
237 IDNA_2003_Strict = IDNA2003Codec(True)
238 IDNA_2003 = IDNA_2003_Practical
239 IDNA_2008_Practical = IDNA2008Codec(True, False, True, False)
240 IDNA_2008_UTS_46 = IDNA2008Codec(True, False, False, False)
241 IDNA_2008_Strict = IDNA2008Codec(False, False, False, True)
242 IDNA_2008_Transitional = IDNA2008Codec(True, True, False, False)
243 IDNA_2008 = IDNA_2008_Practical
244
246 """Escape the characters in label which need it.
247 @param unicode_mode: escapify only special and whitespace (<= 0x20)
248 characters
249 @returns: the escaped string
250 @rtype: string"""
251 if not unicode_mode:
252 text = ''
253 if isinstance(label, text_type):
254 label = label.encode()
255 for c in bytearray(label):
256 if c in _escaped:
257 text += '\\' + chr(c)
258 elif c > 0x20 and c < 0x7F:
259 text += chr(c)
260 else:
261 text += '\\%03d' % c
262 return text.encode()
263
264 text = u''
265 if isinstance(label, binary_type):
266 label = label.decode()
267 for c in label:
268 if c > u'\x20' and c < u'\x7f':
269 text += c
270 else:
271 if c >= u'\x7f':
272 text += c
273 else:
274 text += u'\\%03d' % ord(c)
275 return text
276
278 """Check for empty labels in the middle of a label sequence,
279 labels that are too long, and for too many labels.
280 @raises NameTooLong: the name as a whole is too long
281 @raises EmptyLabel: a label is empty (i.e. the root label) and appears
282 in a position other than the end of the label sequence"""
283
284 l = len(labels)
285 total = 0
286 i = -1
287 j = 0
288 for label in labels:
289 ll = len(label)
290 total += ll + 1
291 if ll > 63:
292 raise LabelTooLong
293 if i < 0 and label == b'':
294 i = j
295 j += 1
296 if total > 255:
297 raise NameTooLong
298 if i >= 0 and i != l - 1:
299 raise EmptyLabel
300
301
303 if isinstance(label, binary_type):
304 return label
305 if isinstance(label, text_type):
306 return label.encode()
307 raise ValueError
308
309
311
312 """A DNS name.
313
314 The dns.name.Name class represents a DNS name as a tuple of labels.
315 Instances of the class are immutable.
316
317 @ivar labels: The tuple of labels in the name. Each label is a string of
318 up to 63 octets."""
319
320 __slots__ = ['labels']
321
323 """Initialize a domain name from a list of labels.
324 @param labels: the labels
325 @type labels: any iterable whose values are strings
326 """
327 labels = [_ensure_bytes(x) for x in labels]
328 super(Name, self).__setattr__('labels', tuple(labels))
329 _validate_labels(self.labels)
330
332 raise TypeError("object doesn't support attribute assignment")
333
336
339
341 return {'labels': self.labels}
342
346
348 """Is the most significant label of this name the root label?
349 @rtype: bool
350 """
351
352 return len(self.labels) > 0 and self.labels[-1] == b''
353
355 """Is this name wild? (I.e. Is the least significant label '*'?)
356 @rtype: bool
357 """
358
359 return len(self.labels) > 0 and self.labels[0] == b'*'
360
362 """Return a case-insensitive hash of the name.
363 @rtype: int
364 """
365
366 h = long(0)
367 for label in self.labels:
368 for c in bytearray(label.lower()):
369 h += (h << 3) + c
370 return int(h % maxint)
371
373 """Compare two names, returning a 3-tuple (relation, order, nlabels).
374
375 I{relation} describes the relation ship between the names,
376 and is one of: dns.name.NAMERELN_NONE,
377 dns.name.NAMERELN_SUPERDOMAIN, dns.name.NAMERELN_SUBDOMAIN,
378 dns.name.NAMERELN_EQUAL, or dns.name.NAMERELN_COMMONANCESTOR
379
380 I{order} is < 0 if self < other, > 0 if self > other, and ==
381 0 if self == other. A relative name is always less than an
382 absolute name. If both names have the same relativity, then
383 the DNSSEC order relation is used to order them.
384
385 I{nlabels} is the number of significant labels that the two names
386 have in common.
387 """
388
389 sabs = self.is_absolute()
390 oabs = other.is_absolute()
391 if sabs != oabs:
392 if sabs:
393 return (NAMERELN_NONE, 1, 0)
394 else:
395 return (NAMERELN_NONE, -1, 0)
396 l1 = len(self.labels)
397 l2 = len(other.labels)
398 ldiff = l1 - l2
399 if ldiff < 0:
400 l = l1
401 else:
402 l = l2
403
404 order = 0
405 nlabels = 0
406 namereln = NAMERELN_NONE
407 while l > 0:
408 l -= 1
409 l1 -= 1
410 l2 -= 1
411 label1 = self.labels[l1].lower()
412 label2 = other.labels[l2].lower()
413 if label1 < label2:
414 order = -1
415 if nlabels > 0:
416 namereln = NAMERELN_COMMONANCESTOR
417 return (namereln, order, nlabels)
418 elif label1 > label2:
419 order = 1
420 if nlabels > 0:
421 namereln = NAMERELN_COMMONANCESTOR
422 return (namereln, order, nlabels)
423 nlabels += 1
424 order = ldiff
425 if ldiff < 0:
426 namereln = NAMERELN_SUPERDOMAIN
427 elif ldiff > 0:
428 namereln = NAMERELN_SUBDOMAIN
429 else:
430 namereln = NAMERELN_EQUAL
431 return (namereln, order, nlabels)
432
433 - def is_subdomain(self, other):
434 """Is self a subdomain of other?
435
436 The notion of subdomain includes equality.
437 @rtype: bool
438 """
439
440 (nr, o, nl) = self.fullcompare(other)
441 if nr == NAMERELN_SUBDOMAIN or nr == NAMERELN_EQUAL:
442 return True
443 return False
444
445 - def is_superdomain(self, other):
446 """Is self a superdomain of other?
447
448 The notion of subdomain includes equality.
449 @rtype: bool
450 """
451
452 (nr, o, nl) = self.fullcompare(other)
453 if nr == NAMERELN_SUPERDOMAIN or nr == NAMERELN_EQUAL:
454 return True
455 return False
456
458 """Return a name which is equal to the current name, but is in
459 DNSSEC canonical form.
460 @rtype: dns.name.Name object
461 """
462
463 return Name([x.lower() for x in self.labels])
464
466 if isinstance(other, Name):
467 return self.fullcompare(other)[1] == 0
468 else:
469 return False
470
472 if isinstance(other, Name):
473 return self.fullcompare(other)[1] != 0
474 else:
475 return True
476
478 if isinstance(other, Name):
479 return self.fullcompare(other)[1] < 0
480 else:
481 return NotImplemented
482
484 if isinstance(other, Name):
485 return self.fullcompare(other)[1] <= 0
486 else:
487 return NotImplemented
488
490 if isinstance(other, Name):
491 return self.fullcompare(other)[1] >= 0
492 else:
493 return NotImplemented
494
496 if isinstance(other, Name):
497 return self.fullcompare(other)[1] > 0
498 else:
499 return NotImplemented
500
502 return '<DNS name ' + self.__str__() + '>'
503
506
507 - def to_text(self, omit_final_dot=False):
508 """Convert name to text format.
509 @param omit_final_dot: If True, don't emit the final dot (denoting the
510 root label) for absolute names. The default is False.
511 @rtype: string
512 """
513
514 if len(self.labels) == 0:
515 return maybe_decode(b'@')
516 if len(self.labels) == 1 and self.labels[0] == b'':
517 return maybe_decode(b'.')
518 if omit_final_dot and self.is_absolute():
519 l = self.labels[:-1]
520 else:
521 l = self.labels
522 s = b'.'.join(map(_escapify, l))
523 return maybe_decode(s)
524
525 - def to_unicode(self, omit_final_dot=False, idna_codec=None):
526 """Convert name to Unicode text format.
527
528 IDN ACE labels are converted to Unicode.
529
530 @param omit_final_dot: If True, don't emit the final dot (denoting the
531 root label) for absolute names. The default is False.
532 @type omit_final_dot: bool
533 @param idna_codec: IDNA encoder/decoder. If None, the
534 IDNA_2003_Practical encoder/decoder is used. The IDNA_2003_Practical
535 decoder does not impose any policy, it just decodes punycode, so if
536 you don't want checking for compliance, you can use this decoder for
537 IDNA2008 as well.
538 @type idna_codec: dns.name.IDNA
539 @rtype: string
540 """
541
542 if len(self.labels) == 0:
543 return u'@'
544 if len(self.labels) == 1 and self.labels[0] == b'':
545 return u'.'
546 if omit_final_dot and self.is_absolute():
547 l = self.labels[:-1]
548 else:
549 l = self.labels
550 if idna_codec is None:
551 idna_codec = IDNA_2003_Practical
552 return u'.'.join([idna_codec.decode(x) for x in l])
553
555 """Convert name to a format suitable for digesting in hashes.
556
557 The name is canonicalized and converted to uncompressed wire format.
558
559 @param origin: If the name is relative and origin is not None, then
560 origin will be appended to it.
561 @type origin: dns.name.Name object
562 @raises NeedAbsoluteNameOrOrigin: All names in wire format are
563 absolute. If self is a relative name, then an origin must be supplied;
564 if it is missing, then this exception is raised
565 @rtype: string
566 """
567
568 if not self.is_absolute():
569 if origin is None or not origin.is_absolute():
570 raise NeedAbsoluteNameOrOrigin
571 labels = list(self.labels)
572 labels.extend(list(origin.labels))
573 else:
574 labels = self.labels
575 dlabels = [struct.pack('!B%ds' % len(x), len(x), x.lower())
576 for x in labels]
577 return b''.join(dlabels)
578
579 - def to_wire(self, file=None, compress=None, origin=None):
580 """Convert name to wire format, possibly compressing it.
581
582 @param file: the file where the name is emitted (typically
583 a BytesIO file). If None, a string containing the wire name
584 will be returned.
585 @type file: file or None
586 @param compress: The compression table. If None (the default) names
587 will not be compressed.
588 @type compress: dict
589 @param origin: If the name is relative and origin is not None, then
590 origin will be appended to it.
591 @type origin: dns.name.Name object
592 @raises NeedAbsoluteNameOrOrigin: All names in wire format are
593 absolute. If self is a relative name, then an origin must be supplied;
594 if it is missing, then this exception is raised
595 """
596
597 if file is None:
598 file = BytesIO()
599 want_return = True
600 else:
601 want_return = False
602
603 if not self.is_absolute():
604 if origin is None or not origin.is_absolute():
605 raise NeedAbsoluteNameOrOrigin
606 labels = list(self.labels)
607 labels.extend(list(origin.labels))
608 else:
609 labels = self.labels
610 i = 0
611 for label in labels:
612 n = Name(labels[i:])
613 i += 1
614 if compress is not None:
615 pos = compress.get(n)
616 else:
617 pos = None
618 if pos is not None:
619 value = 0xc000 + pos
620 s = struct.pack('!H', value)
621 file.write(s)
622 break
623 else:
624 if compress is not None and len(n) > 1:
625 pos = file.tell()
626 if pos <= 0x3fff:
627 compress[n] = pos
628 l = len(label)
629 file.write(struct.pack('!B', l))
630 if l > 0:
631 file.write(label)
632 if want_return:
633 return file.getvalue()
634
636 """The length of the name (in labels).
637 @rtype: int
638 """
639
640 return len(self.labels)
641
644
647
650
652 """Split a name into a prefix and suffix at depth.
653
654 @param depth: the number of labels in the suffix
655 @type depth: int
656 @raises ValueError: the depth was not >= 0 and <= the length of the
657 name.
658 @returns: the tuple (prefix, suffix)
659 @rtype: tuple
660 """
661
662 l = len(self.labels)
663 if depth == 0:
664 return (self, dns.name.empty)
665 elif depth == l:
666 return (dns.name.empty, self)
667 elif depth < 0 or depth > l:
668 raise ValueError(
669 'depth must be >= 0 and <= the length of the name')
670 return (Name(self[: -depth]), Name(self[-depth:]))
671
673 """Return a new name which is the concatenation of self and other.
674 @rtype: dns.name.Name object
675 @raises AbsoluteConcatenation: self is absolute and other is
676 not the empty name
677 """
678
679 if self.is_absolute() and len(other) > 0:
680 raise AbsoluteConcatenation
681 labels = list(self.labels)
682 labels.extend(list(other.labels))
683 return Name(labels)
684
686 """If self is a subdomain of origin, return a new name which is self
687 relative to origin. Otherwise return self.
688 @rtype: dns.name.Name object
689 """
690
691 if origin is not None and self.is_subdomain(origin):
692 return Name(self[: -len(origin)])
693 else:
694 return self
695
697 """If self is a relative name, return a new name which is the
698 concatenation of self and origin. Otherwise return self.
699 @rtype: dns.name.Name object
700 """
701
702 if not self.is_absolute():
703 return self.concatenate(origin)
704 else:
705 return self
706
708 """Return a name with the relativity desired by the caller. If
709 origin is None, then self is returned. Otherwise, if
710 relativize is true the name is relativized, and if relativize is
711 false the name is derelativized.
712 @rtype: dns.name.Name object
713 """
714
715 if origin:
716 if relativize:
717 return self.relativize(origin)
718 else:
719 return self.derelativize(origin)
720 else:
721 return self
722
724 """Return the parent of the name.
725 @rtype: dns.name.Name object
726 @raises NoParent: the name is either the root name or the empty name,
727 and thus has no parent.
728 """
729 if self == root or self == empty:
730 raise NoParent
731 return Name(self.labels[1:])
732
733 root = Name([b''])
734 empty = Name([])
735
736
738 """Convert unicode text into a Name object.
739
740 Labels are encoded in IDN ACE form.
741
742 @param text: The text to convert into a name.
743 @type text: Unicode string
744 @param origin: The origin to append to non-absolute names.
745 @type origin: dns.name.Name
746 @param idna_codec: IDNA encoder/decoder. If None, the default IDNA 2003
747 encoder/decoder is used.
748 @type idna_codec: dns.name.IDNA
749 @rtype: dns.name.Name object
750 """
751
752 if not isinstance(text, text_type):
753 raise ValueError("input to from_unicode() must be a unicode string")
754 if not (origin is None or isinstance(origin, Name)):
755 raise ValueError("origin must be a Name or None")
756 labels = []
757 label = u''
758 escaping = False
759 edigits = 0
760 total = 0
761 if idna_codec is None:
762 idna_codec = IDNA_2003
763 if text == u'@':
764 text = u''
765 if text:
766 if text == u'.':
767 return Name([b''])
768 for c in text:
769 if escaping:
770 if edigits == 0:
771 if c.isdigit():
772 total = int(c)
773 edigits += 1
774 else:
775 label += c
776 escaping = False
777 else:
778 if not c.isdigit():
779 raise BadEscape
780 total *= 10
781 total += int(c)
782 edigits += 1
783 if edigits == 3:
784 escaping = False
785 label += unichr(total)
786 elif c in [u'.', u'\u3002', u'\uff0e', u'\uff61']:
787 if len(label) == 0:
788 raise EmptyLabel
789 labels.append(idna_codec.encode(label))
790 label = u''
791 elif c == u'\\':
792 escaping = True
793 edigits = 0
794 total = 0
795 else:
796 label += c
797 if escaping:
798 raise BadEscape
799 if len(label) > 0:
800 labels.append(idna_codec.encode(label))
801 else:
802 labels.append(b'')
803
804 if (len(labels) == 0 or labels[-1] != b'') and origin is not None:
805 labels.extend(list(origin.labels))
806 return Name(labels)
807
808
809 -def from_text(text, origin=root, idna_codec=None):
810 """Convert text into a Name object.
811
812 @param text: The text to convert into a name.
813 @type text: string
814 @param origin: The origin to append to non-absolute names.
815 @type origin: dns.name.Name
816 @param idna_codec: IDNA encoder/decoder. If None, the default IDNA 2003
817 encoder/decoder is used.
818 @type idna_codec: dns.name.IDNA
819 @rtype: dns.name.Name object
820 """
821
822 if isinstance(text, text_type):
823 return from_unicode(text, origin, idna_codec)
824 if not isinstance(text, binary_type):
825 raise ValueError("input to from_text() must be a string")
826 if not (origin is None or isinstance(origin, Name)):
827 raise ValueError("origin must be a Name or None")
828 labels = []
829 label = b''
830 escaping = False
831 edigits = 0
832 total = 0
833 if text == b'@':
834 text = b''
835 if text:
836 if text == b'.':
837 return Name([b''])
838 for c in bytearray(text):
839 byte_ = struct.pack('!B', c)
840 if escaping:
841 if edigits == 0:
842 if byte_.isdigit():
843 total = int(byte_)
844 edigits += 1
845 else:
846 label += byte_
847 escaping = False
848 else:
849 if not byte_.isdigit():
850 raise BadEscape
851 total *= 10
852 total += int(byte_)
853 edigits += 1
854 if edigits == 3:
855 escaping = False
856 label += struct.pack('!B', total)
857 elif byte_ == b'.':
858 if len(label) == 0:
859 raise EmptyLabel
860 labels.append(label)
861 label = b''
862 elif byte_ == b'\\':
863 escaping = True
864 edigits = 0
865 total = 0
866 else:
867 label += byte_
868 if escaping:
869 raise BadEscape
870 if len(label) > 0:
871 labels.append(label)
872 else:
873 labels.append(b'')
874 if (len(labels) == 0 or labels[-1] != b'') and origin is not None:
875 labels.extend(list(origin.labels))
876 return Name(labels)
877
878
880 """Convert possibly compressed wire format into a Name.
881 @param message: the entire DNS message
882 @type message: string
883 @param current: the offset of the beginning of the name from the start
884 of the message
885 @type current: int
886 @raises dns.name.BadPointer: a compression pointer did not point backwards
887 in the message
888 @raises dns.name.BadLabelType: an invalid label type was encountered.
889 @returns: a tuple consisting of the name that was read and the number
890 of bytes of the wire format message which were consumed reading it
891 @rtype: (dns.name.Name object, int) tuple
892 """
893
894 if not isinstance(message, binary_type):
895 raise ValueError("input to from_wire() must be a byte string")
896 message = dns.wiredata.maybe_wrap(message)
897 labels = []
898 biggest_pointer = current
899 hops = 0
900 count = message[current]
901 current += 1
902 cused = 1
903 while count != 0:
904 if count < 64:
905 labels.append(message[current: current + count].unwrap())
906 current += count
907 if hops == 0:
908 cused += count
909 elif count >= 192:
910 current = (count & 0x3f) * 256 + message[current]
911 if hops == 0:
912 cused += 1
913 if current >= biggest_pointer:
914 raise BadPointer
915 biggest_pointer = current
916 hops += 1
917 else:
918 raise BadLabelType
919 count = message[current]
920 current += 1
921 if hops == 0:
922 cused += 1
923 labels.append('')
924 return (Name(labels), cused)
925