3 from __future__
import print_function
16 from proxyprotocol
import ProxyProtocol
18 from eqdnsmessage
import AssertEqualDNSMessageMixin
23 Try to make an IPv6 socket and bind it, if it fails, no ipv6...
26 sock
= socket
.socket(socket
.AF_INET6
, socket
.SOCK_DGRAM
)
27 sock
.bind(('::1', 56581))
35 class RecursorTest(AssertEqualDNSMessageMixin
, unittest
.TestCase
):
37 Setup all recursors and auths required for the tests
46 _PREFIX
= os
.environ
['PREFIX']
48 _config_template_default
= """
52 local-address=127.0.0.1
54 packetcache-servfail-ttl=15
62 _config_template_yaml_default
= """
82 statistics_interval: 0
84 _config_template
= """
87 _lua_config_file
= None
88 _lua_dns_script_file
= None
91 ns.root. 3600 IN A %s.8
92 ns.root. 3600 IN AAAA ::1
94 _root_DS
= "63149 13 1 a59da3f5c1b97fcd5fa2b3b2b0ac91d38a60d33a"
96 # The default SOA for zones in the authoritative servers
97 _SOA
= "ns1.example.net. hostmaster.example.net. 1 3600 1800 1209600 300"
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
106 . 3600 IN NS ns.root.
107 ns.root. 3600 IN A {prefix}.8
109 example. 3600 IN NS ns1.example.
110 example. 3600 IN NS ns2.example.
111 example. 3600 IN DS 53174 13 1 50c9e913818767c236c06c2d8272723cb78cbf26
113 ns1.example. 3600 IN A {prefix}.10
114 ns2.example. 3600 IN A {prefix}.18
117 example. 3600 IN SOA {soa}
118 example. 3600 IN NS ns1.example.
119 example. 3600 IN NS ns2.example.
120 ns1.example. 3600 IN A {prefix}.10
121 ns2.example. 3600 IN A {prefix}.18
123 secure.example. 3600 IN NS ns.secure.example.
124 secure.example. 3600 IN DS 64723 13 1 53eb985040d3a89bacf29dbddb55a65834706f33
125 ns.secure.example. 3600 IN A {prefix}.9
127 cname-secure.example. 3600 IN NS ns.cname-secure.example.
128 cname-secure.example. 3600 IN DS 49148 13 1 a10314452d5ec4d97fcc6d7e275d217261fe790f
129 ns.cname-secure.example. 3600 IN A {prefix}.15
131 dname-secure.example. 3600 IN NS ns.dname-secure.example.
132 dname-secure.example. 3600 IN DS 42043 13 2 11c67f46b7c4d5968bc5f6cc944d58377b762bda53ddb4f3a6dbe6faf7a9940f
133 ns.dname-secure.example. 3600 IN A {prefix}.13
135 bogus.example. 3600 IN NS ns.bogus.example.
136 bogus.example. 3600 IN DS 65034 13 1 6df3bb50ea538e90eacdd7ae5419730783abb0ee
137 ns.bogus.example. 3600 IN A {prefix}.12
139 insecure.example. 3600 IN NS ns.insecure.example.
140 ns.insecure.example. 3600 IN A {prefix}.13
142 optout.example. 3600 IN NS ns1.optout.example.
143 optout.example. 3600 IN DS 59332 13 1 e664f886ae1b5df03d918bc1217d22afc29925b9
144 ns1.optout.example. 3600 IN A {prefix}.14
146 postresolve_ffi.example. 3600 IN A 1.2.3.4
147 postresolve_ffi.example. 3600 IN A 1.2.3.5
148 postresolve_ffi.example. 3600 IN AAAA ::1
149 postresolve_ffi.example. 3600 IN AAAA ::2
151 insecure-formerr.example. 3600 IN NS ns1.insecure-formerr.example.
152 ns1.insecure-formerr.example. 3600 IN A {prefix}.2
154 ecs-echo.example. 3600 IN NS ns1.ecs-echo.example.
155 ns1.ecs-echo.example. 3600 IN A {prefix}.21
157 islandofsecurity.example. 3600 IN NS ns1.islandofsecurity.example.
158 ns1.islandofsecurity.example. 3600 IN A {prefix}.9
160 sortcname.example. 3600 IN CNAME sort
161 sort.example. 3600 IN A 17.38.42.80
162 sort.example. 3600 IN A 192.168.0.1
163 sort.example. 3600 IN A 17.238.240.5
164 sort.example. 3600 IN MX 25 mx
166 delay1.example. 3600 IN NS ns1.delay1.example.
167 ns1.delay1.example. 3600 IN A {prefix}.16
168 delay1.example. 3600 IN DS 42043 13 2 7319fa605cf117f36e3de070157577ebb9a05a1d1f963d80eda55b5d6e793eb2
170 delay2.example. 3600 IN NS ns1.delay2.example.
171 ns1.delay2.example. 3600 IN A {prefix}.17
172 delay2.example. 3600 IN DS 42043 13 2 60a047b87740c8564c21d5fd34626c10a77a6c41e3b34564230119c2f13937b8
174 cname-nxd.example. 3600 IN CNAME cname-nxd-target.example.
175 cname-nxd-target.example. 3600 IN A 192.0.2.100
176 cname-nodata.example. 3600 IN CNAME cname-nodata-target.example.
177 cname-nodata-target.example. 3600 IN A 192.0.2.101
178 cname-custom-a.example. 3600 IN CNAME cname-custom-a-target.example.
179 cname-custom-a-target.example. 3600 IN A 192.0.2.102
181 'secure.example': """
182 secure.example. 3600 IN SOA {soa}
183 secure.example. 3600 IN NS ns.secure.example.
184 ns.secure.example. 3600 IN A {prefix}.9
185 secure.example. 3600 IN MX 10 mx1.secure.example.
186 secure.example. 3600 IN MX 20 mx2.secure.example.
187 sub.secure.example. 3600 IN MX 10 mx1.secure.example.
188 sub.secure.example. 3600 IN MX 20 mx2.secure.example.
190 naptr.secure.example. 60 IN NAPTR 10 10 "a" "X" "A" s1.secure.example.
191 naptr.secure.example. 60 IN NAPTR 10 10 "s" "Y" "B" service1.secure.example.
192 naptr.secure.example. 60 IN NAPTR 10 10 "s" "Z" "C" service2.secure.example.
193 service1.secure.example. 60 IN SRV 20 100 8080 a.secure.example.
194 service2.secure.example. 60 IN SRV 20 100 8080 b.secure.example.
197 secure.example. 3600 IN A 192.0.2.17
198 mx1.secure.example. 3600 IN A 192.0.2.18
199 mx2.secure.example. 3600 IN AAAA 1::2
200 s1.secure.example. 3600 IN A 192.0.2.19
201 a.secure.example. 3600 IN A 192.0.2.20
202 a.secure.example. 3600 IN A 192.0.2.22
203 b.secure.example. 3600 IN A 192.0.2.21
204 b.secure.example. 3600 IN AAAA 1::3
206 host1.secure.example. 3600 IN A 192.0.2.2
207 cname.secure.example. 3600 IN CNAME host1.secure.example.
208 cname-to-insecure.secure.example. 3600 IN CNAME node1.insecure.example.
209 cname-to-bogus.secure.example. 3600 IN CNAME ted.bogus.example.
210 cname-to-islandofsecurity.secure.example. 3600 IN CNAME node1.islandofsecurity.example.
212 host1.sub.secure.example. 3600 IN A 192.0.2.11
215 sub2.secure.example. 3600 IN CNAME doesnotmatter.insecure.example.
216 insecure.sub2.secure.example. 3600 IN NS ns1.insecure.example.
218 *.wildcard.secure.example. 3600 IN A 192.0.2.10
220 *.cnamewildcard.secure.example. 3600 IN CNAME host1.secure.example.
222 *.cnamewildcardnxdomain.secure.example. 3600 IN CNAME doesnotexist.secure.example.
224 cname-to-formerr.secure.example. 3600 IN CNAME host1.insecure-formerr.example.
226 dname-secure.secure.example. 3600 IN DNAME dname-secure.example.
227 dname-insecure.secure.example. 3600 IN DNAME insecure.example.
228 dname-bogus.secure.example. 3600 IN DNAME bogus.example.
230 non-apex-dnskey.secure.example. 3600 IN DNSKEY 257 3 13 CT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==
231 non-apex-dnskey2.secure.example. 3600 IN DNSKEY 256 3 13 CT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==
232 non-apex-dnskey3.secure.example. 3600 IN DNSKEY 256 3 13 DT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==
234 'dname-secure.example': """
235 dname-secure.example. 3600 IN SOA {soa}
236 dname-secure.example. 3600 IN NS ns.dname-secure.example.
237 ns.dname-secure.example. 3600 IN A {prefix}.13
239 host1.dname-secure.example. IN A 192.0.2.21
241 cname-to-secure.dname-secure.example. 3600 IN CNAME host1.secure.example.
242 cname-to-insecure.dname-secure.example. 3600 IN CNAME node1.insecure.example.
243 cname-to-bogus.dname-secure.example. 3600 IN CNAME ted.bogus.example.
245 'cname-secure.example': """
246 cname-secure.example. 3600 IN SOA {soa}
247 cname-secure.example. 3600 IN NS ns.cname-secure.example.
248 ns.cname-secure.example. 3600 IN A {prefix}.15
249 cname-secure.example. 3600 IN CNAME secure.example.
252 bogus.example. 3600 IN SOA {soa}
253 bogus.example. 3600 IN NS ns1.bogus.example.
254 ns1.bogus.example. 3600 IN A {prefix}.12
255 ted.bogus.example. 3600 IN A 192.0.2.1
256 bill.bogus.example. 3600 IN AAAA 2001:db8:12::3
258 'insecure.sub2.secure.example': """
259 insecure.sub2.secure.example. 3600 IN SOA {soa}
260 insecure.sub2.secure.example. 3600 IN NS ns1.insecure.example.
262 node1.insecure.sub2.secure.example. 3600 IN A 192.0.2.18
264 'insecure.example': """
265 insecure.example. 3600 IN SOA {soa}
266 insecure.example. 3600 IN NS ns1.insecure.example.
267 ns1.insecure.example. 3600 IN A {prefix}.13
269 node1.insecure.example. 3600 IN A 192.0.2.6
271 cname-to-secure.insecure.example. 3600 IN CNAME host1.secure.example.
273 dname-to-secure.insecure.example. 3600 IN DNAME dname-secure.example.
275 'optout.example': """
276 optout.example. 3600 IN SOA {soa}
277 optout.example. 3600 IN NS ns1.optout.example.
278 ns1.optout.example. 3600 IN A {prefix}.14
280 insecure.optout.example. 3600 IN NS ns1.insecure.optout.example.
281 ns1.insecure.optout.example. 3600 IN A {prefix}.15
283 secure.optout.example. 3600 IN NS ns1.secure.optout.example.
284 secure.optout.example. 3600 IN DS 64215 13 1 b88284d7a8d8605c398e8942262f97b9a5a31787
285 ns1.secure.optout.example. 3600 IN A {prefix}.15
287 'insecure.optout.example': """
288 insecure.optout.example. 3600 IN SOA {soa}
289 insecure.optout.example. 3600 IN NS ns1.insecure.optout.example.
290 ns1.insecure.optout.example. 3600 IN A {prefix}.15
292 node1.insecure.optout.example. 3600 IN A 192.0.2.7
294 'secure.optout.example': """
295 secure.optout.example. 3600 IN SOA {soa}
296 secure.optout.example. 3600 IN NS ns1.secure.optout.example.
297 ns1.secure.optout.example. 3600 IN A {prefix}.15
299 node1.secure.optout.example. 3600 IN A 192.0.2.8
301 'islandofsecurity.example': """
302 islandofsecurity.example. 3600 IN SOA {soa}
303 islandofsecurity.example. 3600 IN NS ns1.islandofsecurity.example.
304 ns1.islandofsecurity.example. 3600 IN A {prefix}.9
306 node1.islandofsecurity.example. 3600 IN A 192.0.2.20
308 'undelegated.secure.example': """
309 undelegated.secure.example. 3600 IN SOA {soa}
310 undelegated.secure.example. 3600 IN NS ns1.undelegated.secure.example.
312 node1.undelegated.secure.example. 3600 IN A 192.0.2.21
314 'undelegated.insecure.example': """
315 undelegated.insecure.example. 3600 IN SOA {soa}
316 undelegated.insecure.example. 3600 IN NS ns1.undelegated.insecure.example.
318 node1.undelegated.insecure.example. 3600 IN A 192.0.2.22
321 'delay1.example': """
322 delay1.example. 3600 IN SOA {soa}
323 delay1.example. 3600 IN NS n1.delay1.example.
324 ns1.delay1.example. 3600 IN A {prefix}.16
325 8.delay1.example. 3600 IN A 192.0.2.100
326 *.delay1.example. 0 LUA TXT ";" "local socket=require('socket')" "socket.sleep(tonumber(qname:getRawLabels()[1])/10)" "return 'a'"
327 *.delay1.example. 0 LUA AAAA ";" "local socket=require('socket')" "socket.sleep(tonumber(qname:getRawLabels()[1])/10)" "return '1::2'"
330 'delay2.example': """
331 delay2.example. 3600 IN SOA {soa}
332 delay2.example. 3600 IN NS n1.delay2.example.
333 ns1.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'"
338 # The private keys for the zones (note that DS records should go into
339 # the zonecontent in _zones
342 Private-key-format: v1.2
343 Algorithm: 13 (ECDSAP256SHA256)
344 PrivateKey: rhWuEydDz3QaIspSVj683B8Xq5q/ozzA38XUgzD4Fbo=
348 Private-key-format: v1.2
349 Algorithm: 13 (ECDSAP256SHA256)
350 PrivateKey: Lt0v0Gol3pRUFM7fDdcy0IWN0O/MnEmVPA+VylL8Y4U=
353 'secure.example': """
354 Private-key-format: v1.2
355 Algorithm: 13 (ECDSAP256SHA256)
356 PrivateKey: 1G4WRoOFJJXk+fotDCHVORtJmIG2OUhKi8AO2jDPGZA=
360 Private-key-format: v1.2
361 Algorithm: 13 (ECDSAP256SHA256)
362 PrivateKey: f5jV7Q8kd5hDpMWObsuQ6SQda0ftf+JrO3uZwEg6nVw=
365 'optout.example': """
366 Private-key-format: v1.2
367 Algorithm: 13 (ECDSAP256SHA256)
368 PrivateKey: efmq9G+J4Y2iPnIBRwJiy6Z/nIHSzpsCy/7XHhlS19A=
371 'secure.optout.example': """
372 Private-key-format: v1.2
373 Algorithm: 13 (ECDSAP256SHA256)
374 PrivateKey: xcNUxt1Knj14A00lKQFDboluiJyM2f7FxpgsQaQ3AQ4=
377 'islandofsecurity.example': """
378 Private-key-format: v1.2
379 Algorithm: 13 (ECDSAP256SHA256)
380 PrivateKey: o9F5iix8V68tnMcuOaM2Lt8XXhIIY//SgHIHEePk6cM=
383 'cname-secure.example': """
384 Private-key-format: v1.2
385 Algorithm: 13 (ECDSAP256SHA256)
386 PrivateKey: kvoV/g4IO/tefSro+FLJ5UC7H3BUf0IUtZQSUOfQGyA=
389 'dname-secure.example': """
390 Private-key-format: v1.2
391 Algorithm: 13 (ECDSAP256SHA256)
392 PrivateKey: Ep9uo6+wwjb4MaOmqq7LHav2FLrjotVOeZg8JT1Qk04=
395 'delay1.example': """
396 Private-key-format: v1.2
397 Algorithm: 13 (ECDSAP256SHA256)
398 PrivateKey: Ep9uo6+wwjb4MaOmqq7LHav2FLrjotVOeZg8JT1Qk04=
401 'delay2.example': """
402 Private-key-format: v1.2
403 Algorithm: 13 (ECDSAP256SHA256)
404 PrivateKey: Ep9uo6+wwjb4MaOmqq7LHav2FLrjotVOeZg8JT1Qk04=
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
415 'zones': ['secure.example', 'islandofsecurity.example']},
417 'zones': ['example']},
419 # 11 is used by CircleCI provided resolver
422 'zones': ['bogus.example', 'undelegated.secure.example', 'undelegated.insecure.example']},
424 'zones': ['insecure.example', 'insecure.sub2.secure.example', 'dname-secure.example']},
426 'zones': ['optout.example']},
428 'zones': ['insecure.optout.example', 'secure.optout.example', 'cname-secure.example']},
430 'zones': ['delay1.example']},
432 'zones': ['delay2.example']},
434 'zones': ['example']}
442 # 22: test_EDNSBuffer.py
444 # 24: test_RoutingTag.py
446 _auth_cmd
= ['authbind',
452 def createConfigDir(cls
, confdir
):
454 shutil
.rmtree(confdir
)
456 if e
.errno
!= errno
.ENOENT
:
458 os
.mkdir(confdir
, 0o755)
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
))
466 def generateAuthNamedConf(cls
, confdir
, zones
):
467 with
open(os
.path
.join(confdir
, 'named.conf'), 'w') as namedconf
:
472 for zonename
in zones
:
473 zone
= '.' if zonename
== 'ROOT' else zonename
479 };""" % (zone
, zonename
))
482 def generateAuthConfig(cls
, confdir
, threads
):
483 bind_dnssec_db
= os
.path
.join(confdir
, 'bind-dnssec.sqlite3')
485 with
open(os
.path
.join(confdir
, 'pdns.conf'), 'w') as pdnsconf
:
487 module-dir=../regression-tests/modules
490 bind-config={confdir}/named.conf
491 bind-dnssec-db={bind_dnssec_db}
501 distributor-threads={threads}""".format(confdir
=confdir
,
502 bind_dnssec_db
=bind_dnssec_db
,
505 pdnsutilCmd
= [os
.environ
['PDNSUTIL'],
506 '--config-dir=%s' % confdir
,
510 print(' '.join(pdnsutilCmd
))
512 subprocess
.check_output(pdnsutilCmd
, stderr
=subprocess
.STDOUT
)
513 except subprocess
.CalledProcessError
as e
:
514 raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd
, e
.returncode
, e
.output
))
517 def secureZone(cls
, confdir
, zonename
, key
=None):
518 zone
= '.' if zonename
== 'ROOT' else zonename
520 pdnsutilCmd
= [os
.environ
['PDNSUTIL'],
521 '--config-dir=%s' % confdir
,
525 keyfile
= os
.path
.join(confdir
, 'dnssec.key')
526 with
open(keyfile
, 'w') as fdKeyfile
:
529 pdnsutilCmd
= [os
.environ
['PDNSUTIL'],
530 '--config-dir=%s' % confdir
,
537 print(' '.join(pdnsutilCmd
))
539 subprocess
.check_output(pdnsutilCmd
, stderr
=subprocess
.STDOUT
)
540 except subprocess
.CalledProcessError
as e
:
541 raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd
, e
.returncode
, e
.output
))
544 def generateAllAuthConfig(cls
, confdir
):
546 for auth_suffix
, zoneinfo
in cls
._auth
_zones
.items():
547 threads
= zoneinfo
['threads']
548 zones
= zoneinfo
['zones']
549 authconfdir
= os
.path
.join(confdir
, 'auth-%s' % auth_suffix
)
551 os
.mkdir(authconfdir
)
553 cls
.generateAuthConfig(authconfdir
, threads
)
554 cls
.generateAuthNamedConf(authconfdir
, zones
)
557 cls
.generateAuthZone(authconfdir
,
560 if cls
._zone
_keys
.get(zone
, None):
561 cls
.secureZone(authconfdir
, zone
, cls
._zone
_keys
.get(zone
))
564 def startAllAuth(cls
, confdir
):
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
)
572 def waitForTCPSocket(cls
, ipaddress
, port
):
573 for try_number
in range(0, 100):
575 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
577 sock
.connect((ipaddress
, port
))
580 except Exception as err
:
581 if err
.errno
!= errno
.ECONNREFUSED
:
582 print(f
'Error occurred: {try_number} {err}', file=sys
.stderr
)
586 def startAuth(cls
, confdir
, ipaddress
):
587 print("Launching pdns_server..")
588 authcmd
= list(cls
._auth
_cmd
)
589 authcmd
.append('--config-dir=%s' % confdir
)
591 # auth-8 is the auth serving the root, it gets an ipv6 address
592 if (confdir
[-6:] == "auth-8") and have_ipv6():
594 authcmd
.append('--local-address=%s' % ipconfig
)
595 print(' '.join(authcmd
))
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,
600 stdout
=fdLog
, stderr
=fdLog
,
603 cls
.waitForTCPSocket(ipaddress
, 53)
605 if cls
._auths
[ipaddress
].poll() is not None:
606 print(f
"\n*** startAuth log for {logFile} ***")
607 with
open(logFile
, 'r') as fdLog
:
609 print(f
"*** End startAuth log for {logFile} ***")
610 raise AssertionError('%s failed (%d)' % (authcmd
, cls
._auths
[ipaddress
].returncode
))
613 def generateRecursorConfig(cls
, confdir
):
614 params
= tuple([getattr(cls
, param
) for param
in cls
._config
_params
])
618 recursorconf
= os
.path
.join(confdir
, 'recursor.conf')
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
)
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
:
630 luaconf
.write("addTA('.', '%s')\n" % cls
._root
_DS
)
631 if cls
._lua
_config
_file
:
632 luaconf
.write(cls
._lua
_config
_file
)
633 conf
.write("lua-config-file=%s\n" % luaconfpath
)
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
)
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
)
646 def generateRecursorYamlConfig(cls
, confdir
):
647 params
= tuple([getattr(cls
, param
) for param
in cls
._config
_params
])
651 recursorconf
= os
.path
.join(confdir
, 'recursor.yml')
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
)
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
:
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
)
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
)
684 def startResponders(cls
):
688 def startRecursor(cls
, confdir
, port
):
689 print("Launching pdns_recursor..")
690 recursorcmd
= [os
.environ
['PDNSRECURSOR'],
691 '--config-dir=%s' % confdir
,
692 '--local-port=%s' % port
,
693 '--security-poll-suffix=']
694 print(' '.join(recursorcmd
))
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
)
701 cls
.waitForTCPSocket("127.0.0.1", port
)
703 if cls
._recursor
.poll() is not None:
704 print(f
"\n*** startRecursor log for {logFile} ***")
705 with
open(logFile
, 'r') as fdLog
:
707 print(f
"*** End startRecursor log for {logFile} ***")
708 raise AssertionError('%s failed (%d)' % (recursorcmd
, cls
._recursor
.returncode
))
711 def wipeRecursorCache(cls
, confdir
, name
='.$'):
712 rec_controlCmd
= [os
.environ
['RECCONTROL'],
713 '--config-dir=%s' % confdir
,
717 subprocess
.check_output(rec_controlCmd
, stderr
=subprocess
.STDOUT
)
718 except subprocess
.CalledProcessError
as e
:
719 raise AssertionError('%s failed (%d): %s' % (rec_controlCmd
, e
.returncode
, e
.output
))
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
))
732 cls
.startResponders()
734 confdir
= os
.path
.join('configs', cls
._confdir
)
735 cls
.createConfigDir(confdir
)
736 cls
.generateAllAuthConfig(confdir
)
737 cls
.startAllAuth(confdir
)
739 cls
.generateRecursorConfig(confdir
)
740 cls
.startRecursor(confdir
, cls
._recursorPort
)
742 print("Launching tests..")
745 def tearDownClass(cls
):
746 cls
.tearDownRecursor()
748 cls
.tearDownResponders()
751 def tearDownResponders(cls
):
755 def killProcess(cls
, p
):
756 # Don't try to kill it if it's already dead
757 if p
.poll() is not None:
761 for count
in range(10):
767 print("kill...", p
, file=sys
.stderr
)
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
:
778 def tearDownAuth(cls
):
779 for _
, auth
in cls
._auths
.items():
780 cls
.killProcess(auth
);
783 def tearDownRecursor(cls
):
784 cls
.killProcess(cls
._recursor
)
787 def sendUDPQuery(cls
, query
, timeout
=2.0, decode
=True, fwparams
=dict()):
789 cls
._sock
.settimeout(timeout
)
792 cls
._sock
.send(query
.to_wire())
793 data
= cls
._sock
.recv(4096)
794 except socket
.timeout
:
798 cls
._sock
.settimeout(None)
804 message
= dns
.message
.from_wire(data
, **fwparams
)
808 def sendTCPQuery(cls
, query
, timeout
=2.0):
809 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
811 sock
.settimeout(timeout
)
813 sock
.connect(("127.0.0.1", cls
._recursorPort
))
816 wire
= query
.to_wire()
817 sock
.send(struct
.pack("!H", len(wire
)))
821 (datalen
,) = struct
.unpack("!H", data
)
822 data
= sock
.recv(datalen
)
823 except socket
.timeout
as e
:
824 print("Timeout: %s" % (str(e
)))
826 except socket
.error
as e
:
827 print("Network error: %s" % (str(e
)))
834 message
= dns
.message
.from_wire(data
)
838 def sendTCPQueries(cls
, queries
, timeout
=2.0):
839 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
841 sock
.settimeout(timeout
)
843 sock
.connect(("127.0.0.1", cls
._recursorPort
))
846 for query
in queries
:
847 wire
= query
.to_wire()
848 sock
.send(struct
.pack("!H", len(wire
)))
850 for i
in range(len(queries
)):
852 datalen
= sock
.recv(2)
854 (datalen
,) = struct
.unpack("!H", datalen
)
855 data
.append(sock
.recv(datalen
))
856 except socket
.timeout
as e
:
858 except socket
.error
as e
:
859 print("Network error: %s" % (str(e
)))
866 messages
.append(dns
.message
.from_wire(d
))
870 # This function is called before every tests
871 super(RecursorTest
, self
).setUp()
873 ## Functions for comparisons
874 def assertMessageHasFlags(self
, msg
, flags
, ednsflags
=[]):
875 """Asserts that msg has all the flags from flags set
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'])"""
881 if not isinstance(msg
, dns
.message
.Message
):
882 raise TypeError("msg is not a dns.message.Message")
884 if isinstance(flags
, list):
886 if not isinstance(elem
, str):
887 raise TypeError("flags is not a list of strings")
889 raise TypeError("flags is not a list of strings")
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")
896 raise TypeError("ednsflags is not a list of strings")
898 msgFlags
= dns
.flags
.to_text(msg
.flags
).split()
899 missingFlags
= [flag
for flag
in flags
if flag
not in msgFlags
]
901 msgEdnsFlags
= dns
.flags
.edns_to_text(msg
.ednsflags
).split()
902 missingEdnsFlags
= [ednsflag
for ednsflag
in ednsflags
if ednsflag
not in msgEdnsFlags
]
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
),
910 def assertMessageIsAuthenticated(self
, msg
):
911 """Asserts that the message has the AD bit set
913 @param msg: the dns.message.Message to check"""
915 if not isinstance(msg
, dns
.message
.Message
):
916 raise TypeError("msg is not a dns.message.Message")
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
)
921 def assertRRsetInAnswer(self
, msg
, rrset
):
922 """Asserts the rrset (without comparing TTL) exists in the
923 answer section of msg
925 @param msg: the dns.message.Message to check
926 @param rrset: a dns.rrset.RRset object"""
929 if not isinstance(msg
, dns
.message
.Message
):
930 raise TypeError("msg is not a dns.message.Message")
932 if not isinstance(rrset
, dns
.rrset
.RRset
):
933 raise TypeError("rrset is not a dns.rrset.RRset")
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):
939 self
.assertEqual(ans
, rrset
, "'%s' != '%s'" % (ans
.to_text(), rrset
.to_text()))
943 raise AssertionError("RRset not found in answer\n\n%s" % ret
)
945 def assertRRsetInAdditional(self
, msg
, rrset
):
946 """Asserts the rrset (without comparing TTL) exists in the
947 additional section of msg
949 @param msg: the dns.message.Message to check
950 @param rrset: a dns.rrset.RRset object"""
953 if not isinstance(msg
, dns
.message
.Message
):
954 raise TypeError("msg is not a dns.message.Message")
956 if not isinstance(rrset
, dns
.rrset
.RRset
):
957 raise TypeError("rrset is not a dns.rrset.RRset")
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()))
967 raise AssertionError("RRset not found in additional section\n\n%s" % ret
)
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
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"""
978 if not isinstance(msg
, dns
.message
.Message
):
979 raise TypeError("msg is not a dns.message.Message")
981 if not isinstance(coveredRRset
, dns
.rrset
.RRset
):
982 raise TypeError("coveredRRset is not a dns.rrset.RRset")
988 for ans
in msg
.answer
:
989 ret
+= ans
.to_text() + "\n"
991 if ans
.match(coveredRRset
.name
, coveredRRset
.rdclass
, coveredRRset
.rdtype
, 0, None):
993 if ans
.match(coveredRRset
.name
, dns
.rdataclass
.IN
, dns
.rdatatype
.RRSIG
, coveredRRset
.rdtype
, None):
995 if msgRRSet
and msgRRsigRRSet
:
999 raise AssertionError("RRset for '%s' not found in answer" % msg
.question
[0].to_text())
1001 if not msgRRsigRRSet
:
1002 raise AssertionError("No RRSIGs found in answer for %s:\nFull answer:\n%s" % (msg
.question
[0].to_text(), ret
))
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
))
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
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"""
1019 if not isinstance(msg
, dns
.message
.Message
):
1020 raise TypeError("msg is not a dns.message.Message")
1022 if not isinstance(coveredRRset
, dns
.rrset
.RRset
):
1023 raise TypeError("coveredRRset is not a dns.rrset.RRset")
1025 msgRRsigRRSet
= None
1029 for ans
in msg
.additional
:
1030 ret
+= ans
.to_text() + "\n"
1032 if ans
.match(coveredRRset
.name
, coveredRRset
.rdclass
, coveredRRset
.rdtype
, 0, None):
1034 if ans
.match(coveredRRset
.name
, dns
.rdataclass
.IN
, dns
.rdatatype
.RRSIG
, coveredRRset
.rdtype
, None):
1036 if msgRRSet
and msgRRsigRRSet
:
1040 raise AssertionError("RRset for '%s' not found in additional" % msg
.question
[0].to_text())
1042 if not msgRRsigRRSet
:
1043 raise AssertionError("No RRSIGs found in additional for %s:\nFull answer:\n%s" % (msg
.question
[0].to_text(), ret
))
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
))
1051 def assertNoRRSIGsInAnswer(self
, msg
):
1052 """Checks if there are _no_ RRSIGs in the answer section of msg"""
1054 if not isinstance(msg
, dns
.message
.Message
):
1055 raise TypeError("msg is not a dns.message.Message")
1058 for ans
in msg
.answer
:
1059 if ans
.rdtype
== dns
.rdatatype
.RRSIG
:
1060 ret
+= ans
.name
.to_text() + "\n"
1063 raise AssertionError("RRSIG found in answers for:\n%s" % ret
)
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
])))
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
])))
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
))
1075 if not isinstance(rcode
, int):
1076 if isinstance(rcode
, str):
1077 rcode
= dns
.rcode
.from_text(rcode
)
1079 raise TypeError("rcode is neither a str nor int")
1081 if msg
.rcode() != rcode
:
1082 msgRcode
= dns
.rcode
.to_text(msg
.rcode())
1083 wantedRcode
= dns
.rcode
.to_text(rcode
)
1085 raise AssertionError("Rcode for %s is %s, expected %s." % (msg
.question
[0].to_text(), msgRcode
, wantedRcode
))
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
))
1092 for rrset
in msg
.authority
:
1093 if rrset
.rdtype
== dns
.rdatatype
.SOA
:
1098 raise AssertionError("No SOA record found in the authority section:\n%s" % msg
.to_text())
1100 def assertResponseMatches(self
, query
, expectedRRs
, response
):
1101 expectedResponse
= dns
.message
.make_response(query
)
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
1108 expectedResponse
.answer
= expectedRRs
1109 print(expectedResponse
)
1111 self
.assertEqual(response
, expectedResponse
)
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
1120 return cls
.sendTCPQuery(msg
)
1121 return cls
.sendUDPQuery(msg
)
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
))
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
1139 cls
._sock
.settimeout(timeout
)
1142 cls
._sock
.send(payload
)
1143 data
= cls
._sock
.recv(4096)
1144 except socket
.timeout
:
1148 cls
._sock
.settimeout(None)
1152 message
= dns
.message
.from_wire(data
)
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
)
1160 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
1162 sock
.settimeout(timeout
)
1164 sock
.connect(("127.0.0.1", cls
._recursorPort
))
1167 sock
.send(ppPayload
)
1168 sock
.send(struct
.pack("!H", len(queryPayload
)))
1169 sock
.send(queryPayload
)
1172 (datalen
,) = struct
.unpack("!H", data
)
1173 data
= sock
.recv(datalen
)
1174 except socket
.timeout
as e
:
1175 print("Timeout: %s" % (str(e
)))
1177 except socket
.error
as e
:
1178 print("Network error: %s" % (str(e
)))
1185 message
= dns
.message
.from_wire(data
)