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