Commit 479cd4e8 authored by Grigorii Demidov's avatar Grigorii Demidov Committed by Petr Špaček

Deckard: support for duplicate records

dnspython library by default coalesces RRsets of same type into one and
automaticaly deduplicates equicalent RRs inside one RRset.
This prevented Deckard from testing DNS messages with duplicate RR.

To prevent this, all DNS messages are now parsed using option
one_rr_per_rrset=True. This setting may create multiple RRsets
of the same type but it seems okay for Deckard because the comparison
algorithms compare individual RRs and ignore how they are distributed
among RRsets.
parent f8a2fb8b
import dns.message
import dns.rrset
import dns.rcode
import dns.dnssec
......@@ -74,6 +73,10 @@ def compare_rrs(expected, got):
for rr in got:
if rr not in expected:
raise Exception("unexpected record '%s'" % rr.to_text())
if len(expected) != len(got):
raise Exception("expected %s records but got %s records "
"(a duplicate RR somewhere?)"
% (len(expected), len(got)))
return True
def compare_val(expected, got):
......@@ -252,7 +255,9 @@ class Entry:
def adjust_reply(self, query):
""" Copy scripted reply and adjust to received query. """
answer = dns.message.from_wire(self.message.to_wire(),xfr=self.message.xfr)
answer = dns.message.from_wire(self.message.to_wire(),
xfr=self.message.xfr,
one_rr_per_rrset=True)
answer.use_edns(query.edns, query.ednsflags, options = self.message.options)
if 'copy_id' in self.adjust_fields:
answer.id = query.id
......@@ -263,6 +268,11 @@ class Entry:
answer.question = query.question
# Re-set, as the EDNS might have reset the ext-rcode
answer.set_rcode(self.message.rcode())
# sanity check: adjusted answer should be almost the same
assert len(answer.answer) == len(self.message.answer)
assert len(answer.authority) == len(self.message.authority)
assert len(answer.additional) == len(self.message.additional)
return answer
def set_adjust(self, fields):
......@@ -360,13 +370,6 @@ class Entry:
def __rr_add(self, section, rr):
""" Merge record to existing RRSet, or append to given section. """
if rr.rdtype != dns.rdatatype.SOA:
for existing_rr in section:
if existing_rr.match(rr.name, rr.rdclass, rr.rdtype, rr.covers):
existing_rr += rr
return
section.append(rr)
def set_mandatory(self):
......@@ -603,7 +606,7 @@ class Step:
self.raw_answer = answer
ctx.last_raw_answer = answer
if self.raw_answer is not None:
self.answer = dns.message.from_wire(self.raw_answer)
self.answer = dns.message.from_wire(self.raw_answer,one_rr_per_rrset=True)
log_packet(sock, answer, query = False)
else:
self.answer = None
......
......@@ -37,7 +37,7 @@ def recvfrom_msg(stream, raw = False):
else:
raise Exception ("[recvfrom_msg]: unknown socket type '%i'" % stream.type)
if not raw:
data = dns.message.from_wire(data)
data = dns.message.from_wire(data, one_rr_per_rrset=True)
return data, addr
def sendto_msg(stream, message, addr = None):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment