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