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 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
164 secure.example. 3600 IN A 192.0.2.17
166 host1.secure.example. 3600 IN A 192.0.2.2
167 cname.secure.example. 3600 IN CNAME host1.secure.example.
168 cname-to-insecure.secure.example. 3600 IN CNAME node1.insecure.example.
169 cname-to-bogus.secure.example. 3600 IN CNAME ted.bogus.example.
170 cname-to-islandofsecurity.secure.example. 3600 IN CNAME node1.islandofsecurity.example.
172 host1.sub.secure.example. 3600 IN A 192.0.2.11
175 sub2.secure.example. 3600 IN CNAME doesnotmatter.insecure.example.
176 insecure.sub2.secure.example. 3600 IN NS ns1.insecure.example.
178 *.wildcard.secure.example. 3600 IN A 192.0.2.10
180 *.cnamewildcard.secure.example. 3600 IN CNAME host1.secure.example.
182 *.cnamewildcardnxdomain.secure.example. 3600 IN CNAME doesnotexist.secure.example.
184 cname-to-formerr.secure.example. 3600 IN CNAME host1.insecure-formerr.example.
186 dname-secure.secure.example. 3600 IN DNAME dname-secure.example.
187 dname-insecure.secure.example. 3600 IN DNAME insecure.example.
188 dname-bogus.secure.example. 3600 IN DNAME bogus.example.
190 non-apex-dnskey.secure.example. 3600 IN DNSKEY 257 3 13 CT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==
191 non-apex-dnskey2.secure.example. 3600 IN DNSKEY 256 3 13 CT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==
192 non-apex-dnskey3.secure.example. 3600 IN DNSKEY 256 3 13 DT6AJ4MEOtNDgj0+xLtTLGHf1WbLsKWZI8ONHOt/6q7hTjeWSnY/SGig1dIKZrHg+pJFUSPaxeShv48SYVRKEg==
194 'dname-secure.example': """
195 dname-secure.example. 3600 IN SOA {soa}
196 dname-secure.example. 3600 IN NS ns.dname-secure.example.
197 ns.dname-secure.example. 3600 IN A {prefix}.13
199 host1.dname-secure.example. IN A 192.0.2.21
201 cname-to-secure.dname-secure.example. 3600 IN CNAME host1.secure.example.
202 cname-to-insecure.dname-secure.example. 3600 IN CNAME node1.insecure.example.
203 cname-to-bogus.dname-secure.example. 3600 IN CNAME ted.bogus.example.
205 'cname-secure.example': """
206 cname-secure.example. 3600 IN SOA {soa}
207 cname-secure.example. 3600 IN NS ns.cname-secure.example.
208 ns.cname-secure.example. 3600 IN A {prefix}.15
209 cname-secure.example. 3600 IN CNAME secure.example.
212 bogus.example. 3600 IN SOA {soa}
213 bogus.example. 3600 IN NS ns1.bogus.example.
214 ns1.bogus.example. 3600 IN A {prefix}.12
215 ted.bogus.example. 3600 IN A 192.0.2.1
216 bill.bogus.example. 3600 IN AAAA 2001:db8:12::3
218 'insecure.sub2.secure.example': """
219 insecure.sub2.secure.example. 3600 IN SOA {soa}
220 insecure.sub2.secure.example. 3600 IN NS ns1.insecure.example.
222 node1.insecure.sub2.secure.example. 3600 IN A 192.0.2.18
224 'insecure.example': """
225 insecure.example. 3600 IN SOA {soa}
226 insecure.example. 3600 IN NS ns1.insecure.example.
227 ns1.insecure.example. 3600 IN A {prefix}.13
229 node1.insecure.example. 3600 IN A 192.0.2.6
231 cname-to-secure.insecure.example. 3600 IN CNAME host1.secure.example.
233 dname-to-secure.insecure.example. 3600 IN DNAME dname-secure.example.
235 'optout.example': """
236 optout.example. 3600 IN SOA {soa}
237 optout.example. 3600 IN NS ns1.optout.example.
238 ns1.optout.example. 3600 IN A {prefix}.14
240 insecure.optout.example. 3600 IN NS ns1.insecure.optout.example.
241 ns1.insecure.optout.example. 3600 IN A {prefix}.15
243 secure.optout.example. 3600 IN NS ns1.secure.optout.example.
244 secure.optout.example. 3600 IN DS 64215 13 1 b88284d7a8d8605c398e8942262f97b9a5a31787
245 ns1.secure.optout.example. 3600 IN A {prefix}.15
247 'insecure.optout.example': """
248 insecure.optout.example. 3600 IN SOA {soa}
249 insecure.optout.example. 3600 IN NS ns1.insecure.optout.example.
250 ns1.insecure.optout.example. 3600 IN A {prefix}.15
252 node1.insecure.optout.example. 3600 IN A 192.0.2.7
254 'secure.optout.example': """
255 secure.optout.example. 3600 IN SOA {soa}
256 secure.optout.example. 3600 IN NS ns1.secure.optout.example.
257 ns1.secure.optout.example. 3600 IN A {prefix}.15
259 node1.secure.optout.example. 3600 IN A 192.0.2.8
261 'islandofsecurity.example': """
262 islandofsecurity.example. 3600 IN SOA {soa}
263 islandofsecurity.example. 3600 IN NS ns1.islandofsecurity.example.
264 ns1.islandofsecurity.example. 3600 IN A {prefix}.9
266 node1.islandofsecurity.example. 3600 IN A 192.0.2.20
268 'undelegated.secure.example': """
269 undelegated.secure.example. 3600 IN SOA {soa}
270 undelegated.secure.example. 3600 IN NS ns1.undelegated.secure.example.
272 node1.undelegated.secure.example. 3600 IN A 192.0.2.21
274 'undelegated.insecure.example': """
275 undelegated.insecure.example. 3600 IN SOA {soa}
276 undelegated.insecure.example. 3600 IN NS ns1.undelegated.insecure.example.
278 node1.undelegated.insecure.example. 3600 IN A 192.0.2.22
281 'delay1.example': """
282 delay1.example. 3600 IN SOA {soa}
283 delay1.example. 3600 IN NS n1.delay1.example.
284 ns1.delay1.example. 3600 IN A {prefix}.16
285 *.delay1.example. 0 LUA TXT ";" "local socket=require('socket')" "socket.sleep(tonumber(qname:getRawLabels()[1])/10)" "return 'a'"
288 'delay2.example': """
289 delay2.example. 3600 IN SOA {soa}
290 delay2.example. 3600 IN NS n1.delay2.example.
291 ns1.delay2.example. 3600 IN A {prefix}.17
292 *.delay2.example. 0 LUA TXT ";" "local socket=require('socket')" "socket.sleep(tonumber(qname:getRawLabels()[1])/10)" "return 'a'"
296 # The private keys for the zones (note that DS records should go into
297 # the zonecontent in _zones
300 Private-key-format: v1.2
301 Algorithm: 13 (ECDSAP256SHA256)
302 PrivateKey: rhWuEydDz3QaIspSVj683B8Xq5q/ozzA38XUgzD4Fbo=
306 Private-key-format: v1.2
307 Algorithm: 13 (ECDSAP256SHA256)
308 PrivateKey: Lt0v0Gol3pRUFM7fDdcy0IWN0O/MnEmVPA+VylL8Y4U=
311 'secure.example': """
312 Private-key-format: v1.2
313 Algorithm: 13 (ECDSAP256SHA256)
314 PrivateKey: 1G4WRoOFJJXk+fotDCHVORtJmIG2OUhKi8AO2jDPGZA=
318 Private-key-format: v1.2
319 Algorithm: 13 (ECDSAP256SHA256)
320 PrivateKey: f5jV7Q8kd5hDpMWObsuQ6SQda0ftf+JrO3uZwEg6nVw=
323 'optout.example': """
324 Private-key-format: v1.2
325 Algorithm: 13 (ECDSAP256SHA256)
326 PrivateKey: efmq9G+J4Y2iPnIBRwJiy6Z/nIHSzpsCy/7XHhlS19A=
329 'secure.optout.example': """
330 Private-key-format: v1.2
331 Algorithm: 13 (ECDSAP256SHA256)
332 PrivateKey: xcNUxt1Knj14A00lKQFDboluiJyM2f7FxpgsQaQ3AQ4=
335 'islandofsecurity.example': """
336 Private-key-format: v1.2
337 Algorithm: 13 (ECDSAP256SHA256)
338 PrivateKey: o9F5iix8V68tnMcuOaM2Lt8XXhIIY//SgHIHEePk6cM=
341 'cname-secure.example': """
342 Private-key-format: v1.2
343 Algorithm: 13 (ECDSAP256SHA256)
344 PrivateKey: kvoV/g4IO/tefSro+FLJ5UC7H3BUf0IUtZQSUOfQGyA=
347 'dname-secure.example': """
348 Private-key-format: v1.2
349 Algorithm: 13 (ECDSAP256SHA256)
350 PrivateKey: Ep9uo6+wwjb4MaOmqq7LHav2FLrjotVOeZg8JT1Qk04=
353 'delay1.example': """
354 Private-key-format: v1.2
355 Algorithm: 13 (ECDSAP256SHA256)
356 PrivateKey: Ep9uo6+wwjb4MaOmqq7LHav2FLrjotVOeZg8JT1Qk04=
359 'delay2.example': """
360 Private-key-format: v1.2
361 Algorithm: 13 (ECDSAP256SHA256)
362 PrivateKey: Ep9uo6+wwjb4MaOmqq7LHav2FLrjotVOeZg8JT1Qk04=
366 # This dict is keyed with the suffix of the IP address and its value
367 # is a list of zones hosted on that IP. Note that delegations should
368 # go into the _zones's zonecontent
373 'zones': ['secure.example', 'islandofsecurity.example']},
375 'zones': ['example']},
377 # 11 is used by CircleCI provided resolver
380 'zones': ['bogus.example', 'undelegated.secure.example', 'undelegated.insecure.example']},
382 'zones': ['insecure.example', 'insecure.sub2.secure.example', 'dname-secure.example']},
384 'zones': ['optout.example']},
386 'zones': ['insecure.optout.example', 'secure.optout.example', 'cname-secure.example']},
388 'zones': ['delay1.example']},
390 'zones': ['delay2.example']},
392 'zones': ['example']}
400 # 22: test_EDNSBuffer.py
402 # 24: test_RoutingTag.py
404 _auth_cmd
= ['authbind',
410 def createConfigDir(cls
, confdir
):
412 shutil
.rmtree(confdir
)
414 if e
.errno
!= errno
.ENOENT
:
416 os
.mkdir(confdir
, 0o755)
419 def generateAuthZone(cls
, confdir
, zonename
, zonecontent
):
420 with
open(os
.path
.join(confdir
, '%s.zone' % zonename
), 'w') as zonefile
:
421 zonefile
.write(zonecontent
.format(prefix
=cls
._PREFIX
, soa
=cls
._SOA
))
424 def generateAuthNamedConf(cls
, confdir
, zones
):
425 with
open(os
.path
.join(confdir
, 'named.conf'), 'w') as namedconf
:
430 for zonename
in zones
:
431 zone
= '.' if zonename
== 'ROOT' else zonename
437 };""" % (zone
, zonename
))
440 def generateAuthConfig(cls
, confdir
, threads
):
441 bind_dnssec_db
= os
.path
.join(confdir
, 'bind-dnssec.sqlite3')
443 with
open(os
.path
.join(confdir
, 'pdns.conf'), 'w') as pdnsconf
:
445 module-dir=../regression-tests/modules
448 bind-config={confdir}/named.conf
449 bind-dnssec-db={bind_dnssec_db}
459 distributor-threads={threads}""".format(confdir
=confdir
,
460 bind_dnssec_db
=bind_dnssec_db
,
463 pdnsutilCmd
= [os
.environ
['PDNSUTIL'],
464 '--config-dir=%s' % confdir
,
468 print(' '.join(pdnsutilCmd
))
470 subprocess
.check_output(pdnsutilCmd
, stderr
=subprocess
.STDOUT
)
471 except subprocess
.CalledProcessError
as e
:
472 raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd
, e
.returncode
, e
.output
))
475 def secureZone(cls
, confdir
, zonename
, key
=None):
476 zone
= '.' if zonename
== 'ROOT' else zonename
478 pdnsutilCmd
= [os
.environ
['PDNSUTIL'],
479 '--config-dir=%s' % confdir
,
483 keyfile
= os
.path
.join(confdir
, 'dnssec.key')
484 with
open(keyfile
, 'w') as fdKeyfile
:
487 pdnsutilCmd
= [os
.environ
['PDNSUTIL'],
488 '--config-dir=%s' % confdir
,
495 print(' '.join(pdnsutilCmd
))
497 subprocess
.check_output(pdnsutilCmd
, stderr
=subprocess
.STDOUT
)
498 except subprocess
.CalledProcessError
as e
:
499 raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd
, e
.returncode
, e
.output
))
502 def generateAllAuthConfig(cls
, confdir
):
504 for auth_suffix
, zoneinfo
in cls
._auth
_zones
.items():
505 threads
= zoneinfo
['threads']
506 zones
= zoneinfo
['zones']
507 authconfdir
= os
.path
.join(confdir
, 'auth-%s' % auth_suffix
)
509 os
.mkdir(authconfdir
)
511 cls
.generateAuthConfig(authconfdir
, threads
)
512 cls
.generateAuthNamedConf(authconfdir
, zones
)
515 cls
.generateAuthZone(authconfdir
,
518 if cls
._zone
_keys
.get(zone
, None):
519 cls
.secureZone(authconfdir
, zone
, cls
._zone
_keys
.get(zone
))
522 def startAllAuth(cls
, confdir
):
524 for auth_suffix
, _
in cls
._auth
_zones
.items():
525 authconfdir
= os
.path
.join(confdir
, 'auth-%s' % auth_suffix
)
526 ipaddress
= cls
._PREFIX
+ '.' + auth_suffix
527 cls
.startAuth(authconfdir
, ipaddress
)
530 def waitForTCPSocket(cls
, ipaddress
, port
):
531 for try_number
in range(0, 100):
533 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
535 sock
.connect((ipaddress
, port
))
538 except Exception as err
:
539 if err
.errno
!= errno
.ECONNREFUSED
:
540 print(f
'Error occurred: {try_number} {err}', file=sys
.stderr
)
544 def startAuth(cls
, confdir
, ipaddress
):
545 print("Launching pdns_server..")
546 authcmd
= list(cls
._auth
_cmd
)
547 authcmd
.append('--config-dir=%s' % confdir
)
549 # auth-8 is the auth serving the root, it gets an ipv6 address
550 if (confdir
[-6:] == "auth-8") and have_ipv6():
552 authcmd
.append('--local-address=%s' % ipconfig
)
553 print(' '.join(authcmd
))
555 logFile
= os
.path
.join(confdir
, 'pdns.log')
556 with
open(logFile
, 'w') as fdLog
:
557 cls
._auths
[ipaddress
] = subprocess
.Popen(authcmd
, close_fds
=True,
558 stdout
=fdLog
, stderr
=fdLog
,
561 cls
.waitForTCPSocket(ipaddress
, 53)
563 if cls
._auths
[ipaddress
].poll() is not None:
564 print(f
"\n*** startAuth log for {logFile} ***")
565 with
open(logFile
, 'r') as fdLog
:
567 print(f
"*** End startAuth log for {logFile} ***")
568 raise AssertionError('%s failed (%d)' % (authcmd
, cls
._auths
[ipaddress
].returncode
))
571 def generateRecursorConfig(cls
, confdir
):
572 params
= tuple([getattr(cls
, param
) for param
in cls
._config
_params
])
576 recursorconf
= os
.path
.join(confdir
, 'recursor.conf')
578 with
open(recursorconf
, 'w') as conf
:
579 conf
.write("# Autogenerated by recursortests.py\n")
580 conf
.write(cls
._config
_template
_default
)
581 conf
.write(cls
._config
_template
% params
)
583 conf
.write("socket-dir=%s\n" % confdir
)
584 if cls
._lua
_config
_file
or cls
._root
_DS
:
585 luaconfpath
= os
.path
.join(confdir
, 'conffile.lua')
586 with
open(luaconfpath
, 'w') as luaconf
:
588 luaconf
.write("addTA('.', '%s')\n" % cls
._root
_DS
)
589 if cls
._lua
_config
_file
:
590 luaconf
.write(cls
._lua
_config
_file
)
591 conf
.write("lua-config-file=%s\n" % luaconfpath
)
592 if cls
._lua
_dns
_script
_file
:
593 luascriptpath
= os
.path
.join(confdir
, 'dnsscript.lua')
594 with
open(luascriptpath
, 'w') as luascript
:
595 luascript
.write(cls
._lua
_dns
_script
_file
)
596 conf
.write("lua-dns-script=%s\n" % luascriptpath
)
598 roothintspath
= os
.path
.join(confdir
, 'root.hints')
599 with
open(roothintspath
, 'w') as roothints
:
600 roothints
.write(cls
._roothints
)
601 conf
.write("hint-file=%s\n" % roothintspath
)
604 def startResponders(cls
):
608 def startRecursor(cls
, confdir
, port
):
609 print("Launching pdns_recursor..")
610 recursorcmd
= [os
.environ
['PDNSRECURSOR'],
611 '--config-dir=%s' % confdir
,
612 '--local-port=%s' % port
,
613 '--security-poll-suffix=']
614 print(' '.join(recursorcmd
))
616 logFile
= os
.path
.join(confdir
, 'recursor.log')
617 with
open(logFile
, 'w') as fdLog
:
618 cls
._recursor
= subprocess
.Popen(recursorcmd
, close_fds
=True,
619 stdout
=fdLog
, stderr
=fdLog
)
621 cls
.waitForTCPSocket("127.0.0.1", port
)
623 if cls
._recursor
.poll() is not None:
624 print(f
"\n*** startRecursor log for {logFile} ***")
625 with
open(logFile
, 'r') as fdLog
:
627 print(f
"*** End startRecursor log for {logFile} ***")
628 raise AssertionError('%s failed (%d)' % (recursorcmd
, _recursor
.returncode
))
631 def wipeRecursorCache(cls
, confdir
):
632 rec_controlCmd
= [os
.environ
['RECCONTROL'],
633 '--config-dir=%s' % confdir
,
637 subprocess
.check_output(rec_controlCmd
, stderr
=subprocess
.STDOUT
)
638 except subprocess
.CalledProcessError
as e
:
639 raise AssertionError('%s failed (%d): %s' % (rec_controlCmd
, e
.returncode
, e
.output
))
642 def setUpSockets(cls
):
643 print("Setting up UDP socket..")
644 cls
._sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_DGRAM
)
645 cls
._sock
.settimeout(2.0)
646 cls
._sock
.connect(("127.0.0.1", cls
._recursorPort
))
652 cls
.startResponders()
654 confdir
= os
.path
.join('configs', cls
._confdir
)
655 cls
.createConfigDir(confdir
)
656 cls
.generateAllAuthConfig(confdir
)
657 cls
.startAllAuth(confdir
)
659 cls
.generateRecursorConfig(confdir
)
660 cls
.startRecursor(confdir
, cls
._recursorPort
)
662 print("Launching tests..")
665 def tearDownClass(cls
):
666 cls
.tearDownRecursor()
668 cls
.tearDownResponders()
671 def tearDownResponders(cls
):
675 def killProcess(cls
, p
):
676 # Don't try to kill it if it's already dead
677 if p
.poll() is not None:
681 for count
in range(10):
687 print("kill...", p
, file=sys
.stderr
)
691 # There is a race-condition with the poll() and
692 # kill() statements, when the process is dead on the
693 # kill(), this is fine
694 if e
.errno
!= errno
.ESRCH
:
698 def tearDownAuth(cls
):
699 for _
, auth
in cls
._auths
.items():
700 cls
.killProcess(auth
);
703 def tearDownRecursor(cls
):
704 cls
.killProcess(cls
._recursor
)
707 def sendUDPQuery(cls
, query
, timeout
=2.0, decode
=True, fwparams
=dict()):
709 cls
._sock
.settimeout(timeout
)
712 cls
._sock
.send(query
.to_wire())
713 data
= cls
._sock
.recv(4096)
714 except socket
.timeout
:
718 cls
._sock
.settimeout(None)
724 message
= dns
.message
.from_wire(data
, **fwparams
)
728 def sendTCPQuery(cls
, query
, timeout
=2.0):
729 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
731 sock
.settimeout(timeout
)
733 sock
.connect(("127.0.0.1", cls
._recursorPort
))
736 wire
= query
.to_wire()
737 sock
.send(struct
.pack("!H", len(wire
)))
741 (datalen
,) = struct
.unpack("!H", data
)
742 data
= sock
.recv(datalen
)
743 except socket
.timeout
as e
:
744 print("Timeout: %s" % (str(e
)))
746 except socket
.error
as e
:
747 print("Network error: %s" % (str(e
)))
754 message
= dns
.message
.from_wire(data
)
758 def sendTCPQueries(cls
, queries
, timeout
=2.0):
759 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
761 sock
.settimeout(timeout
)
763 sock
.connect(("127.0.0.1", cls
._recursorPort
))
766 for query
in queries
:
767 wire
= query
.to_wire()
768 sock
.send(struct
.pack("!H", len(wire
)))
770 for i
in range(len(queries
)):
772 datalen
= sock
.recv(2)
774 (datalen
,) = struct
.unpack("!H", datalen
)
775 data
.append(sock
.recv(datalen
))
776 except socket
.timeout
as e
:
778 except socket
.error
as e
:
779 print("Network error: %s" % (str(e
)))
786 messages
.append(dns
.message
.from_wire(d
))
790 # This function is called before every tests
791 super(RecursorTest
, self
).setUp()
793 ## Functions for comparisons
794 def assertMessageHasFlags(self
, msg
, flags
, ednsflags
=[]):
795 """Asserts that msg has all the flags from flags set
797 @param msg: the dns.message.Message to check
798 @param flags: a list of strings with flag mnemonics (like ['RD', 'RA'])
799 @param ednsflags: a list of strings with edns-flag mnemonics (like ['DO'])"""
801 if not isinstance(msg
, dns
.message
.Message
):
802 raise TypeError("msg is not a dns.message.Message")
804 if isinstance(flags
, list):
806 if not isinstance(elem
, str):
807 raise TypeError("flags is not a list of strings")
809 raise TypeError("flags is not a list of strings")
811 if isinstance(ednsflags
, list):
812 for elem
in ednsflags
:
813 if not isinstance(elem
, str):
814 raise TypeError("ednsflags is not a list of strings")
816 raise TypeError("ednsflags is not a list of strings")
818 msgFlags
= dns
.flags
.to_text(msg
.flags
).split()
819 missingFlags
= [flag
for flag
in flags
if flag
not in msgFlags
]
821 msgEdnsFlags
= dns
.flags
.edns_to_text(msg
.ednsflags
).split()
822 missingEdnsFlags
= [ednsflag
for ednsflag
in ednsflags
if ednsflag
not in msgEdnsFlags
]
824 if len(missingFlags
) or len(missingEdnsFlags
) or len(msgFlags
) > len(flags
):
825 raise AssertionError("Expected flags '%s' (EDNS: '%s'), found '%s' (EDNS: '%s') in query %s" %
826 (' '.join(flags
), ' '.join(ednsflags
),
827 ' '.join(msgFlags
), ' '.join(msgEdnsFlags
),
830 def assertMessageIsAuthenticated(self
, msg
):
831 """Asserts that the message has the AD bit set
833 @param msg: the dns.message.Message to check"""
835 if not isinstance(msg
, dns
.message
.Message
):
836 raise TypeError("msg is not a dns.message.Message")
838 msgFlags
= dns
.flags
.to_text(msg
.flags
)
839 self
.assertTrue('AD' in msgFlags
, "No AD flag found in the message for %s" % msg
.question
[0].name
)
841 def assertRRsetInAnswer(self
, msg
, rrset
):
842 """Asserts the rrset (without comparing TTL) exists in the
843 answer section of msg
845 @param msg: the dns.message.Message to check
846 @param rrset: a dns.rrset.RRset object"""
849 if not isinstance(msg
, dns
.message
.Message
):
850 raise TypeError("msg is not a dns.message.Message")
852 if not isinstance(rrset
, dns
.rrset
.RRset
):
853 raise TypeError("rrset is not a dns.rrset.RRset")
856 for ans
in msg
.answer
:
857 ret
+= "%s\n" % ans
.to_text()
858 if ans
.match(rrset
.name
, rrset
.rdclass
, rrset
.rdtype
, 0, None):
859 self
.assertEqual(ans
, rrset
, "'%s' != '%s'" % (ans
.to_text(), rrset
.to_text()))
863 raise AssertionError("RRset not found in answer\n\n%s" % ret
)
865 def assertMatchingRRSIGInAnswer(self
, msg
, coveredRRset
, keys
=None):
866 """Looks for coveredRRset in the answer section and if there is an RRSIG RRset
867 that covers that RRset. If keys is not None, this function will also try to
868 validate the RRset against the RRSIG
870 @param msg: The dns.message.Message to check
871 @param coveredRRset: The RRSet to check for
872 @param keys: a dictionary keyed by dns.name.Name with node or rdataset values to use for validation"""
874 if not isinstance(msg
, dns
.message
.Message
):
875 raise TypeError("msg is not a dns.message.Message")
877 if not isinstance(coveredRRset
, dns
.rrset
.RRset
):
878 raise TypeError("coveredRRset is not a dns.rrset.RRset")
884 for ans
in msg
.answer
:
885 ret
+= ans
.to_text() + "\n"
887 if ans
.match(coveredRRset
.name
, coveredRRset
.rdclass
, coveredRRset
.rdtype
, 0, None):
889 if ans
.match(coveredRRset
.name
, dns
.rdataclass
.IN
, dns
.rdatatype
.RRSIG
, coveredRRset
.rdtype
, None):
891 if msgRRSet
and msgRRsigRRSet
:
895 raise AssertionError("RRset for '%s' not found in answer" % msg
.question
[0].to_text())
897 if not msgRRsigRRSet
:
898 raise AssertionError("No RRSIGs found in answer for %s:\nFull answer:\n%s" % (msg
.question
[0].to_text(), ret
))
902 dns
.dnssec
.validate(msgRRSet
, msgRRsigRRSet
.to_rdataset(), keys
)
903 except dns
.dnssec
.ValidationFailure
as e
:
904 raise AssertionError("Signature validation failed for %s:\n%s" % (msg
.question
[0].to_text(), e
))
906 def assertNoRRSIGsInAnswer(self
, msg
):
907 """Checks if there are _no_ RRSIGs in the answer section of msg"""
909 if not isinstance(msg
, dns
.message
.Message
):
910 raise TypeError("msg is not a dns.message.Message")
913 for ans
in msg
.answer
:
914 if ans
.rdtype
== dns
.rdatatype
.RRSIG
:
915 ret
+= ans
.name
.to_text() + "\n"
918 raise AssertionError("RRSIG found in answers for:\n%s" % ret
)
920 def assertAnswerEmpty(self
, msg
):
921 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
])))
923 def assertRcodeEqual(self
, msg
, rcode
):
924 if not isinstance(msg
, dns
.message
.Message
):
925 raise TypeError("msg is not a dns.message.Message but a %s" % type(msg
))
927 if not isinstance(rcode
, int):
928 if isinstance(rcode
, str):
929 rcode
= dns
.rcode
.from_text(rcode
)
931 raise TypeError("rcode is neither a str nor int")
933 if msg
.rcode() != rcode
:
934 msgRcode
= dns
.rcode
.to_text(msg
.rcode())
935 wantedRcode
= dns
.rcode
.to_text(rcode
)
937 raise AssertionError("Rcode for %s is %s, expected %s." % (msg
.question
[0].to_text(), msgRcode
, wantedRcode
))
939 def assertAuthorityHasSOA(self
, msg
):
940 if not isinstance(msg
, dns
.message
.Message
):
941 raise TypeError("msg is not a dns.message.Message but a %s" % type(msg
))
944 for rrset
in msg
.authority
:
945 if rrset
.rdtype
== dns
.rdatatype
.SOA
:
950 raise AssertionError("No SOA record found in the authority section:\n%s" % msg
.to_text())
952 def assertResponseMatches(self
, query
, expectedRRs
, response
):
953 expectedResponse
= dns
.message
.make_response(query
)
955 if query
.flags
& dns
.flags
.RD
:
956 expectedResponse
.flags |
= dns
.flags
.RA
957 if query
.flags
& dns
.flags
.CD
:
958 expectedResponse
.flags |
= dns
.flags
.CD
960 expectedResponse
.answer
= expectedRRs
961 print(expectedResponse
)
963 self
.assertEqual(response
, expectedResponse
)
966 def sendQuery(cls
, name
, rdtype
, useTCP
=False):
967 """Helper function that creates the query"""
968 msg
= dns
.message
.make_query(name
, rdtype
, want_dnssec
=True)
969 msg
.flags |
= dns
.flags
.AD
972 return cls
.sendTCPQuery(msg
)
973 return cls
.sendUDPQuery(msg
)
975 def createQuery(self
, name
, rdtype
, flags
, ednsflags
):
976 """Helper function that creates the query with the specified flags.
977 The flags need to be strings (no checking is performed atm)"""
978 msg
= dns
.message
.make_query(name
, rdtype
)
979 msg
.flags
= dns
.flags
.from_text(flags
)
980 msg
.flags
+= dns
.flags
.from_text('RD')
981 msg
.use_edns(edns
=0, ednsflags
=dns
.flags
.edns_from_text(ednsflags
))
985 def sendUDPQueryWithProxyProtocol(cls
, query
, v6
, source
, destination
, sourcePort
, destinationPort
, values
=[], timeout
=2.0):
986 queryPayload
= query
.to_wire()
987 ppPayload
= ProxyProtocol
.getPayload(False, False, v6
, source
, destination
, sourcePort
, destinationPort
, values
)
988 payload
= ppPayload
+ queryPayload
991 cls
._sock
.settimeout(timeout
)
994 cls
._sock
.send(payload
)
995 data
= cls
._sock
.recv(4096)
996 except socket
.timeout
:
1000 cls
._sock
.settimeout(None)
1004 message
= dns
.message
.from_wire(data
)
1008 def sendTCPQueryWithProxyProtocol(cls
, query
, v6
, source
, destination
, sourcePort
, destinationPort
, values
=[], timeout
=2.0):
1009 queryPayload
= query
.to_wire()
1010 ppPayload
= ProxyProtocol
.getPayload(False, False, v6
, source
, destination
, sourcePort
, destinationPort
, values
)
1012 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
1014 sock
.settimeout(timeout
)
1016 sock
.connect(("127.0.0.1", cls
._recursorPort
))
1019 sock
.send(ppPayload
)
1020 sock
.send(struct
.pack("!H", len(queryPayload
)))
1021 sock
.send(queryPayload
)
1024 (datalen
,) = struct
.unpack("!H", data
)
1025 data
= sock
.recv(datalen
)
1026 except socket
.timeout
as e
:
1027 print("Timeout: %s" % (str(e
)))
1029 except socket
.error
as e
:
1030 print("Network error: %s" % (str(e
)))
1037 message
= dns
.message
.from_wire(data
)