]> git.ipfire.org Git - thirdparty/pdns.git/blame - regression-tests.recursor-dnssec/recursortests.py
Add simple NODATA tests
[thirdparty/pdns.git] / regression-tests.recursor-dnssec / recursortests.py
CommitLineData
7568b07d
PL
1#!/usr/bin/env python2
2
3import errno
4import shutil
5import os
6import socket
7import struct
8import subprocess
9import sys
10import time
11import unittest
12import dns
13import dns.message
14
15class RecursorTest(unittest.TestCase):
16 """
17 Setup all recursors and auths required for the tests
18 """
19
20 _confdir = 'recursor'
21
22 _recursorStartupDelay = 2.0
23 _recursorPort = 5300
24
25 _recursor = None
26
27 _PREFIX = os.environ['PREFIX']
28
29 _config_template_default = """
30daemon=no
31trace=yes
32dont-query=
33local-address=127.0.0.1
34packetcache-ttl=0
35packetcache-servfail-ttl=0
36max-cache-ttl=15
37threads=1
38loglevel=9
39disable-syslog=yes
40"""
41 _config_template = """
42"""
43 _config_params = []
44 _lua_config_file = None
45 _roothints = """
46. 3600 IN NS ns.root.
47ns.root. 3600 IN A %s.8
48""" % _PREFIX
49 _root_DS = "63149 13 1 a59da3f5c1b97fcd5fa2b3b2b0ac91d38a60d33a"
50
51 # The default SOA for zones in the authoritative servers
52 _SOA = "ns1.example.net. hostmaster.example.net. 1 3600 1800 1209600 300"
53
54 # The definitions of the zones on the authoritative servers, the key is the
55 # zonename and the value is the zonefile content. several strings are replaced:
56 # - {soa} => value of _SOA
57 # - {prefix} value of _PREFIX
58 _zones = {
59 'ROOT': """
60. 3600 IN SOA {soa}
61. 3600 IN NS ns.root.
62ns.root. 3600 IN A {prefix}.8
63
64example. 3600 IN NS ns1.example.
65example. 3600 IN NS ns2.example.
66example. 3600 IN DS 53174 13 1 50c9e913818767c236c06c2d8272723cb78cbf26
67
68ns1.example. 3600 IN A {prefix}.10
69ns2.example. 3600 IN A {prefix}.11
70 """,
71 'example': """
72example. 3600 IN SOA {soa}
73example. 3600 IN NS ns1.nic.example.
74example. 3600 IN NS ns2.nic.example.
75ns1.example. 3600 IN A {prefix}.10
76ns2.example. 3600 IN A {prefix}.11
77
78secure.example. 3600 IN NS ns.secure.example.
79secure.example. 3600 IN DS 64723 13 1 53eb985040d3a89bacf29dbddb55a65834706f33
80ns.secure.example. 3600 IN A {prefix}.9
81
82bogus.example. 3600 IN NS ns.bogus.example.
83bogus.example. 3600 IN DS 65034 13 1 6df3bb50ea538e90eacdd7ae5419730783abb0ee
84ns.bogus.example. 3600 IN A {prefix}.12
85
86insecure.example. 3600 IN NS ns.insecure.example.
87ns.insecure.example. 3600 IN A {prefix}.13
11886ab9
PL
88
89optout.example. 3600 IN NS ns1.optout.example.
90optout.example. 3600 IN DS 59332 13 1 e664f886ae1b5df03d918bc1217d22afc29925b9
91ns1.optout.example. 3600 IN A {prefix}.14
7568b07d
PL
92 """,
93 'secure.example': """
94secure.example. 3600 IN SOA {soa}
95secure.example. 3600 IN NS ns.secure.example.
96ns.secure.example. 3600 IN A {prefix}.9
97
98host1.secure.example. 3600 IN A 192.0.2.2
05537f80 99cname.secure.example. 3600 IN CNAME host1.secure.example.
46419ee3
PL
100
101host1.sub.secure.example. 3600 IN A 192.0.2.11
fdb27cb2
PL
102
103*.wildcard.secure.example. 3600 IN A 192.0.2.10
52033c6f
PL
104
105*.cnamewildcard.secure.example. 3600 IN CNAME host1.secure.example.
106
107*.cnamewildcardnxdomain.secure.example. 3600 IN CNAME doesntexist.secure.example.
7568b07d
PL
108 """,
109 'bogus.example': """
110bogus.example. 3600 IN SOA {soa}
111bogus.example. 3600 IN NS ns1.bogus.example.
112ns1.bogus.example. 3600 IN A {prefix}.12
113ted.bogus.example. 3600 IN A 192.0.2.1
114bill.bogus.example. 3600 IN AAAA 2001:db8:12::3
115 """,
116 'insecure.example': """
117insecure.example. 3600 IN SOA {soa}
118insecure.example. 3600 IN NS ns1.insecure.example.
119ns1.insecure.example. 3600 IN A {prefix}.13
120
121node1.insecure.example. 3600 IN A 192.0.2.6
11886ab9
PL
122 """,
123 'optout.example': """
124optout.example. 3600 IN SOA {soa}
125optout.example. 3600 IN NS ns1.optout.example.
126ns1.optout.example. 3600 IN A {prefix}.14
127
128insecure.optout.example. 3600 IN NS ns1.insecure.optout.example.
129ns1.insecure.optout.example. 3600 IN A {prefix}.15
130
131secure.optout.example. 3600 IN NS ns1.secure.optout.example.
132secure.optout.example. 3600 IN DS 64215 13 1 b88284d7a8d8605c398e8942262f97b9a5a31787
133ns1.secure.optout.example. 3600 IN A {prefix}.15
134 """,
135 'insecure.optout.example': """
136insecure.optout.example. 3600 IN SOA {soa}
137insecure.optout.example. 3600 IN NS ns1.insecure.optout.example.
138ns1.insecure.optout.example. 3600 IN A {prefix}.15
139
140node1.insecure.optout.example. 3600 IN A 192.0.2.7
141 """,
142 'secure.optout.example': """
143secure.optout.example. 3600 IN SOA {soa}
144secure.optout.example. 3600 IN NS ns1.secure.optout.example.
145ns1.secure.optout.example. 3600 IN A {prefix}.15
146
147node1.secure.optout.example. 3600 IN A 192.0.2.8
7568b07d
PL
148 """
149 }
150
151 # The private keys for the zones (note that DS records should go into
152 # the zonecontent in _zones
153 _zone_keys = {
154 'ROOT': """
155Private-key-format: v1.2
156Algorithm: 13 (ECDSAP256SHA256)
157PrivateKey: rhWuEydDz3QaIspSVj683B8Xq5q/ozzA38XUgzD4Fbo=
158 """,
159
160 'example': """
161Private-key-format: v1.2
162Algorithm: 13 (ECDSAP256SHA256)
163PrivateKey: Lt0v0Gol3pRUFM7fDdcy0IWN0O/MnEmVPA+VylL8Y4U=
164 """,
165
166 'secure.example': """
167Private-key-format: v1.2
168Algorithm: 13 (ECDSAP256SHA256)
169PrivateKey: 1G4WRoOFJJXk+fotDCHVORtJmIG2OUhKi8AO2jDPGZA=
170 """,
171
172 'bogus.example': """
173Private-key-format: v1.2
174Algorithm: 13 (ECDSAP256SHA256)
175PrivateKey: f5jV7Q8kd5hDpMWObsuQ6SQda0ftf+JrO3uZwEg6nVw=
176 """,
11886ab9
PL
177
178 'optout.example': """
179Private-key-format: v1.2
180Algorithm: 13 (ECDSAP256SHA256)
181PrivateKey: efmq9G+J4Y2iPnIBRwJiy6Z/nIHSzpsCy/7XHhlS19A=
182 """,
183
184 'secure.optout.example': """
185Private-key-format: v1.2
186Algorithm: 13 (ECDSAP256SHA256)
187PrivateKey: xcNUxt1Knj14A00lKQFDboluiJyM2f7FxpgsQaQ3AQ4=
188 """
7568b07d
PL
189 }
190
191 # This dict is keyed with the suffix of the IP address and its value
192 # is a list of zones hosted on that IP. Note that delegations should
193 # go into the _zones's zonecontent
194 _auth_zones = {
195 '8': ['ROOT'],
196 '9': ['secure.example'],
197 '10': ['example'],
198 '11': ['example'],
199 '12': ['bogus.example'],
11886ab9
PL
200 '13': ['insecure.example'],
201 '14': ['optout.example'],
202 '15': ['insecure.optout.example', 'secure.optout.example']
7568b07d
PL
203 }
204
cb54e9b5
PL
205 _auth_cmd = ['authbind',
206 os.environ['PDNS']]
207 _auth_env = {}
7568b07d
PL
208 _auths = {}
209
210 @classmethod
211 def createConfigDir(cls, confdir):
212 try:
213 shutil.rmtree(confdir)
214 except OSError as e:
215 if e.errno != errno.ENOENT:
216 raise
217 os.mkdir(confdir, 0755)
218
219 @classmethod
220 def generateAuthZone(cls, confdir, zonename, zonecontent):
221 with open(os.path.join(confdir, '%s.zone' % zonename), 'w') as zonefile:
222 zonefile.write(zonecontent.format(prefix=cls._PREFIX, soa=cls._SOA))
223
224 @classmethod
225 def generateAuthNamedConf(cls, confdir, zones):
226 with open(os.path.join(confdir, 'named.conf'), 'w') as namedconf:
227 namedconf.write("""
228options {
229 directory "%s";
230};""" % confdir)
231 for zonename in zones:
232 zone = '.' if zonename == 'ROOT' else zonename
233
234 namedconf.write("""
235 zone "%s" {
236 type master;
237 file "%s.zone";
238 };""" % (zone, zonename))
239
240 @classmethod
241 def generateAuthConfig(cls, confdir):
242 bind_dnssec_db = os.path.join(confdir, 'bind-dnssec.sqlite3')
243
244 with open(os.path.join(confdir, 'pdns.conf'), 'w') as pdnsconf:
245 pdnsconf.write("""
246module-dir=../regression-tests/modules
247launch=bind
248daemon=no
249local-ipv6=
250bind-config={confdir}/named.conf
251bind-dnssec-db={bind_dnssec_db}
252socket-dir={confdir}
253cache-ttl=0
254negquery-cache-ttl=0
255query-cache-ttl=0
256log-dns-queries=yes
257log-dns-details=yes
258loglevel=9
259distributor-threads=1""".format(confdir=confdir,
260 bind_dnssec_db=bind_dnssec_db))
261
262 pdnsutilCmd = [os.environ['PDNSUTIL'],
263 '--config-dir=%s' % confdir,
264 'create-bind-db',
265 bind_dnssec_db]
266
267 print ' '.join(pdnsutilCmd)
268 try:
269 subprocess.check_output(pdnsutilCmd, stderr=subprocess.STDOUT)
270 except subprocess.CalledProcessError as e:
271 print e.output
272 raise
273
274 @classmethod
275 def secureZone(cls, confdir, zonename, key=None):
276 zone = '.' if zonename == 'ROOT' else zonename
277 if not key:
278 pdnsutilCmd = [os.environ['PDNSUTIL'],
279 '--config-dir=%s' % confdir,
280 'secure-zone',
281 zone]
282 else:
283 keyfile = os.path.join(confdir, 'dnssec.key')
284 with open(keyfile, 'w') as fdKeyfile:
285 fdKeyfile.write(key)
286
287 pdnsutilCmd = [os.environ['PDNSUTIL'],
288 '--config-dir=%s' % confdir,
289 'import-zone-key',
290 zone,
291 keyfile,
292 'active',
293 'ksk']
294
295 print ' '.join(pdnsutilCmd)
296 try:
297 subprocess.check_output(pdnsutilCmd, stderr=subprocess.STDOUT)
298 except subprocess.CalledProcessError as e:
299 print e.output
300 raise
301
302 @classmethod
303 def generateAllAuthConfig(cls, confdir):
304 if cls._auth_zones:
305 for auth_suffix, zones in cls._auth_zones.items():
306 authconfdir = os.path.join(confdir, 'auth-%s' % auth_suffix)
307
308 os.mkdir(authconfdir)
309
310 cls.generateAuthConfig(authconfdir)
311 cls.generateAuthNamedConf(authconfdir, zones)
312
11886ab9
PL
313 for zone in zones:
314 cls.generateAuthZone(authconfdir,
315 zone,
316 cls._zones[zone])
317 if cls._zone_keys.get(zone, None):
318 cls.secureZone(authconfdir, zone, cls._zone_keys.get(zone))
7568b07d
PL
319
320 @classmethod
321 def startAllAuth(cls, confdir):
322 if cls._auth_zones:
323 for auth_suffix, _ in cls._auth_zones.items():
324 authconfdir = os.path.join(confdir, 'auth-%s' % auth_suffix)
325 ipaddress = cls._PREFIX + '.' + auth_suffix
326 cls.startAuth(authconfdir, ipaddress)
327
328 @classmethod
329 def startAuth(cls, confdir, ipaddress):
330 print("Launching pdns_server..")
11886ab9 331 authcmd = list(cls._auth_cmd)
cb54e9b5
PL
332 authcmd.append('--config-dir=%s' % confdir)
333 authcmd.append('--local-address=%s' % ipaddress)
7568b07d
PL
334 print(' '.join(authcmd))
335
336 logFile = os.path.join(confdir, 'pdns.log')
337 with open(logFile, 'w') as fdLog:
338 cls._auths[ipaddress] = subprocess.Popen(authcmd, close_fds=True,
cb54e9b5
PL
339 stdout=fdLog, stderr=fdLog,
340 env=cls._auth_env)
7568b07d
PL
341
342 time.sleep(2)
343
344 if cls._auths[ipaddress].poll() is not None:
345 try:
346 cls._auths[ipaddress].kill()
347 except OSError as e:
348 if e.errno != errno.ESRCH:
349 raise
350 with open(logFile, 'r') as fdLog:
351 print fdLog.read()
352 sys.exit(cls._auths[ipaddress].returncode)
353
354 @classmethod
355 def generateRecursorConfig(cls, confdir):
356 params = tuple([getattr(cls, param) for param in cls._config_params])
357 if len(params):
358 print(params)
359
360 recursorconf = os.path.join(confdir, 'recursor.conf')
361
362 with open(recursorconf, 'w') as conf:
363 conf.write("# Autogenerated by recursortests.py\n")
364 conf.write(cls._config_template_default)
365 conf.write(cls._config_template % params)
366 conf.write("\n")
367 conf.write("socket-dir=%s\n" % confdir)
368 if cls._lua_config_file or cls._root_DS:
369 luaconfpath = os.path.join(confdir, 'conffile.lua')
370 with open(luaconfpath, 'w') as luaconf:
371 if cls._root_DS:
372 luaconf.write("addDS('.', '%s')" % cls._root_DS)
373 if cls._lua_config_file:
374 luaconf.write(cls._lua_config_file)
375 conf.write("lua-config-file=%s\n" % luaconfpath)
376 if cls._roothints:
377 roothintspath = os.path.join(confdir, 'root.hints')
378 with open(roothintspath, 'w') as roothints:
379 roothints.write(cls._roothints)
380 conf.write("hint-file=%s\n" % roothintspath)
381
382 @classmethod
383 def startRecursor(cls, confdir, port):
384 print("Launching pdns_recursor..")
385 recursorcmd = [os.environ['PDNSRECURSOR'],
386 '--config-dir=%s' % confdir,
387 '--local-port=%s' % port]
388 print(' '.join(recursorcmd))
389
390 logFile = os.path.join(confdir, 'recursor.log')
391 with open(logFile, 'w') as fdLog:
392 cls._recursor = subprocess.Popen(recursorcmd, close_fds=True,
393 stdout=fdLog, stderr=fdLog)
394
395 if 'PDNSRECURSOR_FAST_TESTS' in os.environ:
396 delay = 0.5
397 else:
398 delay = cls._recursorStartupDelay
399
400 time.sleep(delay)
401
402 if cls._recursor.poll() is not None:
403 try:
404 cls._recursor.kill()
405 except OSError as e:
406 if e.errno != errno.ESRCH:
407 raise
408 with open(logFile, 'r') as fdLog:
409 print fdLog.read()
410 sys.exit(cls._recursor.returncode)
411
412 @classmethod
413 def wipeRecursorCache(cls, confdir):
414 rec_controlCmd = [os.environ['RECCONTROL'],
415 '--config-dir=%s' % confdir,
416 'wipe-cache',
417 '.$']
418 try:
419 subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT)
420 except subprocess.CalledProcessError as e:
421 print e.output
422 raise
423
424 @classmethod
425 def setUpSockets(cls):
426 print("Setting up UDP socket..")
427 cls._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
428 cls._sock.settimeout(2.0)
429 cls._sock.connect(("127.0.0.1", cls._recursorPort))
430
431 @classmethod
432 def setUpClass(cls):
433 cls.setUpSockets()
434 confdir = os.path.join('configs', cls._confdir)
435 cls.createConfigDir(confdir)
436 cls.generateAllAuthConfig(confdir)
437 cls.startAllAuth(confdir)
438
439 cls.generateRecursorConfig(confdir)
440 cls.startRecursor(confdir, cls._recursorPort)
441
442 print("Launching tests..")
443
444 @classmethod
445 def tearDownClass(cls):
446 cls.tearDownRecursor()
447 cls.tearDownAuth()
448
449 @classmethod
450 def tearDownAuth(cls):
451 if 'PDNSRECURSOR_FAST_TESTS' in os.environ:
452 delay = 0.1
453 else:
454 delay = 1.0
455
456 for _, auth in cls._auths.items():
457 try:
458 auth.terminate()
459 if auth.poll() is None:
460 time.sleep(delay)
461 if auth.poll() is None:
462 auth.kill()
463 auth.wait()
464 except OSError as e:
465 if e.errno != errno.ESRCH:
466 raise
467
468 @classmethod
469 def tearDownRecursor(cls):
470 if 'PDNSRECURSOR_FAST_TESTS' in os.environ:
471 delay = 0.1
472 else:
473 delay = 1.0
474 try:
475 if cls._recursor:
476 cls._recursor.terminate()
477 if cls._recursor.poll() is None:
478 time.sleep(delay)
479 if cls._recursor.poll() is None:
480 cls._recursor.kill()
481 cls._recursor.wait()
482 except OSError as e:
483 # There is a race-condition with the poll() and
484 # kill() statements, when the process is dead on the
485 # kill(), this is fine
486 if e.errno != errno.ESRCH:
487 raise
488
489
490 @classmethod
491 def sendUDPQuery(cls, query, timeout=2.0):
492 if timeout:
493 cls._sock.settimeout(timeout)
494
495 try:
496 cls._sock.send(query.to_wire())
497 data = cls._sock.recv(4096)
498 except socket.timeout:
499 data = None
500 finally:
501 if timeout:
502 cls._sock.settimeout(None)
503
504 message = None
505 if data:
506 message = dns.message.from_wire(data)
507 return message
508
509 @classmethod
510 def sendTCPQuery(cls, query, timeout=2.0):
511 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
512 if timeout:
513 sock.settimeout(timeout)
514
515 sock.connect(("127.0.0.1", cls._recursorPort))
516
517 try:
518 wire = query.to_wire()
519 sock.send(struct.pack("!H", len(wire)))
520 sock.send(wire)
521 data = sock.recv(2)
522 if data:
523 (datalen,) = struct.unpack("!H", data)
524 data = sock.recv(datalen)
525 except socket.timeout as e:
526 print("Timeout: %s" % (str(e)))
527 data = None
528 except socket.error as e:
529 print("Network error: %s" % (str(e)))
530 data = None
531 finally:
532 sock.close()
533
534 message = None
535 if data:
536 message = dns.message.from_wire(data)
537 return message
538
539 def setUp(self):
540 # This function is called before every tests
541 return
542
543 ## Functions for comparisons
544 def assertMessageHasFlags(self, msg, flags, ednsflags=[]):
545 """Asserts that msg has all the flags from flags set
546
547 @param msg: the dns.message.Message to check
548 @param flags: a list of strings with flag mnemonics (like ['RD', 'RA'])
549 @param ednsflags: a list of strings with edns-flag mnemonics (like ['DO'])"""
550
551 if not isinstance(msg, dns.message.Message):
552 raise TypeError("msg is not a dns.message.Message")
553
554 if isinstance(flags, list):
555 for elem in flags:
556 if not isinstance(elem, str):
557 raise TypeError("flags is not a list of strings")
558 else:
559 raise TypeError("flags is not a list of strings")
560
561 if isinstance(ednsflags, list):
562 for elem in ednsflags:
563 if not isinstance(elem, str):
564 raise TypeError("ednsflags is not a list of strings")
565 else:
566 raise TypeError("ednsflags is not a list of strings")
567
568 msgFlags = dns.flags.to_text(msg.flags).split()
569 missingFlags = [flag for flag in flags if flag not in msgFlags]
570
571 msgEdnsFlags = dns.flags.edns_to_text(msg.flags).split()
572 missingEdnsFlags = [ednsflag for ednsflag in ednsflags if ednsflag not in msgEdnsFlags]
573
574 if len(missingFlags) or len(missingEdnsFlags) or len(msgFlags) > len(flags):
575 raise AssertionError("Expected flags '%s' (EDNS: '%s'), found '%s' (EDNS: '%s') in query %s" %
576 (' '.join(flags), ' '.join(ednsflags),
577 ' '.join(msgFlags), ' '.join(msgEdnsFlags),
578 msg.question[0]))
579
580 def assertMessageIsAuthenticated(self, msg):
581 """Asserts that the message has the AD bit set
582
583 @param msg: the dns.message.Message to check"""
584
585 if not isinstance(msg, dns.message.Message):
586 raise TypeError("msg is not a dns.message.Message")
587
588 msgFlags = dns.flags.to_text(msg.flags)
589 self.assertTrue('AD' in msgFlags, "No AD flag found in the message for %s" % msg.question[0].name)
590
591 def assertRRsetInAnswer(self, msg, rrset):
592 """Asserts the rrset (without comparing TTL) exists in the
593 answer section of msg
594
595 @param msg: the dns.message.Message to check
596 @param rrset: a dns.rrset.RRset object"""
597
598 ret = ''
599 if not isinstance(msg, dns.message.Message):
600 raise TypeError("msg is not a dns.message.Message")
601
602 if not isinstance(rrset, dns.rrset.RRset):
603 raise TypeError("rrset is not a dns.rrset.RRset")
604
605 found = False
606 for ans in msg.answer:
607 ret += "%s\n" % ans.to_text()
608 if ans.match(rrset.name, rrset.rdclass, rrset.rdtype, 0, None):
609 self.assertEqual(ans, rrset)
610 found = True
611
612 if not found:
613 raise AssertionError("RRset not found in answer")
614
615 def assertMatchingRRSIGInAnswer(self, msg, coveredRRset, keys=None):
616 """Looks for coveredRRset in the answer section and if there is an RRSIG RRset
617 that covers that RRset. If keys is not None, this function will also try to
618 validate the RRset against the RRSIG
619
620 @param msg: The dns.message.Message to check
621 @param coveredRRset: The RRSet to check for
622 @param keys: a dictionary keyed by dns.name.Name with node or rdataset values to use for validation"""
623
624 if not isinstance(msg, dns.message.Message):
625 raise TypeError("msg is not a dns.message.Message")
626
627 if not isinstance(coveredRRset, dns.rrset.RRset):
628 raise TypeError("coveredRRset is not a dns.rrset.RRset")
629
630 msgRRsigRRSet = None
631 msgRRSet = None
632
633 ret = ''
634 for ans in msg.answer:
635 ret += ans.to_text() + "\n"
636
637 if ans.match(coveredRRset.name, coveredRRset.rdclass, coveredRRset.rdtype, 0, None):
638 msgRRSet = ans
639 if ans.match(coveredRRset.name, dns.rdataclass.IN, dns.rdatatype.RRSIG, coveredRRset.rdtype, None):
640 msgRRsigRRSet = ans
641 if msgRRSet and msgRRsigRRSet:
642 break
643
644 if not msgRRSet:
645 raise AssertionError("RRset for '%s' not found in answer" % msg.question[0].to_text())
646
647 if not msgRRsigRRSet:
648 raise AssertionError("No RRSIGs found in answer for %s:\nFull answer:\n%s" % (msg.question[0].to_text(), ret))
649
650 if keys:
651 try:
652 dns.dnssec.validate(msgRRSet, msgRRsigRRSet.to_rdataset(), keys)
653 except dns.dnssec.ValidationFailure as e:
654 raise AssertionError("Signature validation failed for %s:\n%s" % (msg.question[0].to_text(), e))
655
656 def assertNoRRSIGsInAnswer(self, msg):
657 """Checks if there are _no_ RRSIGs in the answer section of msg"""
658
659 if not isinstance(msg, dns.message.Message):
660 raise TypeError("msg is not a dns.message.Message")
661
662 ret = ""
663 for ans in msg.answer:
664 if ans.rdtype == dns.rdatatype.RRSIG:
665 ret += ans.name.to_text() + "\n"
666
667 if len(ret):
668 raise AssertionError("RRSIG found in answers for:\n%s" % ret)
669
670 def assertAnswerEmpty(self, msg):
671 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])))
672
673 def assertRcodeEqual(self, msg, rcode):
674 if not isinstance(msg, dns.message.Message):
675 raise TypeError("msg is not a dns.message.Message but a %s" % type(msg))
676
677 if not isinstance(rcode, int):
678 if isinstance(rcode, str):
679 rcode = dns.rcode.from_text(rcode)
680 else:
681 raise TypeError("rcode is neither a str nor int")
682
683 if msg.rcode() != rcode:
684 msgRcode = dns.rcode._by_value[msg.rcode()]
685 wantedRcode = dns.rcode._by_value[rcode]
686
687 raise AssertionError("Rcode for %s is %s, expected %s." % (msg.question[0].to_text(), msgRcode, wantedRcode))
05537f80
PL
688
689 def assertAuthorityHasSOA(self, msg):
690 if not isinstance(msg, dns.message.Message):
691 raise TypeError("msg is not a dns.message.Message but a %s" % type(msg))
692
693 found = False
694 for rrset in msg.authority:
695 if rrset.rdtype == dns.rdatatype.SOA:
696 found = True
697 break
698
699 if not found:
700 raise AssertionError("No SOA record found in the authority section:\n%s" % msg.to_text())