Mobile Device Web Site Testing? Try Dynamic DNS

If you’re developing responsive web sites and applications these days, I’d guess you’re familiar with the little niggles involved in getting mobile devices to connect to your development machine. If you use a desktop machine to host your development system then you might assign it a static IP address and access it that way, but if you develop on a laptop you need to deal with the possibility of your laptop’s IP address changing as you move from network to network (e.g. from home to work and back again).

This is where services like xip.io come in. If you know the IP address that your laptop currently has, you can use a URL with a xip.io domain name to connect to your web server over the local network. The approach is baked-in to pow - it automatically creates xip.io aliases for the virtual hosts for each of your applications.

One minor annoyance with this approach is the need to change the URL when you move from one network to another. If you pack up and head home halfway through a debugging session you’ll find all the pages you have opened on your mobile devices stop working when you’re on a different network and your laptop has a different IP address.

And what if you don’t use pow? I build Ruby on Rails applications on a laptop running Ubuntu, where pow won’t run. Instead I use Phusion Passenger to manage my local applications under development, using the default Ubuntu Apache 2 packages as a basis. For each application that I’m working on I create an Apache configuration file in /etc/apache2/sites-available, usually by copying an existing one from a similar application. To use xip.io I’d need to update these files whenever the IP address allocated to my laptop changed.

It’s Just Dynamic DNS, Really

What xip.io provides is a mapping from a known hostname in DNS to the IP address for your laptop, albeit with the unfortunate side-effect of needing a different hostname when you move network. But there’s another well-known service out there that has been doing pretty much the same thing for years: Dynamic DNS.

Dynamic DNS is used to give a fixed hostname to a network device with a dynamically allocated address; its most common use these days is probably on consumer broadband connections where you want to be able to access some computer or service at home while you’re out and about. I use the Free DNS service from afraid.org for just this purpose.

But we can also use the same service to give a name to our development laptop’s (dynamic) network connection. If we arrange for our laptop to update a dynamic DNS entry with its address on the local network, then we can use that hostname on our mobile devices. Even better, the hostname won’t change when we move between networks.

System Administration is (sometimes) Debugging

I knew from past forays into the Ubuntu networking system that the system will run scripts in /etc/network/if-up.d when a network interface comes up, so I knew I probably just needed to add a script to update the IP address for a dynamic DNS entry. What I didn’t know was what information was made available to the script when it runs, and a couple of minutes of searching didn’t turn up the documentation. But one of the nice things about a Unix system is that you can sometimes treat system administration as a debugging problem, and apply tools and techniques from that domain. Here’s my first attempt at a dynamic DNS update script:

#!/bin/sh

set -e

exec >/tmp/ddu.log 2>&1

env
for i in $*
do
echo $i
done

The first couple of lines I copied from the other scripts in the directory: set -e causes the script to exit immediately if any command fails. The exec line redirects the output and error streams for the script to a file so we can see what’s going on - print debugging for Unix scripts. I put this in /etc/network/if-up.d/dynamic-dns-update and cycled the wireless network connection using NetworkManager (clicking on the currently active wireless network will force a disconnect and then a reconnect). After this, /tmp/ddu.log contained the following, amongst other things:

METHOD=NetworkManager
MODE=start
PHASE=post-up
DHCP4_IP_ADDRESS=192.168.1.89
IFACE=wlan0
# [ rest of environment omitted ]

This told me a few things:

  1. NetworkManager is hooked up to the standard Debian networking scripts sufficiently to get an if-up.d script to run.
  2. The environment contains exactly the information I’d need to update the Dynamic DNS service, including the IP address.
  3. There were no command line arguments passed to the script, because the file only contained lines that looked like they came from env. I initially thought that the IP address might be passed as a command line argument, but this experiment showed it was in the environment instead.

Hooking Things Together

My next step was to create a new dynamic DNS update candidate hostname here and change the script to make a request to Free DNS:

#!/bin/sh

set -e

exec >/tmp/ddu.log 2>&1

update='http://freedns.afraid.org/dynamic/update.php'
id='id from web site'

curl -o - "${update}?${id}&address=${DHCP4_IP_ADDRESS}"

The id value is the one that you find embedded in the ‘Direct URL’ link on the dynamic DNS page. It’s a unique id that is specific to the hostname you’re updating. I also add the IP address for the laptop to the request, otherwise the service will use the IP address that it sees the request coming from - probably the Internet side of your router if you are behind a NAT firewall, and not what we want here. I left the exec line in so I could check for errors if things didn’t work, but happily they did - my new domain name was updated correctly, taking the address of my laptop on the private network.

But What If It Goes Wrong?

The script was working, but it needed a bit of tidying up before I could leave it: firstly, I wanted to tighten the script up a bit so it only did the update if it was really the wireless interface coming up. Secondly, it was dumping debugging information into /tmp - status messages in the system log would be cleaner. I changed the exec line to the following to check whether any output redirection or logging was provided to the script:

( ls -l /proc/$$/fd >/tmp/ddu.log 2>&1 )

After cycling the wireless connection again the output in /tmp/ddu.log was:

total 0
lr-x------ 1 root root 64 May 28 13:06 0 -> /dev/null
lrwx------ 1 root root 64 May 28 13:06 1 -> /dev/null
lr-x------ 1 root root 64 May 28 13:06 10 -> /etc/network/if-up.d/dynamic-dns-update
lrwx------ 1 root root 64 May 28 13:06 2 -> /dev/null

which tells us that whatever runs our script is going sending any output it produces to /dev/null (i.e. throwing it away).

Adding entries to the system log is pretty easy though: the logger command will do it for us. Here’s the final version of the script:

#!/bin/sh

set -e

[ "$PHASE" = "post-up" ] || exit 0
[ "$IFACE" = "wlan0" ] || exit 0

update='http://freedns.afraid.org/dynamic/update.php'
id='id from web site'

curl -o - "${update}?${id}&address=${DHCP4_IP_ADDRESS}" | logger -t $0 -p syslog.info

Here we’re checking that the script is being executed because the wireless connection is coming up, and we send the output from curl to the system log, where it comes out like this:

May 28 09:42:37 sputnik /etc/network/if-up.d/dynamic-dns-update: Updated [hostname] to 192.168.1.89 in 0.764 seconds

Success!

28 May 2014