kaashif's blog

Programming, software freedom and Unix


Updating DNS records

When running a website on a residential connection, a problem one might run into is the dynamic IP address usually assigned by one’s ISP. There are a few dynamic DNS services which basically let you have a subdomain (e.g. mydomain.example.com) and let you update it to point to your IP address whenever it changes. At one time, your IP might be 10.0.0.1, and your domain correctly

resolves to 10.0.0.1, but after you reboot your modem or router, your IP may change to 10.0.1.2 and your domain will still point to your old IP (thus won’t work), until your dynamic DNS client somehow updates the A record of your domain when your IP changes.

Assuming that getting a static IP is impossible, there is only one solution: dynamic DNS. While it is possible to transfer your records to someone other than your domain registrar to manage DNS records, I just keep all things related to my domain with Namecheap, which is where my domain was registered.

Choosing a client

Several websites, DynDNS and NoIP being examples, offer their own clients. You can install these, put your site-specific username and password into a config file, and let your DNS be handled automatically. Usually, these programs can do a lot more than update your DNS, and are needlessly complicated if that’s all you want to do. The same goes for most multi-service dynamic DNS clients, for example, ddclient.

Most services provide a URL with which you can update your A records. Fetch this URL, and the A records on your domain will be changed to match the IP you fetched the URL with. It’s a very simple system, and can be automated within minutes, with a simple script. Because I like to keep my servers bare, and avoid installing anything bulky (like Python, Ruby, and the like), I’m going to ues Perl, which is in the base system of every good *BSD, and comes installed by default on most GNU/Linux distros.

Writing the client

There is some Perl boilerplate we have to get out of the way - the shebang and some “use” statements.

#!/usr/bin/env perl
use strict;
use warnings;

It’s worth commenting on my use of “env”. On FreeBSD (the OS my servers use), Perl is located at /usr/local/bin/perl, while on most GNU/Linux systems, it’s at /usr/bin/perl. Using “env” avoids the issue of finding the Perl binary, and is more cross-platform.

Next, we have to find the URL, password, hosts, and domain we will use. Namecheap uses the word “host” to mean the subdomain, e.g. the “www” part of “www.fsf.com”. For this example, let’s just say we want “ftp”, “www” and "@", "@" being no subdomain, e.g. “mydomain.com”.

my $password = "blahblahblah";
my $domain = "mydomain.com";
my @hosts = ("ftp", "www", "@");
my $update_url = "http://dynamicdns.com/update?domain=$domain&password=$password&host=";

Notice we didn’t attempt to put the hosts into the initial definition of the update URL. We will do that when we loop over the array and fetch the URL using cURL, thus updating the domain.

foreach(@hosts)
{
	my $final_url = "$update_url$_";
	my $output = `curl -s "$final_url"`;

So far, we have fetched the URL, so the IP of the host’s A record should be updated at this point. We still need to check for errors and such, so let’s do that next.

	if ($output =~ /<ErrCount>0/)
	{
		print("Update of $_.$domain succeeded.");
	}
	else
	{
		print("Update of $_.$domain failed!");
	}
}

It’s possible to use XML::Simple and actually parse the XML output by the server handling DNS updates, but that would be overkill and waste everyone’s time. So I just regex the output for errors, it works fine, and the problem is generally obvious if it fails, which is why I didn’t print the output. It’s perfectly possible to add some verbosity and do all of this “correct” stuff, but no-one, not even you, will ever care (unless you just do it for practice).

So save this script in /usr/local/bin/dnsup, or somewhere else appropriate and memorable.

Scheduling updates

Your IP address can change instantly at any moment, so you must balance the downtime users experience with the practical considerations of running a script repeatedly. Personally, I run the script every 10 minutes. My IP only seems to change once in a blue moon, and when it does (due to me rebooting the firewall or router), I tend to notice right away, get impatient, and run the script manually. That’s beside the point, though, just add this to your /etc/crontab to make it run every 10 minutes.

*/10 * * * * nobody /usr/local/bin/dnsup

I run it as “nobody” to avoid any unnecessary root usage. What if you made a mistake in the script and overwrote / with garbage? A program having privileges is bad, don’t do it.

At this point, you should be free of any possibly proprietary, bloated DNS clients and you should have an automatically updating domain.