diff options
Diffstat (limited to 'dns/zone.py')
| -rw-r--r-- | dns/zone.py | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/dns/zone.py b/dns/zone.py index d7b04cf..6d19409 100644 --- a/dns/zone.py +++ b/dns/zone.py @@ -18,6 +18,7 @@ from __future__ import generators import builtins +import re import sys import dns.exception @@ -29,6 +30,7 @@ import dns.rdata import dns.rrset import dns.tokenizer import dns.ttl +import dns.grange class BadZone(dns.exception.DNSException): """The zone is malformed.""" @@ -627,6 +629,166 @@ class _MasterReader(object): rds = n.find_rdataset(rdclass, rdtype, covers, True) rds.add(rd, ttl) + def _parse_modify(self, side): + # Here we catch everything in '{' '}' in a group so we can replace it + # with ''. + is_generate1 = re.compile("^.*\$({(\+|-?)(\d+),(\d+),(.)}).*$") + is_generate2 = re.compile("^.*\$({(\+|-?)(\d+)}).*$") + is_generate3 = re.compile("^.*\$({(\+|-?)(\d+),(\d+)}).*$") + # Sometimes there are modifiers in the hostname. These come after + # the dollar sign. They are in the form: ${offset[,width[,base]]}. + # Make names + g1 = is_generate1.match(side) + if g1: + mod, sign, offset, width, base = g1.groups() + if sign == '': + sign = '+' + g2 = is_generate2.match(side) + if g2: + mod, sign, offset = g2.groups() + if sign == '': + sign = '+' + width = 0 + base = 'd' + g3 = is_generate3.match(side) + if g3: + mod, sign, offset, width = g1.groups() + if sign == '': + sign = '+' + width = g1.groups()[2] + base = 'd' + + if not (g1 or g2 or g3): + mod = '' + sign = '+' + offset = 0 + width = 0 + base = 'd' + + if base != 'd': + raise NotImplemented + + return mod, sign, offset, width, base + + def _generate_line(self): + # range lhs [ttl] [class] type rhs [ comment ] + """Process one line containing the GENERATE statement from a DNS + master file.""" + if self.current_origin is None: + raise UnknownOrigin + + token = self.tok.get() + # Range (required) + try: + start, stop, step = dns.grange.from_text(token.value) + token = self.tok.get() + if not token.is_identifier(): + raise dns.exception.SyntaxError + except: + raise dns.exception.SyntaxError + + # lhs (required) + try: + lhs = token.value + token = self.tok.get() + if not token.is_identifier(): + raise dns.exception.SyntaxError + except: + raise dns.exception.SyntaxError + + # TTL + try: + ttl = dns.ttl.from_text(token.value) + token = self.tok.get() + if not token.is_identifier(): + raise dns.exception.SyntaxError + except dns.ttl.BadTTL: + ttl = self.ttl + # Class + try: + rdclass = dns.rdataclass.from_text(token.value) + token = self.tok.get() + if not token.is_identifier(): + raise dns.exception.SyntaxError + except dns.exception.SyntaxError: + raise dns.exception.SyntaxError + except: + rdclass = self.zone.rdclass + if rdclass != self.zone.rdclass: + raise dns.exception.SyntaxError("RR class is not zone's class") + # Type + try: + rdtype = dns.rdatatype.from_text(token.value) + token = self.tok.get() + if not token.is_identifier(): + raise dns.exception.SyntaxError + except: + raise dns.exception.SyntaxError("unknown rdatatype '%s'" % + token.value) + + # lhs (required) + try: + rhs = token.value + except: + raise dns.exception.SyntaxError + + + lmod, lsign, loffset, lwidth, lbase = self._parse_modify(lhs) + rmod, rsign, roffset, rwidth, rbase = self._parse_modify(rhs) + for i in range(start, stop + 1, step): + # +1 because bind is inclusive and python is exclusive + + if lsign == '+': + lindex = i + int(loffset) + elif lsign == '-': + lindex = i - int(loffset) + + if rsign == '-': + rindex = i - int(roffset) + elif rsign == '+': + rindex = i + int(roffset) + + lzfindex = str(lindex).zfill(int(lwidth)) + rzfindex = str(rindex).zfill(int(rwidth)) + + + name = lhs.replace('$%s' % (lmod), lzfindex) + rdata = rhs.replace('$%s' % (rmod), rzfindex) + + self.last_name = dns.name.from_text(name, self.current_origin) + name = self.last_name + if not name.is_subdomain(self.zone.origin): + self._eat_line() + return + if self.relativize: + name = name.relativize(self.zone.origin) + + n = self.zone.nodes.get(name) + if n is None: + n = self.zone.node_factory() + self.zone.nodes[name] = n + try: + rd = dns.rdata.from_text(rdclass, rdtype, rdata, + self.current_origin, False) + except dns.exception.SyntaxError: + # Catch and reraise. + (ty, va) = sys.exc_info()[:2] + raise va + except: + # All exceptions that occur in the processing of rdata + # are treated as syntax errors. This is not strictly + # correct, but it is correct almost all of the time. + # We convert them to syntax errors so that we can emit + # helpful filename:line info. + (ty, va) = sys.exc_info()[:2] + raise dns.exception.SyntaxError("caught exception %s: %s" % + (str(ty), str(va))) + + rd.choose_relativity(self.zone.origin, self.relativize) + covers = rd.covers() + rds = n.find_rdataset(rdclass, rdtype, covers, True) + rds.add(rd, ttl) + def read(self): """Read a DNS master file and build a zone object. @@ -687,6 +849,8 @@ class _MasterReader(object): self.tok = dns.tokenizer.Tokenizer(self.current_file, filename) self.current_origin = new_origin + elif u == '$GENERATE': + self._generate_line() else: raise dns.exception.SyntaxError("Unknown master file directive '" + u + "'") continue |
