]> git.ipfire.org Git - thirdparty/pdns.git/blame - regression-tests.recursor-dnssec/recursortests.py
rec: do not use DNSKEYs found below an apex for validation
[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
9166ee1b
RG
16from proxyprotocol import ProxyProtocol
17
753a8109
PD
18from eqdnsmessage import AssertEqualDNSMessageMixin
19
fb027663
PL
20
21def have_ipv6():
22 """
23 Try to make an IPv6 socket and bind it, if it fails, no ipv6...
24 """
25 try:
26 sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
27 sock.bind(('::1', 56581))
28 sock.close()
29 return True
30 except:
31 return False
32 return False
33
34
753a8109 35class RecursorTest(AssertEqualDNSMessageMixin, unittest.TestCase):
7568b07d
PL
36 """
37 Setup all recursors and auths required for the tests
38 """
39
40 _confdir = 'recursor'
41
42 _recursorStartupDelay = 2.0
43 _recursorPort = 5300
44
45 _recursor = None
46
47 _PREFIX = os.environ['PREFIX']
48
49 _config_template_default = """
50daemon=no
51trace=yes
52dont-query=
53local-address=127.0.0.1
54packetcache-ttl=0
55packetcache-servfail-ttl=0
56max-cache-ttl=15
57threads=1
58loglevel=9
59disable-syslog=yes
949cd0f2 60log-common-errors=yes
7568b07d
PL
61"""
62 _config_template = """
63"""
64 _config_params = []
65 _lua_config_file = None
18e0e4df 66 _lua_dns_script_file = None
7568b07d
PL
67 _roothints = """
68. 3600 IN NS ns.root.
69ns.root. 3600 IN A %s.8
8064abbb 70ns.root. 3600 IN AAAA ::1
7568b07d
PL
71""" % _PREFIX
72 _root_DS = "63149 13 1 a59da3f5c1b97fcd5fa2b3b2b0ac91d38a60d33a"
73
74 # The default SOA for zones in the authoritative servers
75 _SOA = "ns1.example.net. hostmaster.example.net. 1 3600 1800 1209600 300"
76
77 # The definitions of the zones on the authoritative servers, the key is the
78 # zonename and the value is the zonefile content. several strings are replaced:
79 # - {soa} => value of _SOA
80 # - {prefix} value of _PREFIX
81 _zones = {
82 'ROOT': """
83. 3600 IN SOA {soa}
84. 3600 IN NS ns.root.
85ns.root. 3600 IN A {prefix}.8
86
87example. 3600 IN NS ns1.example.
88example. 3600 IN NS ns2.example.
89example. 3600 IN DS 53174 13 1 50c9e913818767c236c06c2d8272723cb78cbf26
90
91ns1.example. 3600 IN A {prefix}.10
4709a495 92ns2.example. 3600 IN A {prefix}.18
7568b07d
PL
93 """,
94 'example': """
95example. 3600 IN SOA {soa}
f3be2401
RG
96example. 3600 IN NS ns1.example.
97example. 3600 IN NS ns2.example.
7568b07d 98ns1.example. 3600 IN A {prefix}.10
4709a495 99ns2.example. 3600 IN A {prefix}.18
7568b07d
PL
100
101secure.example. 3600 IN NS ns.secure.example.
102secure.example. 3600 IN DS 64723 13 1 53eb985040d3a89bacf29dbddb55a65834706f33
103ns.secure.example. 3600 IN A {prefix}.9
104
d5b8ccd4
PL
105cname-secure.example. 3600 IN NS ns.cname-secure.example.
106cname-secure.example. 3600 IN DS 49148 13 1 a10314452d5ec4d97fcc6d7e275d217261fe790f
107ns.cname-secure.example. 3600 IN A {prefix}.15
108
9516e835
PL
109dname-secure.example. 3600 IN NS ns.dname-secure.example.
110dname-secure.example. 3600 IN DS 42043 13 2 11c67f46b7c4d5968bc5f6cc944d58377b762bda53ddb4f3a6dbe6faf7a9940f
111ns.dname-secure.example. 3600 IN A {prefix}.13
112
7568b07d
PL
113bogus.example. 3600 IN NS ns.bogus.example.
114bogus.example. 3600 IN DS 65034 13 1 6df3bb50ea538e90eacdd7ae5419730783abb0ee
115ns.bogus.example. 3600 IN A {prefix}.12
116
117insecure.example. 3600 IN NS ns.insecure.example.
118ns.insecure.example. 3600 IN A {prefix}.13
11886ab9
PL
119
120optout.example. 3600 IN NS ns1.optout.example.
121optout.example. 3600 IN DS 59332 13 1 e664f886ae1b5df03d918bc1217d22afc29925b9
122ns1.optout.example. 3600 IN A {prefix}.14
962a980d
PL
123
124insecure-formerr.example. 3600 IN NS ns1.insecure-formerr.example.
125ns1.insecure-formerr.example. 3600 IN A {prefix}.2
3bebf5f0 126
9a0b88e8
RG
127ecs-echo.example. 3600 IN NS ns1.ecs-echo.example.
128ns1.ecs-echo.example. 3600 IN A {prefix}.21
129
3bebf5f0
PL
130islandofsecurity.example. 3600 IN NS ns1.islandofsecurity.example.
131ns1.islandofsecurity.example. 3600 IN A {prefix}.9
2fed5be3
PD
132
133sortcname.example. 3600 IN CNAME sort
134sort.example. 3600 IN A 17.38.42.80
135sort.example. 3600 IN A 192.168.0.1
136sort.example. 3600 IN A 17.238.240.5
137sort.example. 3600 IN MX 25 mx
c51c551e 138
a79f06c4
OM
139delay1.example. 3600 IN NS ns1.delay1.example.
140ns1.delay1.example. 3600 IN A {prefix}.16
4709a495
OM
141delay1.example. 3600 IN DS 42043 13 2 7319fa605cf117f36e3de070157577ebb9a05a1d1f963d80eda55b5d6e793eb2
142
a79f06c4
OM
143delay2.example. 3600 IN NS ns1.delay2.example.
144ns1.delay2.example. 3600 IN A {prefix}.17
4709a495 145delay2.example. 3600 IN DS 42043 13 2 60a047b87740c8564c21d5fd34626c10a77a6c41e3b34564230119c2f13937b8
b7284b4d
RG
146
147cname-nxd.example. 3600 IN CNAME cname-nxd-target.example.
148cname-nxd-target.example. 3600 IN A 192.0.2.100
149cname-nodata.example. 3600 IN CNAME cname-nodata-target.example.
150cname-nodata-target.example. 3600 IN A 192.0.2.101
151cname-custom-a.example. 3600 IN CNAME cname-custom-a-target.example.
152cname-custom-a-target.example. 3600 IN A 192.0.2.102
7568b07d
PL
153 """,
154 'secure.example': """
155secure.example. 3600 IN SOA {soa}
156secure.example. 3600 IN NS ns.secure.example.
157ns.secure.example. 3600 IN A {prefix}.9
158
d5b8ccd4
PL
159secure.example. 3600 IN A 192.0.2.17
160
7568b07d 161host1.secure.example. 3600 IN A 192.0.2.2
05537f80 162cname.secure.example. 3600 IN CNAME host1.secure.example.
6552b37b 163cname-to-insecure.secure.example. 3600 IN CNAME node1.insecure.example.
faa3b298 164cname-to-bogus.secure.example. 3600 IN CNAME ted.bogus.example.
3bebf5f0 165cname-to-islandofsecurity.secure.example. 3600 IN CNAME node1.islandofsecurity.example.
46419ee3
PL
166
167host1.sub.secure.example. 3600 IN A 192.0.2.11
fdb27cb2 168
694ef440
PL
169;; See #4158
170sub2.secure.example. 3600 IN CNAME doesnotmatter.insecure.example.
171insecure.sub2.secure.example. 3600 IN NS ns1.insecure.example.
172
fdb27cb2 173*.wildcard.secure.example. 3600 IN A 192.0.2.10
52033c6f
PL
174
175*.cnamewildcard.secure.example. 3600 IN CNAME host1.secure.example.
176
ef2ea4bf 177*.cnamewildcardnxdomain.secure.example. 3600 IN CNAME doesnotexist.secure.example.
962a980d
PL
178
179cname-to-formerr.secure.example. 3600 IN CNAME host1.insecure-formerr.example.
9516e835
PL
180
181dname-secure.secure.example. 3600 IN DNAME dname-secure.example.
182dname-insecure.secure.example. 3600 IN DNAME insecure.example.
183dname-bogus.secure.example. 3600 IN DNAME bogus.example.
90b85dd0
PD
184
185non-apex-dnskey.secure.example. 3600 IN DNSKEY 257 3 13 CT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==
d5b8ccd4 186 """,
9516e835
PL
187 'dname-secure.example': """
188dname-secure.example. 3600 IN SOA {soa}
189dname-secure.example. 3600 IN NS ns.dname-secure.example.
190ns.dname-secure.example. 3600 IN A {prefix}.13
191
192host1.dname-secure.example. IN A 192.0.2.21
193
194cname-to-secure.dname-secure.example. 3600 IN CNAME host1.secure.example.
195cname-to-insecure.dname-secure.example. 3600 IN CNAME node1.insecure.example.
196cname-to-bogus.dname-secure.example. 3600 IN CNAME ted.bogus.example.
197""",
d5b8ccd4
PL
198 'cname-secure.example': """
199cname-secure.example. 3600 IN SOA {soa}
200cname-secure.example. 3600 IN NS ns.cname-secure.example.
201ns.cname-secure.example. 3600 IN A {prefix}.15
202cname-secure.example. 3600 IN CNAME secure.example.
7568b07d
PL
203 """,
204 'bogus.example': """
205bogus.example. 3600 IN SOA {soa}
206bogus.example. 3600 IN NS ns1.bogus.example.
207ns1.bogus.example. 3600 IN A {prefix}.12
208ted.bogus.example. 3600 IN A 192.0.2.1
209bill.bogus.example. 3600 IN AAAA 2001:db8:12::3
694ef440
PL
210 """,
211 'insecure.sub2.secure.example': """
212insecure.sub2.secure.example. 3600 IN SOA {soa}
213insecure.sub2.secure.example. 3600 IN NS ns1.insecure.example.
214
215node1.insecure.sub2.secure.example. 3600 IN A 192.0.2.18
7568b07d
PL
216 """,
217 'insecure.example': """
218insecure.example. 3600 IN SOA {soa}
219insecure.example. 3600 IN NS ns1.insecure.example.
220ns1.insecure.example. 3600 IN A {prefix}.13
221
222node1.insecure.example. 3600 IN A 192.0.2.6
6552b37b
PL
223
224cname-to-secure.insecure.example. 3600 IN CNAME host1.secure.example.
9516e835
PL
225
226dname-to-secure.insecure.example. 3600 IN DNAME dname-secure.example.
11886ab9
PL
227 """,
228 'optout.example': """
229optout.example. 3600 IN SOA {soa}
230optout.example. 3600 IN NS ns1.optout.example.
231ns1.optout.example. 3600 IN A {prefix}.14
232
233insecure.optout.example. 3600 IN NS ns1.insecure.optout.example.
234ns1.insecure.optout.example. 3600 IN A {prefix}.15
235
236secure.optout.example. 3600 IN NS ns1.secure.optout.example.
237secure.optout.example. 3600 IN DS 64215 13 1 b88284d7a8d8605c398e8942262f97b9a5a31787
238ns1.secure.optout.example. 3600 IN A {prefix}.15
239 """,
240 'insecure.optout.example': """
241insecure.optout.example. 3600 IN SOA {soa}
242insecure.optout.example. 3600 IN NS ns1.insecure.optout.example.
243ns1.insecure.optout.example. 3600 IN A {prefix}.15
244
245node1.insecure.optout.example. 3600 IN A 192.0.2.7
246 """,
247 'secure.optout.example': """
248secure.optout.example. 3600 IN SOA {soa}
249secure.optout.example. 3600 IN NS ns1.secure.optout.example.
250ns1.secure.optout.example. 3600 IN A {prefix}.15
251
252node1.secure.optout.example. 3600 IN A 192.0.2.8
3bebf5f0
PL
253 """,
254 'islandofsecurity.example': """
255islandofsecurity.example. 3600 IN SOA {soa}
256islandofsecurity.example. 3600 IN NS ns1.islandofsecurity.example.
257ns1.islandofsecurity.example. 3600 IN A {prefix}.9
258
259node1.islandofsecurity.example. 3600 IN A 192.0.2.20
3a7ddb0b
PL
260 """,
261 'undelegated.secure.example': """
262undelegated.secure.example. 3600 IN SOA {soa}
263undelegated.secure.example. 3600 IN NS ns1.undelegated.secure.example.
264
265node1.undelegated.secure.example. 3600 IN A 192.0.2.21
266 """,
267 'undelegated.insecure.example': """
268undelegated.insecure.example. 3600 IN SOA {soa}
269undelegated.insecure.example. 3600 IN NS ns1.undelegated.insecure.example.
270
271node1.undelegated.insecure.example. 3600 IN A 192.0.2.22
272 """,
c51c551e 273
a79f06c4
OM
274 'delay1.example': """
275delay1.example. 3600 IN SOA {soa}
276delay1.example. 3600 IN NS n1.delay1.example.
277ns1.delay1.example. 3600 IN A {prefix}.16
278*.delay1.example. 0 LUA TXT ";" "local socket=require('socket')" "socket.sleep(tonumber(qname:getRawLabels()[1])/10)" "return 'a'"
279 """,
280
281 'delay2.example': """
282delay2.example. 3600 IN SOA {soa}
283delay2.example. 3600 IN NS n1.delay2.example.
284ns1.delay2.example. 3600 IN A {prefix}.17
285*.delay2.example. 0 LUA TXT ";" "local socket=require('socket')" "socket.sleep(tonumber(qname:getRawLabels()[1])/10)" "return 'a'"
c51c551e 286 """
7568b07d
PL
287 }
288
289 # The private keys for the zones (note that DS records should go into
290 # the zonecontent in _zones
291 _zone_keys = {
292 'ROOT': """
293Private-key-format: v1.2
294Algorithm: 13 (ECDSAP256SHA256)
295PrivateKey: rhWuEydDz3QaIspSVj683B8Xq5q/ozzA38XUgzD4Fbo=
296 """,
297
298 'example': """
299Private-key-format: v1.2
300Algorithm: 13 (ECDSAP256SHA256)
301PrivateKey: Lt0v0Gol3pRUFM7fDdcy0IWN0O/MnEmVPA+VylL8Y4U=
302 """,
303
304 'secure.example': """
305Private-key-format: v1.2
306Algorithm: 13 (ECDSAP256SHA256)
307PrivateKey: 1G4WRoOFJJXk+fotDCHVORtJmIG2OUhKi8AO2jDPGZA=
308 """,
309
310 'bogus.example': """
311Private-key-format: v1.2
312Algorithm: 13 (ECDSAP256SHA256)
313PrivateKey: f5jV7Q8kd5hDpMWObsuQ6SQda0ftf+JrO3uZwEg6nVw=
314 """,
11886ab9
PL
315
316 'optout.example': """
317Private-key-format: v1.2
318Algorithm: 13 (ECDSAP256SHA256)
319PrivateKey: efmq9G+J4Y2iPnIBRwJiy6Z/nIHSzpsCy/7XHhlS19A=
320 """,
321
322 'secure.optout.example': """
323Private-key-format: v1.2
324Algorithm: 13 (ECDSAP256SHA256)
325PrivateKey: xcNUxt1Knj14A00lKQFDboluiJyM2f7FxpgsQaQ3AQ4=
3bebf5f0
PL
326 """,
327
328 'islandofsecurity.example': """
329Private-key-format: v1.2
330Algorithm: 13 (ECDSAP256SHA256)
331PrivateKey: o9F5iix8V68tnMcuOaM2Lt8XXhIIY//SgHIHEePk6cM=
d5b8ccd4
PL
332 """,
333
334 'cname-secure.example': """
335Private-key-format: v1.2
336Algorithm: 13 (ECDSAP256SHA256)
337PrivateKey: kvoV/g4IO/tefSro+FLJ5UC7H3BUf0IUtZQSUOfQGyA=
9516e835
PL
338""",
339
340 'dname-secure.example': """
341Private-key-format: v1.2
342Algorithm: 13 (ECDSAP256SHA256)
343PrivateKey: Ep9uo6+wwjb4MaOmqq7LHav2FLrjotVOeZg8JT1Qk04=
4709a495
OM
344""",
345
346 'delay1.example': """
347Private-key-format: v1.2
348Algorithm: 13 (ECDSAP256SHA256)
349PrivateKey: Ep9uo6+wwjb4MaOmqq7LHav2FLrjotVOeZg8JT1Qk04=
350""",
351
352 'delay2.example': """
353Private-key-format: v1.2
354Algorithm: 13 (ECDSAP256SHA256)
355PrivateKey: Ep9uo6+wwjb4MaOmqq7LHav2FLrjotVOeZg8JT1Qk04=
d5b8ccd4 356"""
7568b07d
PL
357 }
358
359 # This dict is keyed with the suffix of the IP address and its value
360 # is a list of zones hosted on that IP. Note that delegations should
361 # go into the _zones's zonecontent
362 _auth_zones = {
c51c551e
OM
363 '8': {'threads': 1,
364 'zones': ['ROOT']},
365 '9': {'threads': 1,
366 'zones': ['secure.example', 'islandofsecurity.example']},
367 '10': {'threads': 1,
368 'zones': ['example']},
4709a495
OM
369
370 # 11 is used by CircleCI provided resolver
371
c51c551e
OM
372 '12': {'threads': 1,
373 'zones': ['bogus.example', 'undelegated.secure.example', 'undelegated.insecure.example']},
374 '13': {'threads': 1,
375 'zones': ['insecure.example', 'insecure.sub2.secure.example', 'dname-secure.example']},
376 '14': {'threads': 1,
377 'zones': ['optout.example']},
378 '15': {'threads': 1,
379 'zones': ['insecure.optout.example', 'secure.optout.example', 'cname-secure.example']},
a79f06c4
OM
380 '16': {'threads': 2,
381 'zones': ['delay1.example']},
382 '17': {'threads': 2,
4709a495
OM
383 'zones': ['delay2.example']},
384 '18': {'threads': 1,
385 'zones': ['example']}
7568b07d 386 }
0cfe4767
OM
387 # Other IPs used:
388 # 2: test_Interop.py
389 # 3-7: free?
390 # 19: free?
391 # 20: free?
392 # 21: test_ECS.py
393 # 22: test_EDNSBuffer.py
394 # 23: test_Lua.py
395 # 24: test_RoutingTag.py
7568b07d 396
cb54e9b5
PL
397 _auth_cmd = ['authbind',
398 os.environ['PDNS']]
399 _auth_env = {}
7568b07d
PL
400 _auths = {}
401
402 @classmethod
403 def createConfigDir(cls, confdir):
404 try:
405 shutil.rmtree(confdir)
406 except OSError as e:
407 if e.errno != errno.ENOENT:
408 raise
7a0ea291 409 os.mkdir(confdir, 0o755)
7568b07d
PL
410
411 @classmethod
412 def generateAuthZone(cls, confdir, zonename, zonecontent):
413 with open(os.path.join(confdir, '%s.zone' % zonename), 'w') as zonefile:
414 zonefile.write(zonecontent.format(prefix=cls._PREFIX, soa=cls._SOA))
415
416 @classmethod
417 def generateAuthNamedConf(cls, confdir, zones):
418 with open(os.path.join(confdir, 'named.conf'), 'w') as namedconf:
419 namedconf.write("""
420options {
421 directory "%s";
422};""" % confdir)
423 for zonename in zones:
424 zone = '.' if zonename == 'ROOT' else zonename
425
426 namedconf.write("""
427 zone "%s" {
428 type master;
429 file "%s.zone";
430 };""" % (zone, zonename))
431
432 @classmethod
c51c551e 433 def generateAuthConfig(cls, confdir, threads):
7568b07d
PL
434 bind_dnssec_db = os.path.join(confdir, 'bind-dnssec.sqlite3')
435
436 with open(os.path.join(confdir, 'pdns.conf'), 'w') as pdnsconf:
437 pdnsconf.write("""
438module-dir=../regression-tests/modules
439launch=bind
440daemon=no
7568b07d
PL
441bind-config={confdir}/named.conf
442bind-dnssec-db={bind_dnssec_db}
443socket-dir={confdir}
444cache-ttl=0
445negquery-cache-ttl=0
446query-cache-ttl=0
447log-dns-queries=yes
448log-dns-details=yes
449loglevel=9
c51c551e 450enable-lua-records
9516e835 451dname-processing=yes
c51c551e
OM
452distributor-threads={threads}""".format(confdir=confdir,
453 bind_dnssec_db=bind_dnssec_db,
454 threads=threads))
7568b07d
PL
455
456 pdnsutilCmd = [os.environ['PDNSUTIL'],
457 '--config-dir=%s' % confdir,
458 'create-bind-db',
459 bind_dnssec_db]
460
7a0ea291 461 print(' '.join(pdnsutilCmd))
7568b07d
PL
462 try:
463 subprocess.check_output(pdnsutilCmd, stderr=subprocess.STDOUT)
464 except subprocess.CalledProcessError as e:
ff0bc6a6 465 raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd, e.returncode, e.output))
7568b07d
PL
466
467 @classmethod
468 def secureZone(cls, confdir, zonename, key=None):
469 zone = '.' if zonename == 'ROOT' else zonename
470 if not key:
471 pdnsutilCmd = [os.environ['PDNSUTIL'],
472 '--config-dir=%s' % confdir,
473 'secure-zone',
474 zone]
475 else:
476 keyfile = os.path.join(confdir, 'dnssec.key')
477 with open(keyfile, 'w') as fdKeyfile:
478 fdKeyfile.write(key)
479
480 pdnsutilCmd = [os.environ['PDNSUTIL'],
481 '--config-dir=%s' % confdir,
482 'import-zone-key',
483 zone,
484 keyfile,
485 'active',
486 'ksk']
487
7a0ea291 488 print(' '.join(pdnsutilCmd))
7568b07d
PL
489 try:
490 subprocess.check_output(pdnsutilCmd, stderr=subprocess.STDOUT)
491 except subprocess.CalledProcessError as e:
ff0bc6a6 492 raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd, e.returncode, e.output))
7568b07d
PL
493
494 @classmethod
495 def generateAllAuthConfig(cls, confdir):
496 if cls._auth_zones:
c51c551e
OM
497 for auth_suffix, zoneinfo in cls._auth_zones.items():
498 threads = zoneinfo['threads']
499 zones = zoneinfo['zones']
7568b07d
PL
500 authconfdir = os.path.join(confdir, 'auth-%s' % auth_suffix)
501
502 os.mkdir(authconfdir)
503
c51c551e 504 cls.generateAuthConfig(authconfdir, threads)
7568b07d
PL
505 cls.generateAuthNamedConf(authconfdir, zones)
506
11886ab9
PL
507 for zone in zones:
508 cls.generateAuthZone(authconfdir,
509 zone,
510 cls._zones[zone])
511 if cls._zone_keys.get(zone, None):
512 cls.secureZone(authconfdir, zone, cls._zone_keys.get(zone))
7568b07d
PL
513
514 @classmethod
515 def startAllAuth(cls, confdir):
516 if cls._auth_zones:
517 for auth_suffix, _ in cls._auth_zones.items():
518 authconfdir = os.path.join(confdir, 'auth-%s' % auth_suffix)
519 ipaddress = cls._PREFIX + '.' + auth_suffix
520 cls.startAuth(authconfdir, ipaddress)
521
522 @classmethod
523 def startAuth(cls, confdir, ipaddress):
524 print("Launching pdns_server..")
11886ab9 525 authcmd = list(cls._auth_cmd)
cb54e9b5
PL
526 authcmd.append('--config-dir=%s' % confdir)
527 authcmd.append('--local-address=%s' % ipaddress)
fb027663 528 if (confdir[-4:] == "ROOT") and have_ipv6():
8064abbb
PL
529 authcmd.append('--local-ipv6=::1')
530 else:
531 authcmd.append('--local-ipv6=')
7568b07d
PL
532 print(' '.join(authcmd))
533
534 logFile = os.path.join(confdir, 'pdns.log')
535 with open(logFile, 'w') as fdLog:
536 cls._auths[ipaddress] = subprocess.Popen(authcmd, close_fds=True,
cb54e9b5
PL
537 stdout=fdLog, stderr=fdLog,
538 env=cls._auth_env)
7568b07d
PL
539
540 time.sleep(2)
541
542 if cls._auths[ipaddress].poll() is not None:
543 try:
544 cls._auths[ipaddress].kill()
545 except OSError as e:
546 if e.errno != errno.ESRCH:
547 raise
548 with open(logFile, 'r') as fdLog:
7a0ea291 549 print(fdLog.read())
7568b07d
PL
550 sys.exit(cls._auths[ipaddress].returncode)
551
552 @classmethod
553 def generateRecursorConfig(cls, confdir):
554 params = tuple([getattr(cls, param) for param in cls._config_params])
555 if len(params):
556 print(params)
557
558 recursorconf = os.path.join(confdir, 'recursor.conf')
559
560 with open(recursorconf, 'w') as conf:
561 conf.write("# Autogenerated by recursortests.py\n")
562 conf.write(cls._config_template_default)
563 conf.write(cls._config_template % params)
564 conf.write("\n")
565 conf.write("socket-dir=%s\n" % confdir)
566 if cls._lua_config_file or cls._root_DS:
567 luaconfpath = os.path.join(confdir, 'conffile.lua')
568 with open(luaconfpath, 'w') as luaconf:
569 if cls._root_DS:
8f29eeaa 570 luaconf.write("addTA('.', '%s')\n" % cls._root_DS)
7568b07d
PL
571 if cls._lua_config_file:
572 luaconf.write(cls._lua_config_file)
573 conf.write("lua-config-file=%s\n" % luaconfpath)
18e0e4df
RG
574 if cls._lua_dns_script_file:
575 luascriptpath = os.path.join(confdir, 'dnsscript.lua')
576 with open(luascriptpath, 'w') as luascript:
577 luascript.write(cls._lua_dns_script_file)
578 conf.write("lua-dns-script=%s\n" % luascriptpath)
7568b07d
PL
579 if cls._roothints:
580 roothintspath = os.path.join(confdir, 'root.hints')
581 with open(roothintspath, 'w') as roothints:
582 roothints.write(cls._roothints)
583 conf.write("hint-file=%s\n" % roothintspath)
584
962a980d
PL
585 @classmethod
586 def startResponders(cls):
587 pass
588
7568b07d
PL
589 @classmethod
590 def startRecursor(cls, confdir, port):
591 print("Launching pdns_recursor..")
592 recursorcmd = [os.environ['PDNSRECURSOR'],
593 '--config-dir=%s' % confdir,
962a980d
PL
594 '--local-port=%s' % port,
595 '--security-poll-suffix=']
7568b07d
PL
596 print(' '.join(recursorcmd))
597
598 logFile = os.path.join(confdir, 'recursor.log')
599 with open(logFile, 'w') as fdLog:
600 cls._recursor = subprocess.Popen(recursorcmd, close_fds=True,
601 stdout=fdLog, stderr=fdLog)
602
603 if 'PDNSRECURSOR_FAST_TESTS' in os.environ:
604 delay = 0.5
605 else:
606 delay = cls._recursorStartupDelay
607
608 time.sleep(delay)
609
610 if cls._recursor.poll() is not None:
611 try:
612 cls._recursor.kill()
613 except OSError as e:
614 if e.errno != errno.ESRCH:
615 raise
616 with open(logFile, 'r') as fdLog:
7a0ea291 617 print(fdLog.read())
7568b07d
PL
618 sys.exit(cls._recursor.returncode)
619
620 @classmethod
621 def wipeRecursorCache(cls, confdir):
622 rec_controlCmd = [os.environ['RECCONTROL'],
623 '--config-dir=%s' % confdir,
624 'wipe-cache',
625 '.$']
626 try:
627 subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT)
628 except subprocess.CalledProcessError as e:
ff0bc6a6 629 raise AssertionError('%s failed (%d): %s' % (rec_controlCmd, e.returncode, e.output))
7568b07d
PL
630
631 @classmethod
632 def setUpSockets(cls):
633 print("Setting up UDP socket..")
634 cls._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
635 cls._sock.settimeout(2.0)
636 cls._sock.connect(("127.0.0.1", cls._recursorPort))
637
638 @classmethod
639 def setUpClass(cls):
640 cls.setUpSockets()
962a980d
PL
641
642 cls.startResponders()
643
7568b07d
PL
644 confdir = os.path.join('configs', cls._confdir)
645 cls.createConfigDir(confdir)
646 cls.generateAllAuthConfig(confdir)
647 cls.startAllAuth(confdir)
648
649 cls.generateRecursorConfig(confdir)
650 cls.startRecursor(confdir, cls._recursorPort)
651
652 print("Launching tests..")
653
654 @classmethod
655 def tearDownClass(cls):
656 cls.tearDownRecursor()
657 cls.tearDownAuth()
962a980d
PL
658 cls.tearDownResponders()
659
660 @classmethod
661 def tearDownResponders(cls):
662 pass
7568b07d
PL
663
664 @classmethod
665 def tearDownAuth(cls):
666 if 'PDNSRECURSOR_FAST_TESTS' in os.environ:
667 delay = 0.1
668 else:
669 delay = 1.0
670
671 for _, auth in cls._auths.items():
672 try:
673 auth.terminate()
674 if auth.poll() is None:
675 time.sleep(delay)
676 if auth.poll() is None:
677 auth.kill()
678 auth.wait()
679 except OSError as e:
680 if e.errno != errno.ESRCH:
681 raise
682
683 @classmethod
684 def tearDownRecursor(cls):
685 if 'PDNSRECURSOR_FAST_TESTS' in os.environ:
686 delay = 0.1
687 else:
688 delay = 1.0
689 try:
690 if cls._recursor:
691 cls._recursor.terminate()
692 if cls._recursor.poll() is None:
693 time.sleep(delay)
694 if cls._recursor.poll() is None:
695 cls._recursor.kill()
696 cls._recursor.wait()
697 except OSError as e:
698 # There is a race-condition with the poll() and
699 # kill() statements, when the process is dead on the
700 # kill(), this is fine
701 if e.errno != errno.ESRCH:
702 raise
703
7568b07d 704 @classmethod
fb611f07 705 def sendUDPQuery(cls, query, timeout=2.0, decode=True, fwparams=dict()):
7568b07d
PL
706 if timeout:
707 cls._sock.settimeout(timeout)
708
709 try:
710 cls._sock.send(query.to_wire())
711 data = cls._sock.recv(4096)
712 except socket.timeout:
713 data = None
714 finally:
715 if timeout:
716 cls._sock.settimeout(None)
717
718 message = None
719 if data:
fb611f07
RG
720 if not decode:
721 return data
554c69f9 722 message = dns.message.from_wire(data, **fwparams)
7568b07d
PL
723 return message
724
725 @classmethod
726 def sendTCPQuery(cls, query, timeout=2.0):
727 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
728 if timeout:
729 sock.settimeout(timeout)
730
731 sock.connect(("127.0.0.1", cls._recursorPort))
732
733 try:
734 wire = query.to_wire()
735 sock.send(struct.pack("!H", len(wire)))
736 sock.send(wire)
737 data = sock.recv(2)
738 if data:
739 (datalen,) = struct.unpack("!H", data)
740 data = sock.recv(datalen)
741 except socket.timeout as e:
742 print("Timeout: %s" % (str(e)))
743 data = None
744 except socket.error as e:
745 print("Network error: %s" % (str(e)))
746 data = None
747 finally:
748 sock.close()
749
750 message = None
751 if data:
752 message = dns.message.from_wire(data)
753 return message
754
c51c551e
OM
755 @classmethod
756 def sendTCPQueries(cls, queries, timeout=2.0):
757 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
758 if timeout:
759 sock.settimeout(timeout)
760
761 sock.connect(("127.0.0.1", cls._recursorPort))
762 data = []
763 try:
764 for query in queries:
765 wire = query.to_wire()
766 sock.send(struct.pack("!H", len(wire)))
767 sock.send(wire)
768 for i in range(len(queries)):
769 try:
770 datalen = sock.recv(2)
771 if datalen:
772 (datalen,) = struct.unpack("!H", datalen)
773 data.append(sock.recv(datalen))
774 except socket.timeout as e:
775 continue
776 except socket.error as e:
777 print("Network error: %s" % (str(e)))
778 data = None
779 finally:
780 sock.close()
781
782 messages = []
783 for d in data:
784 messages.append(dns.message.from_wire(d))
785 return messages
786
7568b07d
PL
787 def setUp(self):
788 # This function is called before every tests
753a8109 789 super(RecursorTest, self).setUp()
7568b07d
PL
790
791 ## Functions for comparisons
792 def assertMessageHasFlags(self, msg, flags, ednsflags=[]):
793 """Asserts that msg has all the flags from flags set
794
795 @param msg: the dns.message.Message to check
796 @param flags: a list of strings with flag mnemonics (like ['RD', 'RA'])
797 @param ednsflags: a list of strings with edns-flag mnemonics (like ['DO'])"""
798
799 if not isinstance(msg, dns.message.Message):
800 raise TypeError("msg is not a dns.message.Message")
801
802 if isinstance(flags, list):
803 for elem in flags:
804 if not isinstance(elem, str):
805 raise TypeError("flags is not a list of strings")
806 else:
807 raise TypeError("flags is not a list of strings")
808
809 if isinstance(ednsflags, list):
810 for elem in ednsflags:
811 if not isinstance(elem, str):
812 raise TypeError("ednsflags is not a list of strings")
813 else:
814 raise TypeError("ednsflags is not a list of strings")
815
816 msgFlags = dns.flags.to_text(msg.flags).split()
817 missingFlags = [flag for flag in flags if flag not in msgFlags]
818
8b16b9c2 819 msgEdnsFlags = dns.flags.edns_to_text(msg.ednsflags).split()
7568b07d
PL
820 missingEdnsFlags = [ednsflag for ednsflag in ednsflags if ednsflag not in msgEdnsFlags]
821
822 if len(missingFlags) or len(missingEdnsFlags) or len(msgFlags) > len(flags):
823 raise AssertionError("Expected flags '%s' (EDNS: '%s'), found '%s' (EDNS: '%s') in query %s" %
824 (' '.join(flags), ' '.join(ednsflags),
825 ' '.join(msgFlags), ' '.join(msgEdnsFlags),
826 msg.question[0]))
827
828 def assertMessageIsAuthenticated(self, msg):
829 """Asserts that the message has the AD bit set
830
831 @param msg: the dns.message.Message to check"""
832
833 if not isinstance(msg, dns.message.Message):
834 raise TypeError("msg is not a dns.message.Message")
835
836 msgFlags = dns.flags.to_text(msg.flags)
837 self.assertTrue('AD' in msgFlags, "No AD flag found in the message for %s" % msg.question[0].name)
838
839 def assertRRsetInAnswer(self, msg, rrset):
840 """Asserts the rrset (without comparing TTL) exists in the
841 answer section of msg
842
843 @param msg: the dns.message.Message to check
844 @param rrset: a dns.rrset.RRset object"""
845
846 ret = ''
847 if not isinstance(msg, dns.message.Message):
848 raise TypeError("msg is not a dns.message.Message")
849
850 if not isinstance(rrset, dns.rrset.RRset):
851 raise TypeError("rrset is not a dns.rrset.RRset")
852
853 found = False
854 for ans in msg.answer:
855 ret += "%s\n" % ans.to_text()
856 if ans.match(rrset.name, rrset.rdclass, rrset.rdtype, 0, None):
de9650d1 857 self.assertEqual(ans, rrset, "'%s' != '%s'" % (ans.to_text(), rrset.to_text()))
7568b07d
PL
858 found = True
859
860 if not found:
6552b37b 861 raise AssertionError("RRset not found in answer\n\n%s" % ret)
7568b07d
PL
862
863 def assertMatchingRRSIGInAnswer(self, msg, coveredRRset, keys=None):
864 """Looks for coveredRRset in the answer section and if there is an RRSIG RRset
865 that covers that RRset. If keys is not None, this function will also try to
866 validate the RRset against the RRSIG
867
868 @param msg: The dns.message.Message to check
869 @param coveredRRset: The RRSet to check for
870 @param keys: a dictionary keyed by dns.name.Name with node or rdataset values to use for validation"""
871
872 if not isinstance(msg, dns.message.Message):
873 raise TypeError("msg is not a dns.message.Message")
874
875 if not isinstance(coveredRRset, dns.rrset.RRset):
876 raise TypeError("coveredRRset is not a dns.rrset.RRset")
877
878 msgRRsigRRSet = None
879 msgRRSet = None
880
881 ret = ''
882 for ans in msg.answer:
883 ret += ans.to_text() + "\n"
884
885 if ans.match(coveredRRset.name, coveredRRset.rdclass, coveredRRset.rdtype, 0, None):
886 msgRRSet = ans
887 if ans.match(coveredRRset.name, dns.rdataclass.IN, dns.rdatatype.RRSIG, coveredRRset.rdtype, None):
888 msgRRsigRRSet = ans
889 if msgRRSet and msgRRsigRRSet:
890 break
891
892 if not msgRRSet:
893 raise AssertionError("RRset for '%s' not found in answer" % msg.question[0].to_text())
894
895 if not msgRRsigRRSet:
896 raise AssertionError("No RRSIGs found in answer for %s:\nFull answer:\n%s" % (msg.question[0].to_text(), ret))
897
898 if keys:
899 try:
900 dns.dnssec.validate(msgRRSet, msgRRsigRRSet.to_rdataset(), keys)
901 except dns.dnssec.ValidationFailure as e:
902 raise AssertionError("Signature validation failed for %s:\n%s" % (msg.question[0].to_text(), e))
903
904 def assertNoRRSIGsInAnswer(self, msg):
905 """Checks if there are _no_ RRSIGs in the answer section of msg"""
906
907 if not isinstance(msg, dns.message.Message):
908 raise TypeError("msg is not a dns.message.Message")
909
910 ret = ""
911 for ans in msg.answer:
912 if ans.rdtype == dns.rdatatype.RRSIG:
913 ret += ans.name.to_text() + "\n"
914
915 if len(ret):
916 raise AssertionError("RRSIG found in answers for:\n%s" % ret)
917
918 def assertAnswerEmpty(self, msg):
919 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])))
920
921 def assertRcodeEqual(self, msg, rcode):
922 if not isinstance(msg, dns.message.Message):
923 raise TypeError("msg is not a dns.message.Message but a %s" % type(msg))
924
925 if not isinstance(rcode, int):
926 if isinstance(rcode, str):
927 rcode = dns.rcode.from_text(rcode)
928 else:
929 raise TypeError("rcode is neither a str nor int")
930
931 if msg.rcode() != rcode:
3d144e24
PD
932 msgRcode = dns.rcode.to_text(msg.rcode())
933 wantedRcode = dns.rcode.to_text(rcode)
7568b07d
PL
934
935 raise AssertionError("Rcode for %s is %s, expected %s." % (msg.question[0].to_text(), msgRcode, wantedRcode))
05537f80
PL
936
937 def assertAuthorityHasSOA(self, msg):
938 if not isinstance(msg, dns.message.Message):
939 raise TypeError("msg is not a dns.message.Message but a %s" % type(msg))
940
941 found = False
942 for rrset in msg.authority:
943 if rrset.rdtype == dns.rdatatype.SOA:
944 found = True
945 break
946
947 if not found:
948 raise AssertionError("No SOA record found in the authority section:\n%s" % msg.to_text())
f3f19ebe
PL
949
950 def assertResponseMatches(self, query, expectedRRs, response):
951 expectedResponse = dns.message.make_response(query)
952
953 if query.flags & dns.flags.RD:
954 expectedResponse.flags |= dns.flags.RA
955 if query.flags & dns.flags.CD:
956 expectedResponse.flags |= dns.flags.CD
957
958 expectedResponse.answer = expectedRRs
959 print(expectedResponse)
960 print(response)
4bfebc93 961 self.assertEqual(response, expectedResponse)
c5b0071d
RG
962
963 @classmethod
964 def sendQuery(cls, name, rdtype, useTCP=False):
965 """Helper function that creates the query"""
966 msg = dns.message.make_query(name, rdtype, want_dnssec=True)
967 msg.flags |= dns.flags.AD
968
969 if useTCP:
970 return cls.sendTCPQuery(msg)
971 return cls.sendUDPQuery(msg)
972
973 def createQuery(self, name, rdtype, flags, ednsflags):
974 """Helper function that creates the query with the specified flags.
975 The flags need to be strings (no checking is performed atm)"""
976 msg = dns.message.make_query(name, rdtype)
977 msg.flags = dns.flags.from_text(flags)
978 msg.flags += dns.flags.from_text('RD')
979 msg.use_edns(edns=0, ednsflags=dns.flags.edns_from_text(ednsflags))
980 return msg
9166ee1b
RG
981
982 @classmethod
983 def sendUDPQueryWithProxyProtocol(cls, query, v6, source, destination, sourcePort, destinationPort, values=[], timeout=2.0):
984 queryPayload = query.to_wire()
985 ppPayload = ProxyProtocol.getPayload(False, False, v6, source, destination, sourcePort, destinationPort, values)
986 payload = ppPayload + queryPayload
987
988 if timeout:
989 cls._sock.settimeout(timeout)
990
991 try:
992 cls._sock.send(payload)
993 data = cls._sock.recv(4096)
994 except socket.timeout:
995 data = None
996 finally:
997 if timeout:
998 cls._sock.settimeout(None)
999
1000 message = None
1001 if data:
1002 message = dns.message.from_wire(data)
1003 return message
1004
1005 @classmethod
1006 def sendTCPQueryWithProxyProtocol(cls, query, v6, source, destination, sourcePort, destinationPort, values=[], timeout=2.0):
1007 queryPayload = query.to_wire()
1008 ppPayload = ProxyProtocol.getPayload(False, False, v6, source, destination, sourcePort, destinationPort, values)
1009
1010 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1011 if timeout:
1012 sock.settimeout(timeout)
1013
1014 sock.connect(("127.0.0.1", cls._recursorPort))
1015
1016 try:
1017 sock.send(ppPayload)
1018 sock.send(struct.pack("!H", len(queryPayload)))
1019 sock.send(queryPayload)
1020 data = sock.recv(2)
1021 if data:
1022 (datalen,) = struct.unpack("!H", data)
1023 data = sock.recv(datalen)
1024 except socket.timeout as e:
1025 print("Timeout: %s" % (str(e)))
1026 data = None
1027 except socket.error as e:
1028 print("Network error: %s" % (str(e)))
1029 data = None
1030 finally:
1031 sock.close()
1032
1033 message = None
1034 if data:
1035 message = dns.message.from_wire(data)
1036 return message