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