1# -*- coding: utf-8
2# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
3
4# Copyright (C) 2003-2017 Nominum, Inc.
5#
6# Permission to use, copy, modify, and distribute this software and its
7# documentation for any purpose with or without fee is hereby granted,
8# provided that the above copyright notice and this permission notice
9# appear in all copies.
10#
11# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
12# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
14# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
19from typing import Dict # pylint: disable=unused-import
20import copy
21import operator
22import pickle
23import unittest
24
25from io import BytesIO
26
27import dns.name
28import dns.reversename
29import dns.e164
30
31# pylint: disable=line-too-long,unsupported-assignment-operation
32
33
34class NameTestCase(unittest.TestCase):
35    def setUp(self):
36        self.origin = dns.name.from_text('example.')
37
38    def testFromTextRel1(self):
39        n = dns.name.from_text('foo.bar')
40        self.assertEqual(n.labels, (b'foo', b'bar', b''))
41
42    def testFromTextRel2(self):
43        n = dns.name.from_text('foo.bar', origin=self.origin)
44        self.assertEqual(n.labels, (b'foo', b'bar', b'example', b''))
45
46    def testFromTextRel3(self):
47        n = dns.name.from_text('foo.bar', origin=None)
48        self.assertEqual(n.labels, (b'foo', b'bar'))
49
50    def testFromTextRel4(self):
51        n = dns.name.from_text('@', origin=None)
52        self.assertEqual(n, dns.name.empty)
53
54    def testFromTextRel5(self):
55        n = dns.name.from_text('@', origin=self.origin)
56        self.assertEqual(n, self.origin)
57
58    def testFromTextAbs1(self):
59        n = dns.name.from_text('foo.bar.')
60        self.assertEqual(n.labels, (b'foo', b'bar', b''))
61
62    def testTortureFromText(self):
63        good = [
64            br'.',
65            br'a',
66            br'a.',
67            br'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
68            br'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
69            br'\000.\008.\010.\032.\046.\092.\099.\255',
70            br'\\',
71            br'\..\.',
72            br'\\.\\',
73            br'!"#%&/()=+-',
74            br'\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255.\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255.\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255.\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255',
75            ]
76        bad = [
77            br'..',
78            br'.a',
79            br'\\..',
80            b'\\',		# yes, we don't want the 'r' prefix!
81            br'\0',
82            br'\00',
83            br'\00Z',
84            br'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
85            br'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
86            br'\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255.\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255.\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255.\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255',
87            ]
88        for t in good:
89            try:
90                dns.name.from_text(t)
91            except Exception:
92                self.fail("good test '%s' raised an exception" % t)
93        for t in bad:
94            caught = False
95            try:
96                dns.name.from_text(t)
97            except Exception:
98                caught = True
99            if not caught:
100                self.fail("bad test '%s' did not raise an exception" % t)
101
102    def testImmutable1(self):
103        def bad():
104            self.origin.labels = ()
105        self.assertRaises(TypeError, bad)
106
107    def testImmutable2(self):
108        def bad():
109            self.origin.labels[0] = 'foo'
110        self.assertRaises(TypeError, bad)
111
112    def testAbs1(self):
113        self.assertTrue(dns.name.root.is_absolute())
114
115    def testAbs2(self):
116        self.assertFalse(dns.name.empty.is_absolute())
117
118    def testAbs3(self):
119        self.assertTrue(self.origin.is_absolute())
120
121    def testAbs4(self):
122        n = dns.name.from_text('foo', origin=None)
123        self.assertFalse(n.is_absolute())
124
125    def testWild1(self):
126        n = dns.name.from_text('*.foo', origin=None)
127        self.assertTrue(n.is_wild())
128
129    def testWild2(self):
130        n = dns.name.from_text('*a.foo', origin=None)
131        self.assertFalse(n.is_wild())
132
133    def testWild3(self):
134        n = dns.name.from_text('a.*.foo', origin=None)
135        self.assertFalse(n.is_wild())
136
137    def testWild4(self):
138        self.assertFalse(dns.name.root.is_wild())
139
140    def testWild5(self):
141        self.assertFalse(dns.name.empty.is_wild())
142
143    def testHash1(self):
144        n1 = dns.name.from_text('fOo.COM')
145        n2 = dns.name.from_text('foo.com')
146        self.assertEqual(hash(n1), hash(n2))
147
148    def testCompare1(self):
149        n1 = dns.name.from_text('a')
150        n2 = dns.name.from_text('b')
151        self.assertLess(n1, n2)
152        self.assertLessEqual(n1, n2)
153        self.assertGreater(n2, n1)
154        self.assertGreaterEqual(n2, n1)
155
156    def testCompare2(self):
157        n1 = dns.name.from_text('')
158        n2 = dns.name.from_text('b')
159        self.assertLess(n1, n2)
160        self.assertLessEqual(n1, n2)
161        self.assertGreater(n2, n1)
162        self.assertGreaterEqual(n2, n1)
163
164    def testCompare3(self):
165        self.assertLess(dns.name.empty, dns.name.root)
166        self.assertGreater(dns.name.root, dns.name.empty)
167
168    def testCompare4(self):
169        self.assertNotEqual(dns.name.root, 1)
170
171    def testSubdomain1(self):
172        self.assertFalse(dns.name.empty.is_subdomain(dns.name.root))
173
174    def testSubdomain2(self):
175        self.assertFalse(dns.name.root.is_subdomain(dns.name.empty))
176
177    def testSubdomain3(self):
178        n = dns.name.from_text('foo', origin=self.origin)
179        self.assertTrue(n.is_subdomain(self.origin))
180
181    def testSubdomain4(self):
182        n = dns.name.from_text('foo', origin=self.origin)
183        self.assertTrue(n.is_subdomain(dns.name.root))
184
185    def testSubdomain5(self):
186        n = dns.name.from_text('foo', origin=self.origin)
187        self.assertTrue(n.is_subdomain(n))
188
189    def testSuperdomain1(self):
190        self.assertFalse(dns.name.empty.is_superdomain(dns.name.root))
191
192    def testSuperdomain2(self):
193        self.assertFalse(dns.name.root.is_superdomain(dns.name.empty))
194
195    def testSuperdomain3(self):
196        n = dns.name.from_text('foo', origin=self.origin)
197        self.assertTrue(self.origin.is_superdomain(n))
198
199    def testSuperdomain4(self):
200        n = dns.name.from_text('foo', origin=self.origin)
201        self.assertTrue(dns.name.root.is_superdomain(n))
202
203    def testSuperdomain5(self):
204        n = dns.name.from_text('foo', origin=self.origin)
205        self.assertTrue(n.is_superdomain(n))
206
207    def testCanonicalize1(self):
208        n = dns.name.from_text('FOO.bar', origin=self.origin)
209        c = n.canonicalize()
210        self.assertEqual(c.labels, (b'foo', b'bar', b'example', b''))
211
212    def testToText1(self):
213        n = dns.name.from_text('FOO.bar', origin=self.origin)
214        t = n.to_text()
215        self.assertEqual(t, 'FOO.bar.example.')
216
217    def testToText2(self):
218        n = dns.name.from_text('FOO.bar', origin=self.origin)
219        t = n.to_text(True)
220        self.assertEqual(t, 'FOO.bar.example')
221
222    def testToText3(self):
223        n = dns.name.from_text('FOO.bar', origin=None)
224        t = n.to_text()
225        self.assertEqual(t, 'FOO.bar')
226
227    def testToText4(self):
228        t = dns.name.empty.to_text()
229        self.assertEqual(t, '@')
230
231    def testToText5(self):
232        t = dns.name.root.to_text()
233        self.assertEqual(t, '.')
234
235    def testToText6(self):
236        n = dns.name.from_text('FOO bar', origin=None)
237        t = n.to_text()
238        self.assertEqual(t, r'FOO\032bar')
239
240    def testToText7(self):
241        n = dns.name.from_text(r'FOO\.bar', origin=None)
242        t = n.to_text()
243        self.assertEqual(t, r'FOO\.bar')
244
245    def testToText8(self):
246        n = dns.name.from_text(r'\070OO\.bar', origin=None)
247        t = n.to_text()
248        self.assertEqual(t, r'FOO\.bar')
249
250    def testToText9(self):
251        n = dns.name.from_text('FOO bar', origin=None)
252        t = n.to_unicode()
253        self.assertEqual(t, 'FOO\\032bar')
254
255    def testToText10(self):
256        t = dns.name.empty.to_unicode()
257        self.assertEqual(t, '@')
258
259    def testToText11(self):
260        t = dns.name.root.to_unicode()
261        self.assertEqual(t, '.')
262
263    def testToText12(self):
264        n = dns.name.from_text(r'a\.b.c')
265        t = n.to_unicode()
266        self.assertEqual(t, r'a\.b.c.')
267
268    def testToText13(self):
269        n = dns.name.from_text(r'\150\151\152\153\154\155\156\157\158\159.')
270        t = n.to_text()
271        self.assertEqual(t, r'\150\151\152\153\154\155\156\157\158\159.')
272
273    def testToText14(self):
274        # Something that didn't start as unicode should go to escapes and not
275        # raise due to interpreting arbitrary binary DNS labels as UTF-8.
276        n = dns.name.from_text(r'\150\151\152\153\154\155\156\157\158\159.')
277        t = n.to_unicode()
278        self.assertEqual(t, r'\150\151\152\153\154\155\156\157\158\159.')
279
280    def testSlice1(self):
281        n = dns.name.from_text(r'a.b.c.', origin=None)
282        s = n[:]
283        self.assertEqual(s, (b'a', b'b', b'c', b''))
284
285    def testSlice2(self):
286        n = dns.name.from_text(r'a.b.c.', origin=None)
287        s = n[:2]
288        self.assertEqual(s, (b'a', b'b'))
289
290    def testSlice3(self):
291        n = dns.name.from_text(r'a.b.c.', origin=None)
292        s = n[2:]
293        self.assertEqual(s, (b'c', b''))
294
295    def testEmptyLabel1(self):
296        def bad():
297            dns.name.Name(['a', '', 'b'])
298        self.assertRaises(dns.name.EmptyLabel, bad)
299
300    def testEmptyLabel2(self):
301        def bad():
302            dns.name.Name(['', 'b'])
303        self.assertRaises(dns.name.EmptyLabel, bad)
304
305    def testEmptyLabel3(self):
306        n = dns.name.Name(['b', ''])
307        self.assertTrue(n)
308
309    def testLongLabel(self):
310        n = dns.name.Name(['a' * 63])
311        self.assertTrue(n)
312
313    def testLabelTooLong(self):
314        def bad():
315            dns.name.Name(['a' * 64, 'b'])
316        self.assertRaises(dns.name.LabelTooLong, bad)
317
318    def testLongName(self):
319        n = dns.name.Name(['a' * 63, 'a' * 63, 'a' * 63, 'a' * 62])
320        self.assertTrue(n)
321
322    def testNameTooLong(self):
323        def bad():
324            dns.name.Name(['a' * 63, 'a' * 63, 'a' * 63, 'a' * 63])
325        self.assertRaises(dns.name.NameTooLong, bad)
326
327    def testConcat1(self):
328        n1 = dns.name.Name(['a', 'b'])
329        n2 = dns.name.Name(['c', 'd'])
330        e = dns.name.Name(['a', 'b', 'c', 'd'])
331        r = n1 + n2
332        self.assertEqual(r, e)
333
334    def testConcat2(self):
335        n1 = dns.name.Name(['a', 'b'])
336        n2 = dns.name.Name([])
337        e = dns.name.Name(['a', 'b'])
338        r = n1 + n2
339        self.assertEqual(r, e)
340
341    def testConcat3(self):
342        n1 = dns.name.Name([])
343        n2 = dns.name.Name(['a', 'b'])
344        e = dns.name.Name(['a', 'b'])
345        r = n1 + n2
346        self.assertEqual(r, e)
347
348    def testConcat4(self):
349        n1 = dns.name.Name(['a', 'b', ''])
350        n2 = dns.name.Name([])
351        e = dns.name.Name(['a', 'b', ''])
352        r = n1 + n2
353        self.assertEqual(r, e)
354
355    def testConcat5(self):
356        n1 = dns.name.Name(['a', 'b'])
357        n2 = dns.name.Name(['c', ''])
358        e = dns.name.Name(['a', 'b', 'c', ''])
359        r = n1 + n2
360        self.assertEqual(r, e)
361
362    def testConcat6(self):
363        def bad():
364            n1 = dns.name.Name(['a', 'b', ''])
365            n2 = dns.name.Name(['c'])
366            return n1 + n2
367        self.assertRaises(dns.name.AbsoluteConcatenation, bad)
368
369    def testBadEscape(self):
370        def bad():
371            n = dns.name.from_text(r'a.b\0q1.c.')
372        self.assertRaises(dns.name.BadEscape, bad)
373
374    def testDigestable1(self):
375        n = dns.name.from_text('FOO.bar')
376        d = n.to_digestable()
377        self.assertEqual(d, b'\x03foo\x03bar\x00')
378
379    def testDigestable2(self):
380        n1 = dns.name.from_text('FOO.bar')
381        n2 = dns.name.from_text('foo.BAR.')
382        d1 = n1.to_digestable()
383        d2 = n2.to_digestable()
384        self.assertEqual(d1, d2)
385
386    def testDigestable3(self):
387        d = dns.name.root.to_digestable()
388        self.assertEqual(d, b'\x00')
389
390    def testDigestable4(self):
391        n = dns.name.from_text('FOO.bar', None)
392        d = n.to_digestable(dns.name.root)
393        self.assertEqual(d, b'\x03foo\x03bar\x00')
394
395    def testBadDigestable(self):
396        def bad():
397            n = dns.name.from_text('FOO.bar', None)
398            n.to_digestable()
399        self.assertRaises(dns.name.NeedAbsoluteNameOrOrigin, bad)
400
401    def testToWire1(self):
402        n = dns.name.from_text('FOO.bar')
403        f = BytesIO()
404        compress = {} # type: Dict[dns.name.Name,int]
405        n.to_wire(f, compress)
406        self.assertEqual(f.getvalue(), b'\x03FOO\x03bar\x00')
407
408    def testToWire2(self):
409        n = dns.name.from_text('FOO.bar')
410        f = BytesIO()
411        compress = {} # type: Dict[dns.name.Name,int]
412        n.to_wire(f, compress)
413        n.to_wire(f, compress)
414        self.assertEqual(f.getvalue(), b'\x03FOO\x03bar\x00\xc0\x00')
415
416    def testToWire3(self):
417        n1 = dns.name.from_text('FOO.bar')
418        n2 = dns.name.from_text('foo.bar')
419        f = BytesIO()
420        compress = {} # type: Dict[dns.name.Name,int]
421        n1.to_wire(f, compress)
422        n2.to_wire(f, compress)
423        self.assertEqual(f.getvalue(), b'\x03FOO\x03bar\x00\xc0\x00')
424
425    def testToWire4(self):
426        n1 = dns.name.from_text('FOO.bar')
427        n2 = dns.name.from_text('a.foo.bar')
428        f = BytesIO()
429        compress = {} # type: Dict[dns.name.Name,int]
430        n1.to_wire(f, compress)
431        n2.to_wire(f, compress)
432        self.assertEqual(f.getvalue(), b'\x03FOO\x03bar\x00\x01\x61\xc0\x00')
433
434    def testToWire5(self):
435        n1 = dns.name.from_text('FOO.bar')
436        n2 = dns.name.from_text('a.foo.bar')
437        f = BytesIO()
438        compress = {} # type: Dict[dns.name.Name,int]
439        n1.to_wire(f, compress)
440        n2.to_wire(f, None)
441        self.assertEqual(f.getvalue(),
442                         b'\x03FOO\x03bar\x00\x01\x61\x03foo\x03bar\x00')
443
444    def testToWire6(self):
445        n = dns.name.from_text('FOO.bar')
446        v = n.to_wire()
447        self.assertEqual(v, b'\x03FOO\x03bar\x00')
448
449    def testToWireRelativeNameWithOrigin(self):
450        n = dns.name.from_text('FOO', None)
451        o = dns.name.from_text('bar')
452        v = n.to_wire(origin=o)
453        self.assertEqual(v, b'\x03FOO\x03bar\x00')
454
455    def testToWireRelativeNameWithoutOrigin(self):
456        n = dns.name.from_text('FOO', None)
457        def bad():
458            v = n.to_wire()
459        self.assertRaises(dns.name.NeedAbsoluteNameOrOrigin, bad)
460
461    def testBadToWire(self):
462        def bad():
463            n = dns.name.from_text('FOO.bar', None)
464            f = BytesIO()
465            compress = {} # type: Dict[dns.name.Name,int]
466            n.to_wire(f, compress)
467        self.assertRaises(dns.name.NeedAbsoluteNameOrOrigin, bad)
468
469    def testGiantCompressionTable(self):
470        # Only the first 16KiB of a message can have compression pointers.
471        f = BytesIO()
472        compress = {}  # type: Dict[dns.name.Name,int]
473        # exactly 16 bytes encoded
474        n = dns.name.from_text('0000000000.com.')
475        n.to_wire(f, compress)
476        # There are now two entries in the compression table (for the full
477        # name, and for the com. suffix.
478        self.assertEqual(len(compress), 2)
479        for i in range(1023):
480            # exactly 16 bytes encoded with compression
481            n = dns.name.from_text(f'{i:013d}.com')
482            n.to_wire(f, compress)
483        # There are now 1025 entries in the compression table with
484        # the last entry at offset 16368.
485        self.assertEqual(len(compress), 1025)
486        self.assertEqual(compress[n], 16368)
487        # Adding another name should not increase the size of the compression
488        # table, as the pointer would be at offset 16384, which is too big.
489        n = dns.name.from_text('toobig.com.')
490        n.to_wire(f, compress)
491        self.assertEqual(len(compress), 1025)
492
493    def testSplit1(self):
494        n = dns.name.from_text('foo.bar.')
495        (prefix, suffix) = n.split(2)
496        ep = dns.name.from_text('foo', None)
497        es = dns.name.from_text('bar.', None)
498        self.assertEqual(prefix, ep)
499        self.assertEqual(suffix, es)
500
501    def testSplit2(self):
502        n = dns.name.from_text('foo.bar.')
503        (prefix, suffix) = n.split(1)
504        ep = dns.name.from_text('foo.bar', None)
505        es = dns.name.from_text('.', None)
506        self.assertEqual(prefix, ep)
507        self.assertEqual(suffix, es)
508
509    def testSplit3(self):
510        n = dns.name.from_text('foo.bar.')
511        (prefix, suffix) = n.split(0)
512        ep = dns.name.from_text('foo.bar.', None)
513        es = dns.name.from_text('', None)
514        self.assertEqual(prefix, ep)
515        self.assertEqual(suffix, es)
516
517    def testSplit4(self):
518        n = dns.name.from_text('foo.bar.')
519        (prefix, suffix) = n.split(3)
520        ep = dns.name.from_text('', None)
521        es = dns.name.from_text('foo.bar.', None)
522        self.assertEqual(prefix, ep)
523        self.assertEqual(suffix, es)
524
525    def testBadSplit1(self):
526        def bad():
527            n = dns.name.from_text('foo.bar.')
528            n.split(-1)
529        self.assertRaises(ValueError, bad)
530
531    def testBadSplit2(self):
532        def bad():
533            n = dns.name.from_text('foo.bar.')
534            n.split(4)
535        self.assertRaises(ValueError, bad)
536
537    def testRelativize1(self):
538        n = dns.name.from_text('a.foo.bar.', None)
539        o = dns.name.from_text('bar.', None)
540        e = dns.name.from_text('a.foo', None)
541        self.assertEqual(n.relativize(o), e)
542
543    def testRelativize2(self):
544        n = dns.name.from_text('a.foo.bar.', None)
545        o = n
546        e = dns.name.empty
547        self.assertEqual(n.relativize(o), e)
548
549    def testRelativize3(self):
550        n = dns.name.from_text('a.foo.bar.', None)
551        o = dns.name.from_text('blaz.', None)
552        e = n
553        self.assertEqual(n.relativize(o), e)
554
555    def testRelativize4(self):
556        n = dns.name.from_text('a.foo', None)
557        o = dns.name.root
558        e = n
559        self.assertEqual(n.relativize(o), e)
560
561    def testDerelativize1(self):
562        n = dns.name.from_text('a.foo', None)
563        o = dns.name.from_text('bar.', None)
564        e = dns.name.from_text('a.foo.bar.', None)
565        self.assertEqual(n.derelativize(o), e)
566
567    def testDerelativize2(self):
568        n = dns.name.empty
569        o = dns.name.from_text('a.foo.bar.', None)
570        e = o
571        self.assertEqual(n.derelativize(o), e)
572
573    def testDerelativize3(self):
574        n = dns.name.from_text('a.foo.bar.', None)
575        o = dns.name.from_text('blaz.', None)
576        e = n
577        self.assertEqual(n.derelativize(o), e)
578
579    def testChooseRelativity1(self):
580        n = dns.name.from_text('a.foo.bar.', None)
581        o = dns.name.from_text('bar.', None)
582        e = dns.name.from_text('a.foo', None)
583        self.assertEqual(n.choose_relativity(o, True), e)
584
585    def testChooseRelativity2(self):
586        n = dns.name.from_text('a.foo.bar.', None)
587        o = dns.name.from_text('bar.', None)
588        e = n
589        self.assertEqual(n.choose_relativity(o, False), e)
590
591    def testChooseRelativity3(self):
592        n = dns.name.from_text('a.foo', None)
593        o = dns.name.from_text('bar.', None)
594        e = dns.name.from_text('a.foo.bar.', None)
595        self.assertEqual(n.choose_relativity(o, False), e)
596
597    def testChooseRelativity4(self):
598        n = dns.name.from_text('a.foo', None)
599        o = None
600        e = n
601        self.assertEqual(n.choose_relativity(o, True), e)
602
603    def testChooseRelativity5(self):
604        n = dns.name.from_text('a.foo', None)
605        o = None
606        e = n
607        self.assertEqual(n.choose_relativity(o, False), e)
608
609    def testChooseRelativity6(self):
610        n = dns.name.from_text('a.foo.', None)
611        o = None
612        e = n
613        self.assertEqual(n.choose_relativity(o, True), e)
614
615    def testChooseRelativity7(self):
616        n = dns.name.from_text('a.foo.', None)
617        o = None
618        e = n
619        self.assertEqual(n.choose_relativity(o, False), e)
620
621    def testFromWire1(self):
622        w = b'\x03foo\x00\xc0\x00'
623        (n1, cused1) = dns.name.from_wire(w, 0)
624        (n2, cused2) = dns.name.from_wire(w, cused1)
625        en1 = dns.name.from_text('foo.')
626        en2 = en1
627        ecused1 = 5
628        ecused2 = 2
629        self.assertEqual(n1, en1)
630        self.assertEqual(cused1, ecused1)
631        self.assertEqual(n2, en2)
632        self.assertEqual(cused2, ecused2)
633
634    def testFromWire2(self):
635        w = b'\x03foo\x00\x01a\xc0\x00\x01b\xc0\x05'
636        current = 0
637        (n1, cused1) = dns.name.from_wire(w, current)
638        current += cused1
639        (n2, cused2) = dns.name.from_wire(w, current)
640        current += cused2
641        (n3, cused3) = dns.name.from_wire(w, current)
642        en1 = dns.name.from_text('foo.')
643        en2 = dns.name.from_text('a.foo.')
644        en3 = dns.name.from_text('b.a.foo.')
645        ecused1 = 5
646        ecused2 = 4
647        ecused3 = 4
648        self.assertEqual(n1, en1)
649        self.assertEqual(cused1, ecused1)
650        self.assertEqual(n2, en2)
651        self.assertEqual(cused2, ecused2)
652        self.assertEqual(n3, en3)
653        self.assertEqual(cused3, ecused3)
654
655    def testBadFromWire1(self):
656        def bad():
657            w = b'\x03foo\xc0\x04'
658            dns.name.from_wire(w, 0)
659        self.assertRaises(dns.name.BadPointer, bad)
660
661    def testBadFromWire2(self):
662        def bad():
663            w = b'\x03foo\xc0\x05'
664            dns.name.from_wire(w, 0)
665        self.assertRaises(dns.name.BadPointer, bad)
666
667    def testBadFromWire3(self):
668        def bad():
669            w = b'\xbffoo'
670            dns.name.from_wire(w, 0)
671        self.assertRaises(dns.name.BadLabelType, bad)
672
673    def testBadFromWire4(self):
674        def bad():
675            w = b'\x41foo'
676            dns.name.from_wire(w, 0)
677        self.assertRaises(dns.name.BadLabelType, bad)
678
679    def testParent1(self):
680        n = dns.name.from_text('foo.bar.')
681        self.assertEqual(n.parent(), dns.name.from_text('bar.'))
682        self.assertEqual(n.parent().parent(), dns.name.root)
683
684    def testParent2(self):
685        n = dns.name.from_text('foo.bar', None)
686        self.assertEqual(n.parent(), dns.name.from_text('bar', None))
687        self.assertEqual(n.parent().parent(), dns.name.empty)
688
689    def testParent3(self):
690        def bad():
691            n = dns.name.root
692            n.parent()
693        self.assertRaises(dns.name.NoParent, bad)
694
695    def testParent4(self):
696        def bad():
697            n = dns.name.empty
698            n.parent()
699        self.assertRaises(dns.name.NoParent, bad)
700
701    def testFromUnicode1(self):
702        n = dns.name.from_text('foo.bar')
703        self.assertEqual(n.labels, (b'foo', b'bar', b''))
704
705    def testFromUnicode2(self):
706        n = dns.name.from_text('foo\u1234bar.bar')
707        self.assertEqual(n.labels, (b'xn--foobar-r5z', b'bar', b''))
708
709    def testFromUnicodeAlternateDot1(self):
710        n = dns.name.from_text('foo\u3002bar')
711        self.assertEqual(n.labels, (b'foo', b'bar', b''))
712
713    def testFromUnicodeAlternateDot2(self):
714        n = dns.name.from_text('foo\uff0ebar')
715        self.assertEqual(n.labels, (b'foo', b'bar', b''))
716
717    def testFromUnicodeAlternateDot3(self):
718        n = dns.name.from_text('foo\uff61bar')
719        self.assertEqual(n.labels, (b'foo', b'bar', b''))
720
721    def testFromUnicodeRoot(self):
722        n = dns.name.from_text('.')
723        self.assertEqual(n.labels, (b'',))
724
725    def testFromUnicodeAlternateRoot1(self):
726        n = dns.name.from_text('\u3002')
727        self.assertEqual(n.labels, (b'',))
728
729    def testFromUnicodeAlternateRoot2(self):
730        n = dns.name.from_text('\uff0e')
731        self.assertEqual(n.labels, (b'',))
732
733    def testFromUnicodeAlternateRoot3(self):
734        n = dns.name.from_text('\uff61')
735        self.assertEqual(n.labels, (b'', ))
736
737    def testFromUnicodeIDNA2003Explicit(self):
738        t = 'Königsgäßchen'
739        e = dns.name.from_unicode(t, idna_codec=dns.name.IDNA_2003)
740        self.assertEqual(str(e), 'xn--knigsgsschen-lcb0w.')
741
742    def testFromUnicodeIDNA2003Default(self):
743        t = 'Königsgäßchen'
744        e = dns.name.from_unicode(t)
745        self.assertEqual(str(e), 'xn--knigsgsschen-lcb0w.')
746
747    @unittest.skipUnless(dns.name.have_idna_2008,
748                         'Python idna cannot be imported; no IDNA2008')
749    def testFromUnicodeIDNA2008(self):
750        t = 'Königsgäßchen'
751        def bad():
752            codec = dns.name.IDNA_2008_Strict
753            return dns.name.from_unicode(t, idna_codec=codec)
754        self.assertRaises(dns.name.IDNAException, bad)
755        e1 = dns.name.from_unicode(t, idna_codec=dns.name.IDNA_2008)
756        self.assertEqual(str(e1), 'xn--knigsgchen-b4a3dun.')
757        c2 = dns.name.IDNA_2008_Transitional
758        e2 = dns.name.from_unicode(t, idna_codec=c2)
759        self.assertEqual(str(e2), 'xn--knigsgsschen-lcb0w.')
760
761    @unittest.skipUnless(dns.name.have_idna_2008,
762                         'Python idna cannot be imported; no IDNA2008')
763    def testFromUnicodeIDNA2008Mixed(self):
764        # the IDN rules for names are very restrictive, disallowing
765        # practical names like '_sip._tcp.Königsgäßchen'.  Dnspython
766        # has a "practical" mode which permits labels which are purely
767        # ASCII to go straight through, and thus not invalid useful
768        # things in the real world.
769        t = '_sip._tcp.Königsgäßchen'
770        def bad1():
771            codec = dns.name.IDNA_2008_Strict
772            return dns.name.from_unicode(t, idna_codec=codec)
773        def bad2():
774            codec = dns.name.IDNA_2008_UTS_46
775            return dns.name.from_unicode(t, idna_codec=codec)
776        def bad3():
777            codec = dns.name.IDNA_2008_Transitional
778            return dns.name.from_unicode(t, idna_codec=codec)
779        self.assertRaises(dns.name.IDNAException, bad1)
780        self.assertRaises(dns.name.IDNAException, bad2)
781        self.assertRaises(dns.name.IDNAException, bad3)
782        e = dns.name.from_unicode(t,
783                                  idna_codec=dns.name.IDNA_2008_Practical)
784        self.assertEqual(str(e), '_sip._tcp.xn--knigsgchen-b4a3dun.')
785
786    def testFromUnicodeEscapes(self):
787        n = dns.name.from_unicode(r'\097.\098.\099.')
788        t = n.to_unicode()
789        self.assertEqual(t, 'a.b.c.')
790
791    def testToUnicode1(self):
792        n = dns.name.from_text('foo.bar')
793        s = n.to_unicode()
794        self.assertEqual(s, 'foo.bar.')
795
796    def testToUnicode2(self):
797        n = dns.name.from_text('foo\u1234bar.bar')
798        s = n.to_unicode()
799        self.assertEqual(s, 'foo\u1234bar.bar.')
800
801    def testToUnicode3(self):
802        n = dns.name.from_text('foo.bar')
803        s = n.to_unicode()
804        self.assertEqual(s, 'foo.bar.')
805
806    @unittest.skipUnless(dns.name.have_idna_2008,
807                         'Python idna cannot be imported; no IDNA2008')
808    def testToUnicode4(self):
809        n = dns.name.from_text('ドメイン.テスト',
810                               idna_codec=dns.name.IDNA_2008)
811        s = n.to_unicode()
812        self.assertEqual(str(n), 'xn--eckwd4c7c.xn--zckzah.')
813        self.assertEqual(s, 'ドメイン.テスト.')
814
815    @unittest.skipUnless(dns.name.have_idna_2008,
816                         'Python idna cannot be imported; no IDNA2008')
817    def testToUnicode5(self):
818        # Exercise UTS 46 remapping in decode.  This doesn't normally happen
819        # as you can see from us having to instantiate the codec as
820        # transitional with strict decoding, not one of our usual choices.
821        codec = dns.name.IDNA2008Codec(True, True, False, True)
822        n = dns.name.from_text('xn--gro-7ka.com')
823        self.assertEqual(n.to_unicode(idna_codec=codec),
824                         'gross.com.')
825
826    @unittest.skipUnless(dns.name.have_idna_2008,
827                         'Python idna cannot be imported; no IDNA2008')
828    def testToUnicode6(self):
829        # Test strict 2008 decoding without UTS 46
830        n = dns.name.from_text('xn--gro-7ka.com')
831        self.assertEqual(n.to_unicode(idna_codec=dns.name.IDNA_2008_Strict),
832                         'groß.com.')
833
834    def testDefaultDecodeIsJustPunycode(self):
835        # groß.com. in IDNA2008 form, pre-encoded.
836        n = dns.name.from_text('xn--gro-7ka.com')
837        # output using default codec which just decodes the punycode and
838        # doesn't test for IDNA2003 or IDNA2008.
839        self.assertEqual(n.to_unicode(), 'groß.com.')
840
841    def testStrictINDA2003Decode(self):
842        # groß.com. in IDNA2008 form, pre-encoded.
843        n = dns.name.from_text('xn--gro-7ka.com')
844        def bad():
845            # This throws in IDNA2003 because it doesn't "round trip".
846            n.to_unicode(idna_codec=dns.name.IDNA_2003_Strict)
847        self.assertRaises(dns.name.IDNAException, bad)
848
849    def testINDA2008Decode(self):
850        # groß.com. in IDNA2008 form, pre-encoded.
851        n = dns.name.from_text('xn--gro-7ka.com')
852        self.assertEqual(n.to_unicode(idna_codec=dns.name.IDNA_2008),
853                         'groß.com.')
854
855    def testToUnicodeOmitFinalDot(self):
856        # groß.com. in IDNA2008 form, pre-encoded.
857        n = dns.name.from_text('xn--gro-7ka.com')
858        self.assertEqual(n.to_unicode(True, dns.name.IDNA_2008),
859                         'groß.com')
860
861    def testIDNA2003Misc(self):
862        self.assertEqual(dns.name.IDNA_2003.encode(''), b'')
863        self.assertRaises(dns.name.LabelTooLong,
864                          lambda: dns.name.IDNA_2003.encode('x' * 64))
865
866    @unittest.skipUnless(dns.name.have_idna_2008,
867                         'Python idna cannot be imported; no IDNA2008')
868    def testIDNA2008Misc(self):
869        self.assertEqual(dns.name.IDNA_2008.encode(''), b'')
870        self.assertRaises(dns.name.LabelTooLong,
871                          lambda: dns.name.IDNA_2008.encode('x' * 64))
872        self.assertRaises(dns.name.LabelTooLong,
873                          lambda: dns.name.IDNA_2008.encode('groß' + 'x' * 60))
874
875    def testReverseIPv4(self):
876        e = dns.name.from_text('1.0.0.127.in-addr.arpa.')
877        n = dns.reversename.from_address('127.0.0.1')
878        self.assertEqual(e, n)
879
880    def testReverseIPv6(self):
881        e = dns.name.from_text('1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.')
882        n = dns.reversename.from_address(b'::1')
883        self.assertEqual(e, n)
884
885    def testReverseIPv6MappedIpv4(self):
886        e = dns.name.from_text('1.0.0.127.in-addr.arpa.')
887        n = dns.reversename.from_address('::ffff:127.0.0.1')
888        self.assertEqual(e, n)
889
890    def testBadReverseIPv4(self):
891        def bad():
892            dns.reversename.from_address('127.0.foo.1')
893        self.assertRaises(dns.exception.SyntaxError, bad)
894
895    def testBadReverseIPv6(self):
896        def bad():
897            dns.reversename.from_address('::1::1')
898        self.assertRaises(dns.exception.SyntaxError, bad)
899
900    def testReverseIPv4AlternateOrigin(self):
901        e = dns.name.from_text('1.0.0.127.foo.bar.')
902        origin = dns.name.from_text('foo.bar')
903        n = dns.reversename.from_address('127.0.0.1', v4_origin=origin)
904        self.assertEqual(e, n)
905
906    def testReverseIPv6AlternateOrigin(self):
907        e = dns.name.from_text('1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.foo.bar.')
908        origin = dns.name.from_text('foo.bar')
909        n = dns.reversename.from_address(b'::1', v6_origin=origin)
910        self.assertEqual(e, n)
911
912    def testForwardIPv4(self):
913        n = dns.name.from_text('1.0.0.127.in-addr.arpa.')
914        e = '127.0.0.1'
915        text = dns.reversename.to_address(n)
916        self.assertEqual(text, e)
917
918    def testForwardIPv6(self):
919        n = dns.name.from_text('1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.')
920        e = '::1'
921        text = dns.reversename.to_address(n)
922        self.assertEqual(text, e)
923
924    def testForwardIPv4AlternateOrigin(self):
925        n = dns.name.from_text('1.0.0.127.foo.bar.')
926        e = '127.0.0.1'
927        origin = dns.name.from_text('foo.bar')
928        text = dns.reversename.to_address(n, v4_origin=origin)
929        self.assertEqual(text, e)
930
931    def testForwardIPv6AlternateOrigin(self):
932        n = dns.name.from_text('1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.foo.bar.')
933        e = '::1'
934        origin = dns.name.from_text('foo.bar')
935        text = dns.reversename.to_address(n, v6_origin=origin)
936        self.assertEqual(text, e)
937
938    def testUnknownReverseOrigin(self):
939        n = dns.name.from_text('1.2.3.4.unknown.')
940        with self.assertRaises(dns.exception.SyntaxError):
941            dns.reversename.to_address(n)
942
943    def testE164ToEnum(self):
944        text = '+1 650 555 1212'
945        e = dns.name.from_text('2.1.2.1.5.5.5.0.5.6.1.e164.arpa.')
946        n = dns.e164.from_e164(text)
947        self.assertEqual(n, e)
948
949    def testEnumToE164(self):
950        n = dns.name.from_text('2.1.2.1.5.5.5.0.5.6.1.e164.arpa.')
951        e = '+16505551212'
952        text = dns.e164.to_e164(n)
953        self.assertEqual(text, e)
954
955    def testBadEnumToE164(self):
956        n = dns.name.from_text('2.1.2.q.5.5.5.0.5.6.1.e164.arpa.')
957        self.assertRaises(dns.exception.SyntaxError,
958                          lambda: dns.e164.to_e164(n))
959
960    def test_incompatible_relations(self):
961        n1 = dns.name.from_text('example')
962        n2 = 'abc'
963        for oper in [operator.lt, operator.le, operator.ge, operator.gt]:
964            self.assertRaises(TypeError, lambda: oper(n1, n2))
965        self.assertFalse(n1 == n2)
966        self.assertTrue(n1 != n2)
967
968    def testFromUnicodeSimpleEscape(self):
969        n = dns.name.from_unicode(r'a.\b')
970        e = dns.name.from_unicode(r'a.b')
971        self.assertEqual(n, e)
972
973    def testFromUnicodeBadEscape(self):
974        def bad1():
975            n = dns.name.from_unicode(r'a.b\0q1.c.')
976        self.assertRaises(dns.name.BadEscape, bad1)
977        def bad2():
978            n = dns.name.from_unicode(r'a.b\0')
979        self.assertRaises(dns.name.BadEscape, bad2)
980
981    def testFromUnicodeNotString(self):
982        def bad():
983            dns.name.from_unicode(b'123')
984        self.assertRaises(ValueError, bad)
985
986    def testFromUnicodeBadOrigin(self):
987        def bad():
988            dns.name.from_unicode('example', 123)
989        self.assertRaises(ValueError, bad)
990
991    def testFromUnicodeEmptyLabel(self):
992        def bad():
993            dns.name.from_unicode('a..b.example')
994        self.assertRaises(dns.name.EmptyLabel, bad)
995
996    def testFromUnicodeEmptyName(self):
997        self.assertEqual(dns.name.from_unicode('@', None), dns.name.empty)
998
999    def testFromTextNotString(self):
1000        def bad():
1001            dns.name.from_text(123)
1002        self.assertRaises(ValueError, bad)
1003
1004    def testFromTextBadOrigin(self):
1005        def bad():
1006            dns.name.from_text('example', 123)
1007        self.assertRaises(ValueError, bad)
1008
1009    def testFromWireNotBytes(self):
1010        def bad():
1011            dns.name.from_wire(123, 0)
1012        self.assertRaises(ValueError, bad)
1013
1014    def testBadPunycode(self):
1015        c = dns.name.IDNACodec()
1016        with self.assertRaises(dns.name.IDNAException):
1017            c.decode(b'xn--0000h')
1018
1019    def testRootLabel2003StrictDecode(self):
1020        c = dns.name.IDNA_2003_Strict
1021        self.assertEqual(c.decode(b''), '')
1022
1023    @unittest.skipUnless(dns.name.have_idna_2008,
1024                         'Python idna cannot be imported; no IDNA2008')
1025    def testRootLabel2008StrictDecode(self):
1026        c = dns.name.IDNA_2008_Strict
1027        self.assertEqual(c.decode(b''), '')
1028
1029    @unittest.skipUnless(dns.name.have_idna_2008,
1030                         'Python idna cannot be imported; no IDNA2008')
1031    def testCodecNotFoundRaises(self):
1032        dns.name.have_idna_2008 = False
1033        with self.assertRaises(dns.name.NoIDNA2008):
1034            c = dns.name.IDNA2008Codec()
1035            c.encode('Königsgäßchen')
1036        with self.assertRaises(dns.name.NoIDNA2008):
1037            c = dns.name.IDNA2008Codec(strict_decode=True)
1038            c.decode('xn--eckwd4c7c.xn--zckzah.')
1039        dns.name.have_idna_2008 = True
1040
1041    @unittest.skipUnless(dns.name.have_idna_2008,
1042                         'Python idna cannot be imported; no IDNA2008')
1043    def testBadPunycodeStrict2008(self):
1044        c = dns.name.IDNA2008Codec(strict_decode=True)
1045        with self.assertRaises(dns.name.IDNAException):
1046            c.decode(b'xn--0000h')
1047
1048    def testRelativizeSubtractionSyntax(self):
1049        n = dns.name.from_text('foo.example.')
1050        o = dns.name.from_text('example.')
1051        e = dns.name.from_text('foo', None)
1052        self.assertEqual(n - o, e)
1053
1054    def testCopy(self):
1055        n1 = dns.name.from_text('foo.example.')
1056        n2 = copy.copy(n1)
1057        self.assertTrue(n1 is not n2)
1058        # the Name constructor always copies labels, so there is no
1059        # difference between copy and deepcopy
1060        self.assertTrue(n1.labels is not n2.labels)
1061        self.assertEqual(len(n1.labels), len(n2.labels))
1062        for i, l in enumerate(n1.labels):
1063            self.assertTrue(l is n2[i])
1064
1065    def testDeepCopy(self):
1066        n1 = dns.name.from_text('foo.example.')
1067        n2 = copy.deepcopy(n1)
1068        self.assertTrue(n1 is not n2)
1069        self.assertTrue(n1.labels is not n2.labels)
1070        self.assertEqual(len(n1.labels), len(n2.labels))
1071        for i, l in enumerate(n1.labels):
1072            self.assertTrue(l is n2[i])
1073
1074    def testNoAttributeDeletion(self):
1075        n = dns.name.from_text('foo.example.')
1076        with self.assertRaises(TypeError):
1077            del n.labels
1078
1079    def testUnicodeEscapify(self):
1080        n = dns.name.from_unicode('Königsgäßchen;\ttext')
1081        self.assertEqual(n.to_unicode(), 'königsgässchen\\;\\009text.')
1082
1083    def test_pickle(self):
1084        n1 = dns.name.from_text('foo.example')
1085        p = pickle.dumps(n1)
1086        n2 = pickle.loads(p)
1087        self.assertEqual(n1, n2)
1088
1089if __name__ == '__main__':
1090    unittest.main()
1091