Skip to content

Instantly share code, notes, and snippets.

@nall
Last active November 22, 2019 14:14
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save nall/5332833 to your computer and use it in GitHub Desktop.
Save nall/5332833 to your computer and use it in GitHub Desktop.
Crontab script for Synology boxes. Updaes a DNS BIND server when hostname or WAN IP changes. Requires the Synology DNSServer package to be installed (but doesn't need to be running).
#!/opt/bin/perl
use warnings;
use strict;
# Copyright (c) 2013 Jon Nall, STUNTAZ!!! All rights reserved.
#
# This script is intended to be run on a Synology NAS. It attempts
# to determine the external IP where the NAS is connected and then
# checks to see if it needs to update a DNS record. The server is
# updated if any of these are true:
# * The file /var/tmp/ddns_nsupdate.ip doesn't exist
# * The determined external IP is different since the last time the script was run
# * The requested dynamic hostname is different since the last time the script was run
#
# If an update is required, nsupdate is invoked to remove any prior A records
# on the DNS server and a new A record is written
#
# nsupdate is provided with the Synology DNSServer package, so that must be
# installed, but you don't need to actually run it or modify your firewall to
# make the DNS port on the NAS available
#
# The only items you should need to update are the fields in the %OPT hash.
# Generating TSIG keys is beyond the scope of this documentation, but this
# might help:
# http://linux.yyz.us/nsupdate
#
# Crontab
# Add this to your crontab by editing /etc/crontab and reloading it with
# /usr/syno/etc/rc.d/S04crond.sh stop
# /usr/syno/etc/rc.d/S04crond.sh start
#
my %OPT =
(
Debug => 0,
Volume => "YOUR VOLUME WHERE DNSServer IS INSTALLED", # (e.g., /volume1)
TSIG_Key => 'YOUR TSIG KEY',
TSIG_Secret => 'YOUR TSIG SECRET',
DDNS_Hostname => 'YOUR FULLY QUALIFIED DDNS HOSTNAME', # e.g., my-dyn-host.example.com
DDNS_TTL => 'YOUR RECORD TTL VALUE', # e.g., 86400 (1 day),
);
&main();
exit(0);
sub printD(@)
{
return unless($OPT{Debug});
print STDERR @_;
}
sub getWanIP()
{
my $DDNSD = '/usr/syno/sbin/ddnsd';
my $wan_ip = undef;
printD "Determining WAN IP...\n";
my $ip_output = `$DDNSD -e`;
chomp($ip_output);
if($ip_output =~ /(\d+\.\d+\.\d+\.\d+)/)
{
$wan_ip = $1;
}
printD "Found current WAN IP '$wan_ip' ($ip_output)\n";
die "Cannot find WAN IP from '$ip_output'\n" if(!defined $wan_ip);
return $wan_ip;
}
sub needsUpdate($$)
{
my $curIP = shift;
my $hostname = shift;
my $lastIP = undef;
my $lastHostname = undef;
my $STATE_FILE = '/var/tmp/ddns_nsupdate.ip';
if(-e $STATE_FILE)
{
open(IP_FILE, "<$STATE_FILE") or die "Cannot open state file '$STATE_FILE' for reading\n";
$lastHostname = <IP_FILE>;
chomp($lastHostname);
my $ip = <IP_FILE>;
chomp($ip);
printD "Read IP state file '$ip' '$lastHostname'\n";
if($ip =~ /(\d+\.\d+\.\d+\.\d+)/)
{
$lastIP = $1;
}
close(IP_FILE);
}
else
{
printD "No state file found. Assuming an update is needed\n";
}
open(IP_FILE, ">$STATE_FILE") or die "Cannot open state file '$STATE_FILE' for writing\n";
print IP_FILE "$hostname\n";
print IP_FILE "$curIP\n";
close(IP_FILE);
my $needsUpdate = 1;
if(!defined $lastIP || !defined $lastHostname)
{
printD "No previous IP and/or hostname found. Update needed\n";
}
elsif($hostname ne $lastHostname)
{
printD "Found Hostname change from '$lastHostname' -> '$hostname'. Updated needed\n";
}
elsif($curIP ne $lastIP)
{
printD "Found IP change from '$lastIP' -> '$curIP'. Updated needed\n";
}
else
{
printD "No change found since last update. Not updating\n";
$needsUpdate = 0;
}
return ($needsUpdate, $lastHostname);
}
sub updateDNS($$)
{
my $wan_ip = shift;
my $lastHostname = shift;
my $deletions = "";
$deletions .= "update delete $OPT{DDNS_Hostname} A\n";
$deletions .= "update delete $lastHostname A\n" if(defined $lastHostname);
printD "Updating DNS with hostname $OPT{DDNS_Hostname} -> $wan_ip...\n";
my $NSUPDATE = $OPT{Volume} . '/@appstore/DNSServer/bin/nsupdate';
open(NSUPDATE, "|$NSUPDATE");
my $nsupdate = sprintf <<EOU;
key $OPT{TSIG_Key} $OPT{TSIG_Secret}
$deletions
update add $OPT{DDNS_Hostname} $OPT{DDNS_TTL} A $wan_ip
send
EOU
printD "NSUPDATE:\n";
map { printD "\t$_\n"; } split(/\n/, $nsupdate);
print NSUPDATE $nsupdate;
close(NSUPDATE);
die "Error updating DNS: $?\n" if($? != 0);
}
sub main()
{
my $curIP = getWanIP();
my ($needsUpdate, $lastHostname) = needsUpdate($curIP, $OPT{DDNS_Hostname});
updateDNS($curIP, $lastHostname) if($needsUpdate);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment