This will roughly explain how to get wildcard DNS working for free (any-words-here.yoursubdomain.yourdomain.com) on a connection which gets assigned dynamic IPs. I’d like to make sure subdomain.example.com is always up to date with my home IP, and be able to request subdomains (like someword.subdomain.example.com) that resolve to the same IP. These are useful if you want to use multiple virtual hosts in apache hosted from a machine on your home network, accessible to the outside world using a fixed hostname.
Requirements
Your own domain (example.com)
Your have a server on the outside world, running PHP (for this example anyway)
A way of requesting a remote web page automatically
Introduction
Our previous internet provider gave me a static IP (2 actually!) on a standard ADSL broadband connection. I also own a domain (for this, I’ll say example.com) – so I setup a zone specifically for my house (subdomain.example.com) – this was nice and easy using any DNS provider as it never changed. I also created NS records pointing subdomain.example.com at my home IP, so that I could create requests for blah.subdomain.example.com
We’ve recently moved house. We’re now with Virgin, and they don’t provide static IPs – so I thought I could use something like dyndns.com or no-ip.com (which runs a small daemon which updates your IP to point to something like yourname.no-ip.com). However, these don’t support recursive wildcard DNS requests without paying for a subscription (i.e. something.yourname.no-ip.com) so I’d only be able to have a single domain name for my home, which makes using Virtual Hosts in apache not really possible…
Solution!
Find a free DNS host which support an API, and supports wildcard DNS records for a fixed zone. I’ve done the hard work for you, and found Zerigo
Setup a free account on Zerigo and creat a new zone for subdomain.example.com – my primary DNS is hosted elsewhere for example.com, so I just created an NS record for subdomain.example.com on my main DNS to point at a.ns.zerigo.com. You could probably host both on Zerigo if you wanted.
Then, create 2 hosts on this new zone – “” (empty hostname) and “*” (the wildcard host) – give these any old IP; they’ll be updated in a minute!
So, now if you do something like (from your machine):
nslookup banana.subdomain.example.com a.ns.zerigo.com
you should get the IP you’ve given both these hosts. If you do, everything’s working fine.
Now, the best bit about Zerigo is that it has a rather nifty REST api for updating DNS zones and hosts, and they provide a PHP client library along with some fairly simple examples. So I created a small PHP script (using their provided API – thank you!) which just updates your zone to point to the client IP requesting the script, provided it’s not the same as it used to be (with very simple authentication):
The PHP script to do it for you
<?php $WILDCARD_ZONE="subdomain.example.com" // rather nasty "authentication" - does the job though... (and it's fine provided the logs on your server are private!) // just check the MD5 of a GET param is the same as one we're expecting if (isset($_GET["key"]) && "abc123abc123abc123abc123abc123ab"===md5($_GET["key"])) { // get the last IP we updated it to - if it's not the same, write the new one into the file // for use next time. $oldIP=file_get_contents("oldIP"); $remoteIP=$_SERVER['REMOTE_ADDR']; if ($oldIP != $remoteIP) { file_put_contents("oldIP", $remoteIP); } else { // IP hasn't changed, so nothing to do - save superfluous calls to the Zerigo API die("No change: $oldIP, $remoteIP"); } // include the zerigo API require_once("zerigo-dns_api_php/zerigo_ns.php"); ZerigoAPI::$api_user = 'you@zerigouser.com'; ZerigoAPI::$api_key = 'abc123abc123abc123abc123abc123ab'; // get all zones on your account (up to 50 of them anyway) $zones = NSZone::find_all(array('per_page'=>50, 'page'=>1)); // Now get the zone we're trying to update $homeZone = null; foreach ($zones as $zone) { if ($zone->domain == $WILDCARD_ZONE) { $homeZone = $zone; } } // found the zone we want? if ($homeZone) { // update all the hosts that match what we're fudging (i.e. the empty host and the wildcard) foreach ($homeZone->hosts as $host) { if ($host->hostname == "" || $host->hostname == "*") { // bit of debug to go in my cron logs echo "Updating " . $host->hostname . "." . $homeZone->domain; echo " from " . $host->data . " to " . $remoteIP . "<br/>\n"; $host->data = $remoteIP; // call the Zerigo API to update the remote IP for this host if ($host->save()) { echo "Updated.<br/>\n<br/>\n"; } else echo "Failed.<br/>\n<br/>\n"; } } } exit(); } header("Status: 404 Not Found");
(You’ll also need the Zerigo PHP api stuff too, which can be found here: Zerigo PHP Libs)
Getting it to automatically update
I just set a box on my home network to fetch this every now and again – at the moment, I’ve just got a cron job that does it hourly and it keeps my remote IP up to date (with a low (1 hour) TTL) on Zerigo’s DNS servers. I presume there’s a way in Windows to create a scheduled job that does it too, so this method is pretty cross-platform (and because PHP can run on pretty much anything, it works on all client/server setups).
Hooray!
Leave a Reply