Dynamic DNS with NearlyFreeSpeech.NET and Python

Update (14 April 2016): I've since rewritten this script - you can find the new version in this post.

Since I got it, one of the things I've been looking to do with my Raspberry Pi is to access it remotely. This is a pretty easy task - give the Pi a static IP and forward the appropriate port on the router. However, my ISP assigns a dynamic IP address and having to check what my IP is before being able to connect is tiresome. On top of that IP addresses aren't particularly memorable so assigning a domain or subdomain would make it much easier to use.

If your ISP offers the option, the solution is as simple as requesting a static IP address and assigning the subdomain to that IP. Unfortunately my ISP only gives static addresses to business customers and the extra $30/month isn't really worth it just for the static address.

Another option is to use a dynamic DNS service. They've been around for ages - I remember using one to run games of Total Annihilation over a 56k modem - but you have to either put up with the domain offered to you or pay a fee for their service. Since I already have DNS provided by NearlyFreeSpeech.NET I wondered if I could set up some sort of dynamic DNS through them and promptly stumbled upon a post by Joshua Arrington in which he did exactly that using the NFS.NET API and a script written in PHP.

Although the solution was a good one, there were a couple of things I wanted to change. Firstly, my site is static and switching it to a dynamic site just to implement one thing seemed a bit like overkill. I also preferred the idea of running the script from a machine on my network rather than having to secure a server-side script. Just to make things more difficult for myself, I wanted to avoid installing PHP and instead opted to port the script to Python as it's supported out-of-the-box on the Raspbian OS. Making this much easier was the fact that the pynfsn library implements the DNS part of the API.

If you're interested in doing the same thing, these are the steps you need to follow:

$ git clone https://github.com/goncalopp/pynfsn.git
$ cd pynfsn
$ python setup.py install # you may need to run this using sudo
#! /usr/bin/env python

from pynfsn import pynfsn
import re
from urllib2 import urlopen
from time import strftime, localtime

user = "user" # your NFS username (not the one used to ssh into your site)
key = "key" # API key (contact NFS for one)
domain = "domain" # your NFS-hosted domain
subdomain = "subdomain" # the subdomain you're setting up for dDNS
ttl = 3600 # this is the default value (one hour)

api = pynfsn.NFSN(user, key).dns(domain)
currentip = re.findall(r"[0-9]+(?:\.[0-9]+){3}", urlopen('http://checkip.dyndns.org/').read())[0]
listedip = api.listRRs(subdomain)[0]['data'].encode('ascii', 'ignore')
logtime = strftime("%Y-%m-%d %H%M", localtime())

if currentip != listedip: # check to see if the ip has changed; if so:
    api.removeRR(subdomain, 'A', listedip) # a) remove the old entry, and
    api.addRR(subdomain, 'A', currentip, ttl) # b) add a new one
    print (logtime + " DNS record updated from " + listedip + " to " + currentip)
else:
    print (logtime + " No update required.")
0 */2 * * * /usr/local/bin/nfsddns.py >> /var/log/nfsddns.log 2>&1

So far, this has been working well for me although the occasional 'Connection reset by peer' error shows up in the log file. I'm not entirely sure what's causing this; sometimes it happens twice in a row but the next four times the script runs fine. It's not a major problem as the script generally runs fine the next time and removing the comparison between the listed and current IPs should stop it completely.

This is the first program I've written in Python, so if you do use it please be aware that there may be security or other issues that I haven't picked up. Please let me know if I've missed something obvious!

Update (9 January 2015): Switched the order of the IP address variables in the output as they were back-to-front. Thanks to Paul for spotting it and letting me know.