Skip to content

Instantly share code, notes, and snippets.

@funasoul
Forked from gmr/bindify.py
Last active October 30, 2021 15:44
Show Gist options
  • Save funasoul/ba0eb9eae6b9c4abb6bbc5b4f6ca1a56 to your computer and use it in GitHub Desktop.
Save funasoul/ba0eb9eae6b9c4abb6bbc5b4f6ca1a56 to your computer and use it in GitHub Desktop.
Convert tinydns zone files to bind
#!/usr/bin/env python
import collections
import datetime
from os import path
import pickle
import sys
if len(sys.argv) < 2:
print("Usage: bindify.py zonefile")
sys.exit(0)
# Destination path
PATH = "/root/tinydns_local/root"
DOMAIN = sys.argv[1].split("-")[-1]
A = collections.namedtuple("A", ["address", "hostname"])
CNAME = collections.namedtuple("CNAME", ["alias", "canonical"])
COMMENT = collections.namedtuple("COMMENT", ["value"])
MX = collections.namedtuple("MX", ["hostname", "priority", "mailhost"])
NS = collections.namedtuple("NS", ["hostname"])
PTR = collections.namedtuple("PTR", ["address", "fqdn"])
TXT = collections.namedtuple("TXT", ["hostname", "value"])
ZONE = collections.namedtuple("ZONE", ["filename", "type"])
HEADER = """\
$TTL 3600
@ IN SOA ns.%s. root.%s. (
%s01 ; serial
14400 ; refresh
3600 ; retry
1048576 ; expire
2560 ; minimum
)
""" % (
DOMAIN,
DOMAIN,
datetime.datetime.now().strftime("%Y%m%d"),
)
NAMESERVERS = ["ns.fun.bio.keio.ac.jp"]
records = []
ptrs = []
try:
zones = pickle.loads(open("zones.pickle", "rb").read())
except IOError:
zones = {}
for nameserver in NAMESERVERS:
records.append(NS(nameserver))
with open(sys.argv[1], "r") as handle:
for line in handle:
line = line.strip()
if len(line):
line = line.replace("\\052", "*")
parts = line[1:].split(":")
if line[0:2] in ["#=", "#+", "#C"] or line[0:3] in ["# =", "# +", "# C"]:
# print('Skipping: %s' % line)
continue
elif line[0] == "#":
records.append(COMMENT(line[1:].strip()))
elif line[0] == "=":
hostname = ".".join(parts[0].split(".")[0:])
records.append(A(parts[1], hostname))
ptrs.append(PTR(parts[1], parts[0]))
elif line[0] == "+":
hostname = ".".join(parts[0].split(".")[0:])
records.append(A(parts[1], hostname))
elif line[0] == "^":
tmp = parts[0].split(".")
tmp.reverse()
ptrs.append(PTR(".".join(tmp[2:]), parts[1]))
elif line[0] == "C":
hostname = ".".join(parts[0].split(".")[0:])
records.append(CNAME(hostname, parts[1]))
elif line[0] == "@":
hostname = ".".join(parts[0].split(".")[0:])
if parts[1]:
records.append(A(parts[1], hostname))
ptrs.append(PTR(parts[1], parts[0]))
records.append(MX(parts[1], parts[3] or 10, parts[0]))
else:
records.append(MX(hostname, parts[1] or 10, parts[0]))
elif line[0] == "'":
records.append(TXT(parts[0], parts[1]))
else: # Preserve Whitespace
records.append(None)
# Write out the main zone file
filename = filename = "%s/db.%s" % (PATH, DOMAIN)
with open(filename, "w") as handle:
# Dont append zones that already exist
if DOMAIN not in zones:
zones[DOMAIN] = ZONE(filename, "master")
handle.write(HEADER)
skip_record = False
for offset, record in enumerate(records):
# Preserve Whitespace
if record is None:
handle.write("\n")
continue
if skip_record:
skip_record = False
continue
record_type = record.__class__.__name__
if record_type == "COMMENT":
handle.write("; %s\n" % record.value)
elif record_type == "A":
# Special case for mailman, yay!
if (
len(records) > offset + 1
and records[offset + 1].__class__.__name__ == "MX"
):
handle.write(
"{0:<45} IN A {1}\n".format(record.hostname, record.address)
)
handle.write(
"{0:<45} IN MX {1:<5} {2}.\n".format(
" ", records[offset + 1][1], records[offset + 1][2]
)
)
skip_record = True
continue
handle.write(
"{0:<45} IN A {1}\n".format(record.hostname, record.address)
)
elif record_type == "CNAME":
handle.write("{0:<45} IN CNAME {1}.\n".format(*record))
elif record_type == "MX":
handle.write("{0:<45} IN MX {1:<5} {2}.\n".format(*record))
elif record_type == "NS":
handle.write("{0:<45} IN NS {1}.\n".format(" ", record[0]))
elif record_type == "TXT":
handle.write('{0:<45} IN TXT "{1}"\n'.format(*record))
try:
ptr_zones = pickle.loads(open("rev-dns-zones.pickle", "rb").read())
except IOError:
ptr_zones = {}
for record in ptrs:
octets = record.address.split(".")
subnet = ".".join(octets[0:3])
if subnet not in ptr_zones.keys():
ptr_zones[subnet] = dict()
ptr_zones[subnet][int(octets[3])] = record.fqdn
open("rev-dns-zones.pickle", "wb").write(pickle.dumps(ptr_zones))
for zone in ptr_zones:
rev = zone.split(".")
rev.reverse()
name = "%s.in-addr.arpa" % ".".join(rev)
filename = "%s/db.%s" % (PATH, zone)
# Dont append zones that already exist
if name not in zones:
zones[name] = ZONE(filename, "master")
with open(filename, "w") as handle:
handle.write(HEADER)
for nameserver in NAMESERVERS:
handle.write("{0:<10} IN NS {1}.\n".format(" ", nameserver))
handle.write("\n")
for key in sorted(ptr_zones[zone]):
handle.write("{0:<10} IN PTR {1}.\n".format(key, ptr_zones[zone][key]))
with open("%s/nsd.conf.local" % PATH, "w") as handle:
handle.write(
"""\
#
# Do any local configuration here
#
"""
)
for zone in sorted(zones):
filename = path.basename(zones[zone].filename)
handle.write(
"zone:\n"
+ '\tname: "{0}"\n'.format(zone)
+ '\tzonefile: "{0}/{1}"\n\n'.format(PATH, filename)
)
open("zones.pickle", "wb").write(pickle.dumps(zones))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment