3 from __future__
import print_function
16 from eqdnsmessage
import AssertEqualDNSMessageMixin
18 class RecursorTest(AssertEqualDNSMessageMixin
, unittest
.TestCase
):
20 Setup all recursors and auths required for the tests
25 _recursorStartupDelay
= 2.0
30 _PREFIX
= os
.environ
['PREFIX']
32 _config_template_default
= """
36 local-address=127.0.0.1
38 packetcache-servfail-ttl=0
44 _config_template
= """
47 _lua_config_file
= None
48 _lua_dns_script_file
= None
51 ns.root. 3600 IN A %s.8
53 _root_DS
= "63149 13 1 a59da3f5c1b97fcd5fa2b3b2b0ac91d38a60d33a"
55 # The default SOA for zones in the authoritative servers
56 _SOA
= "ns1.example.net. hostmaster.example.net. 1 3600 1800 1209600 300"
58 # The definitions of the zones on the authoritative servers, the key is the
59 # zonename and the value is the zonefile content. several strings are replaced:
60 # - {soa} => value of _SOA
61 # - {prefix} value of _PREFIX
66 ns.root. 3600 IN A {prefix}.8
68 example. 3600 IN NS ns1.example.
69 example. 3600 IN NS ns2.example.
70 example. 3600 IN DS 53174 13 1 50c9e913818767c236c06c2d8272723cb78cbf26
72 ns1.example. 3600 IN A {prefix}.10
73 ns2.example. 3600 IN A {prefix}.11
76 example. 3600 IN SOA {soa}
77 example. 3600 IN NS ns1.example.
78 example. 3600 IN NS ns2.example.
79 ns1.example. 3600 IN A {prefix}.10
80 ns2.example. 3600 IN A {prefix}.11
82 secure.example. 3600 IN NS ns.secure.example.
83 secure.example. 3600 IN DS 64723 13 1 53eb985040d3a89bacf29dbddb55a65834706f33
84 ns.secure.example. 3600 IN A {prefix}.9
86 cname-secure.example. 3600 IN NS ns.cname-secure.example.
87 cname-secure.example. 3600 IN DS 49148 13 1 a10314452d5ec4d97fcc6d7e275d217261fe790f
88 ns.cname-secure.example. 3600 IN A {prefix}.15
90 dname-secure.example. 3600 IN NS ns.dname-secure.example.
91 dname-secure.example. 3600 IN DS 42043 13 2 11c67f46b7c4d5968bc5f6cc944d58377b762bda53ddb4f3a6dbe6faf7a9940f
92 ns.dname-secure.example. 3600 IN A {prefix}.13
94 bogus.example. 3600 IN NS ns.bogus.example.
95 bogus.example. 3600 IN DS 65034 13 1 6df3bb50ea538e90eacdd7ae5419730783abb0ee
96 ns.bogus.example. 3600 IN A {prefix}.12
98 insecure.example. 3600 IN NS ns.insecure.example.
99 ns.insecure.example. 3600 IN A {prefix}.13
101 optout.example. 3600 IN NS ns1.optout.example.
102 optout.example. 3600 IN DS 59332 13 1 e664f886ae1b5df03d918bc1217d22afc29925b9
103 ns1.optout.example. 3600 IN A {prefix}.14
105 insecure-formerr.example. 3600 IN NS ns1.insecure-formerr.example.
106 ns1.insecure-formerr.example. 3600 IN A {prefix}.2
108 ecs-echo.example. 3600 IN NS ns1.ecs-echo.example.
109 ns1.ecs-echo.example. 3600 IN A {prefix}.21
111 islandofsecurity.example. 3600 IN NS ns1.islandofsecurity.example.
112 ns1.islandofsecurity.example. 3600 IN A {prefix}.9
114 sortcname.example. 3600 IN CNAME sort
115 sort.example. 3600 IN A 17.38.42.80
116 sort.example. 3600 IN A 192.168.0.1
117 sort.example. 3600 IN A 17.238.240.5
118 sort.example. 3600 IN MX 25 mx
120 'secure.example': """
121 secure.example. 3600 IN SOA {soa}
122 secure.example. 3600 IN NS ns.secure.example.
123 ns.secure.example. 3600 IN A {prefix}.9
125 secure.example. 3600 IN A 192.0.2.17
127 host1.secure.example. 3600 IN A 192.0.2.2
128 cname.secure.example. 3600 IN CNAME host1.secure.example.
129 cname-to-insecure.secure.example. 3600 IN CNAME node1.insecure.example.
130 cname-to-bogus.secure.example. 3600 IN CNAME ted.bogus.example.
131 cname-to-islandofsecurity.secure.example. 3600 IN CNAME node1.islandofsecurity.example.
133 host1.sub.secure.example. 3600 IN A 192.0.2.11
136 sub2.secure.example. 3600 IN CNAME doesnotmatter.insecure.example.
137 insecure.sub2.secure.example. 3600 IN NS ns1.insecure.example.
139 *.wildcard.secure.example. 3600 IN A 192.0.2.10
141 *.cnamewildcard.secure.example. 3600 IN CNAME host1.secure.example.
143 *.cnamewildcardnxdomain.secure.example. 3600 IN CNAME doesntexist.secure.example.
145 cname-to-formerr.secure.example. 3600 IN CNAME host1.insecure-formerr.example.
147 dname-secure.secure.example. 3600 IN DNAME dname-secure.example.
148 dname-insecure.secure.example. 3600 IN DNAME insecure.example.
149 dname-bogus.secure.example. 3600 IN DNAME bogus.example.
151 'dname-secure.example': """
152 dname-secure.example. 3600 IN SOA {soa}
153 dname-secure.example. 3600 IN NS ns.dname-secure.example.
154 ns.dname-secure.example. 3600 IN A {prefix}.13
156 host1.dname-secure.example. IN A 192.0.2.21
158 cname-to-secure.dname-secure.example. 3600 IN CNAME host1.secure.example.
159 cname-to-insecure.dname-secure.example. 3600 IN CNAME node1.insecure.example.
160 cname-to-bogus.dname-secure.example. 3600 IN CNAME ted.bogus.example.
162 'cname-secure.example': """
163 cname-secure.example. 3600 IN SOA {soa}
164 cname-secure.example. 3600 IN NS ns.cname-secure.example.
165 ns.cname-secure.example. 3600 IN A {prefix}.15
166 cname-secure.example. 3600 IN CNAME secure.example.
169 bogus.example. 3600 IN SOA {soa}
170 bogus.example. 3600 IN NS ns1.bogus.example.
171 ns1.bogus.example. 3600 IN A {prefix}.12
172 ted.bogus.example. 3600 IN A 192.0.2.1
173 bill.bogus.example. 3600 IN AAAA 2001:db8:12::3
175 'insecure.sub2.secure.example': """
176 insecure.sub2.secure.example. 3600 IN SOA {soa}
177 insecure.sub2.secure.example. 3600 IN NS ns1.insecure.example.
179 node1.insecure.sub2.secure.example. 3600 IN A 192.0.2.18
181 'insecure.example': """
182 insecure.example. 3600 IN SOA {soa}
183 insecure.example. 3600 IN NS ns1.insecure.example.
184 ns1.insecure.example. 3600 IN A {prefix}.13
186 node1.insecure.example. 3600 IN A 192.0.2.6
188 cname-to-secure.insecure.example. 3600 IN CNAME host1.secure.example.
190 dname-to-secure.insecure.example. 3600 IN DNAME dname-secure.example.
192 'optout.example': """
193 optout.example. 3600 IN SOA {soa}
194 optout.example. 3600 IN NS ns1.optout.example.
195 ns1.optout.example. 3600 IN A {prefix}.14
197 insecure.optout.example. 3600 IN NS ns1.insecure.optout.example.
198 ns1.insecure.optout.example. 3600 IN A {prefix}.15
200 secure.optout.example. 3600 IN NS ns1.secure.optout.example.
201 secure.optout.example. 3600 IN DS 64215 13 1 b88284d7a8d8605c398e8942262f97b9a5a31787
202 ns1.secure.optout.example. 3600 IN A {prefix}.15
204 'insecure.optout.example': """
205 insecure.optout.example. 3600 IN SOA {soa}
206 insecure.optout.example. 3600 IN NS ns1.insecure.optout.example.
207 ns1.insecure.optout.example. 3600 IN A {prefix}.15
209 node1.insecure.optout.example. 3600 IN A 192.0.2.7
211 'secure.optout.example': """
212 secure.optout.example. 3600 IN SOA {soa}
213 secure.optout.example. 3600 IN NS ns1.secure.optout.example.
214 ns1.secure.optout.example. 3600 IN A {prefix}.15
216 node1.secure.optout.example. 3600 IN A 192.0.2.8
218 'islandofsecurity.example': """
219 islandofsecurity.example. 3600 IN SOA {soa}
220 islandofsecurity.example. 3600 IN NS ns1.islandofsecurity.example.
221 ns1.islandofsecurity.example. 3600 IN A {prefix}.9
223 node1.islandofsecurity.example. 3600 IN A 192.0.2.20
225 'undelegated.secure.example': """
226 undelegated.secure.example. 3600 IN SOA {soa}
227 undelegated.secure.example. 3600 IN NS ns1.undelegated.secure.example.
229 node1.undelegated.secure.example. 3600 IN A 192.0.2.21
231 'undelegated.insecure.example': """
232 undelegated.insecure.example. 3600 IN SOA {soa}
233 undelegated.insecure.example. 3600 IN NS ns1.undelegated.insecure.example.
235 node1.undelegated.insecure.example. 3600 IN A 192.0.2.22
239 # The private keys for the zones (note that DS records should go into
240 # the zonecontent in _zones
243 Private-key-format: v1.2
244 Algorithm: 13 (ECDSAP256SHA256)
245 PrivateKey: rhWuEydDz3QaIspSVj683B8Xq5q/ozzA38XUgzD4Fbo=
249 Private-key-format: v1.2
250 Algorithm: 13 (ECDSAP256SHA256)
251 PrivateKey: Lt0v0Gol3pRUFM7fDdcy0IWN0O/MnEmVPA+VylL8Y4U=
254 'secure.example': """
255 Private-key-format: v1.2
256 Algorithm: 13 (ECDSAP256SHA256)
257 PrivateKey: 1G4WRoOFJJXk+fotDCHVORtJmIG2OUhKi8AO2jDPGZA=
261 Private-key-format: v1.2
262 Algorithm: 13 (ECDSAP256SHA256)
263 PrivateKey: f5jV7Q8kd5hDpMWObsuQ6SQda0ftf+JrO3uZwEg6nVw=
266 'optout.example': """
267 Private-key-format: v1.2
268 Algorithm: 13 (ECDSAP256SHA256)
269 PrivateKey: efmq9G+J4Y2iPnIBRwJiy6Z/nIHSzpsCy/7XHhlS19A=
272 'secure.optout.example': """
273 Private-key-format: v1.2
274 Algorithm: 13 (ECDSAP256SHA256)
275 PrivateKey: xcNUxt1Knj14A00lKQFDboluiJyM2f7FxpgsQaQ3AQ4=
278 'islandofsecurity.example': """
279 Private-key-format: v1.2
280 Algorithm: 13 (ECDSAP256SHA256)
281 PrivateKey: o9F5iix8V68tnMcuOaM2Lt8XXhIIY//SgHIHEePk6cM=
284 'cname-secure.example': """
285 Private-key-format: v1.2
286 Algorithm: 13 (ECDSAP256SHA256)
287 PrivateKey: kvoV/g4IO/tefSro+FLJ5UC7H3BUf0IUtZQSUOfQGyA=
290 'dname-secure.example': """
291 Private-key-format: v1.2
292 Algorithm: 13 (ECDSAP256SHA256)
293 PrivateKey: Ep9uo6+wwjb4MaOmqq7LHav2FLrjotVOeZg8JT1Qk04=
297 # This dict is keyed with the suffix of the IP address and its value
298 # is a list of zones hosted on that IP. Note that delegations should
299 # go into the _zones's zonecontent
302 '9': ['secure.example', 'islandofsecurity.example'],
305 '12': ['bogus.example', 'undelegated.secure.example', 'undelegated.insecure.example'],
306 '13': ['insecure.example', 'insecure.sub2.secure.example', 'dname-secure.example'],
307 '14': ['optout.example'],
308 '15': ['insecure.optout.example', 'secure.optout.example', 'cname-secure.example']
311 _auth_cmd
= ['authbind',
317 def createConfigDir(cls
, confdir
):
319 shutil
.rmtree(confdir
)
321 if e
.errno
!= errno
.ENOENT
:
323 os
.mkdir(confdir
, 0o755)
326 def generateAuthZone(cls
, confdir
, zonename
, zonecontent
):
327 with
open(os
.path
.join(confdir
, '%s.zone' % zonename
), 'w') as zonefile
:
328 zonefile
.write(zonecontent
.format(prefix
=cls
._PREFIX
, soa
=cls
._SOA
))
331 def generateAuthNamedConf(cls
, confdir
, zones
):
332 with
open(os
.path
.join(confdir
, 'named.conf'), 'w') as namedconf
:
337 for zonename
in zones
:
338 zone
= '.' if zonename
== 'ROOT' else zonename
344 };""" % (zone
, zonename
))
347 def generateAuthConfig(cls
, confdir
):
348 bind_dnssec_db
= os
.path
.join(confdir
, 'bind-dnssec.sqlite3')
350 with
open(os
.path
.join(confdir
, 'pdns.conf'), 'w') as pdnsconf
:
352 module-dir=../regression-tests/modules
356 bind-config={confdir}/named.conf
357 bind-dnssec-db={bind_dnssec_db}
366 distributor-threads=1""".format(confdir
=confdir
,
367 bind_dnssec_db
=bind_dnssec_db
))
369 pdnsutilCmd
= [os
.environ
['PDNSUTIL'],
370 '--config-dir=%s' % confdir
,
374 print(' '.join(pdnsutilCmd
))
376 subprocess
.check_output(pdnsutilCmd
, stderr
=subprocess
.STDOUT
)
377 except subprocess
.CalledProcessError
as e
:
378 raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd
, e
.returncode
, e
.output
))
381 def secureZone(cls
, confdir
, zonename
, key
=None):
382 zone
= '.' if zonename
== 'ROOT' else zonename
384 pdnsutilCmd
= [os
.environ
['PDNSUTIL'],
385 '--config-dir=%s' % confdir
,
389 keyfile
= os
.path
.join(confdir
, 'dnssec.key')
390 with
open(keyfile
, 'w') as fdKeyfile
:
393 pdnsutilCmd
= [os
.environ
['PDNSUTIL'],
394 '--config-dir=%s' % confdir
,
401 print(' '.join(pdnsutilCmd
))
403 subprocess
.check_output(pdnsutilCmd
, stderr
=subprocess
.STDOUT
)
404 except subprocess
.CalledProcessError
as e
:
405 raise AssertionError('%s failed (%d): %s' % (pdnsutilCmd
, e
.returncode
, e
.output
))
408 def generateAllAuthConfig(cls
, confdir
):
410 for auth_suffix
, zones
in cls
._auth
_zones
.items():
411 authconfdir
= os
.path
.join(confdir
, 'auth-%s' % auth_suffix
)
413 os
.mkdir(authconfdir
)
415 cls
.generateAuthConfig(authconfdir
)
416 cls
.generateAuthNamedConf(authconfdir
, zones
)
419 cls
.generateAuthZone(authconfdir
,
422 if cls
._zone
_keys
.get(zone
, None):
423 cls
.secureZone(authconfdir
, zone
, cls
._zone
_keys
.get(zone
))
426 def startAllAuth(cls
, confdir
):
428 for auth_suffix
, _
in cls
._auth
_zones
.items():
429 authconfdir
= os
.path
.join(confdir
, 'auth-%s' % auth_suffix
)
430 ipaddress
= cls
._PREFIX
+ '.' + auth_suffix
431 cls
.startAuth(authconfdir
, ipaddress
)
434 def startAuth(cls
, confdir
, ipaddress
):
435 print("Launching pdns_server..")
436 authcmd
= list(cls
._auth
_cmd
)
437 authcmd
.append('--config-dir=%s' % confdir
)
438 authcmd
.append('--local-address=%s' % ipaddress
)
439 print(' '.join(authcmd
))
441 logFile
= os
.path
.join(confdir
, 'pdns.log')
442 with
open(logFile
, 'w') as fdLog
:
443 cls
._auths
[ipaddress
] = subprocess
.Popen(authcmd
, close_fds
=True,
444 stdout
=fdLog
, stderr
=fdLog
,
449 if cls
._auths
[ipaddress
].poll() is not None:
451 cls
._auths
[ipaddress
].kill()
453 if e
.errno
!= errno
.ESRCH
:
455 with
open(logFile
, 'r') as fdLog
:
457 sys
.exit(cls
._auths
[ipaddress
].returncode
)
460 def generateRecursorConfig(cls
, confdir
):
461 params
= tuple([getattr(cls
, param
) for param
in cls
._config
_params
])
465 recursorconf
= os
.path
.join(confdir
, 'recursor.conf')
467 with
open(recursorconf
, 'w') as conf
:
468 conf
.write("# Autogenerated by recursortests.py\n")
469 conf
.write(cls
._config
_template
_default
)
470 conf
.write(cls
._config
_template
% params
)
472 conf
.write("socket-dir=%s\n" % confdir
)
473 if cls
._lua
_config
_file
or cls
._root
_DS
:
474 luaconfpath
= os
.path
.join(confdir
, 'conffile.lua')
475 with
open(luaconfpath
, 'w') as luaconf
:
477 luaconf
.write("addTA('.', '%s')\n" % cls
._root
_DS
)
478 if cls
._lua
_config
_file
:
479 luaconf
.write(cls
._lua
_config
_file
)
480 conf
.write("lua-config-file=%s\n" % luaconfpath
)
481 if cls
._lua
_dns
_script
_file
:
482 luascriptpath
= os
.path
.join(confdir
, 'dnsscript.lua')
483 with
open(luascriptpath
, 'w') as luascript
:
484 luascript
.write(cls
._lua
_dns
_script
_file
)
485 conf
.write("lua-dns-script=%s\n" % luascriptpath
)
487 roothintspath
= os
.path
.join(confdir
, 'root.hints')
488 with
open(roothintspath
, 'w') as roothints
:
489 roothints
.write(cls
._roothints
)
490 conf
.write("hint-file=%s\n" % roothintspath
)
493 def startResponders(cls
):
497 def startRecursor(cls
, confdir
, port
):
498 print("Launching pdns_recursor..")
499 recursorcmd
= [os
.environ
['PDNSRECURSOR'],
500 '--config-dir=%s' % confdir
,
501 '--local-port=%s' % port
,
502 '--security-poll-suffix=']
503 print(' '.join(recursorcmd
))
505 logFile
= os
.path
.join(confdir
, 'recursor.log')
506 with
open(logFile
, 'w') as fdLog
:
507 cls
._recursor
= subprocess
.Popen(recursorcmd
, close_fds
=True,
508 stdout
=fdLog
, stderr
=fdLog
)
510 if 'PDNSRECURSOR_FAST_TESTS' in os
.environ
:
513 delay
= cls
._recursorStartupDelay
517 if cls
._recursor
.poll() is not None:
521 if e
.errno
!= errno
.ESRCH
:
523 with
open(logFile
, 'r') as fdLog
:
525 sys
.exit(cls
._recursor
.returncode
)
528 def wipeRecursorCache(cls
, confdir
):
529 rec_controlCmd
= [os
.environ
['RECCONTROL'],
530 '--config-dir=%s' % confdir
,
534 subprocess
.check_output(rec_controlCmd
, stderr
=subprocess
.STDOUT
)
535 except subprocess
.CalledProcessError
as e
:
536 raise AssertionError('%s failed (%d): %s' % (rec_controlCmd
, e
.returncode
, e
.output
))
539 def setUpSockets(cls
):
540 print("Setting up UDP socket..")
541 cls
._sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_DGRAM
)
542 cls
._sock
.settimeout(2.0)
543 cls
._sock
.connect(("127.0.0.1", cls
._recursorPort
))
549 cls
.startResponders()
551 confdir
= os
.path
.join('configs', cls
._confdir
)
552 cls
.createConfigDir(confdir
)
553 cls
.generateAllAuthConfig(confdir
)
554 cls
.startAllAuth(confdir
)
556 cls
.generateRecursorConfig(confdir
)
557 cls
.startRecursor(confdir
, cls
._recursorPort
)
559 print("Launching tests..")
562 def tearDownClass(cls
):
563 cls
.tearDownRecursor()
565 cls
.tearDownResponders()
568 def tearDownResponders(cls
):
572 def tearDownAuth(cls
):
573 if 'PDNSRECURSOR_FAST_TESTS' in os
.environ
:
578 for _
, auth
in cls
._auths
.items():
581 if auth
.poll() is None:
583 if auth
.poll() is None:
587 if e
.errno
!= errno
.ESRCH
:
591 def tearDownRecursor(cls
):
592 if 'PDNSRECURSOR_FAST_TESTS' in os
.environ
:
598 cls
._recursor
.terminate()
599 if cls
._recursor
.poll() is None:
601 if cls
._recursor
.poll() is None:
605 # There is a race-condition with the poll() and
606 # kill() statements, when the process is dead on the
607 # kill(), this is fine
608 if e
.errno
!= errno
.ESRCH
:
612 def sendUDPQuery(cls
, query
, timeout
=2.0, decode
=True, fwparams
=dict()):
614 cls
._sock
.settimeout(timeout
)
617 cls
._sock
.send(query
.to_wire())
618 data
= cls
._sock
.recv(4096)
619 except socket
.timeout
:
623 cls
._sock
.settimeout(None)
629 message
= dns
.message
.from_wire(data
, **fwparams
)
633 def sendTCPQuery(cls
, query
, timeout
=2.0):
634 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
636 sock
.settimeout(timeout
)
638 sock
.connect(("127.0.0.1", cls
._recursorPort
))
641 wire
= query
.to_wire()
642 sock
.send(struct
.pack("!H", len(wire
)))
646 (datalen
,) = struct
.unpack("!H", data
)
647 data
= sock
.recv(datalen
)
648 except socket
.timeout
as e
:
649 print("Timeout: %s" % (str(e
)))
651 except socket
.error
as e
:
652 print("Network error: %s" % (str(e
)))
659 message
= dns
.message
.from_wire(data
)
663 # This function is called before every tests
664 super(RecursorTest
, self
).setUp()
666 ## Functions for comparisons
667 def assertMessageHasFlags(self
, msg
, flags
, ednsflags
=[]):
668 """Asserts that msg has all the flags from flags set
670 @param msg: the dns.message.Message to check
671 @param flags: a list of strings with flag mnemonics (like ['RD', 'RA'])
672 @param ednsflags: a list of strings with edns-flag mnemonics (like ['DO'])"""
674 if not isinstance(msg
, dns
.message
.Message
):
675 raise TypeError("msg is not a dns.message.Message")
677 if isinstance(flags
, list):
679 if not isinstance(elem
, str):
680 raise TypeError("flags is not a list of strings")
682 raise TypeError("flags is not a list of strings")
684 if isinstance(ednsflags
, list):
685 for elem
in ednsflags
:
686 if not isinstance(elem
, str):
687 raise TypeError("ednsflags is not a list of strings")
689 raise TypeError("ednsflags is not a list of strings")
691 msgFlags
= dns
.flags
.to_text(msg
.flags
).split()
692 missingFlags
= [flag
for flag
in flags
if flag
not in msgFlags
]
694 msgEdnsFlags
= dns
.flags
.edns_to_text(msg
.ednsflags
).split()
695 missingEdnsFlags
= [ednsflag
for ednsflag
in ednsflags
if ednsflag
not in msgEdnsFlags
]
697 if len(missingFlags
) or len(missingEdnsFlags
) or len(msgFlags
) > len(flags
):
698 raise AssertionError("Expected flags '%s' (EDNS: '%s'), found '%s' (EDNS: '%s') in query %s" %
699 (' '.join(flags
), ' '.join(ednsflags
),
700 ' '.join(msgFlags
), ' '.join(msgEdnsFlags
),
703 def assertMessageIsAuthenticated(self
, msg
):
704 """Asserts that the message has the AD bit set
706 @param msg: the dns.message.Message to check"""
708 if not isinstance(msg
, dns
.message
.Message
):
709 raise TypeError("msg is not a dns.message.Message")
711 msgFlags
= dns
.flags
.to_text(msg
.flags
)
712 self
.assertTrue('AD' in msgFlags
, "No AD flag found in the message for %s" % msg
.question
[0].name
)
714 def assertRRsetInAnswer(self
, msg
, rrset
):
715 """Asserts the rrset (without comparing TTL) exists in the
716 answer section of msg
718 @param msg: the dns.message.Message to check
719 @param rrset: a dns.rrset.RRset object"""
722 if not isinstance(msg
, dns
.message
.Message
):
723 raise TypeError("msg is not a dns.message.Message")
725 if not isinstance(rrset
, dns
.rrset
.RRset
):
726 raise TypeError("rrset is not a dns.rrset.RRset")
729 for ans
in msg
.answer
:
730 ret
+= "%s\n" % ans
.to_text()
731 if ans
.match(rrset
.name
, rrset
.rdclass
, rrset
.rdtype
, 0, None):
732 self
.assertEqual(ans
, rrset
, "'%s' != '%s'" % (ans
.to_text(), rrset
.to_text()))
736 raise AssertionError("RRset not found in answer\n\n%s" % ret
)
738 def assertMatchingRRSIGInAnswer(self
, msg
, coveredRRset
, keys
=None):
739 """Looks for coveredRRset in the answer section and if there is an RRSIG RRset
740 that covers that RRset. If keys is not None, this function will also try to
741 validate the RRset against the RRSIG
743 @param msg: The dns.message.Message to check
744 @param coveredRRset: The RRSet to check for
745 @param keys: a dictionary keyed by dns.name.Name with node or rdataset values to use for validation"""
747 if not isinstance(msg
, dns
.message
.Message
):
748 raise TypeError("msg is not a dns.message.Message")
750 if not isinstance(coveredRRset
, dns
.rrset
.RRset
):
751 raise TypeError("coveredRRset is not a dns.rrset.RRset")
757 for ans
in msg
.answer
:
758 ret
+= ans
.to_text() + "\n"
760 if ans
.match(coveredRRset
.name
, coveredRRset
.rdclass
, coveredRRset
.rdtype
, 0, None):
762 if ans
.match(coveredRRset
.name
, dns
.rdataclass
.IN
, dns
.rdatatype
.RRSIG
, coveredRRset
.rdtype
, None):
764 if msgRRSet
and msgRRsigRRSet
:
768 raise AssertionError("RRset for '%s' not found in answer" % msg
.question
[0].to_text())
770 if not msgRRsigRRSet
:
771 raise AssertionError("No RRSIGs found in answer for %s:\nFull answer:\n%s" % (msg
.question
[0].to_text(), ret
))
775 dns
.dnssec
.validate(msgRRSet
, msgRRsigRRSet
.to_rdataset(), keys
)
776 except dns
.dnssec
.ValidationFailure
as e
:
777 raise AssertionError("Signature validation failed for %s:\n%s" % (msg
.question
[0].to_text(), e
))
779 def assertNoRRSIGsInAnswer(self
, msg
):
780 """Checks if there are _no_ RRSIGs in the answer section of msg"""
782 if not isinstance(msg
, dns
.message
.Message
):
783 raise TypeError("msg is not a dns.message.Message")
786 for ans
in msg
.answer
:
787 if ans
.rdtype
== dns
.rdatatype
.RRSIG
:
788 ret
+= ans
.name
.to_text() + "\n"
791 raise AssertionError("RRSIG found in answers for:\n%s" % ret
)
793 def assertAnswerEmpty(self
, msg
):
794 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
])))
796 def assertRcodeEqual(self
, msg
, rcode
):
797 if not isinstance(msg
, dns
.message
.Message
):
798 raise TypeError("msg is not a dns.message.Message but a %s" % type(msg
))
800 if not isinstance(rcode
, int):
801 if isinstance(rcode
, str):
802 rcode
= dns
.rcode
.from_text(rcode
)
804 raise TypeError("rcode is neither a str nor int")
806 if msg
.rcode() != rcode
:
807 msgRcode
= dns
.rcode
._by
_value
[msg
.rcode()]
808 wantedRcode
= dns
.rcode
._by
_value
[rcode
]
810 raise AssertionError("Rcode for %s is %s, expected %s." % (msg
.question
[0].to_text(), msgRcode
, wantedRcode
))
812 def assertAuthorityHasSOA(self
, msg
):
813 if not isinstance(msg
, dns
.message
.Message
):
814 raise TypeError("msg is not a dns.message.Message but a %s" % type(msg
))
817 for rrset
in msg
.authority
:
818 if rrset
.rdtype
== dns
.rdatatype
.SOA
:
823 raise AssertionError("No SOA record found in the authority section:\n%s" % msg
.to_text())
825 def assertResponseMatches(self
, query
, expectedRRs
, response
):
826 expectedResponse
= dns
.message
.make_response(query
)
828 if query
.flags
& dns
.flags
.RD
:
829 expectedResponse
.flags |
= dns
.flags
.RA
830 if query
.flags
& dns
.flags
.CD
:
831 expectedResponse
.flags |
= dns
.flags
.CD
833 expectedResponse
.answer
= expectedRRs
834 print(expectedResponse
)
836 self
.assertEquals(response
, expectedResponse
)
839 def sendQuery(cls
, name
, rdtype
, useTCP
=False):
840 """Helper function that creates the query"""
841 msg
= dns
.message
.make_query(name
, rdtype
, want_dnssec
=True)
842 msg
.flags |
= dns
.flags
.AD
845 return cls
.sendTCPQuery(msg
)
846 return cls
.sendUDPQuery(msg
)
848 def createQuery(self
, name
, rdtype
, flags
, ednsflags
):
849 """Helper function that creates the query with the specified flags.
850 The flags need to be strings (no checking is performed atm)"""
851 msg
= dns
.message
.make_query(name
, rdtype
)
852 msg
.flags
= dns
.flags
.from_text(flags
)
853 msg
.flags
+= dns
.flags
.from_text('RD')
854 msg
.use_edns(edns
=0, ednsflags
=dns
.flags
.edns_from_text(ednsflags
))