A simple dynamic DNS solution to be used with Digital Ocean's DNS service. Get the API key from you DO account. https://blog.rune.pm
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

189 lines
7.5 KiB

#!/usr/bin/python3
import urllib.request
import configparser
import json
import logging
import argparse
import requests
import os
import time
import smtplib
import ssl
import datetime
import email
from email.message import EmailMessage
from email import utils
# Config stuff
config = configparser.ConfigParser()
config.read('/Users/rune/Seafile/Seafile/Live/Live/Documents/Code/Python/py-dyndns/config.ini')
config_file = '/home/rune/.config/dyndns/config.ini'
api = config['DYNDNS']['api_key']
ip4 = config['DYNDNS']['ip4']
ip6 = config['DYNDNS']['ip6']
domain_name = config['DYNDNS']['baseuri']
logging.basicConfig(filename=config['DYNDNS']['logfile'],level=logging.INFO)
local_ip4 = urllib.request.urlopen("https://ip4.iurl.no").read().decode('utf-8')
# The program
def updateip(ip, domain):
data = {'data': ip}
headers = {'Authorization': 'Bearer ' + api, "Content-Type": "application/json"}
response = requests.put('https://api.digitalocean.com/v2/domains/'+domain_name+'/records/' + domain, data=json.dumps(data), headers=headers)
return str(response)
def createnewdomain(name, ip):
data = {'name': name[0],
'data': ip,
'type': "A",
'ttl': 3600
}
headers = {'Authorization': 'Bearer ' + api, "Content-Type": "application/json"}
response = requests.post('https://api.digitalocean.com/v2/domains/' + domain_name + '/records',
data=json.dumps(data), headers=headers)
print(str(response))
if str(response) == '<Response [201]>':
return response.json()
else:
return 'Fail'
def runupdate():
for section in config.sections():
if not config.has_option(section, 'subdomainid'):
continue
for name, value in config.items(section):
req = urllib.request.Request('https://api.digitalocean.com/v2/domains/' + domain_name + '/records/' +
value)
req.add_header('Content-Type', 'application/json')
req.add_header('Authorization', 'Bearer ' + api)
current = urllib.request.urlopen(req)
remote = current.read().decode('utf-8')
remoteData = json.loads(remote)
remoteIP4 = remoteData['domain_record']['data']
if remoteIP4 != local_ip4:
response = updateip(local_ip4, value)
if '200' in response:
logging.info(time.strftime("%Y-%m-%d %H:%M")+' - Success! Domain %s updated with IP: %s (old IP: %s)', section,local_ip4,remoteIP4)
else:
logging.error('Failure! '+response)
else:
logging.info(time.strftime("%Y-%m-%d %H:%M")+' - No need to update the IP! Local IP ('+local_ip4+') for '+section+' is the same as '
'Remote IP ('+remoteIP4+')')
def listdomains():
LocalDonains = []
for section in config.sections():
if not config.has_option(section, 'subdomainid'):
continue
for subdomainid, value in config.items(section, 'subdomainid'):
LocalDonains.append(value)
req = urllib.request.Request('https://api.digitalocean.com/v2/domains/' + domain_name + '/records/')
req.add_header('Content-Type', 'application/json')
req.add_header('Authorization', 'Bearer ' + api)
current = urllib.request.urlopen(req)
remote = current.read().decode('utf-8')
remoteData = json.loads(remote)
print('Domains in config.ini are marked with a [+]')
print('================================================')
for k in remoteData["domain_records"]:
if str(k['id']) in LocalDonains:
print('[+] Name : '+k['name']+' Id : '+str(k['id'])+' (IP: '+str(k['data'])+')')
else:
print('Name : '+k['name']+' Id : '+str(k['id'])+' (IP: '+str(k['data'])+')')
def listcurrent():
print('Current local IP :'+local_ip4)
print('=======================================')
for section in config.sections():
if not config.has_option(section, 'subdomainid'):
continue
for name, value in config.items(section):
req = urllib.request.Request('https://api.digitalocean.com/v2/domains/' + domain_name + '/records/' +
value)
req.add_header('Content-Type', 'application/json')
req.add_header('Authorization', 'Bearer ' + api)
current = urllib.request.urlopen(req)
remote = current.read().decode('utf-8')
remoteData = json.loads(remote)
remoteIP4 = remoteData['domain_record']['data']
if remoteIP4 != local_ip4:
print(section+' : '+remoteIP4+' Update is needed!')
else:
print(section+' : '+remoteIP4)
def addnewdomin(newdata):
currentlocalip = urllib.request.urlopen("http://ip4.iurl.no").read().decode('utf-8')
response = createnewdomain(newdata['add'][0], currentlocalip)
if response != 'Fail':
logging.info('Success! Domain %s updated with ID: %s', newdata['add'][0], response['domain_record']['id'])
domainid = str(response['domain_record']['id'])
section = str(newdata['add'][0])
section = section.strip("[']")
config[section] = {
'subdomainid': str(domainid)
}
with open(config_file, 'w') as f:
config.write(f)
print('Domain ' + str(section) + ' added with ID ' + str(domainid) + ' and config.ini updated!')
else:
print('ERROR! Domain not added!')
def sendmail(themail, title, newdata):
now = datetime.datetime.now()
today = str(now.strftime("%Y-%m-%d %H:%M"))
msg = EmailMessage()
msg['Subject'] = f'{title} ' + today
msg['To'] = newdata['send'][0]
msg['From'] = config['DYNDNS']['smtp_from']
msg['Date'] = email.utils.formatdate()
msg['Return-Path'] = config['DYNDNS']['smtp_from']
port = 587 # For starttls
smtp_server = config['DYNDNS']['smtp_server']
login_email = config['DYNDNS']['smtp_user']
password = config['DYNDNS']['smtp_pass']
msg.set_content(themail)
context = ssl.create_default_context()
with smtplib.SMTP(smtp_server, port) as server:
server.ehlo() # Can be omitted
server.starttls(context=context)
server.ehlo() # Can be omitted
server.login(login_email, password)
server.send_message(msg)
def sendlog(newdata):
with open(config['DYNDNS']['logfile'], 'r') as log:
speedlog = log.read()
sendmail(speedlog, 'Log files for dynDNS', newdata)
# Commandline arguments
parser = argparse.ArgumentParser(description='DigitalOcean Dynamic DNS')
parser.add_argument('-l', '--list', help='List the available sub-domains on the domain supplied in the '
'config.ini file.', required=False, action="store_true")
parser.add_argument('-c', '--current', help='List the current IP address for the sub-domains in the config.ini file',
required=False, action="store_true")
parser.add_argument('-a', '--add', help='Add a new domain to your DigitalOcean account and to the config.ini file', required=False, type=str, nargs=1, action='append')
parser.add_argument('-s', '--send', help='Send the logfile to an email address.', required=False, type=str, nargs=1, action='append')
args = vars(parser.parse_args())
if args['list']:
listdomains()
elif args['current']:
listcurrent()
elif args['add']:
addnewdomin(args)
elif args['send']:
sendlog(args)
else:
runupdate()