ascension

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

commit 47afd11be26d8b939bd37327e8230e9bb8a4481a
parent 7efb97ffe9809344f007e82b693bb6190c8381d0
Author: rexxnor <rexxnor+gnunet@brief.li>
Date:   Wed, 26 Sep 2018 15:55:41 +0200

added GNS2DNS support and rudimentary Unittests (incomplete)

Diffstat:
Mgnsmigrator/gnsmigrator.py | 112++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Dgnsmigrator/gnsmigrator_unit_tests.py | 64----------------------------------------------------------------
Agnsmigrator/test/test_unit_gnsmigrator.py | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msetup.py | 5++---
4 files changed, 154 insertions(+), 96 deletions(-)

diff --git a/gnsmigrator/gnsmigrator.py b/gnsmigrator/gnsmigrator.py @@ -2,12 +2,15 @@ """GNS Migrator Usage: - gnsmigrator.py <file> + gnsmigrator.py (-c <csv> | -f <txtfile>) + gnsmigrator.py (-c <csv> | -f <txtfile>) -r <resolver> gnsmigrator.py -h | --help - gnsmigrator.py --version + gnsmigrator.py -v | --version Options: - <file> CSV File containing domains to transfer + <csv> CSV File containing domains to transfer + <txtfile> Text File containing domains to transfer + <resolver> DNS Server that resolves missing domains -h --help Show this screen. -v --version Show version. """ @@ -24,6 +27,7 @@ import docopt GNUNET_ZONE_CREATION_COMMAND = 'gnunet-identity' GNUNET_NAMESTORE_COMMAND = 'gnunet-namestore' GNUNET_GNS_COMMAND = 'gnunet-gns' +GNUNET_ARM_COMMAND = 'gnunet-arm' class GNSMigrator(): """ @@ -75,6 +79,8 @@ class GNSMigrator(): stdout=subprocess.PIPE) pkey_zone = subprocess.check_output(['cut', '-d', ' ', '-f3'], stdin=pkey_line.stdout).decode().strip() + pkey_lookup.stdout.close() + pkey_line.stdout.close() # Create identity in GNUnet if not pkey_zone: subprocess.run([GNUNET_ZONE_CREATION_COMMAND, @@ -87,6 +93,8 @@ class GNSMigrator(): stdout=subprocess.PIPE) pkey_zone = subprocess.check_output(['cut', '-d', ' ', '-f3'], stdin=pkey_line.stdout).decode().strip() + pkey_lookup.stdout.close() + pkey_line.stdout.close() # If it is TLD, don't add PKEY to higher zone as they do not exist if counter > 0: @@ -105,43 +113,70 @@ class GNSMigrator(): counter += 1 @staticmethod - def add_records_to_gns(zonename, zone, domain): + def add_records_to_gns(zonename, zone, domain, dnsresolver): """ Checks if records are present :param zonename: zonename of zone to add records to :param zone: the transfered zone :param domain: full domain of zone """ - # can optimize with for record in zone.iterate_rdatas.filter() + # can optimize with for record in zone.iterate_rdatas.filter() to remove @ records for record in zone.iterate_rdatas(): dnsname, ttl, rtype = record rtype_str = dns.rdatatype.to_text(rtype.rdtype) dnsname_str = str(dnsname) value = str(rtype) + # special case for MX records if rtype_str == 'MX': valuelist = value.split(' ') value = '%s,%s' % (valuelist[0], valuelist[1]) - #if rtype_str != 'SOA': if dnsname_str != '@': - ret = subprocess.run([GNUNET_GNS_COMMAND, - '-t', rtype_str, - '-u', '%s.%s' % (dnsname_str, zonename)], - stdout=subprocess.PIPE) - if 'Got'.encode() not in ret.stdout: - subprocess.run([GNUNET_NAMESTORE_COMMAND, - '-z', zonename, - '-a', '-n', dnsname_str, - '-t', rtype_str, - '-V', value, - '-e', '%ds' % ttl]) - if rtype_str in ['A', 'AAAA']: - # This is EXPERIMENTAL LEgacy HOstname implementation + # Special case for the GNS2DNS case + if rtype_str == 'NS': + + # if no resolver is specified, choose the FQDN nameserver from zone + if not dnsresolver: + dnsresolver = value[:-1] + + pkey_lookup = subprocess.Popen([GNUNET_ZONE_CREATION_COMMAND, '-d'], + stdout=subprocess.PIPE) + pkey_line = subprocess.Popen(['grep', dnsname_str], + stdin=pkey_lookup.stdout, + stdout=subprocess.PIPE) + pkey_zone = subprocess.check_output(['cut', '-d', ' ', '-f3'], + stdin=pkey_line.stdout).decode().strip() + if not pkey_zone: + ret = subprocess.run([GNUNET_GNS_COMMAND, + '-t', 'GNS2DNS', + '-u', '%s.%s' % (dnsname_str, zonename)], + stdout=subprocess.PIPE) + if 'Got'.encode() not in ret.stdout: + subprocess.run([GNUNET_NAMESTORE_COMMAND, + '-z', zonename, + '-a', '-n', dnsname_str, + '-t', 'GNS2DNS', + '-V', '%s.%s@%s' % (dnsname_str, domain, dnsresolver), + '-e', '%ds' % ttl]) + else: + ret = subprocess.run([GNUNET_GNS_COMMAND, + '-t', rtype_str, + '-u', '%s.%s' % (dnsname_str, zonename)], + stdout=subprocess.PIPE) + if 'Got'.encode() not in ret.stdout: subprocess.run([GNUNET_NAMESTORE_COMMAND, '-z', zonename, '-a', '-n', dnsname_str, - '-t', 'LEHO', - '-V', '%s.%s' % (dnsname_str, domain), + '-t', rtype_str, + '-V', value, '-e', '%ds' % ttl]) + if rtype_str in ['A', 'AAAA']: + # This is EXPERIMENTAL LEgacy HOstname implementation + subprocess.run([GNUNET_NAMESTORE_COMMAND, + '-z', zonename, + '-a', '-n', dnsname_str, + '-t', 'LEHO', + '-V', '%s.%s' % (dnsname_str, domain), + '-e', '%ds' % ttl]) @staticmethod @@ -156,13 +191,34 @@ def main(): """ Initializes object and handles arguments """ + # argument parsing from docstring definition args = docopt.docopt(__doc__, version='GNS Migrator 0.0.1') - csvfile = args['<file>'] + + # Checks if GNUnet services are running + try: + subprocess.check_output([GNUNET_ARM_COMMAND, '-I'], timeout=1) + except subprocess.TimeoutExpired: + print('GNUnet Services are not running!') + print('Exiting...') + return 1 + + dnsresolver = args['<resolver>'] + domainlist = [] - with open(csvfile, 'r') as openedcsv: - linereader = csv.reader(openedcsv, delimiter=' ', quotechar='|') - for domain in linereader: - domainlist += domain + + if args.get('<csv>', None): + csvfile = args['<csv>'] + with open(csvfile, 'r') as openedcsv: + linereader = csv.reader(openedcsv, delimiter=' ', quotechar='|') + for domain in linereader: + domainlist += domain + + if args.get('<txtfile>', None): + txtfile = args['<txtfile>'] + with open(txtfile, 'r') as openedtxt: + for line in openedtxt: + domainlist.append(line.rstrip()) + gnsmigrator = GNSMigrator(domainlist) gnsmigrator.bootstrap_zones() @@ -171,11 +227,9 @@ def main(): for domain, zonetuple in gnsmigrator.zones.items(): zone, xfrinfo = zonetuple zonename = gnsmigrator.get_lowest_domain_part(domain) - gnsmigrator.add_records_to_gns(zonename, zone, domain) + gnsmigrator.add_records_to_gns(zonename, zone, domain, dnsresolver) # retain the information needed for a second zone transfer #print(xfrinfo) - if __name__ == '__main__': - # TODO ensure gnunet is runnning main() diff --git a/gnsmigrator/gnsmigrator_unit_tests.py b/gnsmigrator/gnsmigrator_unit_tests.py @@ -1,64 +0,0 @@ -#!/usr/bin/env python3 -""" -Tests of gnsmigrator -""" - -import subprocess -import unittest -from unittest import TestCase -import dns.exception -import dns.resolver -import gnsmigrator - - -class TestGNSMigrator(TestCase): - """ - Short Unit Tests for WebArchiver - """ - - def setUp(self): - """ - Prepare data for tests - """ - self.gnsmigrator = gnsmigrator.GNSMigrator(["example.fantasy"]) - self.gnsmigrator2 = gnsmigrator.GNSMigrator(["example.com"]) - self.gnsmigrator3 = gnsmigrator.GNSMigrator(["bfh.ch"]) - - def test_constructor(self): - """ - Tests constructor - """ - self.assertEqual("example.fantasy", self.gnsmigrator.domainlist) - - def test_get_lowest_domain_part(self): - """ - Tests constructor - """ - self.assertEqual("example", self.gnsmigrator.get_lowest_domain_part( - self.gnsmigrator.domainlist[0])) - - def test_bootstrap_zones(self): - """ - Tests bootstrapping of zones - """ - self.gnsmigrator.bootstrap_zones() - self.assertIn('example', subprocess.check_output(['gnunet-identity', '-d'])) - self.assertIn('fantasy', subprocess.check_output(['gnunet-identity', '-d'])) - - def test_initial_zone_transfer(self): - """ - Tests different ways of zone transfer not working - """ - self.assertRaises(dns.resolver.NoAnswer, self.gnsmigrator.initial_zone_transfer()) - self.assertRaises(dns.resolver.NoAnswer, self.gnsmigrator2.initial_zone_transfer()) - self.assertRaises(dns.exception.FormError, self.gnsmigrator3.initial_zone_transfer()) - -def main(): - """ - Main method - """ - unittest.main() - - -if __name__ == "__main__": - main() diff --git a/gnsmigrator/test/test_unit_gnsmigrator.py b/gnsmigrator/test/test_unit_gnsmigrator.py @@ -0,0 +1,69 @@ +#!/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 @@ -15,15 +15,14 @@ setuptools.setup( author_email="patrick.gerber@students.bfh.ch", description="Tool to migrate DNS Zones to the GNU Name System", long_description=long_description, - long_description_content_type="text/markdown", url="https://gitlab.ti.bfh.ch/gerbp6/gnsmigrator", - packages=setuptools.find_packages(), + packages=['gnsmigrator'], classifiers=[ "Programming Language :: Python :: 3", ], entry_points={ 'console_scripts': [ - 'gnsmigrator=gnsmigrator:main', + 'gnsmigrator=gnsmigrator.gnsmigrator:main', ], }, )