Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things

My own dynamic DNS setup

by Corion (Patriarch)
on Mar 24, 2014 at 08:05 UTC ( #1079497=CUFP: print w/replies, xml ) Need Help??

I'm using, but recently they got more obnoxious with their (free!) service, and being a cheapskate who doesn't want to be annoyed, I set up my own dynamic DNS update with my vhost at Hosteurope. I have set up as the DNS server for the zone with the Hosteurope DNS and run my own DNS server on my vhost to serve entries for * The key setup is done according to the many dynamic DNS articles.

The below program reads the external IP address from my UPnP enabled gateway, a FritzBox. It then sends the signed DNS update packet to my DNS server.

The setup is not particularly secure, as the key can be used to update any dynamic IP address in the zone. But for my purpose of having one name map to a dynamic IP address, that's just enough.

#!/usr/bin/perl -w use strict; use vars qw($VERSION); use Net::UPnP::ControlPoint; use Net::UPnP::GW::Gateway; use Net::DNS '0.74'; # Earlier versions had bugs in the TSIG handling use Getopt::Long; use Pod::Usage; $VERSION= '0.01'; GetOptions( 'v|verbose' => \my $verbose, 'k|key:s' => \my $key, 'n|key-name:s' => \my $key_name, 'f|key-file:s' => \my $keyfile, 's|server:s' => \my $server, 'h|hostname:s' => \my $hostname, 'z|zone:s' => \my $zone, 't|ttl:s' => \my $ttl, 'force' => \my $force, 'help' => \my $help, 'man' => \my $man, ) or pod2usage(2); pod2usage(1) if $help; pod2usage(-verbose => 2) if $man; pod2usage("$0: No hostname to update." unless $hostname; sub status { print "@_\n" if $verbose; }; $ttl||= 600; if( ! $zone) { ($zone=$hostname)=~ s!^\w+\.!!; status("Assuming DNS zone is $zone"); }; my $upnp= Net::UPnP::ControlPoint->new(); my $current_address= ''; my $resolver= Net::DNS::Resolver->new(); # Find the relevant nameserver for the zone if( ! $server ) { my $query= $resolver->search($zone,'NS'); if( $query ) { for my $rr (grep { 'NS' eq $_->type } ($query->answer)) { $server= $rr->nsdname; status "Nameserver for zone '$zone' is '$server'."; }; } else { die "Couldn't find a nameserver for zone '$zone'.\n"; }; }; $resolver->nameservers($server); status "Looking up IP for '$hostname'"; my $query= $resolver->search($hostname); if(! $query) { my $err= $resolver->errorstring(); if( 'NXDOMAIN' ne $err ) { die "Lookup of '$hostname' failed: $err\n"; } else { status "Hostname '$hostname' was not found."; }; } else { for my $rr (grep { 'A' eq $_->type } ($query->answer)) { $current_address= $rr->address; status "Hostname '$hostname' resolves to IP address '$current_ +address'"; }; }; status "Searching local network for UPnP-enabled gateways"; my @devices= $upnp->search( st => 'urn:schemas-upnp-org:device:Interne +tGatewayDevice:1', mx => 3 ); foreach my $dev (@devices) { my $type= $dev->getdevicetype; my $gw= Net::UPnP::GW::Gateway->new; $gw->setdevice( $dev ); my $ip_address= $gw->getexternalipaddress; status sprintf "Gateway '%s' has IP address %s", $dev->getfriendly +name, $ip_address; # XXX Do a name lookup and do an early exit if we don't need to up +date if( $current_address eq $ip_address ) { if( $force ) { status "Current IP address and address in DNS are identica +l, but --force is in effect"; } else { status "Current IP address and address in DNS are identica +l, skipping update"; exit 0; }; }; my $update = Net::DNS::Update->new($zone); $update->push( update => rr_del("$hostname. A") ); $update->push( update => rr_add("$hostname. 600 A $ip_address") ); if( $keyfile ) { status "Signing from key file $keyfile"; $update->sign_tsig( $keyfile ); } else { status "Signing from command line with key named $key_name"; $update->sign_tsig( $key_name, $key ); }; status "Updating IP for $hostname on $server to $ip_address"; status $update->string if $verbose; my $reply= $resolver->send( $update ); if( $reply ) { if( 'NOERROR' eq $reply->header->rcode) { status "Success"; } else { die "Update failed: " . $reply->header->rcode . "\n"; }; } else { die "Update failed: " . $resolver->errorstring . "\n"; }; }; __END__ =head1 NAME update-wan-ip - dynamic DNS update from an UPnP enabled gateway =head1 SYNOPSIS update-wan-ip [options] update-wan-ip -h HOSTNAME -k KEYFILE update-wan-ip -h -k /root/Kdyn-example-com+157+ +12345.key Options: --hostname, -h --key-file, -f --key, -k --key-name, -n --force --verbose --help --man =head1 OPTIONS =over 4 =item B<--hostname>, B<-h> --hostname Set the hostname you want to update. =item B<--key-file>, B<-k> --key-file /root/Kdyn-example-com+157+12345.key Name of the keyfile that contains the key for the DNS updates. Note that the filename also carries the name of the DNS key. This is i +mportant and must match the name of the DNS key in your DNS server. =item B<--key-name>, B<-n> --key-name dyn-example-com Name of the key that is used for the DNS update. If you don't use a keyfile, for example while testing the setup, you can specify the name of the key through this switch. This does not override a name given through a keyfile. =item B<--key>, B<-k> --key AAANN3...== The key that is used for the DNS update. If you don't use a keyfile, for example while testing the setup, you can specify the key through this switch. =back =head1 TROUBLESHOOTING Test with the C<nsupdate> command whether DNS updates work at all: nsupdate -y key-name:key server zone update add 600 A show send Check that both machines have the same time. If not, install C<ntp> on both machines. =cut

Replies are listed 'Best First'.
Re: My own dynamic DNS setup
by Theodore (Friar) on Apr 11, 2014 at 13:01 UTC
    And here is my own perverted pair of scripts, with IPv6/AAAA support :-)

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: CUFP [id://1079497]
Approved by BrowserUk
Front-paged by Arunbear
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others meditating upon the Monastery: (3)
As of 2023-05-31 16:34 GMT
Find Nodes?
    Voting Booth?

    No recent polls found