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