Browse Source

Added cli app + some small changes...

Rune Olsen 10 months ago
3 changed files with 93 additions and 4 deletions
  1. +1
  2. +22
  3. +70

+ 1
- 1
.gitignore View File

@@ -31,7 +31,7 @@ MANIFEST

# PyInstaller
# Usually these files are written by a python script from a template

+ 22
- 3 View File

@@ -11,7 +11,8 @@ DO DynDNS is a small Python app that can:
### Installation
* Download the _tar.gz_ file from the [relase page](
* Untar it and run `./` (remember to read/review the with a text editor)
* The command will copy the file to `/usr/local/bin` and create a .desktop file in `/usr/share/applications/`
* The command will copy the files (`dyndns` and `dyndns-cli`) to `/usr/local/bin` and create a .desktop file
in `/usr/share/applications/` for the main application (`dyndns`)
* Run by executing dyndns from terminal or by searching for it in your DM.

The first run will create two files:
@@ -34,8 +35,26 @@ The logfile can be named anything, but the `~/.config/dyndns/dyndns.log` was cr

The `.desktop` file will look for an icon "dyndns.png" in `~/.config/dyndns/`. You can copy your own icon to that folder and name it "dyndns.png" or you can copy the icon in the un-zipped folder. The application itself will also look for "dyndns.png" in the folder `~/.config/dyndns/`

### dyndns-cli
This is the companion app for the gui app. This is meant to be executed via `crontab`.

It uses the same `config.ini` file as the main app and updates **all** the domains found in the config file.

Edit crontab by executing crontab -e and add the app to it.

Eg. to run once every other hour add the following line to your crontab:

`0 */2 * * * dyndns-cli >/dev/null 2>&1`


* Create a companion app that can be executed in crontab.
* ~~Create a companion app that can be executed in crontab.~~
* Add functionality to delete a record from DigitalOcean Network service.
* **Maybe** add IPv6 functionality...
* **Maybe** add IPv6 functionality...

If you want to run a dynamic DNS solution on a server (eg. not you main machine)
you could use the standalone command line (cli) application [py-dyndns](
This application uses its own config.ini on the machine it is running on. Follow the
instructions on the app's git page.

+ 70
- 0 View File

@@ -0,0 +1,70 @@
import sys
import urllib.request
import configparser
import json
import logging
import argparse
import requests
import os
import time

# Config stuff
linuxfilepath = '~/.config/dyndns/'
filepath = os.path.expanduser(linuxfilepath)
config = configparser.ConfigParser()
local_ip4 = urllib.request.urlopen("").read().decode('utf-8')

try: + 'config.ini')
api = config['DYNDNS']['api_key']
domain_name = config['DYNDNS']['baseuri']
if not os.path.exists(filepath + config['DYNDNS']['logfile']):
open(filepath + config['DYNDNS']['logfile'], 'a').close()
logging.basicConfig(filename=filepath + config['DYNDNS']['logfile'], level=logging.INFO)
except KeyError:
logging.basicConfig(filename=filepath + 'dyndns.log', level=logging.INFO)
logging.error(time.strftime("%Y-%m-%d %H:%M") + ' Missing config.ini in ' + filepath)
print('Woops!\n\nMissing config.ini file!\n\nPlease execute the main \'dyndns\' application to create '
'the default config.ini file, then edit this file before starting the application again!')

# The program

def update_ip(ip, domain):
data = {'data': ip}
headers = {'Authorization': 'Bearer ' + api, "Content-Type": "application/json"}
response = requests.put('' + domain_name + '/records/' + domain,
data=json.dumps(data), headers=headers)
return str(response)

def run_update():
for section in config.sections():
if not config.has_option(section, 'subdomainid'):
for name, value in config.items(section):
req = urllib.request.Request('' + domain_name + '/records/' +
req.add_header('Content-Type', 'application/json')
req.add_header('Authorization', 'Bearer ' + api)
current = urllib.request.urlopen(req)
remote ='utf-8')
remoteData = json.loads(remote)
remoteIP4 = remoteData['domain_record']['data']
if remoteIP4 != local_ip4:
response = update_ip(local_ip4, value)
if '200' in response:"%Y-%m-%d %H:%M") +
' - Success! Domain %s updated with IP: %s (old IP: %s)', section, local_ip4,
logging.error('Failure! ' + response)
else:"%Y-%m-%d %H:%M") + ' - No need to update the IP! Local IP ('
+ local_ip4 + ') for ' + section + ' is the same as Remote IP (' + remoteIP4 + ')')