aboutsummaryrefslogtreecommitdiff
path: root/ascension/ascension.py
diff options
context:
space:
mode:
Diffstat (limited to 'ascension/ascension.py')
-rw-r--r--ascension/ascension.py146
1 files changed, 78 insertions, 68 deletions
diff --git a/ascension/ascension.py b/ascension/ascension.py
index bb65b13..e756bc6 100644
--- a/ascension/ascension.py
+++ b/ascension/ascension.py
@@ -1,49 +1,28 @@
1#!/usr/bin/env python3 1#!/usr/bin/env python3
2# This file is part of Ascension.
3# Copyright (C) 2019 GNUnet e.V.
4#
5# Ascension is free software: you can redistribute it and/or modify it
6# under the terms of the GNU Affero General Public License as published
7# by the Free Software Foundation, either version 3 of the License,
8# or (at your option) any later version.
9#
10# Ascension is distributed in the hope that it will be useful, but
11# WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# Affero General Public License for more details.
14#
15# You should have received a copy of the GNU Affero General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17#
18# SPDX-License-Identifier: AGPL3.0-or-later
19#
20# Author rexxnor
21""" 2"""
22Usage: 3This file is part of Ascension.
23 ascension <domain> [-d] [-p] [-s] [--minimum-ttl=<ttl>] [--dry-run] 4Copyright (C) 2019-2020 GNUnet e.V.
24 ascension <domain> <port> [-d] [-p] [-s] [--minimum-ttl=<ttl>] [--dry-run] 5
25 ascension <domain> -n <transferns> [-d] [-p] [-s] [--minimum-ttl=<ttl>] [--dry-run] 6Ascension is free software: you can redistribute it and/or modify it
26 ascension <domain> -n <transferns> <port> [-d] [-p] [-s] [--minimum-ttl=<ttl>] [--dry-run] 7under the terms of the GNU Affero General Public License as published
27 ascension -p | --public 8by the Free Software Foundation, either version 3 of the License,
28 ascension -d | --debug 9or (at your option) any later version.
29 ascension -s | --standalone 10
30 ascension -h | --help 11Ascension is distributed in the hope that it will be useful, but
31 ascension -v | --version 12WITHOUT ANY WARRANTY; without even the implied warranty of
32 13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
33Options: 14Affero General Public License for more details.
34 <domain> Domain to migrate 15
35 <port> Port for zone transfer 16You should have received a copy of the GNU Affero General Public License
36 <transferns> DNS Server that does the zone transfer 17along with this program. If not, see <http://www.gnu.org/licenses/>.
37 --minimum-ttl=<ttl> Minimum TTL for records to migrate [default: 3600] 18
38 --dry-run Only try if a zone transfer is allowed 19SPDX-License-Identifier: AGPL3.0-or-later
39 -p --public Make records public on the DHT 20
40 -s --standalone Run ascension once 21Author rexxnor
41 -d --debug Enable debugging
42 -h --help Show this screen.
43 -v --version Show version.
44""" 22"""
45 23
46# imports 24# imports
25import argparse
47import logging 26import logging
48import os 27import os
49import queue 28import queue
@@ -56,13 +35,13 @@ import threading
56import dns.query 35import dns.query
57import dns.resolver 36import dns.resolver
58import dns.zone 37import dns.zone
59import docopt
60 38
61# GLOBALS for different environments 39# GLOBALS for different environments
62GNUNET_ZONE_CREATION_COMMAND = 'gnunet-identity' 40GNUNET_ZONE_CREATION_COMMAND = 'gnunet-identity'
63GNUNET_NAMESTORE_COMMAND = 'gnunet-namestore' 41GNUNET_NAMESTORE_COMMAND = 'gnunet-namestore'
64GNUNET_GNS_COMMAND = 'gnunet-gns' 42GNUNET_GNS_COMMAND = 'gnunet-gns'
65GNUNET_ARM_COMMAND = 'gnunet-arm' 43GNUNET_ARM_COMMAND = 'gnunet-arm'
44
66# This is the list of record types Ascension (and GNS) currently 45# This is the list of record types Ascension (and GNS) currently
67# explicitly supports. Record types we encounter that are not 46# explicitly supports. Record types we encounter that are not
68# in this list and not in the OBSOLETE_RECORD_TYPES list will 47# in this list and not in the OBSOLETE_RECORD_TYPES list will
@@ -70,6 +49,7 @@ GNUNET_ARM_COMMAND = 'gnunet-arm'
70SUPPORTED_RECORD_TYPES = [ 49SUPPORTED_RECORD_TYPES = [
71 "A", "AAAA", "NS", "MX", "SRV", "TXT", "CNAME" 50 "A", "AAAA", "NS", "MX", "SRV", "TXT", "CNAME"
72] 51]
52
73# Record types that exist in DNS but that won't ever exist in GNS 53# Record types that exist in DNS but that won't ever exist in GNS
74# as they are not needed anymore (so we should not create a warning 54# as they are not needed anymore (so we should not create a warning
75# if we drop one of these). 55# if we drop one of these).
@@ -122,7 +102,7 @@ class Ascender():
122 keystring = ret.stdout.decode().strip() 102 keystring = ret.stdout.decode().strip()
123 pkey, _, privkey = keystring.split(" ") 103 pkey, _, privkey = keystring.split(" ")
124 self.subzonedict[self.domain] = (pkey, self.minimum, privkey) 104 self.subzonedict[self.domain] = (pkey, self.minimum, privkey)
125 logging.info("executed command: %s", " ".join(ret.args)) 105 logging.info("Executed command: %s", " ".join(ret.args))
126 except sp.CalledProcessError: 106 except sp.CalledProcessError:
127 ret = sp.run([GNUNET_ZONE_CREATION_COMMAND, 107 ret = sp.run([GNUNET_ZONE_CREATION_COMMAND,
128 '-dqpe', self.domain], 108 '-dqpe', self.domain],
@@ -194,7 +174,7 @@ class Ascender():
194 174
195 def add_records_to_gns(self) -> None: 175 def add_records_to_gns(self) -> None:
196 """ 176 """
197 Extracts records from zone and adds them to GNS 177 Extracts records from transferred zone and adds them to GNS
198 :raises AttributeError: When getting incomplete data 178 :raises AttributeError: When getting incomplete data
199 """ 179 """
200 logging.info("Starting to add records into GNS...") 180 logging.info("Starting to add records into GNS...")
@@ -332,7 +312,7 @@ class Ascender():
332 taskqueue.put(None) 312 taskqueue.put(None)
333 thread.join(timeout=10) 313 thread.join(timeout=10)
334 if thread.is_alive(): 314 if thread.is_alive():
335 logging.critical("thread join timed out, still running") 315 logging.critical("Thread joining timed out, still running")
336 316
337 # Add soa record to GNS once completed (updates the previous one) 317 # Add soa record to GNS once completed (updates the previous one)
338 self.add_soa_record_to_gns(self.soa) 318 self.add_soa_record_to_gns(self.soa)
@@ -739,37 +719,33 @@ class Ascender():
739 logging.info("adding zone %s with %s pkey into %s", zone, pkey, domain) 719 logging.info("adding zone %s with %s pkey into %s", zone, pkey, domain)
740 self.add_pkey_record_to_zone(pkey, domain, label, ttl) 720 self.add_pkey_record_to_zone(pkey, domain, label, ttl)
741 721
742def main(): 722def main(args):
743 """ 723 """
744 Initializes object and handles arguments 724 Initializes object and handles arguments
745 """ 725 """
746 # argument parsing from docstring definition 726 flags = "p" if args.public else "n"
747 args = docopt.docopt(__doc__, version='Ascension 0.11.5')
748
749 # argument parsing
750 debug = args['--debug']
751 domain = args.get('<domain>', None)
752 transferns = args['<transferns>'] if args['<transferns>'] else None
753 port = args['<port>'] if args['<port>'] else "53"
754 flags = "p" if args.get('--public') else "n"
755 standalone = bool(args.get('--standalone'))
756 dryrun = bool(args.get('--dry-run'))
757 minimum = args['--minimum-ttl']
758 727
759 # Change logging severity to debug 728 # Change logging severity to debug
760 if debug: 729 if args.verbose:
761 logging.basicConfig(level=logging.DEBUG) 730 logging.basicConfig(level=logging.DEBUG)
762 731
763 # Initialize class instance 732 # Initialize class instance
764 ascender = Ascender(domain, transferns, port, flags, minimum) 733 ascender = Ascender(args.domain,
734 args.nameserver,
735 args.port,
736 flags,
737 args.ttl)
765 738
766 # Do dry run before GNUnet check 739 # Do dry run before GNUnet check
767 if dryrun: 740 if args.dryrun:
768 dns_zone_serial = ascender.get_dns_zone_serial(ascender.domain, 741 dns_zone_serial = ascender.get_dns_zone_serial(ascender.domain,
769 ascender.transferns) 742 ascender.transferns)
770 if dns_zone_serial is None: 743 if dns_zone_serial is None:
744 logging.critical('The specified domain is not transferrable \
745 using the given option!')
771 return 1 746 return 1
772 else: 747 else:
748 logging.critical('SUCCESS! The specified domain is transferrable!')
773 return 0 749 return 0
774 750
775 # Checks if GNUnet services are running 751 # Checks if GNUnet services are running
@@ -779,7 +755,7 @@ def main():
779 logging.critical('GNUnet services are not running!') 755 logging.critical('GNUnet services are not running!')
780 sys.exit(1) 756 sys.exit(1)
781 757
782 # Set to defaults to use before we get a SOA for the first time 758 # Set defaults to use before we get a SOA for the first time
783 retry = 300 759 retry = 300
784 refresh = 300 760 refresh = 300
785 761
@@ -804,7 +780,7 @@ def main():
804 780
805 if not dns_zone_serial: 781 if not dns_zone_serial:
806 logging.error("Could not get DNS zone serial") 782 logging.error("Could not get DNS zone serial")
807 if standalone: 783 if args.standalone:
808 return 1 784 return 1
809 time.sleep(retry) 785 time.sleep(retry)
810 continue 786 continue
@@ -815,21 +791,21 @@ def main():
815 elif gns_zone_serial == dns_zone_serial: 791 elif gns_zone_serial == dns_zone_serial:
816 logging.info("GNS zone is up to date.") 792 logging.info("GNS zone is up to date.")
817 print("GNS zone is up to date.") 793 print("GNS zone is up to date.")
818 if standalone: 794 if args.standalone:
819 return 0 795 return 0
820 time.sleep(refresh) 796 time.sleep(refresh)
821 continue 797 continue
822 elif gns_zone_serial > dns_zone_serial: 798 elif gns_zone_serial > dns_zone_serial:
823 logging.critical("SOA serial in GNS is bigger than SOA serial in DNS?") 799 logging.critical("SOA serial in GNS is bigger than SOA serial in DNS?")
824 logging.critical("GNS zone: %s, DNS zone: %s", gns_zone_serial, dns_zone_serial) 800 logging.critical("GNS zone: %s, DNS zone: %s", gns_zone_serial, dns_zone_serial)
825 if standalone: 801 if args.standalone:
826 return 1 802 return 1
827 time.sleep(retry) 803 time.sleep(retry)
828 continue 804 continue
829 else: 805 else:
830 logging.info("GNS zone is out of date, performing incremental transfer.") 806 logging.info("GNS zone is out of date, performing incremental transfer.")
831 needsupdate = True 807 needsupdate = True
832 if standalone: 808 if args.standalone:
833 return 1 809 return 1
834 print("GNS zone is out of date, performing incremental transfer.") 810 print("GNS zone is out of date, performing incremental transfer.")
835 811
@@ -846,7 +822,7 @@ def main():
846 retry = int(str(ascender.soa[2]).split(" ")[4]) 822 retry = int(str(ascender.soa[2]).split(" ")[4])
847 except dns.zone.BadZone: 823 except dns.zone.BadZone:
848 logging.critical("Malformed DNS Zone '%s'", ascender.domain) 824 logging.critical("Malformed DNS Zone '%s'", ascender.domain)
849 if standalone: 825 if args.standalone:
850 return 2 826 return 2
851 time.sleep(retry) 827 time.sleep(retry)
852 continue 828 continue
@@ -857,4 +833,38 @@ def main():
857 logging.info("Finished migration of the zone %s", ascender.domain) 833 logging.info("Finished migration of the zone %s", ascender.domain)
858 834
859if __name__ == '__main__': 835if __name__ == '__main__':
860 main() 836 parser = argparse.ArgumentParser(
837 description='Easy tool to migrate DNS zones into GNS',
838 )
839 parser.add_argument('domain', metavar='domain',
840 type=str,
841 help='Domain to be migrated into GNS')
842 parser.add_argument('-n', '--nameserver',
843 help='Nameserver to use for migrating',
844 required=False)
845 parser.add_argument('-P', '--port',
846 help='Port to use for zone transfer with nameserver',
847 required=False,
848 default=53)
849 parser.add_argument('-t', '--ttl',
850 help='Sets the minimum ttl of records added to GNS',
851 required=False, default=3600)
852 parser.add_argument('-s', '--standalone',
853 help='Run ascension once and not as daemon',
854 action='store_true',
855 required=False, default=False)
856 parser.add_argument('-p', '--public',
857 help='Push records to the public DHT',
858 action='store_true',
859 required=False, default=False)
860 parser.add_argument('-g', '--dryrun',
861 help='Tests if zone is transferrable without changing anything',
862 action='store_true',
863 required=False, default=False)
864 parser.add_argument('-v', '--verbose',
865 help='Enable verbose debugging',
866 action='store_true',
867 required=False, default=False)
868 parser.add_argument('-V', '--version', action='version', version='%(prog)s 0.11.5')
869 args = parser.parse_args()
870 main(args)