self.txn.add(name, ttl, rd)
- def _parse_modify(self, side):
+ def _parse_modify(self, side: str) -> Tuple[str, str, int, int, str]:
# Here we catch everything in '{' '}' in a group so we can replace it
# with ''.
is_generate1 = re.compile(r"^.*\$({(\+|-?)(\d+),(\d+),(.)}).*$")
width = 0
base = "d"
- if base != "d":
- raise NotImplementedError()
+ offset = int(offset)
+ width = int(width)
+
+ if sign not in ["+", "-"]:
+ raise dns.exception.SyntaxError(
+ "invalid offset sign %s" % sign
+ )
+ if base not in ["d", "o", "x", "X", "n", "N"]:
+ raise dns.exception.SyntaxError(
+ "invalid type %s" % base
+ )
return mod, sign, offset, width, base
# rhs (required)
rhs = token.value
- # The code currently only supports base 'd', so the last value
- # in the tuple _parse_modify returns is ignored
- lmod, lsign, loffset, lwidth, _ = self._parse_modify(lhs)
- rmod, rsign, roffset, rwidth, _ = self._parse_modify(rhs)
+ def _calculate_index(counter: int, offset_sign: str, offset: int) -> int:
+ """Calculate the index from the counter and offset."""
+ if offset_sign == "-":
+ offset *= -1
+ return counter + offset
+
+ def _format_index(index: int, base: str, width: int) -> str:
+ """Format the index with the given base, and zero-fill it
+ to the given width."""
+ if base in ["d", "o", "x", "X"]:
+ return format(index, base).zfill(width)
+
+ # base can only be n or N here
+ hexa = _format_index(index, "x", width)
+ nibbles = ".".join(hexa[::-1])[:width]
+ if base == "N":
+ nibbles = nibbles.upper()
+ return nibbles
+
+ 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)
+ lindex = _calculate_index(i, lsign, loffset)
+ rindex = _calculate_index(i, rsign, roffset)
- lzfindex = str(lindex).zfill(int(lwidth))
- rzfindex = str(rindex).zfill(int(rwidth))
+ lzfindex = _format_index(lindex, lbase, lwidth)
+ rzfindex = _format_index(rindex, rbase, rwidth)
name = lhs.replace("$%s" % (lmod), lzfindex)
rdata = rhs.replace("$%s" % (rmod), rzfindex)
ns2 3600 IN A 10.0.0.2
"""
+example_generate = """@ 3600 IN SOA foo bar 1 2 3 4 5
+@ 3600 IN NS ns
+$GENERATE 9-12 a.$ A 10.0.0.$
+$GENERATE 80-254/173 b.${0,5,d} A 10.0.1.$
+$GENERATE 80-254/173 c.${0,5,o} A 10.0.2.$
+$GENERATE 80-254/173 d.${0,5,x} A 10.0.3.$
+$GENERATE 80-254/173 e.${0,5,X} A 10.0.4.$
+$GENERATE 80-254/173 f.${0,5,n} A 10.0.5.$
+$GENERATE 80-254/173 g.${0,5,N} A 10.0.6.$
+$GENERATE 218-218/1 h.${0,4,N} A 10.0.7.$
+$GENERATE 218-218/1 i.${0,4,N}j A 10.0.8.$
+$GENERATE 23-24 k.${1,2,d} A 10.0.9.${-1,2,d}
+"""
+
+example_generate_output = """@ 3600 IN SOA foo bar 1 2 3 4 5
+@ 3600 IN NS ns
+a.9 5 IN A 10.0.0.9
+a.10 5 IN A 10.0.0.10
+a.11 5 IN A 10.0.0.11
+a.12 5 IN A 10.0.0.12
+b.00080 5 IN A 10.0.1.80
+b.00253 5 IN A 10.0.1.253
+c.00120 5 IN A 10.0.2.80
+c.00375 5 IN A 10.0.2.253
+d.00050 5 IN A 10.0.3.80
+d.000fd 5 IN A 10.0.3.253
+e.00050 5 IN A 10.0.4.80
+e.000FD 5 IN A 10.0.4.253
+f.0.5.0 5 IN A 10.0.5.80
+f.d.f.0 5 IN A 10.0.5.253
+g.0.5.0 5 IN A 10.0.6.80
+g.D.F.0 5 IN A 10.0.6.253
+i.A.D.j 5 IN A 10.0.8.218
+k.24 5 IN A 10.0.9.22
+k.25 5 IN A 10.0.9.23
+"""
+
something_quite_similar = """@ 3600 IN SOA foo bar 1 2 3 4 5
@ 3600 IN NS ns1
@ 3600 IN NS ns2
f.write("\n")
self.assertEqual(f.getvalue(), example_text_output)
+ def testGenerate(self):
+ z = dns.zone.from_text(example_generate, "example.", relativize=True)
+ f = StringIO()
+ names = list(z.nodes.keys())
+ for n in names:
+ f.write(z[n].to_text(n))
+ f.write("\n")
+ self.assertEqual(f.getvalue(), example_generate_output)
+
def testTorture1(self):
#
# Read a zone containing all our supported RR types, and