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
63 _config_template
= """
66 _lua_config_file
= None
67 _lua_dns_script_file
= None
70 ns.root. 3600 IN A %s.8
71 ns.root. 3600 IN AAAA ::1
73 _root_DS
= "63149 13 1 a59da3f5c1b97fcd5fa2b3b2b0ac91d38a60d33a"
75 # The default SOA for zones in the authoritative servers
76 _SOA
= "ns1.example.net. hostmaster.example.net. 1 3600 1800 1209600 300"
78 # The definitions of the zones on the authoritative servers, the key is the
79 # zonename and the value is the zonefile content. several strings are replaced:
80 # - {soa} => value of _SOA
81 # - {prefix} value of _PREFIX
86 ns.root. 3600 IN A {prefix}.8
88 example. 3600 IN NS ns1.example.
89 example. 3600 IN NS ns2.example.
90 example. 3600 IN DS 53174 13 1 50c9e913818767c236c06c2d8272723cb78cbf26
92 ns1.example. 3600 IN A {prefix}.10
93 ns2.example. 3600 IN A {prefix}.18
96 example. 3600 IN SOA {soa}
97 example. 3600 IN NS ns1.example.
98 example. 3600 IN NS ns2.example.
99 ns1.example. 3600 IN A {prefix}.10
100 ns2.example. 3600 IN A {prefix}.18
102 secure.example. 3600 IN NS ns.secure.example.
103 secure.example. 3600 IN DS 64723 13 1 53eb985040d3a89bacf29dbddb55a65834706f33
104 ns.secure.example. 3600 IN A {prefix}.9
106 cname-secure.example. 3600 IN NS ns.cname-secure.example.
107 cname-secure.example. 3600 IN DS 49148 13 1 a10314452d5ec4d97fcc6d7e275d217261fe790f
108 ns.cname-secure.example. 3600 IN A {prefix}.15
110 dname-secure.example. 3600 IN NS ns.dname-secure.example.
111 dname-secure.example. 3600 IN DS 42043 13 2 11c67f46b7c4d5968bc5f6cc944d58377b762bda53ddb4f3a6dbe6faf7a9940f
112 ns.dname-secure.example. 3600 IN A {prefix}.13
114 bogus.example. 3600 IN NS ns.bogus.example.
115 bogus.example. 3600 IN DS 65034 13 1 6df3bb50ea538e90eacdd7ae5419730783abb0ee
116 ns.bogus.example. 3600 IN A {prefix}.12
118 insecure.example. 3600 IN NS ns.insecure.example.
119 ns.insecure.example. 3600 IN A {prefix}.13
121 optout.example. 3600 IN NS ns1.optout.example.
122 optout.example. 3600 IN DS 59332 13 1 e664f886ae1b5df03d918bc1217d22afc29925b9
123 ns1.optout.example. 3600 IN A {prefix}.14
125 postresolve_ffi.example. 3600 IN A 1.2.3.4
126 postresolve_ffi.example. 3600 IN A 1.2.3.5
127 postresolve_ffi.example. 3600 IN AAAA ::1
128 postresolve_ffi.example. 3600 IN AAAA ::2
130 insecure-formerr.example. 3600 IN NS ns1.insecure-formerr.example.
131 ns1.insecure-formerr.example. 3600 IN A {prefix}.2
133 ecs-echo.example. 3600 IN NS ns1.ecs-echo.example.
134 ns1.ecs-echo.example. 3600 IN A {prefix}.21
136 islandofsecurity.example. 3600 IN NS ns1.islandofsecurity.example.
137 ns1.islandofsecurity.example. 3600 IN A {prefix}.9
139 sortcname.example. 3600 IN CNAME sort
140 sort.example. 3600 IN A 17.38.42.80
141 sort.example. 3600 IN A 192.168.0.1
142 sort.example. 3600 IN A 17.238.240.5
143 sort.example. 3600 IN MX 25 mx
145 delay1.example. 3600 IN NS ns1.delay1.example.
146 ns1.delay1.example. 3600 IN A {prefix}.16
147 delay1.example. 3600 IN DS 42043 13 2 7319fa605cf117f36e3de070157577ebb9a05a1d1f963d80eda55b5d6e793eb2
149 delay2.example. 3600 IN NS ns1.delay2.example.
150 ns1.delay2.example. 3600 IN A {prefix}.17
151 delay2.example. 3600 IN DS 42043 13 2 60a047b87740c8564c21d5fd34626c10a77a6c41e3b34564230119c2f13937b8
153 cname-nxd.example. 3600 IN CNAME cname-nxd-target.example.
154 cname-nxd-target.example. 3600 IN A 192.0.2.100
155 cname-nodata.example. 3600 IN CNAME cname-nodata-target.example.
156 cname-nodata-target.example. 3600 IN A 192.0.2.101
157 cname-custom-a.example. 3600 IN CNAME cname-custom-a-target.example.
158 cname-custom-a-target.example. 3600 IN A 192.0.2.102
160 'secure.example': """
161 secure.example. 3600 IN SOA {soa}
162 secure.example. 3600 IN NS ns.secure.example.
163 ns.secure.example. 3600 IN A {prefix}.9
165 secure.example. 3600 IN A 192.0.2.17
167 host1.secure.example. 3600 IN A 192.0.2.2
168 cname.secure.example. 3600 IN CNAME host1.secure.example.
169 cname-to-insecure.secure.example. 3600 IN CNAME node1.insecure.example.
170 cname-to-bogus.secure.example. 3600 IN CNAME ted.bogus.example.
171 cname-to-islandofsecurity.secure.example. 3600 IN CNAME node1.islandofsecurity.example.
173 host1.sub.secure.example. 3600 IN A 192.0.2.11
176 sub2.secure.example. 3600 IN CNAME doesnotmatter.insecure.example.
177 insecure.sub2.secure.example. 3600 IN NS ns1.insecure.example.
179 *.wildcard.secure.example. 3600 IN A 192.0.2.10
181 *.cnamewildcard.secure.example. 3600 IN CNAME host1.secure.example.
183 *.cnamewildcardnxdomain.secure.example. 3600 IN CNAME doesnotexist.secure.example.
185 cname-to-formerr.secure.example. 3600 IN CNAME host1.insecure-formerr.example.
187 dname-secure.secure.example. 3600 IN DNAME dname-secure.example.
188 dname-insecure.secure.example. 3600 IN DNAME insecure.example.
189 dname-bogus.secure.example. 3600 IN DNAME bogus.example.
191 non-apex-dnskey.secure.example. 3600 IN DNSKEY 257 3 13 CT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==
192 non-apex-dnskey2.secure.example. 3600 IN DNSKEY 256 3 13 CT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==
193 non-apex-dnskey3.secure.example. 3600 IN DNSKEY 256 3 13 DT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==
195 'dname-secure.example': """
196 dname-secure.example. 3600 IN SOA {soa}
197 dname-secure.example. 3600 IN NS ns.dname-secure.example.
198 ns.dname-secure.example. 3600 IN A {prefix}.13
200 host1.dname-secure.example. IN A 192.0.2.21
202 cname-to-secure.dname-secure.example. 3600 IN CNAME host1.secure.example.
203 cname-to-insecure.dname-secure.example. 3600 IN CNAME node1.insecure.example.
204 cname-to-bogus.dname-secure.example. 3600 IN CNAME ted.bogus.example.
206 'cname-secure.example': """
207 cname-secure.example. 3600 IN SOA {soa}
208 cname-secure.example. 3600 IN NS ns.cname-secure.example.
209 ns.cname-secure.example. 3600 IN A {prefix}.15
210 cname-secure.example. 3600 IN CNAME secure.example.
213 bogus.example. 3600 IN SOA {soa}
214 bogus.example. 3600 IN NS ns1.bogus.example.
215 ns1.bogus.example. 3600 IN A {prefix}.12
216 ted.bogus.example. 3600 IN A 192.0.2.1
217 bill.bogus.example. 3600 IN AAAA 2001:db8:12::3
219 'insecure.sub2.secure.example': """
220 insecure.sub2.secure.example. 3600 IN SOA {soa}
221 insecure.sub2.secure.example. 3600 IN NS ns1.insecure.example.
223 node1.insecure.sub2.secure.example. 3600 IN A 192.0.2.18
225 'insecure.example': """
226 insecure.example. 3600 IN SOA {soa}
227 insecure.example. 3600 IN NS ns1.insecure.example.
228 ns1.insecure.example. 3600 IN A {prefix}.13
230 node1.insecure.example. 3600 IN A 192.0.2.6
232 cname-to-secure.insecure.example. 3600 IN CNAME host1.secure.example.
234 dname-to-secure.insecure.example. 3600 IN DNAME dname-secure.example.
236 'optout.example': """
237 optout.example. 3600 IN SOA {soa}
238 optout.example. 3600 IN NS ns1.optout.example.
239 ns1.optout.example. 3600 IN A {prefix}.14
241 insecure.optout.example. 3600 IN NS ns1.insecure.optout.example.
242 ns1.insecure.optout.example. 3600 IN A {prefix}.15
244 secure.optout.example. 3600 IN NS ns1.secure.optout.example.
245 secure.optout.example. 3600 IN DS 64215 13 1 b88284d7a8d8605c398e8942262f97b9a5a31787
246 ns1.secure.optout.example. 3600 IN A {prefix}.15
248 'insecure.optout.example': """
249 insecure.optout.example. 3600 IN SOA {soa}
250 insecure.optout.example. 3600 IN NS ns1.insecure.optout.example.
251 ns1.insecure.optout.example. 3600 IN A {prefix}.15
253 node1.insecure.optout.example. 3600 IN A 192.0.2.7
255 'secure.optout.example': """
256 secure.optout.example. 3600 IN SOA {soa}
257 secure.optout.example. 3600 IN NS ns1.secure.optout.example.
258 ns1.secure.optout.example. 3600 IN A {prefix}.15
260 node1.secure.optout.example. 3600 IN A 192.0.2.8
262 'islandofsecurity.example': """
263 islandofsecurity.example. 3600 IN SOA {soa}
264 islandofsecurity.example. 3600 IN NS ns1.islandofsecurity.example.
265 ns1.islandofsecurity.example. 3600 IN A {prefix}.9
267 node1.islandofsecurity.example. 3600 IN A 192.0.2.20
269 'undelegated.secure.example': """
270 undelegated.secure.example. 3600 IN SOA {soa}
271 undelegated.secure.example. 3600 IN NS ns1.undelegated.secure.example.
273 node1.undelegated.secure.example. 3600 IN A 192.0.2.21
275 'undelegated.insecure.example': """
276 undelegated.insecure.example. 3600 IN SOA {soa}
277 undelegated.insecure.example. 3600 IN NS ns1.undelegated.insecure.example.
279 node1.undelegated.insecure.example. 3600 IN A 192.0.2.22
282 'delay1.example': """
283 delay1.example. 3600 IN SOA {soa}
284 delay1.example. 3600 IN NS n1.delay1.example.
285 ns1.delay1.example. 3600 IN A {prefix}.16
286 *.delay1.example. 0 LUA TXT ";" "local socket=require('socket')" "socket.sleep(tonumber(qname:getRawLabels()[1])/10)" "return 'a'"
289 'delay2.example': """
290 delay2.example. 3600 IN SOA {soa}
291 delay2.example. 3600 IN NS n1.delay2.example.
292 ns1.delay2.example. 3600 IN A {prefix}.17
293 *.delay2.example. 0 LUA TXT ";" "local socket=require('socket')" "socket.sleep(tonumber(qname:getRawLabels()[1])/10)" "return 'a'"
297 # The private keys for the zones (note that DS records should go into
298 # the zonecontent in _zones
301 Private-key-format: v1.2
302 Algorithm: 13 (ECDSAP256SHA256)
303 PrivateKey: rhWuEydDz3QaIspSVj683B8Xq5q/ozzA38XUgzD4Fbo=
307 Private-key-format: v1.2
308 Algorithm: 13 (ECDSAP256SHA256)
309 PrivateKey: Lt0v0Gol3pRUFM7fDdcy0IWN0O/MnEmVPA+VylL8Y4U=
312 'secure.example': """
313 Private-key-format: v1.2
314 Algorithm: 13 (ECDSAP256SHA256)
315 PrivateKey: 1G4WRoOFJJXk+fotDCHVORtJmIG2OUhKi8AO2jDPGZA=
319 Private-key-format: v1.2
320 Algorithm: 13 (ECDSAP256SHA256)
321 PrivateKey: f5jV7Q8kd5hDpMWObsuQ6SQda0ftf+JrO3uZwEg6nVw=
324 'optout.example': """
325 Private-key-format: v1.2
326 Algorithm: 13 (ECDSAP256SHA256)
327 PrivateKey: efmq9G+J4Y2iPnIBRwJiy6Z/nIHSzpsCy/7XHhlS19A=
330 'secure.optout.example': """
331 Private-key-format: v1.2
332 Algorithm: 13 (ECDSAP256SHA256)
333 PrivateKey: xcNUxt1Knj14A00lKQFDboluiJyM2f7FxpgsQaQ3AQ4=
336 'islandofsecurity.example': """
337 Private-key-format: v1.2
338 Algorithm: 13 (ECDSAP256SHA256)
339 PrivateKey: o9F5iix8V68tnMcuOaM2Lt8XXhIIY//SgHIHEePk6cM=
342 'cname-secure.example': """
343 Private-key-format: v1.2
344 Algorithm: 13 (ECDSAP256SHA256)
345 PrivateKey: kvoV/g4IO/tefSro+FLJ5UC7H3BUf0IUtZQSUOfQGyA=
348 'dname-secure.example': """
349 Private-key-format: v1.2
350 Algorithm: 13 (ECDSAP256SHA256)
351 PrivateKey: Ep9uo6+wwjb4MaOmqq7LHav2FLrjotVOeZg8JT1Qk04=
354 'delay1.example': """
355 Private-key-format: v1.2
356 Algorithm: 13 (ECDSAP256SHA256)
357 PrivateKey: Ep9uo6+wwjb4MaOmqq7LHav2FLrjotVOeZg8JT1Qk04=
360 'delay2.example': """
361 Private-key-format: v1.2
362 Algorithm: 13 (ECDSAP256SHA256)
363 PrivateKey: Ep9uo6+wwjb4MaOmqq7LHav2FLrjotVOeZg8JT1Qk04=
367 # This dict is keyed with the suffix of the IP address and its value
368 # is a list of zones hosted on that IP. Note that delegations should
369 # go into the _zones's zonecontent
374 'zones': ['secure.example', 'islandofsecurity.example']},
376 'zones': ['example']},
378 # 11 is used by CircleCI provided resolver
381 'zones': ['bogus.example', 'undelegated.secure.example', 'undelegated.insecure.example']},
383 'zones': ['insecure.example', 'insecure.sub2.secure.example', 'dname-secure.example']},
385 'zones': ['optout.example']},
387 'zones': ['insecure.optout.example', 'secure.optout.example', 'cname-secure.example']},
389 'zones': ['delay1.example']},
391 'zones': ['delay2.example']},
393 'zones': ['example']}
401 # 22: test_EDNSBuffer.py
403 # 24: test_RoutingTag.py
405 _auth_cmd
= ['authbind',
411 def createConfigDir(cls
, confdir
):
413 shutil
.rmtree(confdir
)
415 if e
.errno
!= errno
.ENOENT
:
417 os
.mkdir(confdir
, 0o755)
420 def generateAuthZone(cls
, confdir
, zonename
, zonecontent
):
421 with
open(os
.path
.join(confdir
, '%s.zone' % zonename
), 'w') as zonefile
:
422 zonefile
.write(zonecontent
.format(prefix
=cls
._PREFIX
, soa
=cls
._SOA
))
425 def generateAuthNamedConf(cls
, confdir
, zones
):
426 with
open(os
.path
.join(confdir
, 'named.conf'), 'w') as namedconf
:
431 for zonename
in zones
:
432 zone
= '.' if zonename
== 'ROOT' else zonename
438 };""" % (zone
, zonename
))
441 def generateAuthConfig(cls
, confdir
, threads
):
442 bind_dnssec_db
= os
.path
.join(confdir
, 'bind-dnssec.sqlite3')
444 with
open(os
.path
.join(confdir
, 'pdns.conf'), 'w') as pdnsconf
:
446 module-dir=../regression-tests/modules
449 bind-config={confdir}/named.conf
450 bind-dnssec-db={bind_dnssec_db}
460 distributor-threads={threads}""".format(confdir
=confdir
,
461 bind_dnssec_db
=bind_dnssec_db
,
464 pdnsutilCmd
= [os
.environ
['PDNSUTIL'],
465 '--config-dir=%s' % confdir
,
469 print(' '.join(pdnsutilCmd
))
471 subprocess
.check_output(pdnsutilCmd
, stderr
=subprocess
.STDOUT
)
472 except subprocess
.CalledProcessError
as e
:
473 raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd
, e
.returncode
, e
.output
))
476 def secureZone(cls
, confdir
, zonename
, key
=None):
477 zone
= '.' if zonename
== 'ROOT' else zonename
479 pdnsutilCmd
= [os
.environ
['PDNSUTIL'],
480 '--config-dir=%s' % confdir
,
484 keyfile
= os
.path
.join(confdir
, 'dnssec.key')
485 with
open(keyfile
, 'w') as fdKeyfile
:
488 pdnsutilCmd
= [os
.environ
['PDNSUTIL'],
489 '--config-dir=%s' % confdir
,
496 print(' '.join(pdnsutilCmd
))
498 subprocess
.check_output(pdnsutilCmd
, stderr
=subprocess
.STDOUT
)
499 except subprocess
.CalledProcessError
as e
:
500 raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd
, e
.returncode
, e
.output
))
503 def generateAllAuthConfig(cls
, confdir
):
505 for auth_suffix
, zoneinfo
in cls
._auth
_zones
.items():
506 threads
= zoneinfo
['threads']
507 zones
= zoneinfo
['zones']
508 authconfdir
= os
.path
.join(confdir
, 'auth-%s' % auth_suffix
)
510 os
.mkdir(authconfdir
)
512 cls
.generateAuthConfig(authconfdir
, threads
)
513 cls
.generateAuthNamedConf(authconfdir
, zones
)
516 cls
.generateAuthZone(authconfdir
,
519 if cls
._zone
_keys
.get(zone
, None):
520 cls
.secureZone(authconfdir
, zone
, cls
._zone
_keys
.get(zone
))
523 def startAllAuth(cls
, confdir
):
525 for auth_suffix
, _
in cls
._auth
_zones
.items():
526 authconfdir
= os
.path
.join(confdir
, 'auth-%s' % auth_suffix
)
527 ipaddress
= cls
._PREFIX
+ '.' + auth_suffix
528 cls
.startAuth(authconfdir
, ipaddress
)
531 def waitForTCPSocket(cls
, ipaddress
, port
):
532 for try_number
in range(0, 100):
534 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
536 sock
.connect((ipaddress
, port
))
539 except Exception as err
:
540 if err
.errno
!= errno
.ECONNREFUSED
:
541 print(f
'Error occurred: {try_number} {err}', file=sys
.stderr
)
545 def startAuth(cls
, confdir
, ipaddress
):
546 print("Launching pdns_server..")
547 authcmd
= list(cls
._auth
_cmd
)
548 authcmd
.append('--config-dir=%s' % confdir
)
550 # auth-8 is the auth serving the root, it gets an ipv6 address
551 if (confdir
[-6:] == "auth-8") and have_ipv6():
553 authcmd
.append('--local-address=%s' % ipconfig
)
554 print(' '.join(authcmd
))
556 logFile
= os
.path
.join(confdir
, 'pdns.log')
557 with
open(logFile
, 'w') as fdLog
:
558 cls
._auths
[ipaddress
] = subprocess
.Popen(authcmd
, close_fds
=True,
559 stdout
=fdLog
, stderr
=fdLog
,
562 cls
.waitForTCPSocket(ipaddress
, 53)
564 if cls
._auths
[ipaddress
].poll() is not None:
565 print(f
"\n*** startAuth log for {logFile} ***")
566 with
open(logFile
, 'r') as fdLog
:
568 print(f
"*** End startAuth log for {logFile} ***")
569 raise AssertionError('%s failed (%d)' % (authcmd
, cls
._auths
[ipaddress
].returncode
))
572 def generateRecursorConfig(cls
, confdir
):
573 params
= tuple([getattr(cls
, param
) for param
in cls
._config
_params
])
577 recursorconf
= os
.path
.join(confdir
, 'recursor.conf')
579 with
open(recursorconf
, 'w') as conf
:
580 conf
.write("# Autogenerated by recursortests.py\n")
581 conf
.write(cls
._config
_template
_default
)
582 conf
.write(cls
._config
_template
% params
)
584 conf
.write("socket-dir=%s\n" % confdir
)
585 if cls
._lua
_config
_file
or cls
._root
_DS
:
586 luaconfpath
= os
.path
.join(confdir
, 'conffile.lua')
587 with
open(luaconfpath
, 'w') as luaconf
:
589 luaconf
.write("addTA('.', '%s')\n" % cls
._root
_DS
)
590 if cls
._lua
_config
_file
:
591 luaconf
.write(cls
._lua
_config
_file
)
592 conf
.write("lua-config-file=%s\n" % luaconfpath
)
593 if cls
._lua
_dns
_script
_file
:
594 luascriptpath
= os
.path
.join(confdir
, 'dnsscript.lua')
595 with
open(luascriptpath
, 'w') as luascript
:
596 luascript
.write(cls
._lua
_dns
_script
_file
)
597 conf
.write("lua-dns-script=%s\n" % luascriptpath
)
599 roothintspath
= os
.path
.join(confdir
, 'root.hints')
600 with
open(roothintspath
, 'w') as roothints
:
601 roothints
.write(cls
._roothints
)
602 conf
.write("hint-file=%s\n" % roothintspath
)
605 def startResponders(cls
):
609 def startRecursor(cls
, confdir
, port
):
610 print("Launching pdns_recursor..")
611 recursorcmd
= [os
.environ
['PDNSRECURSOR'],
612 '--config-dir=%s' % confdir
,
613 '--local-port=%s' % port
,
614 '--security-poll-suffix=']
615 print(' '.join(recursorcmd
))
617 logFile
= os
.path
.join(confdir
, 'recursor.log')
618 with
open(logFile
, 'w') as fdLog
:
619 cls
._recursor
= subprocess
.Popen(recursorcmd
, close_fds
=True,
620 stdout
=fdLog
, stderr
=fdLog
)
622 cls
.waitForTCPSocket("127.0.0.1", port
)
624 if cls
._recursor
.poll() is not None:
625 print(f
"\n*** startRecursor log for {logFile} ***")
626 with
open(logFile
, 'r') as fdLog
:
628 print(f
"*** End startRecursor log for {logFile} ***")
629 raise AssertionError('%s failed (%d)' % (recursorcmd
, cls
._recursor
.returncode
))
632 def wipeRecursorCache(cls
, confdir
):
633 rec_controlCmd
= [os
.environ
['RECCONTROL'],
634 '--config-dir=%s' % confdir
,
638 subprocess
.check_output(rec_controlCmd
, stderr
=subprocess
.STDOUT
)
639 except subprocess
.CalledProcessError
as e
:
640 raise AssertionError('%s failed (%d): %s' % (rec_controlCmd
, e
.returncode
, e
.output
))
643 def setUpSockets(cls
):
644 print("Setting up UDP socket..")
645 cls
._sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_DGRAM
)
646 cls
._sock
.settimeout(2.0)
647 cls
._sock
.connect(("127.0.0.1", cls
._recursorPort
))
653 cls
.startResponders()
655 confdir
= os
.path
.join('configs', cls
._confdir
)
656 cls
.createConfigDir(confdir
)
657 cls
.generateAllAuthConfig(confdir
)
658 cls
.startAllAuth(confdir
)
660 cls
.generateRecursorConfig(confdir
)
661 cls
.startRecursor(confdir
, cls
._recursorPort
)
663 print("Launching tests..")
666 def tearDownClass(cls
):
667 cls
.tearDownRecursor()
669 cls
.tearDownResponders()
672 def tearDownResponders(cls
):
676 def killProcess(cls
, p
):
677 # Don't try to kill it if it's already dead
678 if p
.poll() is not None:
682 for count
in range(10):
688 print("kill...", p
, file=sys
.stderr
)
692 # There is a race-condition with the poll() and
693 # kill() statements, when the process is dead on the
694 # kill(), this is fine
695 if e
.errno
!= errno
.ESRCH
:
699 def tearDownAuth(cls
):
700 for _
, auth
in cls
._auths
.items():
701 cls
.killProcess(auth
);
704 def tearDownRecursor(cls
):
705 cls
.killProcess(cls
._recursor
)
708 def sendUDPQuery(cls
, query
, timeout
=2.0, decode
=True, fwparams
=dict()):
710 cls
._sock
.settimeout(timeout
)
713 cls
._sock
.send(query
.to_wire())
714 data
= cls
._sock
.recv(4096)
715 except socket
.timeout
:
719 cls
._sock
.settimeout(None)
725 message
= dns
.message
.from_wire(data
, **fwparams
)
729 def sendTCPQuery(cls
, query
, timeout
=2.0):
730 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
732 sock
.settimeout(timeout
)
734 sock
.connect(("127.0.0.1", cls
._recursorPort
))
737 wire
= query
.to_wire()
738 sock
.send(struct
.pack("!H", len(wire
)))
742 (datalen
,) = struct
.unpack("!H", data
)
743 data
= sock
.recv(datalen
)
744 except socket
.timeout
as e
:
745 print("Timeout: %s" % (str(e
)))
747 except socket
.error
as e
:
748 print("Network error: %s" % (str(e
)))
755 message
= dns
.message
.from_wire(data
)
759 def sendTCPQueries(cls
, queries
, timeout
=2.0):
760 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
762 sock
.settimeout(timeout
)
764 sock
.connect(("127.0.0.1", cls
._recursorPort
))
767 for query
in queries
:
768 wire
= query
.to_wire()
769 sock
.send(struct
.pack("!H", len(wire
)))
771 for i
in range(len(queries
)):
773 datalen
= sock
.recv(2)
775 (datalen
,) = struct
.unpack("!H", datalen
)
776 data
.append(sock
.recv(datalen
))
777 except socket
.timeout
as e
:
779 except socket
.error
as e
:
780 print("Network error: %s" % (str(e
)))
787 messages
.append(dns
.message
.from_wire(d
))
791 # This function is called before every tests
792 super(RecursorTest
, self
).setUp()
794 ## Functions for comparisons
795 def assertMessageHasFlags(self
, msg
, flags
, ednsflags
=[]):
796 """Asserts that msg has all the flags from flags set
798 @param msg: the dns.message.Message to check
799 @param flags: a list of strings with flag mnemonics (like ['RD', 'RA'])
800 @param ednsflags: a list of strings with edns-flag mnemonics (like ['DO'])"""
802 if not isinstance(msg
, dns
.message
.Message
):
803 raise TypeError("msg is not a dns.message.Message")
805 if isinstance(flags
, list):
807 if not isinstance(elem
, str):
808 raise TypeError("flags is not a list of strings")
810 raise TypeError("flags is not a list of strings")
812 if isinstance(ednsflags
, list):
813 for elem
in ednsflags
:
814 if not isinstance(elem
, str):
815 raise TypeError("ednsflags is not a list of strings")
817 raise TypeError("ednsflags is not a list of strings")
819 msgFlags
= dns
.flags
.to_text(msg
.flags
).split()
820 missingFlags
= [flag
for flag
in flags
if flag
not in msgFlags
]
822 msgEdnsFlags
= dns
.flags
.edns_to_text(msg
.ednsflags
).split()
823 missingEdnsFlags
= [ednsflag
for ednsflag
in ednsflags
if ednsflag
not in msgEdnsFlags
]
825 if len(missingFlags
) or len(missingEdnsFlags
) or len(msgFlags
) > len(flags
):
826 raise AssertionError("Expected flags '%s' (EDNS: '%s'), found '%s' (EDNS: '%s') in query %s" %
827 (' '.join(flags
), ' '.join(ednsflags
),
828 ' '.join(msgFlags
), ' '.join(msgEdnsFlags
),
831 def assertMessageIsAuthenticated(self
, msg
):
832 """Asserts that the message has the AD bit set
834 @param msg: the dns.message.Message to check"""
836 if not isinstance(msg
, dns
.message
.Message
):
837 raise TypeError("msg is not a dns.message.Message")
839 msgFlags
= dns
.flags
.to_text(msg
.flags
)
840 self
.assertTrue('AD' in msgFlags
, "No AD flag found in the message for %s" % msg
.question
[0].name
)
842 def assertRRsetInAnswer(self
, msg
, rrset
):
843 """Asserts the rrset (without comparing TTL) exists in the
844 answer section of msg
846 @param msg: the dns.message.Message to check
847 @param rrset: a dns.rrset.RRset object"""
850 if not isinstance(msg
, dns
.message
.Message
):
851 raise TypeError("msg is not a dns.message.Message")
853 if not isinstance(rrset
, dns
.rrset
.RRset
):
854 raise TypeError("rrset is not a dns.rrset.RRset")
857 for ans
in msg
.answer
:
858 ret
+= "%s\n" % ans
.to_text()
859 if ans
.match(rrset
.name
, rrset
.rdclass
, rrset
.rdtype
, 0, None):
860 self
.assertEqual(ans
, rrset
, "'%s' != '%s'" % (ans
.to_text(), rrset
.to_text()))
864 raise AssertionError("RRset not found in answer\n\n%s" % ret
)
866 def assertMatchingRRSIGInAnswer(self
, msg
, coveredRRset
, keys
=None):
867 """Looks for coveredRRset in the answer section and if there is an RRSIG RRset
868 that covers that RRset. If keys is not None, this function will also try to
869 validate the RRset against the RRSIG
871 @param msg: The dns.message.Message to check
872 @param coveredRRset: The RRSet to check for
873 @param keys: a dictionary keyed by dns.name.Name with node or rdataset values to use for validation"""
875 if not isinstance(msg
, dns
.message
.Message
):
876 raise TypeError("msg is not a dns.message.Message")
878 if not isinstance(coveredRRset
, dns
.rrset
.RRset
):
879 raise TypeError("coveredRRset is not a dns.rrset.RRset")
885 for ans
in msg
.answer
:
886 ret
+= ans
.to_text() + "\n"
888 if ans
.match(coveredRRset
.name
, coveredRRset
.rdclass
, coveredRRset
.rdtype
, 0, None):
890 if ans
.match(coveredRRset
.name
, dns
.rdataclass
.IN
, dns
.rdatatype
.RRSIG
, coveredRRset
.rdtype
, None):
892 if msgRRSet
and msgRRsigRRSet
:
896 raise AssertionError("RRset for '%s' not found in answer" % msg
.question
[0].to_text())
898 if not msgRRsigRRSet
:
899 raise AssertionError("No RRSIGs found in answer for %s:\nFull answer:\n%s" % (msg
.question
[0].to_text(), ret
))
903 dns
.dnssec
.validate(msgRRSet
, msgRRsigRRSet
.to_rdataset(), keys
)
904 except dns
.dnssec
.ValidationFailure
as e
:
905 raise AssertionError("Signature validation failed for %s:\n%s" % (msg
.question
[0].to_text(), e
))
907 def assertNoRRSIGsInAnswer(self
, msg
):
908 """Checks if there are _no_ RRSIGs in the answer section of msg"""
910 if not isinstance(msg
, dns
.message
.Message
):
911 raise TypeError("msg is not a dns.message.Message")
914 for ans
in msg
.answer
:
915 if ans
.rdtype
== dns
.rdatatype
.RRSIG
:
916 ret
+= ans
.name
.to_text() + "\n"
919 raise AssertionError("RRSIG found in answers for:\n%s" % ret
)
921 def assertAnswerEmpty(self
, msg
):
922 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
])))
924 def assertRcodeEqual(self
, msg
, rcode
):
925 if not isinstance(msg
, dns
.message
.Message
):
926 raise TypeError("msg is not a dns.message.Message but a %s" % type(msg
))
928 if not isinstance(rcode
, int):
929 if isinstance(rcode
, str):
930 rcode
= dns
.rcode
.from_text(rcode
)
932 raise TypeError("rcode is neither a str nor int")
934 if msg
.rcode() != rcode
:
935 msgRcode
= dns
.rcode
.to_text(msg
.rcode())
936 wantedRcode
= dns
.rcode
.to_text(rcode
)
938 raise AssertionError("Rcode for %s is %s, expected %s." % (msg
.question
[0].to_text(), msgRcode
, wantedRcode
))
940 def assertAuthorityHasSOA(self
, msg
):
941 if not isinstance(msg
, dns
.message
.Message
):
942 raise TypeError("msg is not a dns.message.Message but a %s" % type(msg
))
945 for rrset
in msg
.authority
:
946 if rrset
.rdtype
== dns
.rdatatype
.SOA
:
951 raise AssertionError("No SOA record found in the authority section:\n%s" % msg
.to_text())
953 def assertResponseMatches(self
, query
, expectedRRs
, response
):
954 expectedResponse
= dns
.message
.make_response(query
)
956 if query
.flags
& dns
.flags
.RD
:
957 expectedResponse
.flags |
= dns
.flags
.RA
958 if query
.flags
& dns
.flags
.CD
:
959 expectedResponse
.flags |
= dns
.flags
.CD
961 expectedResponse
.answer
= expectedRRs
962 print(expectedResponse
)
964 self
.assertEqual(response
, expectedResponse
)
967 def sendQuery(cls
, name
, rdtype
, useTCP
=False):
968 """Helper function that creates the query"""
969 msg
= dns
.message
.make_query(name
, rdtype
, want_dnssec
=True)
970 msg
.flags |
= dns
.flags
.AD
973 return cls
.sendTCPQuery(msg
)
974 return cls
.sendUDPQuery(msg
)
976 def createQuery(self
, name
, rdtype
, flags
, ednsflags
):
977 """Helper function that creates the query with the specified flags.
978 The flags need to be strings (no checking is performed atm)"""
979 msg
= dns
.message
.make_query(name
, rdtype
)
980 msg
.flags
= dns
.flags
.from_text(flags
)
981 msg
.flags
+= dns
.flags
.from_text('RD')
982 msg
.use_edns(edns
=0, ednsflags
=dns
.flags
.edns_from_text(ednsflags
))
986 def sendUDPQueryWithProxyProtocol(cls
, query
, v6
, source
, destination
, sourcePort
, destinationPort
, values
=[], timeout
=2.0):
987 queryPayload
= query
.to_wire()
988 ppPayload
= ProxyProtocol
.getPayload(False, False, v6
, source
, destination
, sourcePort
, destinationPort
, values
)
989 payload
= ppPayload
+ queryPayload
992 cls
._sock
.settimeout(timeout
)
995 cls
._sock
.send(payload
)
996 data
= cls
._sock
.recv(4096)
997 except socket
.timeout
:
1001 cls
._sock
.settimeout(None)
1005 message
= dns
.message
.from_wire(data
)
1009 def sendTCPQueryWithProxyProtocol(cls
, query
, v6
, source
, destination
, sourcePort
, destinationPort
, values
=[], timeout
=2.0):
1010 queryPayload
= query
.to_wire()
1011 ppPayload
= ProxyProtocol
.getPayload(False, False, v6
, source
, destination
, sourcePort
, destinationPort
, values
)
1013 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
1015 sock
.settimeout(timeout
)
1017 sock
.connect(("127.0.0.1", cls
._recursorPort
))
1020 sock
.send(ppPayload
)
1021 sock
.send(struct
.pack("!H", len(queryPayload
)))
1022 sock
.send(queryPayload
)
1025 (datalen
,) = struct
.unpack("!H", data
)
1026 data
= sock
.recv(datalen
)
1027 except socket
.timeout
as e
:
1028 print("Timeout: %s" % (str(e
)))
1030 except socket
.error
as e
:
1031 print("Network error: %s" % (str(e
)))
1038 message
= dns
.message
.from_wire(data
)