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