In a local area network, dynamic DNS (DNS UPDATE) enables workstations to automatically register the IP address obtained via DHCP with the DNS server. This is usually handled by a cooperating DHCP server, and you can use the same technology to update the DNS with almost any information you want to. This short article shows you how to do that.

We'll need several things for this:

  • A BIND name server. (BTW, MyDNS also supports dynamic updates.)
  • A zone reserved for performing dynamic DNS updates. If you don't have a suitable zone, you can always take the proverbial example.{com|net|org} or chose from the list of reserved ISO 3166-1 codes, e.g. .aa.
  • Data you want to store, and
  • a small Perl program (or the nsupdate utility).

I'm assuming you have a BIND server set up already, and that you've got that running. There is a lot of literature on the subject. The zone I'm creating for this purpose is called temp.aa -- temp as in temperature.

The first thing you have to do is to generate a TSIG key, using the dnssec-keygen utility supplied as part of the BIND utilities.

dnssec-keygen -a hmac-md5 -b 128 -n HOST weather.daemon.

This creates two files in the current directory: their names begin with a capital K and contain the "host name" we specified on the command line. Concentrate on the .key file which is all we need. It contains a shared key we'll be using on both the update-client and the BIND server, and it looks like this:

weather.daemon. IN KEY 512 3 157 43JQzgL3OhwFsm/ZiFFPzQ==

You'll notice (I hope) that that looks like a DNS resource record. The bit we are now interested in is the rdata -- the base-64-encoded blob on the right. This is the key we need on both the name server and on the client program which will be sending DNS updates to the zone. Tip: if you don't feel like going through all that mess, simply create your own base64-encoded data with a tool like mmencode or openssl:

echo hello-world | openssl enc -a
    aGVsbG8td29ybGQ=

Configure name server

We now want to allow one or more clients to perform dynamic DNS updates on our zone, which we do by configuring a key in named.conf and allowing a particular zone (called temp.aa here) to be updated dynamically. The key definition is easy enough: it gets a name (weather.daemon) an algorithm and the key data -- the base64-encoded blob we generated above.

key "weather.daemon" {
       algorithm HMAC-MD5;
       secret "43JQzgL3OhwFsm/ZiFFPzQ==";
    };

The zone proper is then configured to allow updates by clients with that key with an allow-update clause, in which you can specify address lists or keys that may used to perform updates to this particular zone. (Look here if you need a refresher on zone file format.)

zone "temp.aa" IN {
        type master;
        file "temp.aa";
        allow-transfer { any; };
        also-notify { 192.168.1.182; };
    
        allow-update { 
            key "weather.daemon";
        };
    };

Instead of allowing any old update to the zone by that key, you may wish to grant permissions in a more granular way, and you can do so with the update- policy statement, which is used instead of the allow-update statement. Consider you have a user who wants to update an A record whenever she wants to; we can create a separate key for that user and grant an update to her domain for a particular resource record:

update-policy {
        grant "janes-key" subdomain jane-laptop.temp.aa A;
    };

With this, Jane would be allowed to update only the domain jane- laptop.temp.aa, and only if the update was for an A RR; all other types (and updates on other domains) would be rejected by the server.

If there is some mistake in the key data the client will not be authorized to perform the update. Similarly, the names of the keys must be identical! Check your named logs for errors such as

client 192.168.1.145#57783: request has invalid signature: tsig verify failure

Remember that the shared TSIG key we created above should be protected; anybody in possession of that may update your zone (if he or she also knows the key's name).

What to update?

What are we going to update the DNS with? As an example, I'm going to add a DNS resource record called city.temp.aa, with city being the name of the town I'd like to be in. The resource record will be a TXT record containing the current temperature. But where do I get that from? I'm going to use the Yahoo! Weather API to retrieve the current temperature of the location(s) I'm interested in. I could also use Google's not-very-well-documented (a.k.a. secret) weather API. (Read about that here.)

In order to access the XML feed of the Yahoo! Weather service, we need a Where On Earth IDentifier, a WOEID, of the location in question. To find you WOEID, head on to the Yahoo! Weather page and browse or search for your location. When you've found it (suppose we're interested in Ibiza, Spain) take note of the integer in the target URL. It'll look like

http://weather.yahoo.com/spain/balearic-islands/ibiza-762509/

The number 762509 is the WOEID. (There is also a full API through which you can search for a WOEID, but you have to sign up for that.)

So, we now simply grab the XML from the following URL, substituting the WOEID with the one we want. (Specify the units as either f for Fahrenheit or c for Celsius, as in http://weather.yahooapis.com/forecastrss?w=762509&u=c ) Now we can search our way through the XML looking for the data we're interested in (the code below shows one way of doing that).

Update client

What I'll then do is to use the excellent Net::DNS::Update Perl module(s) to create our updater. Here it is:

#!/usr/bin/perl
    
    use strict;
    use Net::DNS;
    use Net::DNS::Update;
    use XML::Simple;
    use LWP::Simple;
    
    my $zone = 'temp.aa';
    my $mname = '192.168.1.20';
    my $key_name = 'weather.daemon';
    my $key = '43JQzgL3OhwFsm/ZiFFPzQ==';
    my $WOEID = '762509'; # Ibiza
    
    my $scale = 'c';    # 'f'
    my $URL = "http://weather.yahooapis.com/forecastrss?w=${WOEID}&u=${scale}";
    
    my $content = get($URL) or
        die "Couldn't get weather API!";
    
    my $ref = XMLin($content);
    
    
    my $item = $ref->{'channel'}->{'item'};
    my $unit = $ref->{'channel'}->{'yweather:units'}->{'temperature'};
    my $city = $ref->{'channel'}->{'yweather:location'}->{'city'};
    my $temp = $item->{'yweather:condition'}->{'text'} . " " .
            $item->{'yweather:condition'}->{'temp'} . " " .
            $unit;
    
    print qq($city, $temp ), "\n";
    
    dns_update($city, $temp);
    
    sub dns_update() {
        my ($domain, $rdata) = @_;
    
        my $fqdn = lc "${domain}.${zone}";
    
    
        # Create the update packet.
        my $update = Net::DNS::Update->new($zone);
    
        # Delete existing TXT RR
        $update->push(update => rr_del("$fqdn TXT"));
    
        # Add the TXT RR
        $update->push(update => rr_add("$fqdn 120 TXT \"$rdata\""));
    
        # Sign the update packet
        $update->sign_tsig($key_name, $key);
     
        # Send the update to the zone's primary master.
        my $res = Net::DNS::Resolver->new;
        $res->nameservers($mname);
     
        my $reply = $res->send($update);
     
        if ($reply) {
            if ($reply->header->rcode ne 'NOERROR') {
                print "Update of $fqdn failed: ", $reply->header->rcode, "\n";
            }
        } else {
            print "Update of $fqdn failed: ", $res->errorstring, "\n";
        }
    }

(One word of caution: I haven't checked, so you might be wary of presenting this data via the DNS to the wide world: I assume it is property of Yahoo!, meaning you can probably use it for yourself but shouldn't publish it.)

When I run above program, I see:

Ibiza, Partly Cloudy 22 C

and when I query the DNS, I get the following data:

dig ibiza.temp.aa. txt
    ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 8830
    ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1
    
    
    ;; ANSWER SECTION:
    ibiza.temp.aa. 120     IN  TXT  "Partly Cloudy 22 C"
    ...

Can I add more cities? Of course I can! :-)

Dynamically updated zones, such as the one created above, can of course also be master zones. An update causes the SOA of the zone to be incremented, and slave servers are NOTIFY'ied to transfer the zone.

What you add to your DNS is up to you. I've previously discussed how the DNS can be used to store and distribute information such as application or data version numbers, and adding data via a simple little Perl program can be quite useful.

Flattr this
DNS, CLI, dnsbook, weather, and dynamic :: 28 Sep 2010 :: e-mail

Comments

blog comments powered by Disqus