#!/usr/bin/python3 import sqlite3 from pathlib import Path from sqlite3 import Error import urllib.request import json import logging import argparse import requests import os import time homefilepath = Path.home() filepath = homefilepath.joinpath('.config/ddns') database = filepath.joinpath('ddns.db') app_version = '0.1' def get_ip(): cursor = conn.cursor() cursor.execute('SELECT COUNT(ip4_server) FROM ipservers') count = cursor.fetchone()[0] if count != 0: cursor.execute('SELECT ip4_server from ipservers') server = cursor.fetchone() server = server[0] try: current_ip = urllib.request.urlopen(server).read().decode('utf-8') return current_ip except Exception as e: error = str(e) return error else: return None def connect_database(): Path(filepath).mkdir(parents=True, exist_ok=True) conn = None try: conn = sqlite3.connect(database) except Error as e: print(e) finally: if conn: c = conn.cursor() c.execute('''CREATE TABLE IF NOT EXISTS apikey (id integer NOT NULL PRIMARY KEY, api text NOT NULL)''') c.execute('''CREATE TABLE IF NOT EXISTS ipservers (id integer NOT NULL PRIMARY KEY, ip4_server text, ip6_server text)''') c.execute('''CREATE TABLE IF NOT EXISTS domains (id integer PRIMARY KEY, name text NOT NULL)''') c.execute('''CREATE TABLE IF NOT EXISTS subdomains (id integer PRIMARY KEY, main_id integer NOT NULL, name text)''') return conn def get_api(): cursor = conn.cursor() cursor.execute('SELECT COUNT(*) FROM apikey') count = cursor.fetchone()[0] if count == 0: return None else: cursor.execute('SELECT * FROM apikey') rows = cursor.fetchone() return rows[1] def api(api_value): cursor = conn.cursor() cursor.execute('SELECT COUNT(*) FROM apikey') count = cursor.fetchone()[0] if count == 0: cursor.execute('INSERT INTO apikey values(?,?)', (1, api_value)) print('Your API key has been added.') else: cursor.execute('UPDATE apikey SET api = ? WHERE id = 1',(api_value,)) print('Your API key has been updated.') conn.commit() def add_domian(domain): cursor = conn.cursor() cursor.execute('SELECT COUNT(*) FROM domains WHERE name like ?',(domain,)) count = cursor.fetchone()[0] if count == 0: cursor.execute('INSERT INTO domains values(?,?)', (None, domain,)) print('The domain %s has been added to the DB' % (domain)) conn.commit() else: print('Error: Domain name (%s) already in database!' % (domain)) def add_subdomain(sub,top): apikey = get_api() if apikey == None: print("Missing APIkey. Please add one!") else: ip = get_ip() if ip == None or 'urlopen error' in ip: print('Failed to get public IP. Do you have a typo in your URI? Error %s' % (ip)) else: cursor = conn.cursor() cursor.execute('SELECT COUNT(*) FROM domains WHERE name like ?',(top,)) count = cursor.fetchone()[0] if count == 0: print('Error: Top domain %s does not exist in the DB. Please add it with ddns -t %s ' % (top,top)) else: cursor.execute('SELECT id FROM domains WHERE name LIKE ?',(top,)) topdomain_id = cursor.fetchone() topdomain_id = topdomain_id[0] cursor.execute('SELECT count(*) FROM subdomains WHERE main_id LIKE ? AND name like ?',(topdomain_id,sub,)) count = cursor.fetchone()[0] if count != 0: print('The subdomain %s already exists for the domain %s.' % (sub,top)) else: data = {'name': sub,'data': ip,'type': "A",'ttl': 3600} headers = {'Authorization': 'Bearer ' + apikey, "Content-Type": "application/json"} response = requests.post('https://api.digitalocean.com/v2/domains/' + top + '/records', data=json.dumps(data), headers=headers) if str(response) == '': if response != 'Fail': response_data = response.json() domainid = str(response_data['domain_record']['id']) cursor.execute('INSERT INTO subdomains values(?,?,?)',(domainid,topdomain_id,sub,)) conn.commit() print('The subdomain %s for the domain %s has been added.' % (sub,top)) else: return 'Error: %s ' % (str(response)) def show_all_top_domains(): cursor = conn.cursor() apikey = get_api() if apikey != None: req = urllib.request.Request('https://api.digitalocean.com/v2/domains/?per_page=200') req.add_header('Content-Type', 'application/json') req.add_header('Authorization', 'Bearer ' + apikey) current = urllib.request.urlopen(req) remote = current.read().decode('utf-8') remoteData = json.loads(remote) print('Domains in database are marked with a [*]') print('================================================') for k in remoteData["domains"]: cursor.execute('SELECT COUNT(*) FROM domains WHERE name like ?',(k['name'],)) count = cursor.fetchone()[0] if count != 0: print('Name : '+k['name']+ ' *') else: print('Name : '+k['name']) else: print("Missing APIkey. Please add one!") def list_sub_domains(domain): # TODO: Finish this :) apikey = get_api() cursor = conn.cursor() if apikey == None: print("Missing APIkey. Please add one!") else: cursor.execute('SELECT COUNT(*) FROM domains WHERE name LIKE ?',(domain,)) count = cursor.fetchone()[0] if count == 0: print("Error: No such domain. Check spelling or use ddns -d to show all top domains.") else: cursor.execute('SELECT * FROM domains WHERE name LIKE ?', (domain,)) row = cursor.fetchone() print(row) print("List -> " + domain) def domaininfo(domain): print("domaininfo") def show_current_info(): ipserver = None API = get_api() cursor = conn.cursor() cursor.execute('SELECT COUNT(ip4_server) FROM ipservers') count = cursor.fetchone()[0] if count == 0: ipserver = 'No IP resolvers in DB' else: cursor.execute('SELECT * FROM ipservers') ipserver = cursor.fetchall()[0][1] if API == None: API = 'API key not stored in DB' print('Current info.') print('==========================================') print('API key : %s' % (API)) print('IP resolver : %s' % (ipserver)) print('App version : %s' % (app_version)) print('') print('IPv6 is not supported and not listed here.') def ip_server(ipserver, ip_type): cursor = conn.cursor() if ip_type == '4': cursor.execute('SELECT COUNT(ip4_server) FROM ipservers') count = cursor.fetchone()[0] if count == 0: cursor.execute('INSERT INTO ipservers values(?,?,?)', (None, ipserver,None)) conn.commit() print('New IP resolver (%s) for ipv%s added.' % (ipserver, ip_type)) else: cursor.execute('UPDATE ipservers SET ip4_server = ? WHERE id = 1',(ipserver,)) print('IP resolver (%s) for ipv%s updated.' % (ipserver, ip_type)) conn.commit() elif ip_type == '6': cursor.execute('SELECT COUNT(ip6_server) FROM ipservers') count = cursor.fetchone()[0] if count == 0: cursor.execute('INSERT INTO ipservers values(?,?,?)', (None, None,ipserver)) conn.commit() print('New IP resolver (%s) for ipv%s added. \n\r This IP version is not supported.' % (ipserver, ip_type)) else: cursor.execute('UPDATE ipservers SET ip6_server = ? WHERE id = 1',(ipserver,)) print('IP resolver (%s) for ipv%s updated. \n\r This IP version is not supported.' % (ipserver, ip_type)) conn.commit() def updateip(): print("updateip") # Commandline arguments conn = connect_database() parser = argparse.ArgumentParser(prog='ddns', description='Application to use domains from DigitalOcean account as dynamic DNS domain(s).\r\nThe app only supports IP4. IPv6 is planned for a later release!', epilog='Making Selfhosting easier...') parser.add_argument('-a', '--api', help='Add/Change API key.', nargs=1, metavar=('APIkey'), required=False, action="append") parser.add_argument('-l', '--list', help='List subdomains for supplied domain.', nargs=1, metavar=('domain'), required=False, action="append") parser.add_argument('-d', '--domains', help='List top domains on your DigitalOcean account.', required=False, action="store_true") parser.add_argument('-c', '--current', help='List the current IP address for the sub-domain given', required=False, nargs=1, action="append") parser.add_argument('-t', '--top', help='Add a new domain from your DigitalOcean account to use as a dynamic DNS domain', required=False, nargs=1, metavar=('domain'), action='append') parser.add_argument('-s', '--sub', help='Add a new subdomain to dynamic DNS DO domain', required=False, nargs=2, metavar=('subdomain', 'domain'), action='append') parser.add_argument('-i', '--info', help='Show current config info', required=False, action='store_true') parser.add_argument('-p', '--ipserver', help='Set IP server lookup to use. Indicate 4 or 6 for IP type.', required=False, nargs=2, metavar=('ip4.iurl.no', '4'), action="append") args = vars(parser.parse_args()) if args['list']: list_sub_domains(args['list'][0][0]) elif args['domains']: show_all_top_domains() elif args['current']: domaininfo(args['current'][0][0]) elif args['top']: add_domian(args['top'][0][0]) elif args['sub']: add_subdomain(args['sub'][0][0],args['sub'][0][1]) #add_subdomain(args['sub'][0][0]) elif args['info']: show_current_info() elif args['ipserver']: ip_server(args['ipserver'][0][0],args['ipserver'][0][1]) elif args['api']: api(args['api'][0][0]) else: get_ip() # get_api()