ascension

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

commit 1cf160afcf98be8d88ba10f684541ce7cdba7026
parent 48f2f46165affcb602b7a00118ba74efccad61b1
Author: rexxnor <rexxnor+gnunet@brief.li>
Date:   Mon,  7 Jan 2019 12:52:17 +0100

renamed project to ascension

Diffstat:
MREADME.md | 28++++++++++++++--------------
Rgnsmigrator/__init__.py -> ascension/__init__.py | 0
Aascension/ascension.py | 539+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rgnsmigrator/gnunet.py -> ascension/gnunet.py | 0
Rgnsmigrator/test/basic_named.conf -> ascension/test/basic_named.conf | 0
Rgnsmigrator/test/gnunet.zone -> ascension/test/gnunet.zone | 0
Aascension/test/managed-keys.bind | 33+++++++++++++++++++++++++++++++++
Aascension/test/managed-keys.bind.jnl | 0
Aascension/test/test_gns_lookup.conf | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aascension/test/test_gnsmigrator_simple.sh | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aascension/test/test_namestore_multiple.sh | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aascension/test/test_unit_gnsmigrator.py | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dgnsmigrator/gnsmigrator.py | 537-------------------------------------------------------------------------------
Dgnsmigrator/test/test_gnsmigrator_simple.sh | 99-------------------------------------------------------------------------------
Dgnsmigrator/test/test_namestore_multiple.sh | 108-------------------------------------------------------------------------------
Dgnsmigrator/test/test_unit_gnsmigrator.py | 69---------------------------------------------------------------------
Msetup.py | 14+++++++-------
17 files changed, 926 insertions(+), 834 deletions(-)

diff --git a/README.md b/README.md @@ -1,10 +1,10 @@ -# GNS Migrator +# Ascension The main goal is to develop a tool to easily migrate existing DNS Zones to the GNU Name System using (incremental) Zone Transfers. (AXFR/IXFR) ## How to install -To install the gnsmigrator simply execute one of the following commands: +To install the ascension simply execute one of the following commands: ```bash # System wide installation @@ -25,20 +25,20 @@ python3 setup.py develop ``` ## How to use -If you have installed it, simply execute gnsmigrator with one of several options. -You can also just run the file gnsmigrator.py itself directly. +If you have installed it, simply execute ascension with one of several options. +You can also just run the file ascension.py itself directly. -Taken from the dosctring of the gnsmigrator.py file: +Taken from the dosctring of the ascension.py file: ``` -GNS Migrator +Ascension Usage: - gnsmigrator.py <domain> [-d] - gnsmigrator.py <domain> -p <port> [-d] - gnsmigrator.py <domain> -ns <transferns> [-d] - gnsmigrator.py <domain> -ns <transferns> -p <port> [-d] - gnsmigrator.py -h | --help - gnsmigrator.py -v | --version + 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 Options: <port> Port for zone transfer @@ -52,7 +52,7 @@ Options: Example use: ``` # Transfers the sy TLD from ns1.tld.sy. -gnsmigrator sy -ns ns1.tld.sy. +ascension sy -ns ns1.tld.sy. # Transfers the nu TLD from zonedata.iis.se with debug options enabled -gnsmigrator nu -ns zonedata.iis.se. -d +ascension nu -ns zonedata.iis.se. -d ``` diff --git a/gnsmigrator/__init__.py b/ascension/__init__.py diff --git a/ascension/ascension.py b/ascension/ascension.py @@ -0,0 +1,539 @@ +#!/usr/bin/env python3 +"""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 + +Options: + <port> Port for zone transfer + <domain> Domain to migrate + <transferns> DNS Server that does the zone transfer + -d --debug Enable debugging + -h --help Show this screen. + -v --version Show version. +""" + +# imports +import logging +import queue +import re +import sys +import subprocess as sp +import threading +import dns.query +import dns.resolver +import dns.zone +import docopt + +# GLOBALS +GNUNET_ZONE_CREATION_COMMAND = 'gnunet-identity' +GNUNET_NAMESTORE_COMMAND = 'gnunet-namestore' +GNUNET_GNS_COMMAND = 'gnunet-gns' +GNUNET_ARM_COMMAND = 'gnunet-arm' +SUPPORTED_RECORD_TYPES = [ + "A", "AAAA", "NS", "MX", "SOA", "SRV", "TXT", "CNAME" +] + +class Ascender(): + """ + Class that provides migration for any given domain + """ + @classmethod + def __init__(cls, domain, transferns, port): + cls.domain = domain + cls.port = int(port) + cls.soa = None + cls.transferns = transferns + cls.zone = None + cls.zonegenerator = None + + @classmethod + def initial_zone_transfer(cls, serial=None): + """ + Transfer and initialize the zone + """ + if serial: + cls.zonegenerator = dns.query.xfr(cls.transferns, + cls.domain, + rdtype=dns.rdatatype.IXFR, + serial=serial, + port=cls.port) + else: + cls.zonegenerator = dns.query.xfr(cls.transferns, + cls.domain, + port=cls.port) + + + @classmethod + def bootstrap_zone(cls): + """ + Creates the zone in gnunet + """ + reverse_parsing = cls.domain.split('.')[::-1] + reverse_parsing = list(filter(None, reverse_parsing)) + counter = 0 + for domainpart in reverse_parsing: + pkey_lookup = sp.Popen([GNUNET_ZONE_CREATION_COMMAND, + '-d'], + stdout=sp.PIPE) + pkey_line = sp.Popen(['grep', domainpart], + stdin=pkey_lookup.stdout, + stdout=sp.PIPE) + pkey_zone = sp.check_output(['cut', '-d', + ' ', '-f3'], + stdin=pkey_line.stdout) + pkey_zone = pkey_zone.decode().strip() + pkey_lookup.stdout.close() + pkey_line.stdout.close() + # Create identity in GNUnet + try: + ret = sp.run([GNUNET_ZONE_CREATION_COMMAND, + '-C', domainpart], + stdout=sp.DEVNULL, + stderr=sp.DEVNULL) + logging.info("executed command: %s", " ".join(ret.args)) + except sp.CalledProcessError: + logging.info("Zone %s already exists!", domainpart) + pkey_lookup = sp.Popen([GNUNET_ZONE_CREATION_COMMAND, + '-d'], + stdout=sp.PIPE) + pkey_line = sp.Popen(['grep', domainpart], + stdin=pkey_lookup.stdout, + stdout=sp.PIPE) + pkey_zone = sp.check_output(['cut', '-d', ' ', '-f3'], + stdin=pkey_line.stdout) + pkey_zone = pkey_zone.decode().strip() + pkey_lookup.stdout.close() + pkey_line.stdout.close() + + # If it is TLD, don't add PKEY to higher zone + if counter > 0: + result = sp.check_output([GNUNET_GNS_COMMAND, + '-t', 'PKEY', + '-u', '%s.%s' % + (domainpart, + reverse_parsing[counter - 1])]) + + if "No results." in result.decode(): + ret = sp.run([GNUNET_NAMESTORE_COMMAND, + '-z', reverse_parsing[counter - 1], + '-a', '-n', domainpart, + '-t', 'PKEY', + '-V', pkey_zone, + '-e', 'never']) + logging.info("executed command: %s", " ".join(ret.args)) + if ret.returncode != 0: + logging.warning("failed to add record %s", domainpart) + logging.warning("failed to add %s record %s", + "PKEY", domainpart) + + counter += 1 + + @classmethod + def get_current_serial(cls, domain, resolver=None): + """ + Gets the current serial for a given zone + """ + # 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.warning("the domain '%s' does not exist") + master_answer = dns.resolver.query(soa_answer[0].mname, 'A') + try: + if resolver: + zone = dns.zone.from_xfr(dns.query.xfr( + resolver, domain, port=cls.port)) + else: + zone = dns.zone.from_xfr(dns.query.xfr( + master_answer[0].address, domain, + port=cls.port)) + except dns.resolver.NoAnswer: + logging.warning("nameserver for '%s' did not answer", domain) + except dns.exception.FormError: + logging.warning("domain '%s' does not allow xfr requests", domain) + for soa_record in zone.iterate_rdatas(rdtype=dns.rdatatype.SOA): + if not cls.transferns: + mname = soa_record[2].mname + if cls.domain not in mname: + cls.transferns = str(soa_record[2].mname) + "." + domain + else: + cls.transferns = str(soa_record[2].mname) + return soa_record[2].serial + + @classmethod + def mirror_zone(cls): + """ + Extract necessary information from Generator + """ + currentserial = int(cls.get_current_serial(cls.domain, cls.transferns)) + zoneserial = int(cls.get_zone_serial(cls.domain[:-1])) + if zoneserial == 0: + cls.initial_zone_transfer() + cls.zone = dns.zone.from_xfr(cls.zonegenerator) + cls.soa = cls.get_zone_soa(cls.zone) + return + elif zoneserial < currentserial: + cls.initial_zone_transfer(serial=zoneserial) + cls.zone = dns.zone.from_xfr(cls.zonegenerator) + cls.soa = cls.get_zone_soa(cls.zone) + return + elif zoneserial == currentserial: + logging.warning("Nothing to do!") + sys.exit(0) + # should be unnecessary but AXFR SOA might not equal to direct SOA + else: + logging.critical("SOA serial is bigger than zone serial?") + print(zoneserial, currentserial) + sys.exit(1) + + @classmethod + def add_records_to_gns(cls, flags="n"): + """ + Extracts records from zone and adds them to GNS + """ + logging.info("Starting to add records into GNS...") + zonename = cls.get_lowest_domain_part(cls.domain) + + # Defining FIFO Queue + taskqueue = queue.Queue(maxsize=5) + # Defining worker + def worker(): + while True: + # define recordline + recordline = [] + label = "" + + labelrecords = taskqueue.get() + # break if taskqueue is empty + if labelrecords is None: + break + + # execute thing to run on item + label, listofrdatasets = labelrecords + + for rdataset in listofrdatasets: + for test in rdataset: + rdtype = dns.rdatatype.to_text(test.rdtype) + if rdtype not in SUPPORTED_RECORD_TYPES: + continue + try: + ttl = rdataset.ttl + except AttributeError: + ttl = 3600 + + value = str(test) + + # modify value to fit gns syntax + rdtype, value = cls.transform_to_gns_format(test, + rdtype, + zonename, + cls.domain, + label) + # skip record if value is none + if value is None: + continue + + # build recordline + recordline.append("-R") + recordline.append("%d %s %s %s" % + (int(ttl), rdtype, flags, value)) + + # add recordline to gns + if len(recordline) > 1: + cls.add_recordline_to_gns(recordline, zonename, label) + + taskqueue.task_done() + + # Create one thread + thread = threading.Thread(target=worker) + thread.start() + + ## Add glue and rest of A/AAAA records to zone + #for gluerecord in cls.zone.iterate_rdatasets(rdtype=dns.rdatatype.A): + # taskqueue.put(gluerecord) + #for gluerecord in cls.zone.iterate_rdatasets(rdtype=dns.rdatatype.AAAA): + # taskqueue.put(gluerecord) + ## Add NS records to zone + #for nsrecord in cls.zone.iterate_rdatasets(rdtype=dns.rdatatype.NS): + # taskqueue.put(nsrecord) + # Add remaining records to zone + customrdataset = dict() + for remaining in cls.zone.iterate_rdatasets(): + rdataset = remaining[1] + if customrdataset.get(str(remaining[0])) is None: + work = list() + work.append(rdataset) + customrdataset[str(remaining[0])] = work + else: + customrdataset[str(remaining[0])].append(rdataset) + + for label in customrdataset.keys(): + value = customrdataset.get(label) + if value is None: + continue + taskqueue.put((label, value)) + + # Block until all tasks are done + taskqueue.join() + + # Stop workers and threads + taskqueue.put(None) + thread.join() + + # 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, zonename, cls.domain) + #logging.info("All records have been added!") + + @staticmethod + def add_recordline_to_gns(recordline, zonename, label): + """ + Replaces records in zone or adds them if not + :param recordline: records to replace + :param zonename: zonename of zone to add records to + :param label: label under which to add the records + """ + logging.info("trying to add %d records with name %s", len(recordline)/2, label) + ret = sp.run([GNUNET_NAMESTORE_COMMAND, + '-z', zonename, + '-n', str(label), + ] + recordline) + if ret.returncode != 0: + logging.warning("failed adding record with name %s", ' '.join(ret.args)) + else: + logging.info("successfully added record with name %s", ' '.join(ret.args)) + + @staticmethod + def transform_to_gns_format(record, rdtype, zonename, domain, label): + """ + Teansforms value of record to gnunet compatible format + :param record: record to transform + :param rdtype: record value to transform + :param zonename: name of the zone to add to + :param domain: domain of the zone to add + :param domain: label under which the record is stored + """ + value = str(record) + if rdtype == 'SOA': + zonetuple = str(value).split(' ') + domain = str(".".join(domain.split('.')[:-1])) + authns, owner, serial, refresh, retry, expiry, irefresh = zonetuple + if authns[-1] == '.': + authns = authns[:-1] + if owner[-1] == '.': + owner = owner[:-1] + value = "rname=%s.%s mname=%s.%s %d,%d,%d,%d,%d" % ( + authns, domain, owner, domain, + int(serial), int(refresh), int(retry), + int(expiry), int(irefresh) + ) + elif rdtype in ['TXT', 'CNAME']: + if value[-1] == ".": + value = value[:-1] + elif rdtype == 'NS': + if label == '@': + logging.info("ignoring NS record for itself") + return (rdtype, None) + nameserver = str(record) + if value[-1] == ".": + value = value[:-1] + else: + value = "%s.%s" % (value, zonename) + if domain[-1] == ".": + domain = domain[:-1] + if nameserver[-1] == ".": + dnsresolver = nameserver[:-1] + else: + dnsresolver = "%s.%s" % (nameserver, domain) + value = '%s.%s@%s' % (str(label), zonename, dnsresolver) + logging.info("transformed %s record to GNS2DNS format", rdtype) + rdtype = 'GNS2DNS' + elif rdtype == 'MX': + priority, mailserver = str(value).split(' ') + if mailserver[-1] == ".": + mailserver = mailserver[:-1] + value = '%s,%s' % (priority, mailserver) + logging.info("transformed %s record to GNS format", rdtype) + elif rdtype == 'SRV': + # this is the number for a SRV record + rdtype = 'BOX' + srv = 33 + protocols = {'_tcp': 6, '_udp': 17} + + # tearing the record apart + try: + _, proto = str(label).split('.') + except ValueError: + logging.warning("could not parse SRV label %s" % label) + return (rdtype, None) + priority, weight, destport, target = value.split(' ') + + protonum = protocols.get(proto) + if protonum is None: + logging.warning("invalid protocol: %s", proto) + return (rdtype, None) + + #pr w por target + #100 1 443 sipdir.online.lync.com. + value = '%s %s %s %s %s %s %s' % ( + destport, protonum, srv, priority, weight, destport, target + ) + else: + logging.info("Did not transform record of type: %s", rdtype) + return (rdtype, value) + + @staticmethod + def get_zone_serial(zonename): + """ + Extracts the current serial from a given zone + """ + if zonename[-1] == '.': + zonename = zonename[:-1] + try: + serial = sp.check_output([GNUNET_GNS_COMMAND, + '-t', 'SOA', + '-u', '@.%s' % zonename]) + serial = serial.decode() + except sp.CalledProcessError: + serial = "" + soa_serial = 0 + soapattern = re.compile(r'.+\s(\d+),\d+,+\d+,\d+,\d+', re.M) + if re.findall(soapattern, serial): + soa_serial = re.findall(soapattern, serial)[0] + else: + soa_serial = 0 + return soa_serial + + @staticmethod + def get_zone_soa(zone): + """ + Fetches soa record from zone + """ + ret = None + for soarecord in zone.iterate_rdatas(rdtype=dns.rdatatype.SOA): + if str(soarecord[0]) == '@': + ret = soarecord + break + return ret + + @staticmethod + def add_srv_record_to_gns(record, zonename): + # TODO Add support for SRV records + """ + Adds a SRV record to GNS + """ + if not Ascender.check_if_record_exists_in_zone(record, zonename): + value, ttl, rdata = record + rtype_str = str(dns.rdatatype.to_text(rdata.rdtype)) + dnsname_str = str(rdata).split(' ')[3] + debug = " ".join([GNUNET_NAMESTORE_COMMAND, + '-z', zonename, + '-a', '-n', dnsname_str, + '-t', rtype_str, + '-V', str(value), + '-e', '%ds' % ttl]) + print(debug) + ret = sp.run([GNUNET_NAMESTORE_COMMAND, + '-z', zonename, + '-a', '-n', dnsname_str, + '-t', rtype_str, + '-V', str(value), + '-e', '%ds' % ttl]) + logging.info("executed command: %s", " ".join(ret.args)) + if ret.returncode != 0: + logging.warning("failed to add %s record %s", + rtype_str, dnsname_str) + + @staticmethod + def add_soa_record_to_gns(record, zonename, domain): + """ + Adds a SOA record to GNS + """ + # needs to be added in any case + _, ttl, rdata = record + zonetuple = str(rdata).split(' ') + domain = str(".".join(domain.split('.')[:-1])) + authns, owner, serial, refresh, retry, expiry, irefresh = zonetuple + if authns[-1] == '.': + authns = authns[:-1] + if owner[-1] == '.': + owner = owner[:-1] + ret = sp.run([GNUNET_NAMESTORE_COMMAND, + '-z', zonename, + '-a', '-n', '@', + '-t', 'SOA', + '-V', "rname=%s.%s mname=%s.%s %d,%d,%d,%d,%d" + % (authns, domain, owner, domain, + int(serial), int(refresh), int(retry), + int(expiry), int(irefresh) + ), + '-e', '%ds' % ttl]) + logging.info("executed command: %s", " ".join(ret.args)) + if ret.returncode != 0: + logging.warning("failed to add %s record %s", "SOA", "@") + + + @staticmethod + def check_if_record_exists_in_zone(record, zonename): + """ + Checks if the given record exists in GNS + """ + dnsname, _, rdata = record + rtype_str = str(dns.rdatatype.to_text(rdata.rdtype)) + ret = sp.check_output([GNUNET_GNS_COMMAND, + '-t', rtype_str, + '-u', '%s.%s' % + (dnsname, zonename)] + ) + if 'Got ' in ret.decode(): + return True + return False + + @staticmethod + def get_lowest_domain_part(domain): + """ + Returns the lowest domain part in hierarchy + """ + return domain.split('.')[0] + +def main(): + """ + Initializes object and handles arguments + """ + # argument parsing from docstring definition + args = docopt.docopt(__doc__, version='GNS Migrator 0.2.0') + + # Checks if GNUnet services are running + try: + sp.check_output([GNUNET_ARM_COMMAND, '-I'], timeout=1) + except sp.TimeoutExpired: + logging.critical('GNUnet Services are not running!') + sys.exit(1) + + # 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 + + # Change logging severity to debug + if debug: + logging.basicConfig(level=logging.DEBUG) + + # Initialize class instance + ascender = Ascender(domain, transferns, port) + serial = ascender.get_zone_serial(domain) + ascender.initial_zone_transfer(serial) + ascender.bootstrap_zone() + ascender.mirror_zone() + ascender.add_records_to_gns() + +if __name__ == '__main__': + main() diff --git a/gnsmigrator/gnunet.py b/ascension/gnunet.py diff --git a/gnsmigrator/test/basic_named.conf b/ascension/test/basic_named.conf diff --git a/gnsmigrator/test/gnunet.zone b/ascension/test/gnunet.zone diff --git a/ascension/test/managed-keys.bind b/ascension/test/managed-keys.bind @@ -0,0 +1,33 @@ +$ORIGIN . +$TTL 0 ; 0 seconds +@ IN SOA . . ( + 24 ; serial + 0 ; refresh (0 seconds) + 0 ; retry (0 seconds) + 0 ; expire (0 seconds) + 0 ; minimum (0 seconds) + ) + KEYDATA 20181113222352 20181020181422 19700101000000 257 3 8 ( + AwEAAagAIKlVZrpC6Ia7gEzahOR+9W29euxhJhVVLOyQ + bSEW0O8gcCjFFVQUTf6v58fLjwBd0YI0EzrAcQqBGCzh + /RStIoO8g0NfnfL2MTJRkxoXbfDaUeVPQuYEhg37NZWA + JQ9VnMVDxP/VHL496M/QZxkjf5/Efucp2gaDX6RS6CXp + oY68LsvPVjR0ZSwzz1apAzvN9dlzEheX7ICJBBtuA6G3 + LQpzW5hOA2hzCTMjJPJ8LbqF6dsV6DoBQzgul0sGIcGO + Yl7OyQdXfZ57relSQageu+ipAdTTJ25AsRTAoub8ONGc + LmqrAmRLKBP1dfwhYB4N7knNnulqQxA+Uk1ihz0= + ) ; KSK; alg = RSASHA256; key id = 19036 + ; next refresh: Tue, 13 Nov 2018 22:23:52 GMT + ; trusted since: Sat, 20 Oct 2018 18:14:22 GMT + KEYDATA 20181113222352 20181020181422 19700101000000 257 3 8 ( + AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTO + iW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN + 7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5 + LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8 + efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7 + pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLY + A4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws + 9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= + ) ; KSK; alg = RSASHA256; key id = 20326 + ; next refresh: Tue, 13 Nov 2018 22:23:52 GMT + ; trusted since: Sat, 20 Oct 2018 18:14:22 GMT diff --git a/ascension/test/managed-keys.bind.jnl b/ascension/test/managed-keys.bind.jnl Binary files differ. diff --git a/ascension/test/test_gns_lookup.conf b/ascension/test/test_gns_lookup.conf @@ -0,0 +1,57 @@ +@INLINE@ test_gns_defaults.conf + +[namecache] +DISABLE = YES + +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-gns-peer-1/ + +[dht] +START_ON_DEMAND = YES + +[gns] +# PREFIX = valgrind --leak-check=full --track-origins=yes +START_ON_DEMAND = YES +AUTO_IMPORT_PKEY = YES +MAX_PARALLEL_BACKGROUND_QUERIES = 10 +DEFAULT_LOOKUP_TIMEOUT = 15 s +RECORD_PUT_INTERVAL = 1 h +ZONE_PUBLISH_TIME_WINDOW = 1 h +DNS_ROOT=PD67SGHF3E0447TU9HADIVU9OM7V4QHTOG0EBU69TFRI2LG63DR0 + +[namestore] +#PREFIX = valgrind --leak-check=full --track-origins=yes --log-file=/tmp/ns_log + +[revocation] +WORKBITS = 1 + +[dhtcache] +QUOTA = 1 MB +DATABASE = heap + +[topology] +TARGET-CONNECTION-COUNT = 16 +AUTOCONNECT = YES +FRIENDS-ONLY = NO +MINIMUM-FRIENDS = 0 + +[ats] +WAN_QUOTA_IN = 1 GB +WAN_QUOTA_OUT = 1 GB + +[transport] +plugins = tcp +NEIGHBOUR_LIMIT = 50 +PORT = 2091 + +[transport-tcp] +TIMEOUT = 300 s + +[nat] +DISABLEV6 = YES +BINDTO = 127.0.0.1 +ENABLE_UPNP = NO +BEHIND_NAT = NO +ALLOW_NAT = NO +INTERNAL_ADDRESS = 127.0.0.1 +EXTERNAL_ADDRESS = 127.0.0.1 diff --git a/ascension/test/test_gnsmigrator_simple.sh b/ascension/test/test_gnsmigrator_simple.sh @@ -0,0 +1,99 @@ +#!/bin/bash +# Author: rexxnor +# Returns 1 on basic error +# Returns 2 on explicit test case errors +# Returns 3 on implicit test case errors + +# Shutdown named +function cleanup { + pkill named + gnunet-identity -D gnunet + gnunet-identity -D org +} + +# Check for required packages + +if ! [ -x "$(command -v named)" ]; then + echo 'bind/named is not installed' >&2 + exit 1 +fi + +if ! [ -x "$(command -v ascension)" ]; then + echo 'ascension is not installed' >&2 + exit 1 +fi + +# Start named with a simple zone +named -c basic_named.conf -p 5000 + +# Check if domain resolves +nslookup gnunet.org 127.0.0.1 -port=5000 +if [ "$?" -ne 0 ]; then + echo "Something went wrong with named" + cleanup + exit 1 +fi + +# Let ascension run on gnunet.org test domain +ascension gnunet.org -ns 127.0.0.1 -p 5000 +if [ "$?" -ne 0 ]; then + echo "ascension failed adding the records!" + cleanup + exit 1 +fi + +function checkfailexp { + if [ "$?" -ne 0 ]; then + echo "required record not present" + cleanup + exit 2 + fi +} + +function checkfailimp { + if [ "$?" -ne 0 ]; then + echo "implied record not present" + cleanup + exit 3 + fi +} + +# TESTING explicit records +gnunet-gns -t CNAME -u asdf.gnunet +checkfailexp +gnunet-gns -t AAAA -u foo.gnunet +checkfailexp +gnunet-gns -t A -u mail.gnunet +checkfailexp +gnunet-gns -t A -u ns1.gnunet +checkfailexp +gnunet-gns -t A -u ns2.gnunet +checkfailexp +gnunet-gns -t A -u ns2.gnunet.org +checkfailexp +gnunet-gns -t MX -u mail.gnunet +checkfailexp +gnunet-gns -t A -u nextcloud.gnunet +checkfailexp +gnunet-gns -t SOA -u @.gnunet +checkfailexp + +# TESTING implicit records +gnunet-gns -t LEHO -u foo.gnunet +checkfailimp +gnunet-gns -t LEHO -u mail.gnunet +checkfailimp +gnunet-gns -t LEHO -u nextcloud.gnunet +checkfailimp +gnunet-gns -t LEHO -u owncloud.gnunet +checkfailimp +gnunet-gns -t LEHO -u ns1.gnunet +checkfailimp +gnunet-gns -t LEHO -u ns2.gnunet +checkfailimp + +# cleanup if we get this far +cleanup + +# finish +echo "All records added successfully!!" diff --git a/ascension/test/test_namestore_multiple.sh b/ascension/test/test_namestore_multiple.sh @@ -0,0 +1,108 @@ +#!/bin/bash +# This file is in the public domain. +#trap "gnunet-arm -e -c test_gns_lookup.conf" SIGINT +# +#LOCATION=$(command -v gnunet-config) +#if [ -z "$LOCATION" ] +#then +# LOCATION="gnunet-config" +#fi +#$LOCATION --version 1> /dev/null +#if test $? != 0 +#then +# echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" +# exit 77 +#fi +# +#rm -rf "$(gnunet-config -c test_gns_lookup.conf -s PATHS -o GNUNET_HOME -f)" +#command -v timeout &> /dev/null && DO_TIMEOUT="timeout 30" +# +## VARS +MYEGO=myego +#command -v timeout &> /dev/null && DO_TIMEOUT="timeout 15" +# +#gnunet-arm -s -c test_gns_lookup.conf +gnunet-identity -C myego + +# HELPERS +function get_record_type { + arr=$1 + typ=$(echo -n "${arr[0]}" | cut -d' ' -f2) + echo "$typ" +} + +function get_value { + arr=$1 + val=$(echo -n "${arr[0]}" | cut -d' ' -f4-) + echo "$val" +} + +function testing { + label=$1 + records=$2 + recordstring="" + for i in "${records[@]}" + do + recordstring+="-R $i" + done + gnunet-namestore -z "$MYEGO" -n "$label" "$recordstring" + if [ 0 -ne $? ]; then + echo "failed to add record $label: $recordstring" + fi + ret=$(gnunet-namestore -D -z "$MYEGO" -n "$label") + for i in "${records[@]}" + do + value=$(get_value "$i") + if [[ $ret == *"$value"* ]]; then + echo "Value(s) added successfully!" + return 0 + else + exit 1 + fi + done +} + +# TEST CASES +# 1 +echo "Testing adding of single A record with -R" +declare -a arr=('1200 A n 127.0.0.1') +testing test1 "${arr[@]}" +# 2 +echo "Testing adding of multiple A records with -R" +declare -a arr=('1200 A n 127.0.0.1' '2400 A n 127.0.0.2') +testing test2 "${arr[@]}" +# 3 +echo "Testing adding of multiple different records with -R" +declare -a arr=('1200 A n 127.0.0.1' '2400 AAAA n 2002::') +testing test3 "${arr[@]}" +# 4 +echo "Testing adding of single GNS2DNS record with -R" +declare -a arr=('86400 GNS2DNS n gnu.org@127.0.0.1') +testing test4 "${arr[@]}" +# 5 +echo "Testing adding of single GNS2DNS shadow record with -R" +declare -a arr=('86409 GNS2DNS s gnu.org@127.0.0.250') +testing test5 "${arr[@]}" +# 6 +echo "Testing adding of multiple GNS2DNS record with -R" +declare -a arr=('1 GNS2DNS n gnunet.org@127.0.0.1' '3600 GNS2DNS s gnunet.org@127.0.0.2') +testing test6 "${arr[@]}" +# 7 +echo "Testing adding MX record with -R" +declare -a arr=('3600 MX n 10,mail') +testing test7 "${arr[@]}" +# 8 +echo "Testing adding TXT record with -R" +declare -a arr=('3600 TXT n Pretty_Unicorns') +testing test8 "${arr[@]}" +# 8 +echo "Testing adding SRV record with -R" +declare -a arr=('3600 SRV n 0 0 443 testing') +testing _autodiscover_old._tcp "${arr[@]}" +# 9 +echo "Testing adding many A records with -R" +declare -a arr=('3600 A n 127.0.0.1' '3600 A n 127.0.0.2' '3600 A n 127.0.0.3' '3600 A n 127.0.0.4' '3600 A n 127.0.0.5') +testing test9 "${arr[@]}" + +# CLEANUP +gnunet-identity -D "$MYEGO" diff --git a/ascension/test/test_unit_gnsmigrator.py b/ascension/test/test_unit_gnsmigrator.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +""" +Unittests of ascension +""" + +import subprocess +import unittest +import mock +from unittest import TestCase +from ascension import ascension + + +class TestAscender(TestCase): + """ + Short Unit Tests for WebArchiver + """ + @classmethod + def setUpClass(cls): + """ + Prepare data for tests + """ + cls.ascension = ascension.Ascender(["pretty.fantasy"]) + + def test_constructor(self): + """ + Tests constructor + """ + self.assertEqual(["pretty.fantasy"], self.ascension.domainlist) + + def test_get_lowest_domain_part(self): + """ + Tests constructor + """ + self.assertEqual("pretty", self.ascension.get_lowest_domain_part( + self.ascension.domainlist[0])) + + def test_bootstrap_zones(self): + """ + Tests bootstrapping of zones with mocked subprocess commands + """ + self.ascension.bootstrap_zones() + # using mocked subprocesses to prevent inconsistent status + mocked_ident = mock.create_autospec(subprocess.check_output, return_value='fantasy PKEY') + self.assertIn('fantasy', mocked_ident(['gnunet-identity', '-d'])) + mocked_lookup = mock.create_autospec(subprocess.check_output, return_value='Got:') + self.assertIn('Got', mocked_lookup(['gnunet-gns', '-t', 'PKEY', '-u', 'pretty.fantasy'])) + mocked_failed_lookup = mock.create_autospec(subprocess.check_output, return_value=3) + self.assertIs(3, mocked_failed_lookup(['gnunet-gns', '-t', 'PKEY', '-u', 'pretty.pretty.fantasy'])) + + def test_initial_zone_transfer(self): + """ + Tests different ways of zone transfer not working + """ + self.ascension.initial_zone_transfer() + + # Needs to be done via mock stuff or expect weird stuff + #self.assertRaises(dns.resolver.NoAnswer, self.ascension.initial_zone_transfer()) + #self.assertRaises(dns.resolver.NoAnswer, self.ascension2.initial_zone_transfer()) + + @classmethod + def tearDownClass(cls): + """ + Removes all zones and cleans up testing environment + """ + subprocess.run(['gnunet-identity', '-D', 'pretty']) + subprocess.run(['gnunet-identity', '-D', 'fantasy']) + +if __name__ == "__main__": + unittest.main() diff --git a/gnsmigrator/gnsmigrator.py b/gnsmigrator/gnsmigrator.py @@ -1,537 +0,0 @@ -#!/usr/bin/env python3 -"""GNS Migrator - -Usage: - gnsmigrator.py <domain> [-d] - gnsmigrator.py <domain> -p <port> [-d] - gnsmigrator.py <domain> -ns <transferns> [-d] - gnsmigrator.py <domain> -ns <transferns> -p <port> [-d] - gnsmigrator.py -h | --help - gnsmigrator.py -v | --version - -Options: - <port> Port for zone transfer - <domain> Domain to migrate - <transferns> DNS Server that does the zone transfer - -d --debug Enable debugging - -h --help Show this screen. - -v --version Show version. -""" - -# imports -import logging -import queue -import re -import sys -import subprocess as sp -import threading -import dns.query -import dns.resolver -import dns.zone -import docopt - -# GLOBALS -GNUNET_ZONE_CREATION_COMMAND = 'gnunet-identity' -GNUNET_NAMESTORE_COMMAND = 'gnunet-namestore' -GNUNET_GNS_COMMAND = 'gnunet-gns' -GNUNET_ARM_COMMAND = 'gnunet-arm' -SUPPORTED_RECORD_TYPES = [ - "A", "AAAA", "NS", "MX", "SOA", "SRV", "TXT", "CNAME" -] - -class GNSMigrator(): - """ - Class that provides migration for any given domain - """ - @classmethod - def __init__(cls, domain, transferns, port): - cls.domain = domain - cls.port = int(port) - cls.soa = None - cls.transferns = transferns - cls.zone = None - cls.zonegenerator = None - - @classmethod - def initial_zone_transfer(cls, serial=None): - """ - Transfer and initialize the zone - """ - if serial: - cls.zonegenerator = dns.query.xfr(cls.transferns, - cls.domain, - rdtype=dns.rdatatype.IXFR, - serial=serial, - port=cls.port) - else: - cls.zonegenerator = dns.query.xfr(cls.transferns, - cls.domain, - port=cls.port) - - - @classmethod - def bootstrap_zone(cls): - """ - Creates the zone in gnunet - """ - reverse_parsing = cls.domain.split('.')[::-1] - reverse_parsing = list(filter(None, reverse_parsing)) - counter = 0 - for domainpart in reverse_parsing: - pkey_lookup = sp.Popen([GNUNET_ZONE_CREATION_COMMAND, - '-d'], - stdout=sp.PIPE) - pkey_line = sp.Popen(['grep', domainpart], - stdin=pkey_lookup.stdout, - stdout=sp.PIPE) - pkey_zone = sp.check_output(['cut', '-d', - ' ', '-f3'], - stdin=pkey_line.stdout) - pkey_zone = pkey_zone.decode().strip() - pkey_lookup.stdout.close() - pkey_line.stdout.close() - # Create identity in GNUnet - try: - ret = sp.run([GNUNET_ZONE_CREATION_COMMAND, - '-C', domainpart], - stdout=sp.DEVNULL, - stderr=sp.DEVNULL) - logging.info("executed command: %s", " ".join(ret.args)) - except sp.CalledProcessError: - logging.info("Zone %s already exists!", domainpart) - pkey_lookup = sp.Popen([GNUNET_ZONE_CREATION_COMMAND, - '-d'], - stdout=sp.PIPE) - pkey_line = sp.Popen(['grep', domainpart], - stdin=pkey_lookup.stdout, - stdout=sp.PIPE) - pkey_zone = sp.check_output(['cut', '-d', ' ', '-f3'], - stdin=pkey_line.stdout) - pkey_zone = pkey_zone.decode().strip() - pkey_lookup.stdout.close() - pkey_line.stdout.close() - - # If it is TLD, don't add PKEY to higher zone - if counter > 0: - result = sp.check_output([GNUNET_GNS_COMMAND, - '-t', 'PKEY', - '-u', '%s.%s' % - (domainpart, - reverse_parsing[counter - 1])]) - - if "No results." in result.decode(): - ret = sp.run([GNUNET_NAMESTORE_COMMAND, - '-z', reverse_parsing[counter - 1], - '-a', '-n', domainpart, - '-t', 'PKEY', - '-V', pkey_zone, - '-e', 'never']) - logging.info("executed command: %s", " ".join(ret.args)) - if ret.returncode != 0: - logging.warning("failed to add record %s", domainpart) - logging.warning("failed to add %s record %s", - "PKEY", domainpart) - - counter += 1 - - @classmethod - def get_current_serial(cls, domain, resolver=None): - """ - Gets the current serial for a given zone - """ - # 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.warning("the domain '%s' does not exist") - master_answer = dns.resolver.query(soa_answer[0].mname, 'A') - try: - if resolver: - zone = dns.zone.from_xfr(dns.query.xfr( - resolver, domain, port=cls.port)) - else: - zone = dns.zone.from_xfr(dns.query.xfr( - master_answer[0].address, domain, - port=cls.port)) - except dns.resolver.NoAnswer: - logging.warning("nameserver for '%s' did not answer", domain) - except dns.exception.FormError: - logging.warning("domain '%s' does not allow xfr requests", domain) - for soa_record in zone.iterate_rdatas(rdtype=dns.rdatatype.SOA): - if not cls.transferns: - mname = soa_record[2].mname - if cls.domain not in mname: - cls.transferns = str(soa_record[2].mname) + "." + domain - else: - cls.transferns = str(soa_record[2].mname) - return soa_record[2].serial - - @classmethod - def mirror_zone(cls): - """ - Extract necessary information from Generator - """ - currentserial = int(cls.get_current_serial(cls.domain, cls.transferns)) - zoneserial = int(cls.get_zone_serial(cls.domain[:-1])) - if zoneserial == 0: - cls.initial_zone_transfer() - cls.zone = dns.zone.from_xfr(cls.zonegenerator) - cls.soa = cls.get_zone_soa(cls.zone) - return - elif zoneserial < currentserial: - cls.initial_zone_transfer(serial=zoneserial) - cls.zone = dns.zone.from_xfr(cls.zonegenerator) - cls.soa = cls.get_zone_soa(cls.zone) - return - elif zoneserial == currentserial: - logging.warning("Nothing to do!") - sys.exit(0) - # should be unnecessary but AXFR SOA might not equal to direct SOA - else: - logging.critical("SOA serial is bigger than zone serial?") - print(zoneserial, currentserial) - sys.exit(1) - - @classmethod - def add_records_to_gns(cls, flags="n"): - """ - Extracts records from zone and adds them to GNS - """ - logging.info("Starting to add records into GNS...") - zonename = cls.get_lowest_domain_part(cls.domain) - - # Defining FIFO Queue - taskqueue = queue.Queue(maxsize=5) - # Defining worker - def worker(): - while True: - # define recordline - recordline = [] - label = "" - - labelrecords = taskqueue.get() - # break if taskqueue is empty - if labelrecords is None: - break - - # execute thing to run on item - label, listofrdatasets = labelrecords - - for rdataset in listofrdatasets: - for test in rdataset: - rdtype = dns.rdatatype.to_text(test.rdtype) - if rdtype not in SUPPORTED_RECORD_TYPES: - continue - try: - ttl = rdataset.ttl - except AttributeError: - ttl = 3600 - - value = str(test) - - # modify value to fit gns syntax - rdtype, value = cls.transform_to_gns_format(test, - rdtype, - zonename, - cls.domain, - label) - # skip record if value is none - if value is None: - continue - - # build recordline - recordline.append("-R") - recordline.append("%d %s %s %s" % - (int(ttl), rdtype, flags, value)) - - # add recordline to gns - if len(recordline) > 1: - cls.add_recordline_to_gns(recordline, zonename, label) - - taskqueue.task_done() - - # Create one thread - thread = threading.Thread(target=worker) - thread.start() - - ## Add glue and rest of A/AAAA records to zone - #for gluerecord in cls.zone.iterate_rdatasets(rdtype=dns.rdatatype.A): - # taskqueue.put(gluerecord) - #for gluerecord in cls.zone.iterate_rdatasets(rdtype=dns.rdatatype.AAAA): - # taskqueue.put(gluerecord) - ## Add NS records to zone - #for nsrecord in cls.zone.iterate_rdatasets(rdtype=dns.rdatatype.NS): - # taskqueue.put(nsrecord) - # Add remaining records to zone - customrdataset = dict() - for remaining in cls.zone.iterate_rdatasets(): - rdataset = remaining[1] - if customrdataset.get(str(remaining[0])) is None: - work = list() - work.append(rdataset) - customrdataset[str(remaining[0])] = work - else: - customrdataset[str(remaining[0])].append(rdataset) - - for label in customrdataset.keys(): - value = customrdataset.get(label) - if value is None: - continue - taskqueue.put((label, value)) - - # Block until all tasks are done - taskqueue.join() - - # Stop workers and threads - taskqueue.put(None) - thread.join() - - # 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, zonename, cls.domain) - #logging.info("All records have been added!") - - @staticmethod - def add_recordline_to_gns(recordline, zonename, label): - """ - Replaces records in zone or adds them if not - :param recordline: records to replace - :param zonename: zonename of zone to add records to - :param label: label under which to add the records - """ - logging.info("trying to add %d records with name %s", len(recordline)/2, label) - ret = sp.run([GNUNET_NAMESTORE_COMMAND, - '-z', zonename, - '-n', str(label), - ] + recordline) - if ret.returncode != 0: - logging.warning("failed adding record with name %s", ' '.join(ret.args)) - else: - logging.info("successfully added record with name %s", ' '.join(ret.args)) - - @staticmethod - def transform_to_gns_format(record, rdtype, zonename, domain, label): - """ - Teansforms value of record to gnunet compatible format - :param record: record to transform - :param rdtype: record value to transform - :param zonename: name of the zone to add to - :param domain: domain of the zone to add - :param domain: label under which the record is stored - """ - value = str(record) - if rdtype == 'SOA': - zonetuple = str(value).split(' ') - domain = str(".".join(domain.split('.')[:-1])) - authns, owner, serial, refresh, retry, expiry, irefresh = zonetuple - if authns[-1] == '.': - authns = authns[:-1] - if owner[-1] == '.': - owner = owner[:-1] - value = "rname=%s.%s mname=%s.%s %d,%d,%d,%d,%d" % ( - authns, domain, owner, domain, - int(serial), int(refresh), int(retry), - int(expiry), int(irefresh) - ) - elif rdtype in ['TXT', 'CNAME']: - if value[-1] == ".": - value = value[:-1] - elif rdtype == 'NS': - if label == '@': - logging.info("ignoring NS record for itself") - return (rdtype, None) - nameserver = str(record) - if value[-1] == ".": - value = value[:-1] - else: - value = "%s.%s" % (value, zonename) - if nameserver[-1] == ".": - dnsresolver = nameserver[:-1] - else: - dnsresolver = "%s.%s" % (nameserver, domain) - value = '%s.%s@%s' % (str(label), zonename, dnsresolver) - logging.info("transformed %s record to GNS2DNS format", rdtype) - rdtype = 'GNS2DNS' - elif rdtype == 'MX': - priority, mailserver = str(value).split(' ') - if mailserver[-1] == ".": - mailserver = mailserver[:-1] - value = '%s,%s' % (priority, mailserver) - logging.info("transformed %s record to GNS format", rdtype) - elif rdtype == 'SRV': - # this is the number for a SRV record - rdtype = 'BOX' - srv = 33 - protocols = {'_tcp': 6, '_udp': 17} - - # tearing the record apart - try: - _, proto = str(label).split('.') - except ValueError: - logging.warning("could not parse SRV label %s" % label) - return (rdtype, None) - priority, weight, destport, target = value.split(' ') - - protonum = protocols.get(proto) - if protonum is None: - logging.warning("invalid protocol: %s", proto) - return (rdtype, None) - - #pr w por target - #100 1 443 sipdir.online.lync.com. - value = '%s %s %s %s %s %s %s' % ( - destport, protonum, srv, priority, weight, destport, target - ) - else: - logging.info("Did not transform record of type: %s", rdtype) - return (rdtype, value) - - @staticmethod - def get_zone_serial(zonename): - """ - Extracts the current serial from a given zone - """ - if zonename[-1] == '.': - zonename = zonename[:-1] - try: - serial = sp.check_output([GNUNET_GNS_COMMAND, - '-t', 'SOA', - '-u', '@.%s' % zonename]) - serial = serial.decode() - except sp.CalledProcessError: - serial = "" - soa_serial = 0 - soapattern = re.compile(r'.+\s(\d+),\d+,+\d+,\d+,\d+', re.M) - if re.findall(soapattern, serial): - soa_serial = re.findall(soapattern, serial)[0] - else: - soa_serial = 0 - return soa_serial - - @staticmethod - def get_zone_soa(zone): - """ - Fetches soa record from zone - """ - ret = None - for soarecord in zone.iterate_rdatas(rdtype=dns.rdatatype.SOA): - if str(soarecord[0]) == '@': - ret = soarecord - break - return ret - - @staticmethod - def add_srv_record_to_gns(record, zonename): - # TODO Add support for SRV records - """ - Adds a SRV record to GNS - """ - if not GNSMigrator.check_if_record_exists_in_zone(record, zonename): - value, ttl, rdata = record - rtype_str = str(dns.rdatatype.to_text(rdata.rdtype)) - dnsname_str = str(rdata).split(' ')[3] - debug = " ".join([GNUNET_NAMESTORE_COMMAND, - '-z', zonename, - '-a', '-n', dnsname_str, - '-t', rtype_str, - '-V', str(value), - '-e', '%ds' % ttl]) - print(debug) - ret = sp.run([GNUNET_NAMESTORE_COMMAND, - '-z', zonename, - '-a', '-n', dnsname_str, - '-t', rtype_str, - '-V', str(value), - '-e', '%ds' % ttl]) - logging.info("executed command: %s", " ".join(ret.args)) - if ret.returncode != 0: - logging.warning("failed to add %s record %s", - rtype_str, dnsname_str) - - @staticmethod - def add_soa_record_to_gns(record, zonename, domain): - """ - Adds a SOA record to GNS - """ - # needs to be added in any case - _, ttl, rdata = record - zonetuple = str(rdata).split(' ') - domain = str(".".join(domain.split('.')[:-1])) - authns, owner, serial, refresh, retry, expiry, irefresh = zonetuple - if authns[-1] == '.': - authns = authns[:-1] - if owner[-1] == '.': - owner = owner[:-1] - ret = sp.run([GNUNET_NAMESTORE_COMMAND, - '-z', zonename, - '-a', '-n', '@', - '-t', 'SOA', - '-V', "rname=%s.%s mname=%s.%s %d,%d,%d,%d,%d" - % (authns, domain, owner, domain, - int(serial), int(refresh), int(retry), - int(expiry), int(irefresh) - ), - '-e', '%ds' % ttl]) - logging.info("executed command: %s", " ".join(ret.args)) - if ret.returncode != 0: - logging.warning("failed to add %s record %s", "SOA", "@") - - - @staticmethod - def check_if_record_exists_in_zone(record, zonename): - """ - Checks if the given record exists in GNS - """ - dnsname, _, rdata = record - rtype_str = str(dns.rdatatype.to_text(rdata.rdtype)) - ret = sp.check_output([GNUNET_GNS_COMMAND, - '-t', rtype_str, - '-u', '%s.%s' % - (dnsname, zonename)] - ) - if 'Got ' in ret.decode(): - return True - return False - - @staticmethod - def get_lowest_domain_part(domain): - """ - Returns the lowest domain part in hierarchy - """ - return domain.split('.')[0] - -def main(): - """ - Initializes object and handles arguments - """ - # argument parsing from docstring definition - args = docopt.docopt(__doc__, version='GNS Migrator 0.2.0') - - # Checks if GNUnet services are running - try: - sp.check_output([GNUNET_ARM_COMMAND, '-I'], timeout=1) - except sp.TimeoutExpired: - logging.critical('GNUnet Services are not running!') - sys.exit(1) - - # 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 - - # Change logging severity to debug - if debug: - logging.basicConfig(level=logging.DEBUG) - - # Initialize class instance - migrator = GNSMigrator(domain, transferns, port) - serial = migrator.get_zone_serial(domain) - migrator.initial_zone_transfer(serial) - migrator.bootstrap_zone() - migrator.mirror_zone() - migrator.add_records_to_gns() - -if __name__ == '__main__': - main() diff --git a/gnsmigrator/test/test_gnsmigrator_simple.sh b/gnsmigrator/test/test_gnsmigrator_simple.sh @@ -1,99 +0,0 @@ -#!/bin/bash -# Author: rexxnor -# Returns 1 on basic error -# Returns 2 on explicit test case errors -# Returns 3 on implicit test case errors - -# Shutdown named -function cleanup { - pkill named - gnunet-identity -D gnunet - gnunet-identity -D org -} - -# Check for required packages - -if ! [ -x "$(command -v named)" ]; then - echo 'bind/named is not installed' >&2 - exit 1 -fi - -if ! [ -x "$(command -v gnsmigrator)" ]; then - echo 'gnsmigrator is not installed' >&2 - exit 1 -fi - -# Start named with a simple zone -named -c basic_named.conf -p 5000 - -# Check if domain resolves -nslookup gnunet.org 127.0.0.1 -port=5000 -if [ "$?" -ne 0 ]; then - echo "Something went wrong with named" - cleanup - exit 1 -fi - -# Let gnsmigrator run on gnunet.org test domain -gnsmigrator gnunet.org -ns 127.0.0.1 -p 5000 -if [ "$?" -ne 0 ]; then - echo "gnsmigrator failed adding the records!" - cleanup - exit 1 -fi - -function checkfailexp { - if [ "$?" -ne 0 ]; then - echo "required record not present" - cleanup - exit 2 - fi -} - -function checkfailimp { - if [ "$?" -ne 0 ]; then - echo "implied record not present" - cleanup - exit 3 - fi -} - -# TESTING explicit records -gnunet-gns -t CNAME -u asdf.gnunet -checkfailexp -gnunet-gns -t AAAA -u foo.gnunet -checkfailexp -gnunet-gns -t A -u mail.gnunet -checkfailexp -gnunet-gns -t A -u ns1.gnunet -checkfailexp -gnunet-gns -t A -u ns2.gnunet -checkfailexp -gnunet-gns -t A -u ns2.gnunet.org -checkfailexp -gnunet-gns -t MX -u mail.gnunet -checkfailexp -gnunet-gns -t A -u nextcloud.gnunet -checkfailexp -gnunet-gns -t SOA -u @.gnunet -checkfailexp - -# TESTING implicit records -gnunet-gns -t LEHO -u foo.gnunet -checkfailimp -gnunet-gns -t LEHO -u mail.gnunet -checkfailimp -gnunet-gns -t LEHO -u nextcloud.gnunet -checkfailimp -gnunet-gns -t LEHO -u owncloud.gnunet -checkfailimp -gnunet-gns -t LEHO -u ns1.gnunet -checkfailimp -gnunet-gns -t LEHO -u ns2.gnunet -checkfailimp - -# cleanup if we get this far -cleanup - -# finish -echo "All records added successfully!!" diff --git a/gnsmigrator/test/test_namestore_multiple.sh b/gnsmigrator/test/test_namestore_multiple.sh @@ -1,108 +0,0 @@ -#!/bin/bash -# This file is in the public domain. -#trap "gnunet-arm -e -c test_gns_lookup.conf" SIGINT -# -#LOCATION=$(command -v gnunet-config) -#if [ -z "$LOCATION" ] -#then -# LOCATION="gnunet-config" -#fi -#$LOCATION --version 1> /dev/null -#if test $? != 0 -#then -# echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" -# exit 77 -#fi -# -#rm -rf "$(gnunet-config -c test_gns_lookup.conf -s PATHS -o GNUNET_HOME -f)" -#command -v timeout &> /dev/null && DO_TIMEOUT="timeout 30" -# -## VARS -MYEGO=myego -#command -v timeout &> /dev/null && DO_TIMEOUT="timeout 15" -# -#gnunet-arm -s -c test_gns_lookup.conf -gnunet-identity -C myego - -# HELPERS -function get_record_type { - arr=$1 - typ=$(echo -n "${arr[0]}" | cut -d' ' -f2) - echo "$typ" -} - -function get_value { - arr=$1 - val=$(echo -n "${arr[0]}" | cut -d' ' -f4-) - echo "$val" -} - -function testing { - label=$1 - records=$2 - recordstring="" - for i in "${records[@]}" - do - recordstring+="-R $i" - done - gnunet-namestore -z "$MYEGO" -n "$label" "$recordstring" - if [ 0 -ne $? ]; then - echo "failed to add record $label: $recordstring" - fi - ret=$(gnunet-namestore -D -z "$MYEGO" -n "$label") - for i in "${records[@]}" - do - value=$(get_value "$i") - if [[ $ret == *"$value"* ]]; then - echo "Value(s) added successfully!" - return 0 - else - exit 1 - fi - done -} - -# TEST CASES -# 1 -echo "Testing adding of single A record with -R" -declare -a arr=('1200 A n 127.0.0.1') -testing test1 "${arr[@]}" -# 2 -echo "Testing adding of multiple A records with -R" -declare -a arr=('1200 A n 127.0.0.1' '2400 A n 127.0.0.2') -testing test2 "${arr[@]}" -# 3 -echo "Testing adding of multiple different records with -R" -declare -a arr=('1200 A n 127.0.0.1' '2400 AAAA n 2002::') -testing test3 "${arr[@]}" -# 4 -echo "Testing adding of single GNS2DNS record with -R" -declare -a arr=('86400 GNS2DNS n gnu.org@127.0.0.1') -testing test4 "${arr[@]}" -# 5 -echo "Testing adding of single GNS2DNS shadow record with -R" -declare -a arr=('86409 GNS2DNS s gnu.org@127.0.0.250') -testing test5 "${arr[@]}" -# 6 -echo "Testing adding of multiple GNS2DNS record with -R" -declare -a arr=('1 GNS2DNS n gnunet.org@127.0.0.1' '3600 GNS2DNS s gnunet.org@127.0.0.2') -testing test6 "${arr[@]}" -# 7 -echo "Testing adding MX record with -R" -declare -a arr=('3600 MX n 10,mail') -testing test7 "${arr[@]}" -# 8 -echo "Testing adding TXT record with -R" -declare -a arr=('3600 TXT n Pretty_Unicorns') -testing test8 "${arr[@]}" -# 8 -echo "Testing adding TXT record with -R" -declare -a arr=('3600 SRV n _autodiscover_old._tcp.bfh.ch.') -testing test8 "${arr[@]}" -# 9 -echo "Testing adding many A records with -R" -declare -a arr=('3600 A n 127.0.0.1' '3600 A n 127.0.0.2' '3600 A n 127.0.0.3' '3600 A n 127.0.0.4' '3600 A n 127.0.0.5') -testing test9 "${arr[@]}" - -# CLEANUP -gnunet-identity -D "$MYEGO" diff --git a/gnsmigrator/test/test_unit_gnsmigrator.py b/gnsmigrator/test/test_unit_gnsmigrator.py @@ -1,69 +0,0 @@ -#!/usr/bin/env python3 -""" -Unittests of gnsmigrator -""" - -import subprocess -import unittest -import mock -from unittest import TestCase -from gnsmigrator import gnsmigrator - - -class TestGNSMigrator(TestCase): - """ - Short Unit Tests for WebArchiver - """ - @classmethod - def setUpClass(cls): - """ - Prepare data for tests - """ - cls.gnsmigrator = gnsmigrator.GNSMigrator(["pretty.fantasy"]) - - def test_constructor(self): - """ - Tests constructor - """ - self.assertEqual(["pretty.fantasy"], self.gnsmigrator.domainlist) - - def test_get_lowest_domain_part(self): - """ - Tests constructor - """ - self.assertEqual("pretty", self.gnsmigrator.get_lowest_domain_part( - self.gnsmigrator.domainlist[0])) - - def test_bootstrap_zones(self): - """ - Tests bootstrapping of zones with mocked subprocess commands - """ - self.gnsmigrator.bootstrap_zones() - # using mocked subprocesses to prevent inconsistent status - mocked_ident = mock.create_autospec(subprocess.check_output, return_value='fantasy PKEY') - self.assertIn('fantasy', mocked_ident(['gnunet-identity', '-d'])) - mocked_lookup = mock.create_autospec(subprocess.check_output, return_value='Got:') - self.assertIn('Got', mocked_lookup(['gnunet-gns', '-t', 'PKEY', '-u', 'pretty.fantasy'])) - mocked_failed_lookup = mock.create_autospec(subprocess.check_output, return_value=3) - self.assertIs(3, mocked_failed_lookup(['gnunet-gns', '-t', 'PKEY', '-u', 'pretty.pretty.fantasy'])) - - def test_initial_zone_transfer(self): - """ - Tests different ways of zone transfer not working - """ - self.gnsmigrator.initial_zone_transfer() - - # Needs to be done via mock stuff or expect weird stuff - #self.assertRaises(dns.resolver.NoAnswer, self.gnsmigrator.initial_zone_transfer()) - #self.assertRaises(dns.resolver.NoAnswer, self.gnsmigrator2.initial_zone_transfer()) - - @classmethod - def tearDownClass(cls): - """ - Removes all zones and cleans up testing environment - """ - subprocess.run(['gnunet-identity', '-D', 'pretty']) - subprocess.run(['gnunet-identity', '-D', 'fantasy']) - -if __name__ == "__main__": - unittest.main() diff --git a/setup.py b/setup.py @@ -9,20 +9,20 @@ with open("README.md", "r") as fh: long_description = fh.read() setuptools.setup( - name="gnsmigrator", - version="0.1.2", - author="Patrick Gerber", - author_email="patrick.gerber@students.bfh.ch", + name="ascension", + version="0.1.3", + author="rexxnor", + author_email="rexxnor+gnunet@brief.li", description="Tool to migrate DNS Zones to the GNU Name System", long_description=long_description, - url="https://gitlab.ti.bfh.ch/gerbp6/gnsmigrator", - packages=['gnsmigrator'], + url="https://gnunet.org/git/ascension.git/", + packages=['ascension'], classifiers=[ "Programming Language :: Python :: 3", ], entry_points={ 'console_scripts': [ - 'gnsmigrator=gnsmigrator.gnsmigrator:main', + 'ascension=ascension.ascension:main', ], }, )