]> git.ipfire.org Git - thirdparty/pdns.git/blame - regression-tests.recursor-dnssec/recursortests.py
Modernize Python 2 code to get ready for Python 3
[thirdparty/pdns.git] / regression-tests.recursor-dnssec / recursortests.py
CommitLineData
7568b07d
PL
1#!/usr/bin/env python2
2
7a0ea291 3from __future__ import print_function
7568b07d
PL
4import errno
5import shutil
6import os
7import socket
8import struct
9import subprocess
10import sys
11import time
12import unittest
13import dns
14import dns.message
15
16class RecursorTest(unittest.TestCase):
17 """
18 Setup all recursors and auths required for the tests
19 """
20
21 _confdir = 'recursor'
22
23 _recursorStartupDelay = 2.0
24 _recursorPort = 5300
25
26 _recursor = None
27
28 _PREFIX = os.environ['PREFIX']
29
30 _config_template_default = """
31daemon=no
32trace=yes
33dont-query=
34local-address=127.0.0.1
35packetcache-ttl=0
36packetcache-servfail-ttl=0
37max-cache-ttl=15
38threads=1
39loglevel=9
40disable-syslog=yes
41"""
42 _config_template = """
43"""
44 _config_params = []
45 _lua_config_file = None
18e0e4df 46 _lua_dns_script_file = None
7568b07d
PL
47 _roothints = """
48. 3600 IN NS ns.root.
49ns.root. 3600 IN A %s.8
50""" % _PREFIX
51 _root_DS = "63149 13 1 a59da3f5c1b97fcd5fa2b3b2b0ac91d38a60d33a"
52
53 # The default SOA for zones in the authoritative servers
54 _SOA = "ns1.example.net. hostmaster.example.net. 1 3600 1800 1209600 300"
55
56 # The definitions of the zones on the authoritative servers, the key is the
57 # zonename and the value is the zonefile content. several strings are replaced:
58 # - {soa} => value of _SOA
59 # - {prefix} value of _PREFIX
60 _zones = {
61 'ROOT': """
62. 3600 IN SOA {soa}
63. 3600 IN NS ns.root.
64ns.root. 3600 IN A {prefix}.8
65
66example. 3600 IN NS ns1.example.
67example. 3600 IN NS ns2.example.
68example. 3600 IN DS 53174 13 1 50c9e913818767c236c06c2d8272723cb78cbf26
69
70ns1.example. 3600 IN A {prefix}.10
71ns2.example. 3600 IN A {prefix}.11
72 """,
73 'example': """
74example. 3600 IN SOA {soa}
f3be2401
RG
75example. 3600 IN NS ns1.example.
76example. 3600 IN NS ns2.example.
7568b07d
PL
77ns1.example. 3600 IN A {prefix}.10
78ns2.example. 3600 IN A {prefix}.11
79
80secure.example. 3600 IN NS ns.secure.example.
81secure.example. 3600 IN DS 64723 13 1 53eb985040d3a89bacf29dbddb55a65834706f33
82ns.secure.example. 3600 IN A {prefix}.9
83
d5b8ccd4
PL
84cname-secure.example. 3600 IN NS ns.cname-secure.example.
85cname-secure.example. 3600 IN DS 49148 13 1 a10314452d5ec4d97fcc6d7e275d217261fe790f
86ns.cname-secure.example. 3600 IN A {prefix}.15
87
7568b07d
PL
88bogus.example. 3600 IN NS ns.bogus.example.
89bogus.example. 3600 IN DS 65034 13 1 6df3bb50ea538e90eacdd7ae5419730783abb0ee
90ns.bogus.example. 3600 IN A {prefix}.12
91
92insecure.example. 3600 IN NS ns.insecure.example.
93ns.insecure.example. 3600 IN A {prefix}.13
11886ab9
PL
94
95optout.example. 3600 IN NS ns1.optout.example.
96optout.example. 3600 IN DS 59332 13 1 e664f886ae1b5df03d918bc1217d22afc29925b9
97ns1.optout.example. 3600 IN A {prefix}.14
962a980d
PL
98
99insecure-formerr.example. 3600 IN NS ns1.insecure-formerr.example.
100ns1.insecure-formerr.example. 3600 IN A {prefix}.2
3bebf5f0 101
9a0b88e8
RG
102ecs-echo.example. 3600 IN NS ns1.ecs-echo.example.
103ns1.ecs-echo.example. 3600 IN A {prefix}.21
104
3bebf5f0
PL
105islandofsecurity.example. 3600 IN NS ns1.islandofsecurity.example.
106ns1.islandofsecurity.example. 3600 IN A {prefix}.9
2fed5be3
PD
107
108sortcname.example. 3600 IN CNAME sort
109sort.example. 3600 IN A 17.38.42.80
110sort.example. 3600 IN A 192.168.0.1
111sort.example. 3600 IN A 17.238.240.5
112sort.example. 3600 IN MX 25 mx
7568b07d
PL
113 """,
114 'secure.example': """
115secure.example. 3600 IN SOA {soa}
116secure.example. 3600 IN NS ns.secure.example.
117ns.secure.example. 3600 IN A {prefix}.9
118
d5b8ccd4
PL
119secure.example. 3600 IN A 192.0.2.17
120
7568b07d 121host1.secure.example. 3600 IN A 192.0.2.2
05537f80 122cname.secure.example. 3600 IN CNAME host1.secure.example.
6552b37b 123cname-to-insecure.secure.example. 3600 IN CNAME node1.insecure.example.
faa3b298 124cname-to-bogus.secure.example. 3600 IN CNAME ted.bogus.example.
3bebf5f0 125cname-to-islandofsecurity.secure.example. 3600 IN CNAME node1.islandofsecurity.example.
46419ee3
PL
126
127host1.sub.secure.example. 3600 IN A 192.0.2.11
fdb27cb2 128
694ef440
PL
129;; See #4158
130sub2.secure.example. 3600 IN CNAME doesnotmatter.insecure.example.
131insecure.sub2.secure.example. 3600 IN NS ns1.insecure.example.
132
fdb27cb2 133*.wildcard.secure.example. 3600 IN A 192.0.2.10
52033c6f
PL
134
135*.cnamewildcard.secure.example. 3600 IN CNAME host1.secure.example.
136
137*.cnamewildcardnxdomain.secure.example. 3600 IN CNAME doesntexist.secure.example.
962a980d
PL
138
139cname-to-formerr.secure.example. 3600 IN CNAME host1.insecure-formerr.example.
d5b8ccd4
PL
140 """,
141 'cname-secure.example': """
142cname-secure.example. 3600 IN SOA {soa}
143cname-secure.example. 3600 IN NS ns.cname-secure.example.
144ns.cname-secure.example. 3600 IN A {prefix}.15
145cname-secure.example. 3600 IN CNAME secure.example.
7568b07d
PL
146 """,
147 'bogus.example': """
148bogus.example. 3600 IN SOA {soa}
149bogus.example. 3600 IN NS ns1.bogus.example.
150ns1.bogus.example. 3600 IN A {prefix}.12
151ted.bogus.example. 3600 IN A 192.0.2.1
152bill.bogus.example. 3600 IN AAAA 2001:db8:12::3
694ef440
PL
153 """,
154 'insecure.sub2.secure.example': """
155insecure.sub2.secure.example. 3600 IN SOA {soa}
156insecure.sub2.secure.example. 3600 IN NS ns1.insecure.example.
157
158node1.insecure.sub2.secure.example. 3600 IN A 192.0.2.18
7568b07d
PL
159 """,
160 'insecure.example': """
161insecure.example. 3600 IN SOA {soa}
162insecure.example. 3600 IN NS ns1.insecure.example.
163ns1.insecure.example. 3600 IN A {prefix}.13
164
165node1.insecure.example. 3600 IN A 192.0.2.6
6552b37b
PL
166
167cname-to-secure.insecure.example. 3600 IN CNAME host1.secure.example.
11886ab9
PL
168 """,
169 'optout.example': """
170optout.example. 3600 IN SOA {soa}
171optout.example. 3600 IN NS ns1.optout.example.
172ns1.optout.example. 3600 IN A {prefix}.14
173
174insecure.optout.example. 3600 IN NS ns1.insecure.optout.example.
175ns1.insecure.optout.example. 3600 IN A {prefix}.15
176
177secure.optout.example. 3600 IN NS ns1.secure.optout.example.
178secure.optout.example. 3600 IN DS 64215 13 1 b88284d7a8d8605c398e8942262f97b9a5a31787
179ns1.secure.optout.example. 3600 IN A {prefix}.15
180 """,
181 'insecure.optout.example': """
182insecure.optout.example. 3600 IN SOA {soa}
183insecure.optout.example. 3600 IN NS ns1.insecure.optout.example.
184ns1.insecure.optout.example. 3600 IN A {prefix}.15
185
186node1.insecure.optout.example. 3600 IN A 192.0.2.7
187 """,
188 'secure.optout.example': """
189secure.optout.example. 3600 IN SOA {soa}
190secure.optout.example. 3600 IN NS ns1.secure.optout.example.
191ns1.secure.optout.example. 3600 IN A {prefix}.15
192
193node1.secure.optout.example. 3600 IN A 192.0.2.8
3bebf5f0
PL
194 """,
195 'islandofsecurity.example': """
196islandofsecurity.example. 3600 IN SOA {soa}
197islandofsecurity.example. 3600 IN NS ns1.islandofsecurity.example.
198ns1.islandofsecurity.example. 3600 IN A {prefix}.9
199
200node1.islandofsecurity.example. 3600 IN A 192.0.2.20
3a7ddb0b
PL
201 """,
202 'undelegated.secure.example': """
203undelegated.secure.example. 3600 IN SOA {soa}
204undelegated.secure.example. 3600 IN NS ns1.undelegated.secure.example.
205
206node1.undelegated.secure.example. 3600 IN A 192.0.2.21
207 """,
208 'undelegated.insecure.example': """
209undelegated.insecure.example. 3600 IN SOA {soa}
210undelegated.insecure.example. 3600 IN NS ns1.undelegated.insecure.example.
211
212node1.undelegated.insecure.example. 3600 IN A 192.0.2.22
213 """,
7568b07d
PL
214 }
215
216 # The private keys for the zones (note that DS records should go into
217 # the zonecontent in _zones
218 _zone_keys = {
219 'ROOT': """
220Private-key-format: v1.2
221Algorithm: 13 (ECDSAP256SHA256)
222PrivateKey: rhWuEydDz3QaIspSVj683B8Xq5q/ozzA38XUgzD4Fbo=
223 """,
224
225 'example': """
226Private-key-format: v1.2
227Algorithm: 13 (ECDSAP256SHA256)
228PrivateKey: Lt0v0Gol3pRUFM7fDdcy0IWN0O/MnEmVPA+VylL8Y4U=
229 """,
230
231 'secure.example': """
232Private-key-format: v1.2
233Algorithm: 13 (ECDSAP256SHA256)
234PrivateKey: 1G4WRoOFJJXk+fotDCHVORtJmIG2OUhKi8AO2jDPGZA=
235 """,
236
237 'bogus.example': """
238Private-key-format: v1.2
239Algorithm: 13 (ECDSAP256SHA256)
240PrivateKey: f5jV7Q8kd5hDpMWObsuQ6SQda0ftf+JrO3uZwEg6nVw=
241 """,
11886ab9
PL
242
243 'optout.example': """
244Private-key-format: v1.2
245Algorithm: 13 (ECDSAP256SHA256)
246PrivateKey: efmq9G+J4Y2iPnIBRwJiy6Z/nIHSzpsCy/7XHhlS19A=
247 """,
248
249 'secure.optout.example': """
250Private-key-format: v1.2
251Algorithm: 13 (ECDSAP256SHA256)
252PrivateKey: xcNUxt1Knj14A00lKQFDboluiJyM2f7FxpgsQaQ3AQ4=
3bebf5f0
PL
253 """,
254
255 'islandofsecurity.example': """
256Private-key-format: v1.2
257Algorithm: 13 (ECDSAP256SHA256)
258PrivateKey: o9F5iix8V68tnMcuOaM2Lt8XXhIIY//SgHIHEePk6cM=
d5b8ccd4
PL
259 """,
260
261 'cname-secure.example': """
262Private-key-format: v1.2
263Algorithm: 13 (ECDSAP256SHA256)
264PrivateKey: kvoV/g4IO/tefSro+FLJ5UC7H3BUf0IUtZQSUOfQGyA=
265"""
7568b07d
PL
266 }
267
268 # This dict is keyed with the suffix of the IP address and its value
269 # is a list of zones hosted on that IP. Note that delegations should
270 # go into the _zones's zonecontent
271 _auth_zones = {
272 '8': ['ROOT'],
3bebf5f0 273 '9': ['secure.example', 'islandofsecurity.example'],
7568b07d
PL
274 '10': ['example'],
275 '11': ['example'],
3a7ddb0b 276 '12': ['bogus.example', 'undelegated.secure.example', 'undelegated.insecure.example'],
694ef440 277 '13': ['insecure.example', 'insecure.sub2.secure.example'],
11886ab9 278 '14': ['optout.example'],
d5b8ccd4 279 '15': ['insecure.optout.example', 'secure.optout.example', 'cname-secure.example']
7568b07d
PL
280 }
281
cb54e9b5
PL
282 _auth_cmd = ['authbind',
283 os.environ['PDNS']]
284 _auth_env = {}
7568b07d
PL
285 _auths = {}
286
287 @classmethod
288 def createConfigDir(cls, confdir):
289 try:
290 shutil.rmtree(confdir)
291 except OSError as e:
292 if e.errno != errno.ENOENT:
293 raise
7a0ea291 294 os.mkdir(confdir, 0o755)
7568b07d
PL
295
296 @classmethod
297 def generateAuthZone(cls, confdir, zonename, zonecontent):
298 with open(os.path.join(confdir, '%s.zone' % zonename), 'w') as zonefile:
299 zonefile.write(zonecontent.format(prefix=cls._PREFIX, soa=cls._SOA))
300
301 @classmethod
302 def generateAuthNamedConf(cls, confdir, zones):
303 with open(os.path.join(confdir, 'named.conf'), 'w') as namedconf:
304 namedconf.write("""
305options {
306 directory "%s";
307};""" % confdir)
308 for zonename in zones:
309 zone = '.' if zonename == 'ROOT' else zonename
310
311 namedconf.write("""
312 zone "%s" {
313 type master;
314 file "%s.zone";
315 };""" % (zone, zonename))
316
317 @classmethod
318 def generateAuthConfig(cls, confdir):
319 bind_dnssec_db = os.path.join(confdir, 'bind-dnssec.sqlite3')
320
321 with open(os.path.join(confdir, 'pdns.conf'), 'w') as pdnsconf:
322 pdnsconf.write("""
323module-dir=../regression-tests/modules
324launch=bind
325daemon=no
326local-ipv6=
327bind-config={confdir}/named.conf
328bind-dnssec-db={bind_dnssec_db}
329socket-dir={confdir}
330cache-ttl=0
331negquery-cache-ttl=0
332query-cache-ttl=0
333log-dns-queries=yes
334log-dns-details=yes
335loglevel=9
336distributor-threads=1""".format(confdir=confdir,
337 bind_dnssec_db=bind_dnssec_db))
338
339 pdnsutilCmd = [os.environ['PDNSUTIL'],
340 '--config-dir=%s' % confdir,
341 'create-bind-db',
342 bind_dnssec_db]
343
7a0ea291 344 print(' '.join(pdnsutilCmd))
7568b07d
PL
345 try:
346 subprocess.check_output(pdnsutilCmd, stderr=subprocess.STDOUT)
347 except subprocess.CalledProcessError as e:
7a0ea291 348 print(e.output)
7568b07d
PL
349 raise
350
351 @classmethod
352 def secureZone(cls, confdir, zonename, key=None):
353 zone = '.' if zonename == 'ROOT' else zonename
354 if not key:
355 pdnsutilCmd = [os.environ['PDNSUTIL'],
356 '--config-dir=%s' % confdir,
357 'secure-zone',
358 zone]
359 else:
360 keyfile = os.path.join(confdir, 'dnssec.key')
361 with open(keyfile, 'w') as fdKeyfile:
362 fdKeyfile.write(key)
363
364 pdnsutilCmd = [os.environ['PDNSUTIL'],
365 '--config-dir=%s' % confdir,
366 'import-zone-key',
367 zone,
368 keyfile,
369 'active',
370 'ksk']
371
7a0ea291 372 print(' '.join(pdnsutilCmd))
7568b07d
PL
373 try:
374 subprocess.check_output(pdnsutilCmd, stderr=subprocess.STDOUT)
375 except subprocess.CalledProcessError as e:
7a0ea291 376 print(e.output)
7568b07d
PL
377 raise
378
379 @classmethod
380 def generateAllAuthConfig(cls, confdir):
381 if cls._auth_zones:
382 for auth_suffix, zones in cls._auth_zones.items():
383 authconfdir = os.path.join(confdir, 'auth-%s' % auth_suffix)
384
385 os.mkdir(authconfdir)
386
387 cls.generateAuthConfig(authconfdir)
388 cls.generateAuthNamedConf(authconfdir, zones)
389
11886ab9
PL
390 for zone in zones:
391 cls.generateAuthZone(authconfdir,
392 zone,
393 cls._zones[zone])
394 if cls._zone_keys.get(zone, None):
395 cls.secureZone(authconfdir, zone, cls._zone_keys.get(zone))
7568b07d
PL
396
397 @classmethod
398 def startAllAuth(cls, confdir):
399 if cls._auth_zones:
400 for auth_suffix, _ in cls._auth_zones.items():
401 authconfdir = os.path.join(confdir, 'auth-%s' % auth_suffix)
402 ipaddress = cls._PREFIX + '.' + auth_suffix
403 cls.startAuth(authconfdir, ipaddress)
404
405 @classmethod
406 def startAuth(cls, confdir, ipaddress):
407 print("Launching pdns_server..")
11886ab9 408 authcmd = list(cls._auth_cmd)
cb54e9b5
PL
409 authcmd.append('--config-dir=%s' % confdir)
410 authcmd.append('--local-address=%s' % ipaddress)
7568b07d
PL
411 print(' '.join(authcmd))
412
413 logFile = os.path.join(confdir, 'pdns.log')
414 with open(logFile, 'w') as fdLog:
415 cls._auths[ipaddress] = subprocess.Popen(authcmd, close_fds=True,
cb54e9b5
PL
416 stdout=fdLog, stderr=fdLog,
417 env=cls._auth_env)
7568b07d
PL
418
419 time.sleep(2)
420
421 if cls._auths[ipaddress].poll() is not None:
422 try:
423 cls._auths[ipaddress].kill()
424 except OSError as e:
425 if e.errno != errno.ESRCH:
426 raise
427 with open(logFile, 'r') as fdLog:
7a0ea291 428 print(fdLog.read())
7568b07d
PL
429 sys.exit(cls._auths[ipaddress].returncode)
430
431 @classmethod
432 def generateRecursorConfig(cls, confdir):
433 params = tuple([getattr(cls, param) for param in cls._config_params])
434 if len(params):
435 print(params)
436
437 recursorconf = os.path.join(confdir, 'recursor.conf')
438
439 with open(recursorconf, 'w') as conf:
440 conf.write("# Autogenerated by recursortests.py\n")
441 conf.write(cls._config_template_default)
442 conf.write(cls._config_template % params)
443 conf.write("\n")
444 conf.write("socket-dir=%s\n" % confdir)
445 if cls._lua_config_file or cls._root_DS:
446 luaconfpath = os.path.join(confdir, 'conffile.lua')
447 with open(luaconfpath, 'w') as luaconf:
448 if cls._root_DS:
8f29eeaa 449 luaconf.write("addTA('.', '%s')\n" % cls._root_DS)
7568b07d
PL
450 if cls._lua_config_file:
451 luaconf.write(cls._lua_config_file)
452 conf.write("lua-config-file=%s\n" % luaconfpath)
18e0e4df
RG
453 if cls._lua_dns_script_file:
454 luascriptpath = os.path.join(confdir, 'dnsscript.lua')
455 with open(luascriptpath, 'w') as luascript:
456 luascript.write(cls._lua_dns_script_file)
457 conf.write("lua-dns-script=%s\n" % luascriptpath)
7568b07d
PL
458 if cls._roothints:
459 roothintspath = os.path.join(confdir, 'root.hints')
460 with open(roothintspath, 'w') as roothints:
461 roothints.write(cls._roothints)
462 conf.write("hint-file=%s\n" % roothintspath)
463
962a980d
PL
464 @classmethod
465 def startResponders(cls):
466 pass
467
7568b07d
PL
468 @classmethod
469 def startRecursor(cls, confdir, port):
470 print("Launching pdns_recursor..")
471 recursorcmd = [os.environ['PDNSRECURSOR'],
472 '--config-dir=%s' % confdir,
962a980d
PL
473 '--local-port=%s' % port,
474 '--security-poll-suffix=']
7568b07d
PL
475 print(' '.join(recursorcmd))
476
477 logFile = os.path.join(confdir, 'recursor.log')
478 with open(logFile, 'w') as fdLog:
479 cls._recursor = subprocess.Popen(recursorcmd, close_fds=True,
480 stdout=fdLog, stderr=fdLog)
481
482 if 'PDNSRECURSOR_FAST_TESTS' in os.environ:
483 delay = 0.5
484 else:
485 delay = cls._recursorStartupDelay
486
487 time.sleep(delay)
488
489 if cls._recursor.poll() is not None:
490 try:
491 cls._recursor.kill()
492 except OSError as e:
493 if e.errno != errno.ESRCH:
494 raise
495 with open(logFile, 'r') as fdLog:
7a0ea291 496 print(fdLog.read())
7568b07d
PL
497 sys.exit(cls._recursor.returncode)
498
499 @classmethod
500 def wipeRecursorCache(cls, confdir):
501 rec_controlCmd = [os.environ['RECCONTROL'],
502 '--config-dir=%s' % confdir,
503 'wipe-cache',
504 '.$']
505 try:
506 subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT)
507 except subprocess.CalledProcessError as e:
7a0ea291 508 print(e.output)
7568b07d
PL
509 raise
510
511 @classmethod
512 def setUpSockets(cls):
513 print("Setting up UDP socket..")
514 cls._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
515 cls._sock.settimeout(2.0)
516 cls._sock.connect(("127.0.0.1", cls._recursorPort))
517
518 @classmethod
519 def setUpClass(cls):
520 cls.setUpSockets()
962a980d
PL
521
522 cls.startResponders()
523
7568b07d
PL
524 confdir = os.path.join('configs', cls._confdir)
525 cls.createConfigDir(confdir)
526 cls.generateAllAuthConfig(confdir)
527 cls.startAllAuth(confdir)
528
529 cls.generateRecursorConfig(confdir)
530 cls.startRecursor(confdir, cls._recursorPort)
531
532 print("Launching tests..")
533
534 @classmethod
535 def tearDownClass(cls):
536 cls.tearDownRecursor()
537 cls.tearDownAuth()
962a980d
PL
538 cls.tearDownResponders()
539
540 @classmethod
541 def tearDownResponders(cls):
542 pass
7568b07d
PL
543
544 @classmethod
545 def tearDownAuth(cls):
546 if 'PDNSRECURSOR_FAST_TESTS' in os.environ:
547 delay = 0.1
548 else:
549 delay = 1.0
550
551 for _, auth in cls._auths.items():
552 try:
553 auth.terminate()
554 if auth.poll() is None:
555 time.sleep(delay)
556 if auth.poll() is None:
557 auth.kill()
558 auth.wait()
559 except OSError as e:
560 if e.errno != errno.ESRCH:
561 raise
562
563 @classmethod
564 def tearDownRecursor(cls):
565 if 'PDNSRECURSOR_FAST_TESTS' in os.environ:
566 delay = 0.1
567 else:
568 delay = 1.0
569 try:
570 if cls._recursor:
571 cls._recursor.terminate()
572 if cls._recursor.poll() is None:
573 time.sleep(delay)
574 if cls._recursor.poll() is None:
575 cls._recursor.kill()
576 cls._recursor.wait()
577 except OSError as e:
578 # There is a race-condition with the poll() and
579 # kill() statements, when the process is dead on the
580 # kill(), this is fine
581 if e.errno != errno.ESRCH:
582 raise
583
7568b07d 584 @classmethod
fb611f07 585 def sendUDPQuery(cls, query, timeout=2.0, decode=True, fwparams=dict()):
7568b07d
PL
586 if timeout:
587 cls._sock.settimeout(timeout)
588
589 try:
590 cls._sock.send(query.to_wire())
591 data = cls._sock.recv(4096)
592 except socket.timeout:
593 data = None
594 finally:
595 if timeout:
596 cls._sock.settimeout(None)
597
598 message = None
599 if data:
fb611f07
RG
600 if not decode:
601 return data
554c69f9 602 message = dns.message.from_wire(data, **fwparams)
7568b07d
PL
603 return message
604
605 @classmethod
606 def sendTCPQuery(cls, query, timeout=2.0):
607 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
608 if timeout:
609 sock.settimeout(timeout)
610
611 sock.connect(("127.0.0.1", cls._recursorPort))
612
613 try:
614 wire = query.to_wire()
615 sock.send(struct.pack("!H", len(wire)))
616 sock.send(wire)
617 data = sock.recv(2)
618 if data:
619 (datalen,) = struct.unpack("!H", data)
620 data = sock.recv(datalen)
621 except socket.timeout as e:
622 print("Timeout: %s" % (str(e)))
623 data = None
624 except socket.error as e:
625 print("Network error: %s" % (str(e)))
626 data = None
627 finally:
628 sock.close()
629
630 message = None
631 if data:
632 message = dns.message.from_wire(data)
633 return message
634
635 def setUp(self):
636 # This function is called before every tests
637 return
638
639 ## Functions for comparisons
640 def assertMessageHasFlags(self, msg, flags, ednsflags=[]):
641 """Asserts that msg has all the flags from flags set
642
643 @param msg: the dns.message.Message to check
644 @param flags: a list of strings with flag mnemonics (like ['RD', 'RA'])
645 @param ednsflags: a list of strings with edns-flag mnemonics (like ['DO'])"""
646
647 if not isinstance(msg, dns.message.Message):
648 raise TypeError("msg is not a dns.message.Message")
649
650 if isinstance(flags, list):
651 for elem in flags:
652 if not isinstance(elem, str):
653 raise TypeError("flags is not a list of strings")
654 else:
655 raise TypeError("flags is not a list of strings")
656
657 if isinstance(ednsflags, list):
658 for elem in ednsflags:
659 if not isinstance(elem, str):
660 raise TypeError("ednsflags is not a list of strings")
661 else:
662 raise TypeError("ednsflags is not a list of strings")
663
664 msgFlags = dns.flags.to_text(msg.flags).split()
665 missingFlags = [flag for flag in flags if flag not in msgFlags]
666
8b16b9c2 667 msgEdnsFlags = dns.flags.edns_to_text(msg.ednsflags).split()
7568b07d
PL
668 missingEdnsFlags = [ednsflag for ednsflag in ednsflags if ednsflag not in msgEdnsFlags]
669
670 if len(missingFlags) or len(missingEdnsFlags) or len(msgFlags) > len(flags):
671 raise AssertionError("Expected flags '%s' (EDNS: '%s'), found '%s' (EDNS: '%s') in query %s" %
672 (' '.join(flags), ' '.join(ednsflags),
673 ' '.join(msgFlags), ' '.join(msgEdnsFlags),
674 msg.question[0]))
675
676 def assertMessageIsAuthenticated(self, msg):
677 """Asserts that the message has the AD bit set
678
679 @param msg: the dns.message.Message to check"""
680
681 if not isinstance(msg, dns.message.Message):
682 raise TypeError("msg is not a dns.message.Message")
683
684 msgFlags = dns.flags.to_text(msg.flags)
685 self.assertTrue('AD' in msgFlags, "No AD flag found in the message for %s" % msg.question[0].name)
686
687 def assertRRsetInAnswer(self, msg, rrset):
688 """Asserts the rrset (without comparing TTL) exists in the
689 answer section of msg
690
691 @param msg: the dns.message.Message to check
692 @param rrset: a dns.rrset.RRset object"""
693
694 ret = ''
695 if not isinstance(msg, dns.message.Message):
696 raise TypeError("msg is not a dns.message.Message")
697
698 if not isinstance(rrset, dns.rrset.RRset):
699 raise TypeError("rrset is not a dns.rrset.RRset")
700
701 found = False
702 for ans in msg.answer:
703 ret += "%s\n" % ans.to_text()
704 if ans.match(rrset.name, rrset.rdclass, rrset.rdtype, 0, None):
de9650d1 705 self.assertEqual(ans, rrset, "'%s' != '%s'" % (ans.to_text(), rrset.to_text()))
7568b07d
PL
706 found = True
707
708 if not found:
6552b37b 709 raise AssertionError("RRset not found in answer\n\n%s" % ret)
7568b07d
PL
710
711 def assertMatchingRRSIGInAnswer(self, msg, coveredRRset, keys=None):
712 """Looks for coveredRRset in the answer section and if there is an RRSIG RRset
713 that covers that RRset. If keys is not None, this function will also try to
714 validate the RRset against the RRSIG
715
716 @param msg: The dns.message.Message to check
717 @param coveredRRset: The RRSet to check for
718 @param keys: a dictionary keyed by dns.name.Name with node or rdataset values to use for validation"""
719
720 if not isinstance(msg, dns.message.Message):
721 raise TypeError("msg is not a dns.message.Message")
722
723 if not isinstance(coveredRRset, dns.rrset.RRset):
724 raise TypeError("coveredRRset is not a dns.rrset.RRset")
725
726 msgRRsigRRSet = None
727 msgRRSet = None
728
729 ret = ''
730 for ans in msg.answer:
731 ret += ans.to_text() + "\n"
732
733 if ans.match(coveredRRset.name, coveredRRset.rdclass, coveredRRset.rdtype, 0, None):
734 msgRRSet = ans
735 if ans.match(coveredRRset.name, dns.rdataclass.IN, dns.rdatatype.RRSIG, coveredRRset.rdtype, None):
736 msgRRsigRRSet = ans
737 if msgRRSet and msgRRsigRRSet:
738 break
739
740 if not msgRRSet:
741 raise AssertionError("RRset for '%s' not found in answer" % msg.question[0].to_text())
742
743 if not msgRRsigRRSet:
744 raise AssertionError("No RRSIGs found in answer for %s:\nFull answer:\n%s" % (msg.question[0].to_text(), ret))
745
746 if keys:
747 try:
748 dns.dnssec.validate(msgRRSet, msgRRsigRRSet.to_rdataset(), keys)
749 except dns.dnssec.ValidationFailure as e:
750 raise AssertionError("Signature validation failed for %s:\n%s" % (msg.question[0].to_text(), e))
751
752 def assertNoRRSIGsInAnswer(self, msg):
753 """Checks if there are _no_ RRSIGs in the answer section of msg"""
754
755 if not isinstance(msg, dns.message.Message):
756 raise TypeError("msg is not a dns.message.Message")
757
758 ret = ""
759 for ans in msg.answer:
760 if ans.rdtype == dns.rdatatype.RRSIG:
761 ret += ans.name.to_text() + "\n"
762
763 if len(ret):
764 raise AssertionError("RRSIG found in answers for:\n%s" % ret)
765
766 def assertAnswerEmpty(self, msg):
767 self.assertTrue(len(msg.answer) == 0, "Data found in the the answer section for %s:\n%s" % (msg.question[0].to_text(), '\n'.join([i.to_text() for i in msg.answer])))
768
769 def assertRcodeEqual(self, msg, rcode):
770 if not isinstance(msg, dns.message.Message):
771 raise TypeError("msg is not a dns.message.Message but a %s" % type(msg))
772
773 if not isinstance(rcode, int):
774 if isinstance(rcode, str):
775 rcode = dns.rcode.from_text(rcode)
776 else:
777 raise TypeError("rcode is neither a str nor int")
778
779 if msg.rcode() != rcode:
780 msgRcode = dns.rcode._by_value[msg.rcode()]
781 wantedRcode = dns.rcode._by_value[rcode]
782
783 raise AssertionError("Rcode for %s is %s, expected %s." % (msg.question[0].to_text(), msgRcode, wantedRcode))
05537f80
PL
784
785 def assertAuthorityHasSOA(self, msg):
786 if not isinstance(msg, dns.message.Message):
787 raise TypeError("msg is not a dns.message.Message but a %s" % type(msg))
788
789 found = False
790 for rrset in msg.authority:
791 if rrset.rdtype == dns.rdatatype.SOA:
792 found = True
793 break
794
795 if not found:
796 raise AssertionError("No SOA record found in the authority section:\n%s" % msg.to_text())