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
= """
65 _lua_config_file
= None
66 _lua_dns_script_file
= None
69 ns.root. 3600 IN A %s.8
70 ns.root. 3600 IN AAAA ::1
72 _root_DS
= "63149 13 1 a59da3f5c1b97fcd5fa2b3b2b0ac91d38a60d33a"
74 # The default SOA for zones in the authoritative servers
75 _SOA
= "ns1.example.net. hostmaster.example.net. 1 3600 1800 1209600 300"
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
85 ns.root. 3600 IN A {prefix}.8
87 example. 3600 IN NS ns1.example.
88 example. 3600 IN NS ns2.example.
89 example. 3600 IN DS 53174 13 1 50c9e913818767c236c06c2d8272723cb78cbf26
91 ns1.example. 3600 IN A {prefix}.10
92 ns2.example. 3600 IN A {prefix}.18
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
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
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
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
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
117 insecure.example. 3600 IN NS ns.insecure.example.
118 ns.insecure.example. 3600 IN A {prefix}.13
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
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
129 insecure-formerr.example. 3600 IN NS ns1.insecure-formerr.example.
130 ns1.insecure-formerr.example. 3600 IN A {prefix}.2
132 ecs-echo.example. 3600 IN NS ns1.ecs-echo.example.
133 ns1.ecs-echo.example. 3600 IN A {prefix}.21
135 islandofsecurity.example. 3600 IN NS ns1.islandofsecurity.example.
136 ns1.islandofsecurity.example. 3600 IN A {prefix}.9
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
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
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
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
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 secure.example. 3600 IN MX 10 mx1.secure.example.
164 secure.example. 3600 IN MX 20 mx2.secure.example.
165 sub.secure.example. 3600 IN MX 10 mx1.secure.example.
166 sub.secure.example. 3600 IN MX 20 mx2.secure.example.
168 naptr.secure.example. 60 IN NAPTR 10 10 "a" "X" "A" s1.secure.example.
169 naptr.secure.example. 60 IN NAPTR 10 10 "s" "Y" "B" service1.secure.example.
170 naptr.secure.example. 60 IN NAPTR 10 10 "s" "Z" "C" service2.secure.example.
171 service1.secure.example. 60 IN SRV 20 100 8080 a.secure.example.
172 service2.secure.example. 60 IN SRV 20 100 8080 b.secure.example.
175 secure.example. 3600 IN A 192.0.2.17
176 mx1.secure.example. 3600 IN A 192.0.2.18
177 mx2.secure.example. 3600 IN AAAA 1::2
178 s1.secure.example. 3600 IN A 192.0.2.19
179 a.secure.example. 3600 IN A 192.0.2.20
180 a.secure.example. 3600 IN A 192.0.2.22
181 b.secure.example. 3600 IN A 192.0.2.21
182 b.secure.example. 3600 IN AAAA 1::3
184 host1.secure.example. 3600 IN A 192.0.2.2
185 cname.secure.example. 3600 IN CNAME host1.secure.example.
186 cname-to-insecure.secure.example. 3600 IN CNAME node1.insecure.example.
187 cname-to-bogus.secure.example. 3600 IN CNAME ted.bogus.example.
188 cname-to-islandofsecurity.secure.example. 3600 IN CNAME node1.islandofsecurity.example.
190 host1.sub.secure.example. 3600 IN A 192.0.2.11
193 sub2.secure.example. 3600 IN CNAME doesnotmatter.insecure.example.
194 insecure.sub2.secure.example. 3600 IN NS ns1.insecure.example.
196 *.wildcard.secure.example. 3600 IN A 192.0.2.10
198 *.cnamewildcard.secure.example. 3600 IN CNAME host1.secure.example.
200 *.cnamewildcardnxdomain.secure.example. 3600 IN CNAME doesnotexist.secure.example.
202 cname-to-formerr.secure.example. 3600 IN CNAME host1.insecure-formerr.example.
204 dname-secure.secure.example. 3600 IN DNAME dname-secure.example.
205 dname-insecure.secure.example. 3600 IN DNAME insecure.example.
206 dname-bogus.secure.example. 3600 IN DNAME bogus.example.
208 non-apex-dnskey.secure.example. 3600 IN DNSKEY 257 3 13 CT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==
209 non-apex-dnskey2.secure.example. 3600 IN DNSKEY 256 3 13 CT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==
210 non-apex-dnskey3.secure.example. 3600 IN DNSKEY 256 3 13 DT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==
212 'dname-secure.example': """
213 dname-secure.example. 3600 IN SOA {soa}
214 dname-secure.example. 3600 IN NS ns.dname-secure.example.
215 ns.dname-secure.example. 3600 IN A {prefix}.13
217 host1.dname-secure.example. IN A 192.0.2.21
219 cname-to-secure.dname-secure.example. 3600 IN CNAME host1.secure.example.
220 cname-to-insecure.dname-secure.example. 3600 IN CNAME node1.insecure.example.
221 cname-to-bogus.dname-secure.example. 3600 IN CNAME ted.bogus.example.
223 'cname-secure.example': """
224 cname-secure.example. 3600 IN SOA {soa}
225 cname-secure.example. 3600 IN NS ns.cname-secure.example.
226 ns.cname-secure.example. 3600 IN A {prefix}.15
227 cname-secure.example. 3600 IN CNAME secure.example.
230 bogus.example. 3600 IN SOA {soa}
231 bogus.example. 3600 IN NS ns1.bogus.example.
232 ns1.bogus.example. 3600 IN A {prefix}.12
233 ted.bogus.example. 3600 IN A 192.0.2.1
234 bill.bogus.example. 3600 IN AAAA 2001:db8:12::3
236 'insecure.sub2.secure.example': """
237 insecure.sub2.secure.example. 3600 IN SOA {soa}
238 insecure.sub2.secure.example. 3600 IN NS ns1.insecure.example.
240 node1.insecure.sub2.secure.example. 3600 IN A 192.0.2.18
242 'insecure.example': """
243 insecure.example. 3600 IN SOA {soa}
244 insecure.example. 3600 IN NS ns1.insecure.example.
245 ns1.insecure.example. 3600 IN A {prefix}.13
247 node1.insecure.example. 3600 IN A 192.0.2.6
249 cname-to-secure.insecure.example. 3600 IN CNAME host1.secure.example.
251 dname-to-secure.insecure.example. 3600 IN DNAME dname-secure.example.
253 'optout.example': """
254 optout.example. 3600 IN SOA {soa}
255 optout.example. 3600 IN NS ns1.optout.example.
256 ns1.optout.example. 3600 IN A {prefix}.14
258 insecure.optout.example. 3600 IN NS ns1.insecure.optout.example.
259 ns1.insecure.optout.example. 3600 IN A {prefix}.15
261 secure.optout.example. 3600 IN NS ns1.secure.optout.example.
262 secure.optout.example. 3600 IN DS 64215 13 1 b88284d7a8d8605c398e8942262f97b9a5a31787
263 ns1.secure.optout.example. 3600 IN A {prefix}.15
265 'insecure.optout.example': """
266 insecure.optout.example. 3600 IN SOA {soa}
267 insecure.optout.example. 3600 IN NS ns1.insecure.optout.example.
268 ns1.insecure.optout.example. 3600 IN A {prefix}.15
270 node1.insecure.optout.example. 3600 IN A 192.0.2.7
272 'secure.optout.example': """
273 secure.optout.example. 3600 IN SOA {soa}
274 secure.optout.example. 3600 IN NS ns1.secure.optout.example.
275 ns1.secure.optout.example. 3600 IN A {prefix}.15
277 node1.secure.optout.example. 3600 IN A 192.0.2.8
279 'islandofsecurity.example': """
280 islandofsecurity.example. 3600 IN SOA {soa}
281 islandofsecurity.example. 3600 IN NS ns1.islandofsecurity.example.
282 ns1.islandofsecurity.example. 3600 IN A {prefix}.9
284 node1.islandofsecurity.example. 3600 IN A 192.0.2.20
286 'undelegated.secure.example': """
287 undelegated.secure.example. 3600 IN SOA {soa}
288 undelegated.secure.example. 3600 IN NS ns1.undelegated.secure.example.
290 node1.undelegated.secure.example. 3600 IN A 192.0.2.21
292 'undelegated.insecure.example': """
293 undelegated.insecure.example. 3600 IN SOA {soa}
294 undelegated.insecure.example. 3600 IN NS ns1.undelegated.insecure.example.
296 node1.undelegated.insecure.example. 3600 IN A 192.0.2.22
299 'delay1.example': """
300 delay1.example. 3600 IN SOA {soa}
301 delay1.example. 3600 IN NS n1.delay1.example.
302 ns1.delay1.example. 3600 IN A {prefix}.16
303 8.delay1.example. 3600 IN A 192.0.2.100
304 *.delay1.example. 0 LUA TXT ";" "local socket=require('socket')" "socket.sleep(tonumber(qname:getRawLabels()[1])/10)" "return 'a'"
305 *.delay1.example. 0 LUA AAAA ";" "local socket=require('socket')" "socket.sleep(tonumber(qname:getRawLabels()[1])/10)" "return '1::2'"
308 'delay2.example': """
309 delay2.example. 3600 IN SOA {soa}
310 delay2.example. 3600 IN NS n1.delay2.example.
311 ns1.delay2.example. 3600 IN A {prefix}.17
312 *.delay2.example. 0 LUA TXT ";" "local socket=require('socket')" "socket.sleep(tonumber(qname:getRawLabels()[1])/10)" "return 'a'"
316 # The private keys for the zones (note that DS records should go into
317 # the zonecontent in _zones
320 Private-key-format: v1.2
321 Algorithm: 13 (ECDSAP256SHA256)
322 PrivateKey: rhWuEydDz3QaIspSVj683B8Xq5q/ozzA38XUgzD4Fbo=
326 Private-key-format: v1.2
327 Algorithm: 13 (ECDSAP256SHA256)
328 PrivateKey: Lt0v0Gol3pRUFM7fDdcy0IWN0O/MnEmVPA+VylL8Y4U=
331 'secure.example': """
332 Private-key-format: v1.2
333 Algorithm: 13 (ECDSAP256SHA256)
334 PrivateKey: 1G4WRoOFJJXk+fotDCHVORtJmIG2OUhKi8AO2jDPGZA=
338 Private-key-format: v1.2
339 Algorithm: 13 (ECDSAP256SHA256)
340 PrivateKey: f5jV7Q8kd5hDpMWObsuQ6SQda0ftf+JrO3uZwEg6nVw=
343 'optout.example': """
344 Private-key-format: v1.2
345 Algorithm: 13 (ECDSAP256SHA256)
346 PrivateKey: efmq9G+J4Y2iPnIBRwJiy6Z/nIHSzpsCy/7XHhlS19A=
349 'secure.optout.example': """
350 Private-key-format: v1.2
351 Algorithm: 13 (ECDSAP256SHA256)
352 PrivateKey: xcNUxt1Knj14A00lKQFDboluiJyM2f7FxpgsQaQ3AQ4=
355 'islandofsecurity.example': """
356 Private-key-format: v1.2
357 Algorithm: 13 (ECDSAP256SHA256)
358 PrivateKey: o9F5iix8V68tnMcuOaM2Lt8XXhIIY//SgHIHEePk6cM=
361 'cname-secure.example': """
362 Private-key-format: v1.2
363 Algorithm: 13 (ECDSAP256SHA256)
364 PrivateKey: kvoV/g4IO/tefSro+FLJ5UC7H3BUf0IUtZQSUOfQGyA=
367 'dname-secure.example': """
368 Private-key-format: v1.2
369 Algorithm: 13 (ECDSAP256SHA256)
370 PrivateKey: Ep9uo6+wwjb4MaOmqq7LHav2FLrjotVOeZg8JT1Qk04=
373 'delay1.example': """
374 Private-key-format: v1.2
375 Algorithm: 13 (ECDSAP256SHA256)
376 PrivateKey: Ep9uo6+wwjb4MaOmqq7LHav2FLrjotVOeZg8JT1Qk04=
379 'delay2.example': """
380 Private-key-format: v1.2
381 Algorithm: 13 (ECDSAP256SHA256)
382 PrivateKey: Ep9uo6+wwjb4MaOmqq7LHav2FLrjotVOeZg8JT1Qk04=
386 # This dict is keyed with the suffix of the IP address and its value
387 # is a list of zones hosted on that IP. Note that delegations should
388 # go into the _zones's zonecontent
393 'zones': ['secure.example', 'islandofsecurity.example']},
395 'zones': ['example']},
397 # 11 is used by CircleCI provided resolver
400 'zones': ['bogus.example', 'undelegated.secure.example', 'undelegated.insecure.example']},
402 'zones': ['insecure.example', 'insecure.sub2.secure.example', 'dname-secure.example']},
404 'zones': ['optout.example']},
406 'zones': ['insecure.optout.example', 'secure.optout.example', 'cname-secure.example']},
408 'zones': ['delay1.example']},
410 'zones': ['delay2.example']},
412 'zones': ['example']}
420 # 22: test_EDNSBuffer.py
422 # 24: test_RoutingTag.py
424 _auth_cmd
= ['authbind',
430 def createConfigDir(cls
, confdir
):
432 shutil
.rmtree(confdir
)
434 if e
.errno
!= errno
.ENOENT
:
436 os
.mkdir(confdir
, 0o755)
439 def generateAuthZone(cls
, confdir
, zonename
, zonecontent
):
440 with
open(os
.path
.join(confdir
, '%s.zone' % zonename
), 'w') as zonefile
:
441 zonefile
.write(zonecontent
.format(prefix
=cls
._PREFIX
, soa
=cls
._SOA
))
444 def generateAuthNamedConf(cls
, confdir
, zones
):
445 with
open(os
.path
.join(confdir
, 'named.conf'), 'w') as namedconf
:
450 for zonename
in zones
:
451 zone
= '.' if zonename
== 'ROOT' else zonename
457 };""" % (zone
, zonename
))
460 def generateAuthConfig(cls
, confdir
, threads
):
461 bind_dnssec_db
= os
.path
.join(confdir
, 'bind-dnssec.sqlite3')
463 with
open(os
.path
.join(confdir
, 'pdns.conf'), 'w') as pdnsconf
:
465 module-dir=../regression-tests/modules
468 bind-config={confdir}/named.conf
469 bind-dnssec-db={bind_dnssec_db}
479 distributor-threads={threads}""".format(confdir
=confdir
,
480 bind_dnssec_db
=bind_dnssec_db
,
483 pdnsutilCmd
= [os
.environ
['PDNSUTIL'],
484 '--config-dir=%s' % confdir
,
488 print(' '.join(pdnsutilCmd
))
490 subprocess
.check_output(pdnsutilCmd
, stderr
=subprocess
.STDOUT
)
491 except subprocess
.CalledProcessError
as e
:
492 raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd
, e
.returncode
, e
.output
))
495 def secureZone(cls
, confdir
, zonename
, key
=None):
496 zone
= '.' if zonename
== 'ROOT' else zonename
498 pdnsutilCmd
= [os
.environ
['PDNSUTIL'],
499 '--config-dir=%s' % confdir
,
503 keyfile
= os
.path
.join(confdir
, 'dnssec.key')
504 with
open(keyfile
, 'w') as fdKeyfile
:
507 pdnsutilCmd
= [os
.environ
['PDNSUTIL'],
508 '--config-dir=%s' % confdir
,
515 print(' '.join(pdnsutilCmd
))
517 subprocess
.check_output(pdnsutilCmd
, stderr
=subprocess
.STDOUT
)
518 except subprocess
.CalledProcessError
as e
:
519 raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd
, e
.returncode
, e
.output
))
522 def generateAllAuthConfig(cls
, confdir
):
524 for auth_suffix
, zoneinfo
in cls
._auth
_zones
.items():
525 threads
= zoneinfo
['threads']
526 zones
= zoneinfo
['zones']
527 authconfdir
= os
.path
.join(confdir
, 'auth-%s' % auth_suffix
)
529 os
.mkdir(authconfdir
)
531 cls
.generateAuthConfig(authconfdir
, threads
)
532 cls
.generateAuthNamedConf(authconfdir
, zones
)
535 cls
.generateAuthZone(authconfdir
,
538 if cls
._zone
_keys
.get(zone
, None):
539 cls
.secureZone(authconfdir
, zone
, cls
._zone
_keys
.get(zone
))
542 def startAllAuth(cls
, confdir
):
544 for auth_suffix
, _
in cls
._auth
_zones
.items():
545 authconfdir
= os
.path
.join(confdir
, 'auth-%s' % auth_suffix
)
546 ipaddress
= cls
._PREFIX
+ '.' + auth_suffix
547 cls
.startAuth(authconfdir
, ipaddress
)
550 def waitForTCPSocket(cls
, ipaddress
, port
):
551 for try_number
in range(0, 100):
553 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
555 sock
.connect((ipaddress
, port
))
558 except Exception as err
:
559 if err
.errno
!= errno
.ECONNREFUSED
:
560 print(f
'Error occurred: {try_number} {err}', file=sys
.stderr
)
564 def startAuth(cls
, confdir
, ipaddress
):
565 print("Launching pdns_server..")
566 authcmd
= list(cls
._auth
_cmd
)
567 authcmd
.append('--config-dir=%s' % confdir
)
569 # auth-8 is the auth serving the root, it gets an ipv6 address
570 if (confdir
[-6:] == "auth-8") and have_ipv6():
572 authcmd
.append('--local-address=%s' % ipconfig
)
573 print(' '.join(authcmd
))
575 logFile
= os
.path
.join(confdir
, 'pdns.log')
576 with
open(logFile
, 'w') as fdLog
:
577 cls
._auths
[ipaddress
] = subprocess
.Popen(authcmd
, close_fds
=True,
578 stdout
=fdLog
, stderr
=fdLog
,
581 cls
.waitForTCPSocket(ipaddress
, 53)
583 if cls
._auths
[ipaddress
].poll() is not None:
584 print(f
"\n*** startAuth log for {logFile} ***")
585 with
open(logFile
, 'r') as fdLog
:
587 print(f
"*** End startAuth log for {logFile} ***")
588 raise AssertionError('%s failed (%d)' % (authcmd
, cls
._auths
[ipaddress
].returncode
))
591 def generateRecursorConfig(cls
, confdir
):
592 params
= tuple([getattr(cls
, param
) for param
in cls
._config
_params
])
596 recursorconf
= os
.path
.join(confdir
, 'recursor.conf')
598 with
open(recursorconf
, 'w') as conf
:
599 conf
.write("# Autogenerated by recursortests.py\n")
600 conf
.write(cls
._config
_template
_default
)
601 conf
.write(cls
._config
_template
% params
)
603 conf
.write("socket-dir=%s\n" % confdir
)
604 if cls
._lua
_config
_file
or cls
._root
_DS
:
605 luaconfpath
= os
.path
.join(confdir
, 'conffile.lua')
606 with
open(luaconfpath
, 'w') as luaconf
:
608 luaconf
.write("addTA('.', '%s')\n" % cls
._root
_DS
)
609 if cls
._lua
_config
_file
:
610 luaconf
.write(cls
._lua
_config
_file
)
611 conf
.write("lua-config-file=%s\n" % luaconfpath
)
612 if cls
._lua
_dns
_script
_file
:
613 luascriptpath
= os
.path
.join(confdir
, 'dnsscript.lua')
614 with
open(luascriptpath
, 'w') as luascript
:
615 luascript
.write(cls
._lua
_dns
_script
_file
)
616 conf
.write("lua-dns-script=%s\n" % luascriptpath
)
618 roothintspath
= os
.path
.join(confdir
, 'root.hints')
619 with
open(roothintspath
, 'w') as roothints
:
620 roothints
.write(cls
._roothints
)
621 conf
.write("hint-file=%s\n" % roothintspath
)
624 def startResponders(cls
):
628 def startRecursor(cls
, confdir
, port
):
629 print("Launching pdns_recursor..")
630 recursorcmd
= [os
.environ
['PDNSRECURSOR'],
631 '--config-dir=%s' % confdir
,
632 '--local-port=%s' % port
,
633 '--security-poll-suffix=']
634 print(' '.join(recursorcmd
))
636 logFile
= os
.path
.join(confdir
, 'recursor.log')
637 with
open(logFile
, 'w') as fdLog
:
638 cls
._recursor
= subprocess
.Popen(recursorcmd
, close_fds
=True,
639 stdout
=fdLog
, stderr
=fdLog
)
641 cls
.waitForTCPSocket("127.0.0.1", port
)
643 if cls
._recursor
.poll() is not None:
644 print(f
"\n*** startRecursor log for {logFile} ***")
645 with
open(logFile
, 'r') as fdLog
:
647 print(f
"*** End startRecursor log for {logFile} ***")
648 raise AssertionError('%s failed (%d)' % (recursorcmd
, cls
._recursor
.returncode
))
651 def wipeRecursorCache(cls
, confdir
, name
='.$'):
652 rec_controlCmd
= [os
.environ
['RECCONTROL'],
653 '--config-dir=%s' % confdir
,
657 subprocess
.check_output(rec_controlCmd
, stderr
=subprocess
.STDOUT
)
658 except subprocess
.CalledProcessError
as e
:
659 raise AssertionError('%s failed (%d): %s' % (rec_controlCmd
, e
.returncode
, e
.output
))
662 def setUpSockets(cls
):
663 print("Setting up UDP socket..")
664 cls
._sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_DGRAM
)
665 cls
._sock
.settimeout(2.0)
666 cls
._sock
.connect(("127.0.0.1", cls
._recursorPort
))
672 cls
.startResponders()
674 confdir
= os
.path
.join('configs', cls
._confdir
)
675 cls
.createConfigDir(confdir
)
676 cls
.generateAllAuthConfig(confdir
)
677 cls
.startAllAuth(confdir
)
679 cls
.generateRecursorConfig(confdir
)
680 cls
.startRecursor(confdir
, cls
._recursorPort
)
682 print("Launching tests..")
685 def tearDownClass(cls
):
686 cls
.tearDownRecursor()
688 cls
.tearDownResponders()
691 def tearDownResponders(cls
):
695 def killProcess(cls
, p
):
696 # Don't try to kill it if it's already dead
697 if p
.poll() is not None:
701 for count
in range(10):
707 print("kill...", p
, file=sys
.stderr
)
711 # There is a race-condition with the poll() and
712 # kill() statements, when the process is dead on the
713 # kill(), this is fine
714 if e
.errno
!= errno
.ESRCH
:
718 def tearDownAuth(cls
):
719 for _
, auth
in cls
._auths
.items():
720 cls
.killProcess(auth
);
723 def tearDownRecursor(cls
):
724 cls
.killProcess(cls
._recursor
)
727 def sendUDPQuery(cls
, query
, timeout
=2.0, decode
=True, fwparams
=dict()):
729 cls
._sock
.settimeout(timeout
)
732 cls
._sock
.send(query
.to_wire())
733 data
= cls
._sock
.recv(4096)
734 except socket
.timeout
:
738 cls
._sock
.settimeout(None)
744 message
= dns
.message
.from_wire(data
, **fwparams
)
748 def sendTCPQuery(cls
, query
, timeout
=2.0):
749 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
751 sock
.settimeout(timeout
)
753 sock
.connect(("127.0.0.1", cls
._recursorPort
))
756 wire
= query
.to_wire()
757 sock
.send(struct
.pack("!H", len(wire
)))
761 (datalen
,) = struct
.unpack("!H", data
)
762 data
= sock
.recv(datalen
)
763 except socket
.timeout
as e
:
764 print("Timeout: %s" % (str(e
)))
766 except socket
.error
as e
:
767 print("Network error: %s" % (str(e
)))
774 message
= dns
.message
.from_wire(data
)
778 def sendTCPQueries(cls
, queries
, timeout
=2.0):
779 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
781 sock
.settimeout(timeout
)
783 sock
.connect(("127.0.0.1", cls
._recursorPort
))
786 for query
in queries
:
787 wire
= query
.to_wire()
788 sock
.send(struct
.pack("!H", len(wire
)))
790 for i
in range(len(queries
)):
792 datalen
= sock
.recv(2)
794 (datalen
,) = struct
.unpack("!H", datalen
)
795 data
.append(sock
.recv(datalen
))
796 except socket
.timeout
as e
:
798 except socket
.error
as e
:
799 print("Network error: %s" % (str(e
)))
806 messages
.append(dns
.message
.from_wire(d
))
810 # This function is called before every tests
811 super(RecursorTest
, self
).setUp()
813 ## Functions for comparisons
814 def assertMessageHasFlags(self
, msg
, flags
, ednsflags
=[]):
815 """Asserts that msg has all the flags from flags set
817 @param msg: the dns.message.Message to check
818 @param flags: a list of strings with flag mnemonics (like ['RD', 'RA'])
819 @param ednsflags: a list of strings with edns-flag mnemonics (like ['DO'])"""
821 if not isinstance(msg
, dns
.message
.Message
):
822 raise TypeError("msg is not a dns.message.Message")
824 if isinstance(flags
, list):
826 if not isinstance(elem
, str):
827 raise TypeError("flags is not a list of strings")
829 raise TypeError("flags is not a list of strings")
831 if isinstance(ednsflags
, list):
832 for elem
in ednsflags
:
833 if not isinstance(elem
, str):
834 raise TypeError("ednsflags is not a list of strings")
836 raise TypeError("ednsflags is not a list of strings")
838 msgFlags
= dns
.flags
.to_text(msg
.flags
).split()
839 missingFlags
= [flag
for flag
in flags
if flag
not in msgFlags
]
841 msgEdnsFlags
= dns
.flags
.edns_to_text(msg
.ednsflags
).split()
842 missingEdnsFlags
= [ednsflag
for ednsflag
in ednsflags
if ednsflag
not in msgEdnsFlags
]
844 if len(missingFlags
) or len(missingEdnsFlags
) or len(msgFlags
) > len(flags
):
845 raise AssertionError("Expected flags '%s' (EDNS: '%s'), found '%s' (EDNS: '%s') in query %s" %
846 (' '.join(flags
), ' '.join(ednsflags
),
847 ' '.join(msgFlags
), ' '.join(msgEdnsFlags
),
850 def assertMessageIsAuthenticated(self
, msg
):
851 """Asserts that the message has the AD bit set
853 @param msg: the dns.message.Message to check"""
855 if not isinstance(msg
, dns
.message
.Message
):
856 raise TypeError("msg is not a dns.message.Message")
858 msgFlags
= dns
.flags
.to_text(msg
.flags
)
859 self
.assertTrue('AD' in msgFlags
, "No AD flag found in the message for %s" % msg
.question
[0].name
)
861 def assertRRsetInAnswer(self
, msg
, rrset
):
862 """Asserts the rrset (without comparing TTL) exists in the
863 answer section of msg
865 @param msg: the dns.message.Message to check
866 @param rrset: a dns.rrset.RRset object"""
869 if not isinstance(msg
, dns
.message
.Message
):
870 raise TypeError("msg is not a dns.message.Message")
872 if not isinstance(rrset
, dns
.rrset
.RRset
):
873 raise TypeError("rrset is not a dns.rrset.RRset")
876 for ans
in msg
.answer
:
877 ret
+= "%s\n" % ans
.to_text()
878 if ans
.match(rrset
.name
, rrset
.rdclass
, rrset
.rdtype
, 0, None):
879 self
.assertEqual(ans
, rrset
, "'%s' != '%s'" % (ans
.to_text(), rrset
.to_text()))
883 raise AssertionError("RRset not found in answer\n\n%s" % ret
)
885 def assertRRsetInAdditional(self
, msg
, rrset
):
886 """Asserts the rrset (without comparing TTL) exists in the
887 additional section of msg
889 @param msg: the dns.message.Message to check
890 @param rrset: a dns.rrset.RRset object"""
893 if not isinstance(msg
, dns
.message
.Message
):
894 raise TypeError("msg is not a dns.message.Message")
896 if not isinstance(rrset
, dns
.rrset
.RRset
):
897 raise TypeError("rrset is not a dns.rrset.RRset")
900 for ans
in msg
.additional
:
901 ret
+= "%s\n" % ans
.to_text()
902 if ans
.match(rrset
.name
, rrset
.rdclass
, rrset
.rdtype
, 0, None):
903 self
.assertEqual(ans
, rrset
, "'%s' != '%s'" % (ans
.to_text(), rrset
.to_text()))
907 raise AssertionError("RRset not found in additional section\n\n%s" % ret
)
909 def assertMatchingRRSIGInAnswer(self
, msg
, coveredRRset
, keys
=None):
910 """Looks for coveredRRset in the answer section and if there is an RRSIG RRset
911 that covers that RRset. If keys is not None, this function will also try to
912 validate the RRset against the RRSIG
914 @param msg: The dns.message.Message to check
915 @param coveredRRset: The RRSet to check for
916 @param keys: a dictionary keyed by dns.name.Name with node or rdataset values to use for validation"""
918 if not isinstance(msg
, dns
.message
.Message
):
919 raise TypeError("msg is not a dns.message.Message")
921 if not isinstance(coveredRRset
, dns
.rrset
.RRset
):
922 raise TypeError("coveredRRset is not a dns.rrset.RRset")
928 for ans
in msg
.answer
:
929 ret
+= ans
.to_text() + "\n"
931 if ans
.match(coveredRRset
.name
, coveredRRset
.rdclass
, coveredRRset
.rdtype
, 0, None):
933 if ans
.match(coveredRRset
.name
, dns
.rdataclass
.IN
, dns
.rdatatype
.RRSIG
, coveredRRset
.rdtype
, None):
935 if msgRRSet
and msgRRsigRRSet
:
939 raise AssertionError("RRset for '%s' not found in answer" % msg
.question
[0].to_text())
941 if not msgRRsigRRSet
:
942 raise AssertionError("No RRSIGs found in answer for %s:\nFull answer:\n%s" % (msg
.question
[0].to_text(), ret
))
946 dns
.dnssec
.validate(msgRRSet
, msgRRsigRRSet
.to_rdataset(), keys
)
947 except dns
.dnssec
.ValidationFailure
as e
:
948 raise AssertionError("Signature validation failed for %s:\n%s" % (msg
.question
[0].to_text(), e
))
950 def assertMatchingRRSIGInAdditional(self
, msg
, coveredRRset
, keys
=None):
951 """Looks for coveredRRset in the additional section and if there is an RRSIG RRset
952 that covers that RRset. If keys is not None, this function will also try to
953 validate the RRset against the RRSIG
955 @param msg: The dns.message.Message to check
956 @param coveredRRset: The RRSet to check for
957 @param keys: a dictionary keyed by dns.name.Name with node or rdataset values to use for validation"""
959 if not isinstance(msg
, dns
.message
.Message
):
960 raise TypeError("msg is not a dns.message.Message")
962 if not isinstance(coveredRRset
, dns
.rrset
.RRset
):
963 raise TypeError("coveredRRset is not a dns.rrset.RRset")
969 for ans
in msg
.additional
:
970 ret
+= ans
.to_text() + "\n"
972 if ans
.match(coveredRRset
.name
, coveredRRset
.rdclass
, coveredRRset
.rdtype
, 0, None):
974 if ans
.match(coveredRRset
.name
, dns
.rdataclass
.IN
, dns
.rdatatype
.RRSIG
, coveredRRset
.rdtype
, None):
976 if msgRRSet
and msgRRsigRRSet
:
980 raise AssertionError("RRset for '%s' not found in additional" % msg
.question
[0].to_text())
982 if not msgRRsigRRSet
:
983 raise AssertionError("No RRSIGs found in additional for %s:\nFull answer:\n%s" % (msg
.question
[0].to_text(), ret
))
987 dns
.dnssec
.validate(msgRRSet
, msgRRsigRRSet
.to_rdataset(), keys
)
988 except dns
.dnssec
.ValidationFailure
as e
:
989 raise AssertionError("Signature validation failed for %s:\n%s" % (msg
.question
[0].to_text(), e
))
991 def assertNoRRSIGsInAnswer(self
, msg
):
992 """Checks if there are _no_ RRSIGs in the answer section of msg"""
994 if not isinstance(msg
, dns
.message
.Message
):
995 raise TypeError("msg is not a dns.message.Message")
998 for ans
in msg
.answer
:
999 if ans
.rdtype
== dns
.rdatatype
.RRSIG
:
1000 ret
+= ans
.name
.to_text() + "\n"
1003 raise AssertionError("RRSIG found in answers for:\n%s" % ret
)
1005 def assertAnswerEmpty(self
, msg
):
1006 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
])))
1008 def assertAdditionalEmpty(self
, msg
):
1009 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
])))
1011 def assertRcodeEqual(self
, msg
, rcode
):
1012 if not isinstance(msg
, dns
.message
.Message
):
1013 raise TypeError("msg is not a dns.message.Message but a %s" % type(msg
))
1015 if not isinstance(rcode
, int):
1016 if isinstance(rcode
, str):
1017 rcode
= dns
.rcode
.from_text(rcode
)
1019 raise TypeError("rcode is neither a str nor int")
1021 if msg
.rcode() != rcode
:
1022 msgRcode
= dns
.rcode
.to_text(msg
.rcode())
1023 wantedRcode
= dns
.rcode
.to_text(rcode
)
1025 raise AssertionError("Rcode for %s is %s, expected %s." % (msg
.question
[0].to_text(), msgRcode
, wantedRcode
))
1027 def assertAuthorityHasSOA(self
, msg
):
1028 if not isinstance(msg
, dns
.message
.Message
):
1029 raise TypeError("msg is not a dns.message.Message but a %s" % type(msg
))
1032 for rrset
in msg
.authority
:
1033 if rrset
.rdtype
== dns
.rdatatype
.SOA
:
1038 raise AssertionError("No SOA record found in the authority section:\n%s" % msg
.to_text())
1040 def assertResponseMatches(self
, query
, expectedRRs
, response
):
1041 expectedResponse
= dns
.message
.make_response(query
)
1043 if query
.flags
& dns
.flags
.RD
:
1044 expectedResponse
.flags |
= dns
.flags
.RA
1045 if query
.flags
& dns
.flags
.CD
:
1046 expectedResponse
.flags |
= dns
.flags
.CD
1048 expectedResponse
.answer
= expectedRRs
1049 print(expectedResponse
)
1051 self
.assertEqual(response
, expectedResponse
)
1054 def sendQuery(cls
, name
, rdtype
, useTCP
=False):
1055 """Helper function that creates the query"""
1056 msg
= dns
.message
.make_query(name
, rdtype
, want_dnssec
=True)
1057 msg
.flags |
= dns
.flags
.AD
1060 return cls
.sendTCPQuery(msg
)
1061 return cls
.sendUDPQuery(msg
)
1063 def createQuery(self
, name
, rdtype
, flags
, ednsflags
):
1064 """Helper function that creates the query with the specified flags.
1065 The flags need to be strings (no checking is performed atm)"""
1066 msg
= dns
.message
.make_query(name
, rdtype
)
1067 msg
.flags
= dns
.flags
.from_text(flags
)
1068 msg
.flags
+= dns
.flags
.from_text('RD')
1069 msg
.use_edns(edns
=0, ednsflags
=dns
.flags
.edns_from_text(ednsflags
))
1073 def sendUDPQueryWithProxyProtocol(cls
, query
, v6
, source
, destination
, sourcePort
, destinationPort
, values
=[], timeout
=2.0):
1074 queryPayload
= query
.to_wire()
1075 ppPayload
= ProxyProtocol
.getPayload(False, False, v6
, source
, destination
, sourcePort
, destinationPort
, values
)
1076 payload
= ppPayload
+ queryPayload
1079 cls
._sock
.settimeout(timeout
)
1082 cls
._sock
.send(payload
)
1083 data
= cls
._sock
.recv(4096)
1084 except socket
.timeout
:
1088 cls
._sock
.settimeout(None)
1092 message
= dns
.message
.from_wire(data
)
1096 def sendTCPQueryWithProxyProtocol(cls
, query
, v6
, source
, destination
, sourcePort
, destinationPort
, values
=[], timeout
=2.0):
1097 queryPayload
= query
.to_wire()
1098 ppPayload
= ProxyProtocol
.getPayload(False, False, v6
, source
, destination
, sourcePort
, destinationPort
, values
)
1100 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
1102 sock
.settimeout(timeout
)
1104 sock
.connect(("127.0.0.1", cls
._recursorPort
))
1107 sock
.send(ppPayload
)
1108 sock
.send(struct
.pack("!H", len(queryPayload
)))
1109 sock
.send(queryPayload
)
1112 (datalen
,) = struct
.unpack("!H", data
)
1113 data
= sock
.recv(datalen
)
1114 except socket
.timeout
as e
:
1115 print("Timeout: %s" % (str(e
)))
1117 except socket
.error
as e
:
1118 print("Network error: %s" % (str(e
)))
1125 message
= dns
.message
.from_wire(data
)