Last active
November 22, 2019 14:14
-
-
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).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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