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
42 _recursorStartupDelay
= 2.0
47 _PREFIX
= os
.environ
['PREFIX']
49 _config_template_default
= """
53 local-address=127.0.0.1
55 packetcache-servfail-ttl=0
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 insecure-formerr.example. 3600 IN NS ns1.insecure-formerr.example.
125 ns1.insecure-formerr.example. 3600 IN A {prefix}.2
127 ecs-echo.example. 3600 IN NS ns1.ecs-echo.example.
128 ns1.ecs-echo.example. 3600 IN A {prefix}.21
130 islandofsecurity.example. 3600 IN NS ns1.islandofsecurity.example.
131 ns1.islandofsecurity.example. 3600 IN A {prefix}.9
133 sortcname.example. 3600 IN CNAME sort
134 sort.example. 3600 IN A 17.38.42.80
135 sort.example. 3600 IN A 192.168.0.1
136 sort.example. 3600 IN A 17.238.240.5
137 sort.example. 3600 IN MX 25 mx
139 delay1.example. 3600 IN NS ns1.delay1.example.
140 ns1.delay1.example. 3600 IN A {prefix}.16
141 delay1.example. 3600 IN DS 42043 13 2 7319fa605cf117f36e3de070157577ebb9a05a1d1f963d80eda55b5d6e793eb2
143 delay2.example. 3600 IN NS ns1.delay2.example.
144 ns1.delay2.example. 3600 IN A {prefix}.17
145 delay2.example. 3600 IN DS 42043 13 2 60a047b87740c8564c21d5fd34626c10a77a6c41e3b34564230119c2f13937b8
147 cname-nxd.example. 3600 IN CNAME cname-nxd-target.example.
148 cname-nxd-target.example. 3600 IN A 192.0.2.100
149 cname-nodata.example. 3600 IN CNAME cname-nodata-target.example.
150 cname-nodata-target.example. 3600 IN A 192.0.2.101
151 cname-custom-a.example. 3600 IN CNAME cname-custom-a-target.example.
152 cname-custom-a-target.example. 3600 IN A 192.0.2.102
154 'secure.example': """
155 secure.example. 3600 IN SOA {soa}
156 secure.example. 3600 IN NS ns.secure.example.
157 ns.secure.example. 3600 IN A {prefix}.9
159 secure.example. 3600 IN A 192.0.2.17
161 host1.secure.example. 3600 IN A 192.0.2.2
162 cname.secure.example. 3600 IN CNAME host1.secure.example.
163 cname-to-insecure.secure.example. 3600 IN CNAME node1.insecure.example.
164 cname-to-bogus.secure.example. 3600 IN CNAME ted.bogus.example.
165 cname-to-islandofsecurity.secure.example. 3600 IN CNAME node1.islandofsecurity.example.
167 host1.sub.secure.example. 3600 IN A 192.0.2.11
170 sub2.secure.example. 3600 IN CNAME doesnotmatter.insecure.example.
171 insecure.sub2.secure.example. 3600 IN NS ns1.insecure.example.
173 *.wildcard.secure.example. 3600 IN A 192.0.2.10
175 *.cnamewildcard.secure.example. 3600 IN CNAME host1.secure.example.
177 *.cnamewildcardnxdomain.secure.example. 3600 IN CNAME doesnotexist.secure.example.
179 cname-to-formerr.secure.example. 3600 IN CNAME host1.insecure-formerr.example.
181 dname-secure.secure.example. 3600 IN DNAME dname-secure.example.
182 dname-insecure.secure.example. 3600 IN DNAME insecure.example.
183 dname-bogus.secure.example. 3600 IN DNAME bogus.example.
185 non-apex-dnskey.secure.example. 3600 IN DNSKEY 257 3 13 CT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==
186 non-apex-dnskey2.secure.example. 3600 IN DNSKEY 256 3 13 CT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==
187 non-apex-dnskey3.secure.example. 3600 IN DNSKEY 256 3 13 DT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==
189 'dname-secure.example': """
190 dname-secure.example. 3600 IN SOA {soa}
191 dname-secure.example. 3600 IN NS ns.dname-secure.example.
192 ns.dname-secure.example. 3600 IN A {prefix}.13
194 host1.dname-secure.example. IN A 192.0.2.21
196 cname-to-secure.dname-secure.example. 3600 IN CNAME host1.secure.example.
197 cname-to-insecure.dname-secure.example. 3600 IN CNAME node1.insecure.example.
198 cname-to-bogus.dname-secure.example. 3600 IN CNAME ted.bogus.example.
200 'cname-secure.example': """
201 cname-secure.example. 3600 IN SOA {soa}
202 cname-secure.example. 3600 IN NS ns.cname-secure.example.
203 ns.cname-secure.example. 3600 IN A {prefix}.15
204 cname-secure.example. 3600 IN CNAME secure.example.
207 bogus.example. 3600 IN SOA {soa}
208 bogus.example. 3600 IN NS ns1.bogus.example.
209 ns1.bogus.example. 3600 IN A {prefix}.12
210 ted.bogus.example. 3600 IN A 192.0.2.1
211 bill.bogus.example. 3600 IN AAAA 2001:db8:12::3
213 'insecure.sub2.secure.example': """
214 insecure.sub2.secure.example. 3600 IN SOA {soa}
215 insecure.sub2.secure.example. 3600 IN NS ns1.insecure.example.
217 node1.insecure.sub2.secure.example. 3600 IN A 192.0.2.18
219 'insecure.example': """
220 insecure.example. 3600 IN SOA {soa}
221 insecure.example. 3600 IN NS ns1.insecure.example.
222 ns1.insecure.example. 3600 IN A {prefix}.13
224 node1.insecure.example. 3600 IN A 192.0.2.6
226 cname-to-secure.insecure.example. 3600 IN CNAME host1.secure.example.
228 dname-to-secure.insecure.example. 3600 IN DNAME dname-secure.example.
230 'optout.example': """
231 optout.example. 3600 IN SOA {soa}
232 optout.example. 3600 IN NS ns1.optout.example.
233 ns1.optout.example. 3600 IN A {prefix}.14
235 insecure.optout.example. 3600 IN NS ns1.insecure.optout.example.
236 ns1.insecure.optout.example. 3600 IN A {prefix}.15
238 secure.optout.example. 3600 IN NS ns1.secure.optout.example.
239 secure.optout.example. 3600 IN DS 64215 13 1 b88284d7a8d8605c398e8942262f97b9a5a31787
240 ns1.secure.optout.example. 3600 IN A {prefix}.15
242 'insecure.optout.example': """
243 insecure.optout.example. 3600 IN SOA {soa}
244 insecure.optout.example. 3600 IN NS ns1.insecure.optout.example.
245 ns1.insecure.optout.example. 3600 IN A {prefix}.15
247 node1.insecure.optout.example. 3600 IN A 192.0.2.7
249 'secure.optout.example': """
250 secure.optout.example. 3600 IN SOA {soa}
251 secure.optout.example. 3600 IN NS ns1.secure.optout.example.
252 ns1.secure.optout.example. 3600 IN A {prefix}.15
254 node1.secure.optout.example. 3600 IN A 192.0.2.8
256 'islandofsecurity.example': """
257 islandofsecurity.example. 3600 IN SOA {soa}
258 islandofsecurity.example. 3600 IN NS ns1.islandofsecurity.example.
259 ns1.islandofsecurity.example. 3600 IN A {prefix}.9
261 node1.islandofsecurity.example. 3600 IN A 192.0.2.20
263 'undelegated.secure.example': """
264 undelegated.secure.example. 3600 IN SOA {soa}
265 undelegated.secure.example. 3600 IN NS ns1.undelegated.secure.example.
267 node1.undelegated.secure.example. 3600 IN A 192.0.2.21
269 'undelegated.insecure.example': """
270 undelegated.insecure.example. 3600 IN SOA {soa}
271 undelegated.insecure.example. 3600 IN NS ns1.undelegated.insecure.example.
273 node1.undelegated.insecure.example. 3600 IN A 192.0.2.22
276 'delay1.example': """
277 delay1.example. 3600 IN SOA {soa}
278 delay1.example. 3600 IN NS n1.delay1.example.
279 ns1.delay1.example. 3600 IN A {prefix}.16
280 *.delay1.example. 0 LUA TXT ";" "local socket=require('socket')" "socket.sleep(tonumber(qname:getRawLabels()[1])/10)" "return 'a'"
283 'delay2.example': """
284 delay2.example. 3600 IN SOA {soa}
285 delay2.example. 3600 IN NS n1.delay2.example.
286 ns1.delay2.example. 3600 IN A {prefix}.17
287 *.delay2.example. 0 LUA TXT ";" "local socket=require('socket')" "socket.sleep(tonumber(qname:getRawLabels()[1])/10)" "return 'a'"
291 # The private keys for the zones (note that DS records should go into
292 # the zonecontent in _zones
295 Private-key-format: v1.2
296 Algorithm: 13 (ECDSAP256SHA256)
297 PrivateKey: rhWuEydDz3QaIspSVj683B8Xq5q/ozzA38XUgzD4Fbo=
301 Private-key-format: v1.2
302 Algorithm: 13 (ECDSAP256SHA256)
303 PrivateKey: Lt0v0Gol3pRUFM7fDdcy0IWN0O/MnEmVPA+VylL8Y4U=
306 'secure.example': """
307 Private-key-format: v1.2
308 Algorithm: 13 (ECDSAP256SHA256)
309 PrivateKey: 1G4WRoOFJJXk+fotDCHVORtJmIG2OUhKi8AO2jDPGZA=
313 Private-key-format: v1.2
314 Algorithm: 13 (ECDSAP256SHA256)
315 PrivateKey: f5jV7Q8kd5hDpMWObsuQ6SQda0ftf+JrO3uZwEg6nVw=
318 'optout.example': """
319 Private-key-format: v1.2
320 Algorithm: 13 (ECDSAP256SHA256)
321 PrivateKey: efmq9G+J4Y2iPnIBRwJiy6Z/nIHSzpsCy/7XHhlS19A=
324 'secure.optout.example': """
325 Private-key-format: v1.2
326 Algorithm: 13 (ECDSAP256SHA256)
327 PrivateKey: xcNUxt1Knj14A00lKQFDboluiJyM2f7FxpgsQaQ3AQ4=
330 'islandofsecurity.example': """
331 Private-key-format: v1.2
332 Algorithm: 13 (ECDSAP256SHA256)
333 PrivateKey: o9F5iix8V68tnMcuOaM2Lt8XXhIIY//SgHIHEePk6cM=
336 'cname-secure.example': """
337 Private-key-format: v1.2
338 Algorithm: 13 (ECDSAP256SHA256)
339 PrivateKey: kvoV/g4IO/tefSro+FLJ5UC7H3BUf0IUtZQSUOfQGyA=
342 'dname-secure.example': """
343 Private-key-format: v1.2
344 Algorithm: 13 (ECDSAP256SHA256)
345 PrivateKey: Ep9uo6+wwjb4MaOmqq7LHav2FLrjotVOeZg8JT1Qk04=
348 'delay1.example': """
349 Private-key-format: v1.2
350 Algorithm: 13 (ECDSAP256SHA256)
351 PrivateKey: Ep9uo6+wwjb4MaOmqq7LHav2FLrjotVOeZg8JT1Qk04=
354 'delay2.example': """
355 Private-key-format: v1.2
356 Algorithm: 13 (ECDSAP256SHA256)
357 PrivateKey: Ep9uo6+wwjb4MaOmqq7LHav2FLrjotVOeZg8JT1Qk04=
361 # This dict is keyed with the suffix of the IP address and its value
362 # is a list of zones hosted on that IP. Note that delegations should
363 # go into the _zones's zonecontent
368 'zones': ['secure.example', 'islandofsecurity.example']},
370 'zones': ['example']},
372 # 11 is used by CircleCI provided resolver
375 'zones': ['bogus.example', 'undelegated.secure.example', 'undelegated.insecure.example']},
377 'zones': ['insecure.example', 'insecure.sub2.secure.example', 'dname-secure.example']},
379 'zones': ['optout.example']},
381 'zones': ['insecure.optout.example', 'secure.optout.example', 'cname-secure.example']},
383 'zones': ['delay1.example']},
385 'zones': ['delay2.example']},
387 'zones': ['example']}
395 # 22: test_EDNSBuffer.py
397 # 24: test_RoutingTag.py
399 _auth_cmd
= ['authbind',
405 def createConfigDir(cls
, confdir
):
407 shutil
.rmtree(confdir
)
409 if e
.errno
!= errno
.ENOENT
:
411 os
.mkdir(confdir
, 0o755)
414 def generateAuthZone(cls
, confdir
, zonename
, zonecontent
):
415 with
open(os
.path
.join(confdir
, '%s.zone' % zonename
), 'w') as zonefile
:
416 zonefile
.write(zonecontent
.format(prefix
=cls
._PREFIX
, soa
=cls
._SOA
))
419 def generateAuthNamedConf(cls
, confdir
, zones
):
420 with
open(os
.path
.join(confdir
, 'named.conf'), 'w') as namedconf
:
425 for zonename
in zones
:
426 zone
= '.' if zonename
== 'ROOT' else zonename
432 };""" % (zone
, zonename
))
435 def generateAuthConfig(cls
, confdir
, threads
):
436 bind_dnssec_db
= os
.path
.join(confdir
, 'bind-dnssec.sqlite3')
438 with
open(os
.path
.join(confdir
, 'pdns.conf'), 'w') as pdnsconf
:
440 module-dir=../regression-tests/modules
443 bind-config={confdir}/named.conf
444 bind-dnssec-db={bind_dnssec_db}
454 distributor-threads={threads}""".format(confdir
=confdir
,
455 bind_dnssec_db
=bind_dnssec_db
,
458 pdnsutilCmd
= [os
.environ
['PDNSUTIL'],
459 '--config-dir=%s' % confdir
,
463 print(' '.join(pdnsutilCmd
))
465 subprocess
.check_output(pdnsutilCmd
, stderr
=subprocess
.STDOUT
)
466 except subprocess
.CalledProcessError
as e
:
467 raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd
, e
.returncode
, e
.output
))
470 def secureZone(cls
, confdir
, zonename
, key
=None):
471 zone
= '.' if zonename
== 'ROOT' else zonename
473 pdnsutilCmd
= [os
.environ
['PDNSUTIL'],
474 '--config-dir=%s' % confdir
,
478 keyfile
= os
.path
.join(confdir
, 'dnssec.key')
479 with
open(keyfile
, 'w') as fdKeyfile
:
482 pdnsutilCmd
= [os
.environ
['PDNSUTIL'],
483 '--config-dir=%s' % confdir
,
490 print(' '.join(pdnsutilCmd
))
492 subprocess
.check_output(pdnsutilCmd
, stderr
=subprocess
.STDOUT
)
493 except subprocess
.CalledProcessError
as e
:
494 raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd
, e
.returncode
, e
.output
))
497 def generateAllAuthConfig(cls
, confdir
):
499 for auth_suffix
, zoneinfo
in cls
._auth
_zones
.items():
500 threads
= zoneinfo
['threads']
501 zones
= zoneinfo
['zones']
502 authconfdir
= os
.path
.join(confdir
, 'auth-%s' % auth_suffix
)
504 os
.mkdir(authconfdir
)
506 cls
.generateAuthConfig(authconfdir
, threads
)
507 cls
.generateAuthNamedConf(authconfdir
, zones
)
510 cls
.generateAuthZone(authconfdir
,
513 if cls
._zone
_keys
.get(zone
, None):
514 cls
.secureZone(authconfdir
, zone
, cls
._zone
_keys
.get(zone
))
517 def startAllAuth(cls
, confdir
):
519 for auth_suffix
, _
in cls
._auth
_zones
.items():
520 authconfdir
= os
.path
.join(confdir
, 'auth-%s' % auth_suffix
)
521 ipaddress
= cls
._PREFIX
+ '.' + auth_suffix
522 cls
.startAuth(authconfdir
, ipaddress
)
525 def waitForTCPSocket(cls
, ipaddress
, port
):
526 for try_number
in range(0, 100):
528 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
530 sock
.connect((ipaddress
, port
))
533 except Exception as err
:
534 if err
.errno
!= errno
.ECONNREFUSED
:
535 print(f
'Error occurred: {try_number} {err}', file=sys
.stderr
)
539 def startAuth(cls
, confdir
, ipaddress
):
540 print("Launching pdns_server..")
541 authcmd
= list(cls
._auth
_cmd
)
542 authcmd
.append('--config-dir=%s' % confdir
)
544 # auth-8 is the auth serving the root, it gets an ipv6 address
545 if (confdir
[-6:] == "auth-8") and have_ipv6():
547 authcmd
.append('--local-address=%s' % ipconfig
)
548 print(' '.join(authcmd
))
550 logFile
= os
.path
.join(confdir
, 'pdns.log')
551 with
open(logFile
, 'w') as fdLog
:
552 cls
._auths
[ipaddress
] = subprocess
.Popen(authcmd
, close_fds
=True,
553 stdout
=fdLog
, stderr
=fdLog
,
556 cls
.waitForTCPSocket(ipaddress
, 53)
558 if cls
._auths
[ipaddress
].poll() is not None:
559 print(f
"\n*** startAuth log for {logFile} ***")
560 with
open(logFile
, 'r') as fdLog
:
562 print(f
"*** End startAuth log for {logFile} ***")
563 raise AssertionError('%s failed (%d)' % (authcmd
, cls
._auths
[ipaddress
].returncode
))
566 def generateRecursorConfig(cls
, confdir
):
567 params
= tuple([getattr(cls
, param
) for param
in cls
._config
_params
])
571 recursorconf
= os
.path
.join(confdir
, 'recursor.conf')
573 with
open(recursorconf
, 'w') as conf
:
574 conf
.write("# Autogenerated by recursortests.py\n")
575 conf
.write(cls
._config
_template
_default
)
576 conf
.write(cls
._config
_template
% params
)
578 conf
.write("socket-dir=%s\n" % confdir
)
579 if cls
._lua
_config
_file
or cls
._root
_DS
:
580 luaconfpath
= os
.path
.join(confdir
, 'conffile.lua')
581 with
open(luaconfpath
, 'w') as luaconf
:
583 luaconf
.write("addTA('.', '%s')\n" % cls
._root
_DS
)
584 if cls
._lua
_config
_file
:
585 luaconf
.write(cls
._lua
_config
_file
)
586 conf
.write("lua-config-file=%s\n" % luaconfpath
)
587 if cls
._lua
_dns
_script
_file
:
588 luascriptpath
= os
.path
.join(confdir
, 'dnsscript.lua')
589 with
open(luascriptpath
, 'w') as luascript
:
590 luascript
.write(cls
._lua
_dns
_script
_file
)
591 conf
.write("lua-dns-script=%s\n" % luascriptpath
)
593 roothintspath
= os
.path
.join(confdir
, 'root.hints')
594 with
open(roothintspath
, 'w') as roothints
:
595 roothints
.write(cls
._roothints
)
596 conf
.write("hint-file=%s\n" % roothintspath
)
599 def startResponders(cls
):
603 def startRecursor(cls
, confdir
, port
):
604 print("Launching pdns_recursor..")
605 recursorcmd
= [os
.environ
['PDNSRECURSOR'],
606 '--config-dir=%s' % confdir
,
607 '--local-port=%s' % port
,
608 '--security-poll-suffix=']
609 print(' '.join(recursorcmd
))
611 logFile
= os
.path
.join(confdir
, 'recursor.log')
612 with
open(logFile
, 'w') as fdLog
:
613 cls
._recursor
= subprocess
.Popen(recursorcmd
, close_fds
=True,
614 stdout
=fdLog
, stderr
=fdLog
)
616 cls
.waitForTCPSocket("127.0.0.1", port
)
618 if cls
._recursor
.poll() is not None:
619 print(f
"\n*** startRecursor log for {logFile} ***")
620 with
open(logFile
, 'r') as fdLog
:
622 print(f
"*** End startRecursor log for {logFile} ***")
623 raise AssertionError('%s failed (%d)' % (recursorcmd
, _recursor
.returncode
))
626 def wipeRecursorCache(cls
, confdir
):
627 rec_controlCmd
= [os
.environ
['RECCONTROL'],
628 '--config-dir=%s' % confdir
,
632 subprocess
.check_output(rec_controlCmd
, stderr
=subprocess
.STDOUT
)
633 except subprocess
.CalledProcessError
as e
:
634 raise AssertionError('%s failed (%d): %s' % (rec_controlCmd
, e
.returncode
, e
.output
))
637 def setUpSockets(cls
):
638 print("Setting up UDP socket..")
639 cls
._sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_DGRAM
)
640 cls
._sock
.settimeout(2.0)
641 cls
._sock
.connect(("127.0.0.1", cls
._recursorPort
))
647 cls
.startResponders()
649 confdir
= os
.path
.join('configs', cls
._confdir
)
650 cls
.createConfigDir(confdir
)
651 cls
.generateAllAuthConfig(confdir
)
652 cls
.startAllAuth(confdir
)
654 cls
.generateRecursorConfig(confdir
)
655 cls
.startRecursor(confdir
, cls
._recursorPort
)
657 print("Launching tests..")
660 def tearDownClass(cls
):
661 cls
.tearDownRecursor()
663 cls
.tearDownResponders()
666 def tearDownResponders(cls
):
670 def killProcess(cls
, p
):
671 # Don't try to kill it if it's already dead
672 if p
.poll() is not None:
676 for count
in range(10):
682 print("kill...", p
, file=sys
.stderr
)
686 # There is a race-condition with the poll() and
687 # kill() statements, when the process is dead on the
688 # kill(), this is fine
689 if e
.errno
!= errno
.ESRCH
:
693 def tearDownAuth(cls
):
694 for _
, auth
in cls
._auths
.items():
695 cls
.killProcess(auth
);
698 def tearDownRecursor(cls
):
699 cls
.killProcess(cls
._recursor
)
702 def sendUDPQuery(cls
, query
, timeout
=2.0, decode
=True, fwparams
=dict()):
704 cls
._sock
.settimeout(timeout
)
707 cls
._sock
.send(query
.to_wire())
708 data
= cls
._sock
.recv(4096)
709 except socket
.timeout
:
713 cls
._sock
.settimeout(None)
719 message
= dns
.message
.from_wire(data
, **fwparams
)
723 def sendTCPQuery(cls
, query
, timeout
=2.0):
724 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
726 sock
.settimeout(timeout
)
728 sock
.connect(("127.0.0.1", cls
._recursorPort
))
731 wire
= query
.to_wire()
732 sock
.send(struct
.pack("!H", len(wire
)))
736 (datalen
,) = struct
.unpack("!H", data
)
737 data
= sock
.recv(datalen
)
738 except socket
.timeout
as e
:
739 print("Timeout: %s" % (str(e
)))
741 except socket
.error
as e
:
742 print("Network error: %s" % (str(e
)))
749 message
= dns
.message
.from_wire(data
)
753 def sendTCPQueries(cls
, queries
, timeout
=2.0):
754 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
756 sock
.settimeout(timeout
)
758 sock
.connect(("127.0.0.1", cls
._recursorPort
))
761 for query
in queries
:
762 wire
= query
.to_wire()
763 sock
.send(struct
.pack("!H", len(wire
)))
765 for i
in range(len(queries
)):
767 datalen
= sock
.recv(2)
769 (datalen
,) = struct
.unpack("!H", datalen
)
770 data
.append(sock
.recv(datalen
))
771 except socket
.timeout
as e
:
773 except socket
.error
as e
:
774 print("Network error: %s" % (str(e
)))
781 messages
.append(dns
.message
.from_wire(d
))
785 # This function is called before every tests
786 super(RecursorTest
, self
).setUp()
788 ## Functions for comparisons
789 def assertMessageHasFlags(self
, msg
, flags
, ednsflags
=[]):
790 """Asserts that msg has all the flags from flags set
792 @param msg: the dns.message.Message to check
793 @param flags: a list of strings with flag mnemonics (like ['RD', 'RA'])
794 @param ednsflags: a list of strings with edns-flag mnemonics (like ['DO'])"""
796 if not isinstance(msg
, dns
.message
.Message
):
797 raise TypeError("msg is not a dns.message.Message")
799 if isinstance(flags
, list):
801 if not isinstance(elem
, str):
802 raise TypeError("flags is not a list of strings")
804 raise TypeError("flags is not a list of strings")
806 if isinstance(ednsflags
, list):
807 for elem
in ednsflags
:
808 if not isinstance(elem
, str):
809 raise TypeError("ednsflags is not a list of strings")
811 raise TypeError("ednsflags is not a list of strings")
813 msgFlags
= dns
.flags
.to_text(msg
.flags
).split()
814 missingFlags
= [flag
for flag
in flags
if flag
not in msgFlags
]
816 msgEdnsFlags
= dns
.flags
.edns_to_text(msg
.ednsflags
).split()
817 missingEdnsFlags
= [ednsflag
for ednsflag
in ednsflags
if ednsflag
not in msgEdnsFlags
]
819 if len(missingFlags
) or len(missingEdnsFlags
) or len(msgFlags
) > len(flags
):
820 raise AssertionError("Expected flags '%s' (EDNS: '%s'), found '%s' (EDNS: '%s') in query %s" %
821 (' '.join(flags
), ' '.join(ednsflags
),
822 ' '.join(msgFlags
), ' '.join(msgEdnsFlags
),
825 def assertMessageIsAuthenticated(self
, msg
):
826 """Asserts that the message has the AD bit set
828 @param msg: the dns.message.Message to check"""
830 if not isinstance(msg
, dns
.message
.Message
):
831 raise TypeError("msg is not a dns.message.Message")
833 msgFlags
= dns
.flags
.to_text(msg
.flags
)
834 self
.assertTrue('AD' in msgFlags
, "No AD flag found in the message for %s" % msg
.question
[0].name
)
836 def assertRRsetInAnswer(self
, msg
, rrset
):
837 """Asserts the rrset (without comparing TTL) exists in the
838 answer section of msg
840 @param msg: the dns.message.Message to check
841 @param rrset: a dns.rrset.RRset object"""
844 if not isinstance(msg
, dns
.message
.Message
):
845 raise TypeError("msg is not a dns.message.Message")
847 if not isinstance(rrset
, dns
.rrset
.RRset
):
848 raise TypeError("rrset is not a dns.rrset.RRset")
851 for ans
in msg
.answer
:
852 ret
+= "%s\n" % ans
.to_text()
853 if ans
.match(rrset
.name
, rrset
.rdclass
, rrset
.rdtype
, 0, None):
854 self
.assertEqual(ans
, rrset
, "'%s' != '%s'" % (ans
.to_text(), rrset
.to_text()))
858 raise AssertionError("RRset not found in answer\n\n%s" % ret
)
860 def assertMatchingRRSIGInAnswer(self
, msg
, coveredRRset
, keys
=None):
861 """Looks for coveredRRset in the answer section and if there is an RRSIG RRset
862 that covers that RRset. If keys is not None, this function will also try to
863 validate the RRset against the RRSIG
865 @param msg: The dns.message.Message to check
866 @param coveredRRset: The RRSet to check for
867 @param keys: a dictionary keyed by dns.name.Name with node or rdataset values to use for validation"""
869 if not isinstance(msg
, dns
.message
.Message
):
870 raise TypeError("msg is not a dns.message.Message")
872 if not isinstance(coveredRRset
, dns
.rrset
.RRset
):
873 raise TypeError("coveredRRset is not a dns.rrset.RRset")
879 for ans
in msg
.answer
:
880 ret
+= ans
.to_text() + "\n"
882 if ans
.match(coveredRRset
.name
, coveredRRset
.rdclass
, coveredRRset
.rdtype
, 0, None):
884 if ans
.match(coveredRRset
.name
, dns
.rdataclass
.IN
, dns
.rdatatype
.RRSIG
, coveredRRset
.rdtype
, None):
886 if msgRRSet
and msgRRsigRRSet
:
890 raise AssertionError("RRset for '%s' not found in answer" % msg
.question
[0].to_text())
892 if not msgRRsigRRSet
:
893 raise AssertionError("No RRSIGs found in answer for %s:\nFull answer:\n%s" % (msg
.question
[0].to_text(), ret
))
897 dns
.dnssec
.validate(msgRRSet
, msgRRsigRRSet
.to_rdataset(), keys
)
898 except dns
.dnssec
.ValidationFailure
as e
:
899 raise AssertionError("Signature validation failed for %s:\n%s" % (msg
.question
[0].to_text(), e
))
901 def assertNoRRSIGsInAnswer(self
, msg
):
902 """Checks if there are _no_ RRSIGs in the answer section of msg"""
904 if not isinstance(msg
, dns
.message
.Message
):
905 raise TypeError("msg is not a dns.message.Message")
908 for ans
in msg
.answer
:
909 if ans
.rdtype
== dns
.rdatatype
.RRSIG
:
910 ret
+= ans
.name
.to_text() + "\n"
913 raise AssertionError("RRSIG found in answers for:\n%s" % ret
)
915 def assertAnswerEmpty(self
, msg
):
916 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
])))
918 def assertRcodeEqual(self
, msg
, rcode
):
919 if not isinstance(msg
, dns
.message
.Message
):
920 raise TypeError("msg is not a dns.message.Message but a %s" % type(msg
))
922 if not isinstance(rcode
, int):
923 if isinstance(rcode
, str):
924 rcode
= dns
.rcode
.from_text(rcode
)
926 raise TypeError("rcode is neither a str nor int")
928 if msg
.rcode() != rcode
:
929 msgRcode
= dns
.rcode
.to_text(msg
.rcode())
930 wantedRcode
= dns
.rcode
.to_text(rcode
)
932 raise AssertionError("Rcode for %s is %s, expected %s." % (msg
.question
[0].to_text(), msgRcode
, wantedRcode
))
934 def assertAuthorityHasSOA(self
, msg
):
935 if not isinstance(msg
, dns
.message
.Message
):
936 raise TypeError("msg is not a dns.message.Message but a %s" % type(msg
))
939 for rrset
in msg
.authority
:
940 if rrset
.rdtype
== dns
.rdatatype
.SOA
:
945 raise AssertionError("No SOA record found in the authority section:\n%s" % msg
.to_text())
947 def assertResponseMatches(self
, query
, expectedRRs
, response
):
948 expectedResponse
= dns
.message
.make_response(query
)
950 if query
.flags
& dns
.flags
.RD
:
951 expectedResponse
.flags |
= dns
.flags
.RA
952 if query
.flags
& dns
.flags
.CD
:
953 expectedResponse
.flags |
= dns
.flags
.CD
955 expectedResponse
.answer
= expectedRRs
956 print(expectedResponse
)
958 self
.assertEqual(response
, expectedResponse
)
961 def sendQuery(cls
, name
, rdtype
, useTCP
=False):
962 """Helper function that creates the query"""
963 msg
= dns
.message
.make_query(name
, rdtype
, want_dnssec
=True)
964 msg
.flags |
= dns
.flags
.AD
967 return cls
.sendTCPQuery(msg
)
968 return cls
.sendUDPQuery(msg
)
970 def createQuery(self
, name
, rdtype
, flags
, ednsflags
):
971 """Helper function that creates the query with the specified flags.
972 The flags need to be strings (no checking is performed atm)"""
973 msg
= dns
.message
.make_query(name
, rdtype
)
974 msg
.flags
= dns
.flags
.from_text(flags
)
975 msg
.flags
+= dns
.flags
.from_text('RD')
976 msg
.use_edns(edns
=0, ednsflags
=dns
.flags
.edns_from_text(ednsflags
))
980 def sendUDPQueryWithProxyProtocol(cls
, query
, v6
, source
, destination
, sourcePort
, destinationPort
, values
=[], timeout
=2.0):
981 queryPayload
= query
.to_wire()
982 ppPayload
= ProxyProtocol
.getPayload(False, False, v6
, source
, destination
, sourcePort
, destinationPort
, values
)
983 payload
= ppPayload
+ queryPayload
986 cls
._sock
.settimeout(timeout
)
989 cls
._sock
.send(payload
)
990 data
= cls
._sock
.recv(4096)
991 except socket
.timeout
:
995 cls
._sock
.settimeout(None)
999 message
= dns
.message
.from_wire(data
)
1003 def sendTCPQueryWithProxyProtocol(cls
, query
, v6
, source
, destination
, sourcePort
, destinationPort
, values
=[], timeout
=2.0):
1004 queryPayload
= query
.to_wire()
1005 ppPayload
= ProxyProtocol
.getPayload(False, False, v6
, source
, destination
, sourcePort
, destinationPort
, values
)
1007 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
1009 sock
.settimeout(timeout
)
1011 sock
.connect(("127.0.0.1", cls
._recursorPort
))
1014 sock
.send(ppPayload
)
1015 sock
.send(struct
.pack("!H", len(queryPayload
)))
1016 sock
.send(queryPayload
)
1019 (datalen
,) = struct
.unpack("!H", data
)
1020 data
= sock
.recv(datalen
)
1021 except socket
.timeout
as e
:
1022 print("Timeout: %s" % (str(e
)))
1024 except socket
.error
as e
:
1025 print("Network error: %s" % (str(e
)))
1032 message
= dns
.message
.from_wire(data
)