commit 1cf160afcf98be8d88ba10f684541ce7cdba7026
parent 48f2f46165affcb602b7a00118ba74efccad61b1
Author: rexxnor <rexxnor+gnunet@brief.li>
Date: Mon, 7 Jan 2019 12:52:17 +0100
renamed project to ascension
Diffstat:
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',
],
},
)