ascension

Migrate DNS zones to the GNU Name System
Log | Files | Refs | README | LICENSE

commit c05fb19bbcaa05b3d83fa78ea3fbf4a1f0a8c8bd
parent 50a2f4a12857e43cbe7435c0e36c52044d750d54
Author: rexxnor <rexxnor+gnunet@brief.li>
Date:   Wed, 30 Jan 2019 14:01:10 +0100

fixed creation zone for non-bailiwick records. bugfixes

Diffstat:
MREADME | 16+++++++++-------
Mascension/ascension.py | 121+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Msetup.py | 4++--
3 files changed, 89 insertions(+), 52 deletions(-)

diff --git a/README b/README @@ -38,17 +38,19 @@ Taken from the docstring of the ascension.py file: Ascension Usage: - ascension.py <domain> [-d] - ascension.py <domain> -p <port> [-d] - ascension.py <domain> -ns <transferns> [-d] - ascension.py <domain> -ns <transferns> -p <port> [-d] - ascension.py -h | --help - ascension.py -v | --version + ascension <domain> [-d] [-p] + ascension <domain> <port> [-d] [-p] + ascension <domain> -ns <transferns> [-d] [-p] + ascension <domain> -ns <transferns> <port> [-d] [-p] + ascension -p | --public + ascension -h | --help + ascension -v | --version Options: - <port> Port for zone transfer <domain> Domain to migrate + <port> Port for zone transfer <transferns> DNS Server that does the zone transfer + -p --public Make records public on the DHT -d --debug Enable debugging -h --help Show this screen. -v --version Show version. diff --git a/ascension/ascension.py b/ascension/ascension.py @@ -20,17 +20,19 @@ # Author rexxnor """ Usage: - ascension <domain> [-d] - ascension <domain> -p <port> [-d] - ascension <domain> -ns <transferns> [-d] - ascension <domain> -ns <transferns> -p <port> [-d] + ascension <domain> [-d] [-p] + ascension <domain> <port> [-d] [-p] + ascension <domain> -ns <transferns> [-d] [-p] + ascension <domain> -ns <transferns> <port> [-d] [-p] + ascension -p | --public ascension -h | --help ascension -v | --version Options: - <port> Port for zone transfer <domain> Domain to migrate + <port> Port for zone transfer <transferns> DNS Server that does the zone transfer + -p --public Make records public on the DHT -d --debug Enable debugging -h --help Show this screen. -v --version Show version. @@ -70,13 +72,15 @@ class Ascender(): @classmethod def __init__(cls, domain, transferns, port): cls.domain = domain - if cls.domain == '.': + if domain[-1] == '.': cls.domain = cls.domain[:-1] cls.port = int(port) cls.transferns = transferns cls.soa = None + cls.tld = cls.domain.split(".")[::-1][0] cls.zone = None cls.zonegenerator = None + cls.nscache = dict() @classmethod def initial_zone_transfer(cls, serial=None): @@ -115,15 +119,17 @@ class Ascender(): :param resolver: Nameserver to query in DNS, defaults to None :returns: Serial of the zones SOA record """ + # Makes domains better resolvable + domain = domain + "." # SOA is different if taken directly from SOA record # compared to AXFR/IXFR - changed to respect this try: soa_answer = dns.resolver.query(domain, 'SOA') except dns.resolver.NoAnswer: - logging.critical("the domain '%s' does not exist") + logging.critical("the domain '%s' does not exist", domain) sys.exit(1) except dns.resolver.NXDOMAIN: - logging.critical("the domain '%s' is invalid") + logging.critical("the domain '%s' is invalid", domain) sys.exit(1) master_answer = dns.resolver.query(soa_answer[0].mname, 'A') try: @@ -187,6 +193,7 @@ class Ascender(): # Defining FIFO Queue taskqueue = queue.Queue(maxsize=5) + # Defining worker def worker(): while True: @@ -205,21 +212,20 @@ class Ascender(): subzones = label.split('.') - # ignore SRV records here - if len(subzones) == 2 and subzones[0][0] == "_": - pass - elif len(subzones) > 1: - ttl = cls.soa[1] - cls.create_zone_hierarchy(subzones, ttl) - label = subzones[0] - subdomains = ".".join(subzones[1:]) - domain = "%s.%s" % (subdomains, cls.domain) + # TODO Add the not-in-bailiwick case + # elif len(subzones) > 1: + # ttl = cls.soa[1] + # cls.create_zone_hierarchy(subzones, ttl) + # label = subzones[0] + # subdomains = ".".join(subzones[1:]) + # domain = "%s.%s" % (subdomains, cls.domain) for rdataset in listofrdatasets: for record in rdataset: rdtype = dns.rdatatype.to_text(record.rdtype) if rdtype not in SUPPORTED_RECORD_TYPES: continue + try: ttl = rdataset.ttl except AttributeError: @@ -242,16 +248,23 @@ class Ascender(): if value is None: continue - # build recordline - recordline.append("-R") - - # TODO possible regression here; maybe use a separate - # list to pass those arguments to prevent quoting - # issues in the future. - recordline.append('%d %s %s %s' % - (int(ttl), rdtype, flags, value)) - - # add recordline to gns + if type(value) is list: + for element in value: + # build recordline + recordline.append("-R") + recordline.append('%d %s %s %s' % + (int(ttl), rdtype, flags, element)) + else: + # build recordline + recordline.append("-R") + + # TODO possible regression here; maybe use a separate + # list to pass those arguments to prevent quoting + # issues in the future. + recordline.append('%d %s %s %s' % + (int(ttl), rdtype, flags, value)) + + # add recordline to gns and filter out empty lines if len(recordline) > 1: cls.add_recordline_to_gns(recordline, domain if domain else cls.domain, @@ -263,9 +276,17 @@ class Ascender(): thread = threading.Thread(target=worker) thread.start() + # Unify all records under same label into datastructure customrdataset = dict() try: for remaining in cls.zone.iterate_rdatasets(): + # build lookup table for later GNS2DNS records + domain = "%s.%s" % (str(remaining[0]), cls.domain) + elementlist = [] + for element in remaining[1]: + if dns.rdatatype.to_text(element.rdtype) in ['A', 'AAAA']: + elementlist.append(str(element)) + cls.nscache[str(domain)] = elementlist rdataset = remaining[1] if customrdataset.get(str(remaining[0])) is None: work = list() @@ -291,7 +312,7 @@ class Ascender(): # Add soa record to GNS once completed (updates the previous one) soa = cls.get_zone_soa(cls.zone) - cls.add_soa_record_to_gns(soa, cls.domain) + cls.add_soa_record_to_gns(soa) logging.info("All records have been added!") @staticmethod @@ -315,8 +336,8 @@ class Ascender(): logging.info("successfully added record with name %s", ' '.join(ret.args)) - @staticmethod - def transform_to_gns_format(record, rdtype, zonename, label): + @classmethod + def transform_to_gns_format(cls, record, rdtype, zonename, label): """ Transforms value of record to GNS compatible format :param record: record to transform @@ -333,6 +354,9 @@ class Ascender(): authns = authns[:-1] if owner[-1] == '.': owner = owner[:-1] + # hacky and might cause bugs + authns += cls.tld + owner += cls.tld value = "rname=%s.%s mname=%s.%s %d,%d,%d,%d,%d" % ( authns, zonename, owner, zonename, int(serial), int(refresh), int(retry), @@ -356,9 +380,17 @@ class Ascender(): zonename = zonename[:-1] if nameserver[-1] == ".": dnsresolver = nameserver[:-1] + dnsresolver = cls.nscache.get(dnsresolver, dnsresolver) else: dnsresolver = "%s.%s" % (nameserver, zonename) - value = '%s.%s@%s' % (str(label), zonename, dnsresolver) + dnsresolver = cls.nscache.get(dnsresolver, dnsresolver) + if isinstance(dnsresolver, list): + value = [] + for nsip in dnsresolver: + value.append("%s.%s@%s" % (str(label), zonename, nsip)) + else: + value = '%s.%s@%s' % (str(label), zonename, dnsresolver) + logging.info("transformed %s record to GNS2DNS format", rdtype) rdtype = 'GNS2DNS' elif rdtype == 'MX': @@ -485,28 +517,30 @@ class Ascender(): soa = soarecord return soa - @staticmethod - def add_soa_record_to_gns(record, zonename): + @classmethod + def add_soa_record_to_gns(cls, record): """ Adds a SOA record to GNS :param record: The record to add - :param zonename: The zone to which to add the record """ - _, ttl, rdata = record + label, ttl, rdata = record zonetuple = str(rdata).split(' ') - domain = zonename - zonename = str(".".join(zonename.split('.')[:-1])) authns, owner, serial, refresh, retry, expiry, irefresh = zonetuple if authns[-1] == '.': authns = authns[:-1] + else: + authns = "%s.%s" % (authns, cls.domain) if owner[-1] == '.': owner = owner[:-1] + else: + owner = "%s.%s" % (owner, cls.domain) + ret = sp.run([GNUNET_NAMESTORE_COMMAND, - '-z', domain, - '-a', '-n', '@', + '-z', cls.domain, + '-a', '-n', str(label), '-t', 'SOA', - '-V', "rname=%s.%s mname=%s.%s %d,%d,%d,%d,%d" - % (authns, zonename, owner, zonename, + '-V', "rname=%s mname=%s %d,%d,%d,%d,%d" + % (authns, owner, int(serial), int(refresh), int(retry), int(expiry), int(irefresh) ), @@ -610,13 +644,14 @@ def main(): Initializes object and handles arguments """ # argument parsing from docstring definition - args = docopt.docopt(__doc__, version='Ascension 0.3.0') + args = docopt.docopt(__doc__, version='Ascension 0.5.0') # argument parsing debug = args['--debug'] domain = args.get('<domain>', None) transferns = args['<transferns>'] if args['<transferns>'] else None port = args['<port>'] if args['<port>'] else 53 + flags = "p" if args.get('--public') else "n" # Change logging severity to debug if debug: @@ -638,7 +673,7 @@ def main(): ascender.initial_zone_transfer(serial) ascender.mirror_zone() ascender.bootstrap_zone() - ascender.add_records_to_gns() + ascender.add_records_to_gns(flags=flags) logging.info("Finished migrating of the zone %s", ascender.domain) refresh = int(ascender.get_zone_refresh_time()) retry = int(ascender.get_zone_retry_time()) diff --git a/setup.py b/setup.py @@ -23,12 +23,12 @@ Author rexxnor import setuptools -with open("README.md", "r") as fh: +with open("README", "r") as fh: long_description = fh.read() setuptools.setup( name="ascension", - version="0.4.0", + version="0.5.0", author="rexxnor", author_email="rexxnor+gnunet@brief.li", description="Tool to migrate DNS Zones to the GNU Name System",