summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Wellington <bwelling@xbill.org>2020-06-26 13:59:59 -0700
committerBrian Wellington <bwelling@xbill.org>2020-06-26 13:59:59 -0700
commitfa20a70905d6e62d673c5a00bf0112f51f807fa3 (patch)
tree67a226ba01cdeb171350b75f429a1ce7b6081a4e
parentf8b1646acb393b7cd1cb79fc427502374a8d2ca6 (diff)
downloaddnspython-message.tar.gz
Attempt to refactor per-opcode validation.message
Instead of validating rrsets and sections after parsing them, check the class/type for each record before parsing it. This is more generic, because it moves all of the update logic out of the common code. It's also more flexible, as it allows the update logic to specify that meta-records are empty.
-rw-r--r--dns/message.py71
-rw-r--r--dns/update.py25
2 files changed, 44 insertions, 52 deletions
diff --git a/dns/message.py b/dns/message.py
index fcb8372..dd278f6 100644
--- a/dns/message.py
+++ b/dns/message.py
@@ -610,13 +610,10 @@ class Message:
# What the caller picked is fine.
return value
- def _validate_rrset(self, section, rrset):
- if rrset.rdclass == dns.rdataclass.ANY or \
- rrset.rdclass == dns.rdataclass.NONE:
+ def _parse_rr_header(self, reader, section, rdclass, rdtype):
+ if dns.rdataclass.is_metaclass(rdclass):
raise dns.exception.FormError
-
- def _finish_section(self, section):
- pass
+ return (rdclass, rdtype, None, False)
class QueryMessage(Message):
@@ -653,7 +650,6 @@ class _WireReader:
question_only: Are we only reading the question?
one_rr_per_rrset: Put each RR into its own RRset?
ignore_trailing: Ignore trailing junk at end of request?
- zone_rdclass: The class of the zone in messages which are
DNS dynamic updates.
"""
@@ -666,13 +662,13 @@ class _WireReader:
self.question_only = question_only
self.one_rr_per_rrset = one_rr_per_rrset
self.ignore_trailing = ignore_trailing
- self.zone_rdclass = dns.rdataclass.IN
- def _get_question(self, qcount):
+ def _get_question(self, section_number, qcount):
"""Read the next *qcount* records from the wire data and add them to
the question section.
"""
+ section = self.message.sections[section_number]
for i in range(qcount):
(qname, used) = dns.name.from_wire(self.wire, self.current)
if self.message.origin is not None:
@@ -682,12 +678,11 @@ class _WireReader:
struct.unpack('!HH',
self.wire[self.current:self.current + 4])
self.current += 4
- rrset = self.message.find_rrset(self.message.question, qname,
- rdclass, rdtype, create=True,
- force_unique=True)
- for rrset in self.message.sections[MessageSection.QUESTION]:
- self.message._validate_rrset(MessageSection.QUESTION, rrset)
- self.message._finish_section(MessageSection.QUESTION)
+ (rdclass, rdtype, _, _) = \
+ self.message._parse_rr_header(self, section_number,
+ rdclass, rdtype)
+ rrset = self.message.find_rrset(section, qname, rdclass, rdtype,
+ create=True, force_unique=True)
def _get_section(self, section_number, count):
"""Read the next I{count} records from the wire data and add them to
@@ -758,21 +753,17 @@ class _WireReader:
self.message.first)
self.message.had_tsig = True
else:
- if ttl > 0x7fffffff:
- ttl = 0
- if rdclass in (dns.rdataclass.ANY, dns.rdataclass.NONE):
- deleting = rdclass
- rdclass = self.zone_rdclass
- else:
- deleting = None
- if deleting == dns.rdataclass.ANY or \
- (deleting == dns.rdataclass.NONE and
- section is self.message.answer):
- covers = dns.rdatatype.NONE
+ (rdclass, rdtype, deleting, empty) = \
+ self.message._parse_rr_header(self, section_number,
+ rdclass, rdtype)
+ if empty:
+ if rdlen > 0:
+ raise dns.exception.FormError
rd = None
+ covers = dns.rdatatype.NONE
else:
- rd = dns.rdata.from_wire(rdclass, rdtype, self.wire,
- self.current, rdlen,
+ rd = dns.rdata.from_wire(rdclass, rdtype,
+ self.wire, self.current, rdlen,
self.message.origin)
covers = rd.covers()
if self.message.xfr and rdtype == dns.rdatatype.SOA:
@@ -781,11 +772,10 @@ class _WireReader:
rdclass, rdtype, covers,
deleting, True, force_unique)
if rd is not None:
+ if ttl > 0x7fffffff:
+ ttl = 0
rrset.add(rd, ttl)
self.current += rdlen
- for rrset in self.message.sections[section_number]:
- self.message._validate_rrset(section_number, rrset)
- self.message._finish_section(section_number)
def read(self):
"""Read a wire format DNS message and build a dns.message.Message
@@ -803,7 +793,7 @@ class _WireReader:
self.initialize_message(self.message)
self.one_rr_per_rrset = \
self.message._get_one_rr_per_rrset(self.one_rr_per_rrset)
- self._get_question(qcount)
+ self._get_question(MessageSection.QUESTION, qcount)
if self.question_only:
return
self._get_section(MessageSection.ANSWER, ancount)
@@ -909,7 +899,6 @@ class _TextReader:
tok: the tokenizer.
message: The message object being built.
- zone_rdclass: The class of the zone in messages which are
DNS dynamic updates.
last_name: The most recently read name when building a message object.
one_rr_per_rrset: Put each RR into its own RRset?
@@ -923,7 +912,6 @@ class _TextReader:
self.message = None
self.tok = dns.tokenizer.Tokenizer(text, idna_codec=idna_codec)
self.last_name = None
- self.zone_rdclass = dns.rdataclass.IN
self.one_rr_per_rrset = one_rr_per_rrset
self.origin = origin
self.relativize = relativize
@@ -1003,9 +991,10 @@ class _TextReader:
rdclass = dns.rdataclass.IN
# Type
rdtype = dns.rdatatype.from_text(token.value)
+ (rdclass, rdtype, _, _) = \
+ self.message._parse_rr_header(self, section_number, rdclass, rdtype)
self.message.find_rrset(section, name, rdclass, rdtype, create=True,
force_unique=True)
- self.zone_rdclass = rdclass
self.tok.get_eol()
def _rr_line(self, section_number):
@@ -1014,7 +1003,6 @@ class _TextReader:
"""
section = self.message.sections[section_number]
- deleting = None
# Name
token = self.tok.get(want_leading=True)
if not token.is_whitespace():
@@ -1041,16 +1029,17 @@ class _TextReader:
token = self.tok.get()
if not token.is_identifier():
raise dns.exception.SyntaxError
- if rdclass == dns.rdataclass.ANY or rdclass == dns.rdataclass.NONE:
- deleting = rdclass
- rdclass = self.zone_rdclass
except dns.exception.SyntaxError:
raise dns.exception.SyntaxError
except Exception:
rdclass = dns.rdataclass.IN
# Type
rdtype = dns.rdatatype.from_text(token.value)
+ (rdclass, rdtype, deleting, empty) = \
+ self.message._parse_rr_header(self, section_number, rdclass, rdtype)
token = self.tok.get()
+ if empty and not token.is_eol_or_eof():
+ raise dns.exception.SyntaxError
if not token.is_eol_or_eof():
self.tok.unget(token)
rd = dns.rdata.from_text(rdclass, rdtype, self.tok,
@@ -1124,10 +1113,6 @@ class _TextReader:
line_method(section_number)
if not self.message:
self.message = self._make_message()
- for i in range(4):
- for rrset in self.message.sections[i]:
- self.message._validate_rrset(i, rrset)
- self.message._finish_section(i)
return self.message
diff --git a/dns/update.py b/dns/update.py
index 9615a73..e21a283 100644
--- a/dns/update.py
+++ b/dns/update.py
@@ -300,17 +300,24 @@ class UpdateMessage(dns.message.Message):
# Updates are always one_rr_per_rrset
return True
- def _validate_rrset(self, section, rrset):
+ def _parse_rr_header(self, reader, section, rdclass, rdtype):
+ deleting = None
+ empty = False
if section == UpdateSection.ZONE:
- if rrset.rdtype != dns.rdatatype.SOA:
+ if dns.rdataclass.is_metaclass(rdclass) or \
+ rdtype != dns.rdatatype.SOA or \
+ getattr(reader, 'zone_rdclass', None):
raise dns.exception.FormError
-
- def _finish_section(self, section):
- if section == UpdateSection.ZONE and len(self.zone) != 1:
- raise dns.exception.FormError
- self.zone_rdclass = self.zone[0].rdclass
- # We do NOT want to set origin here, as that would cause
- # from_wire() relativization.
+ reader.zone_rdclass = rdclass
+ else:
+ if not getattr(reader, 'zone_rdclass', None):
+ raise dns.exception.FormError
+ if rdclass in (dns.rdataclass.ANY, dns.rdataclass.NONE):
+ deleting = rdclass
+ rdclass = reader.zone_rdclass
+ empty = (deleting == dns.rdataclass.ANY or
+ section == UpdateSection.PREREQ)
+ return (rdclass, rdtype, deleting, empty)
# backwards compatibility
Update = UpdateMessage