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