Commit 99ed087a8d4baeaa36242f45a4b6329e55d5d5ea
1 parent
de0f5780
Added config file support + various smaler changes
new functionality: - ability to load a config file (no more hard-coding settings) that can be optional, required or ignored - added RecordTTL setting (used to be hard-coded to 1h) Fixes: - ExpireAfter now also accepts just a number of seconds (without unit s) Other changes: - AllowDebugKey now defaults to 'off' - UpdateTXT and DeleteTXT no longer need a trailing whitespace - sanitized perodSeconds so that it only does a single multiplication
Showing
1 changed file
with
62 additions
and
29 deletions
dyndns.pl
@@ -26,12 +26,13 @@ use POSIX; | @@ -26,12 +26,13 @@ use POSIX; | ||
26 | use Net::DNS; | 26 | use Net::DNS; |
27 | use feature 'state'; | 27 | use feature 'state'; |
28 | 28 | ||
29 | +my $ConfigFile = 'optional'; # hardcoded, either optional, required or ignore | ||
29 | 30 | ||
30 | ############################## | 31 | ############################## |
31 | -# Configuration section | 32 | +# Configuration section (defaults, can be set in config file) |
32 | my $DNSServer = '192.168.1.1'; # DNS Server to communicate with (use IP!) | 33 | my $DNSServer = '192.168.1.1'; # DNS Server to communicate with (use IP!) |
33 | my $ExpandCNAMEs = 1; # CNAME levels to expand (0 to disable) | 34 | my $ExpandCNAMEs = 1; # CNAME levels to expand (0 to disable) |
34 | -my $AllowDebugKey = 'on'; # Debuging, 'off' to disable, '' for always on | 35 | +my $AllowDebugKey = 'off'; # Debuging, 'off' to disable, '' for always on |
35 | # and other values to enable with debug= param. | 36 | # and other values to enable with debug= param. |
36 | my $AuthMode = 'remote'; # either 'static', 'remote' or 'both' | 37 | my $AuthMode = 'remote'; # either 'static', 'remote' or 'both' |
37 | my $StaticSigner = ''; # required for AuthMode 'static' or 'both' | 38 | my $StaticSigner = ''; # required for AuthMode 'static' or 'both' |
@@ -40,8 +41,9 @@ my $RequireRR = ''; # Require existing record of this type for upd. | @@ -40,8 +41,9 @@ my $RequireRR = ''; # Require existing record of this type for upd. | ||
40 | my $ExpireAfter = '1w'; # Expire time for registrations in minutes, | 41 | my $ExpireAfter = '1w'; # Expire time for registrations in minutes, |
41 | # hours, weeks or seconds. format: [0-9]+[mhws]? | 42 | # hours, weeks or seconds. format: [0-9]+[mhws]? |
42 | my @ReplaceRR = ('A', 'AAAA', 'TXT'); # Records types to replace in update | 43 | my @ReplaceRR = ('A', 'AAAA', 'TXT'); # Records types to replace in update |
43 | -my $UpdateTXT = 'Last DynDNS update on '; | ||
44 | -my $DeleteTXT = 'DynDNS cleared on '; | 44 | +my $RecordTTL = '1h'; # TTL for created records, format: [0-9]+[mhws]? |
45 | +my $UpdateTXT = 'Last DynDNS update on'; # if set add TXT with this+date on update | ||
46 | +my $DeleteTXT = 'DynDNS cleared on'; # if set add TXT with this+date on delete | ||
45 | 47 | ||
46 | 48 | ||
47 | ############################## | 49 | ############################## |
@@ -60,16 +62,10 @@ sub is_ipv6 { return $_[0]=~/^([0-9a-fA-F]{0,4}(:|$)){3,8}/i; } | @@ -60,16 +62,10 @@ sub is_ipv6 { return $_[0]=~/^([0-9a-fA-F]{0,4}(:|$)){3,8}/i; } | ||
60 | sub periodSeconds($) { | 62 | sub periodSeconds($) { |
61 | my ($number, $units) = ($_[0]=~/^(\d+)([smhdw])?$/); | 63 | my ($number, $units) = ($_[0]=~/^(\d+)([smhdw])?$/); |
62 | if($number && $units && $units cmp 's') { | 64 | if($number && $units && $units cmp 's') { |
63 | - $number *= 60; # Convert to minutes | ||
64 | - if($units cmp 'm') { | ||
65 | - $number *= 60; # Convert to hours | ||
66 | - if($units cmp 'h') { | ||
67 | - $number *= 24; # Convert to days | ||
68 | - if($units cmp 'd') { | ||
69 | - $number *= 7; # Convert to weeks | ||
70 | - } | ||
71 | - } | ||
72 | - } | 65 | + $number *= ($units eq 'm') ? 60 # Seconds per minute |
66 | + : ($units cmp 'h') ? 3600 # Seconds per hour | ||
67 | + : ($units cmp 'd') ? 86400 # Seconds per day | ||
68 | + : 604800; # Seconds per week | ||
73 | } | 69 | } |
74 | return $number; | 70 | return $number; |
75 | } | 71 | } |
@@ -163,6 +159,7 @@ sub get_authinfo($$) { | @@ -163,6 +159,7 @@ sub get_authinfo($$) { | ||
163 | sub DNS_Update($$$$$$$) { | 159 | sub DNS_Update($$$$$$$) { |
164 | my ($dnsdomain, $dnshost, $ipv4, $ipv6, $signer, $key, $debug) = @_; | 160 | my ($dnsdomain, $dnshost, $ipv4, $ipv6, $signer, $key, $debug) = @_; |
165 | my $dnsupdate = Net::DNS::Update->new($dnsdomain); | 161 | my $dnsupdate = Net::DNS::Update->new($dnsdomain); |
162 | + my $ttl = periodSeconds($RecordTTL); | ||
166 | 163 | ||
167 | # If $RequireRR is set, ensure an records of specified type exist for the name | 164 | # If $RequireRR is set, ensure an records of specified type exist for the name |
168 | $dnsupdate->push(pre => yxrrset("$dnshost. $RequireRR")) if($RequireRR); | 165 | $dnsupdate->push(pre => yxrrset("$dnshost. $RequireRR")) if($RequireRR); |
@@ -173,13 +170,14 @@ sub DNS_Update($$$$$$$) { | @@ -173,13 +170,14 @@ sub DNS_Update($$$$$$$) { | ||
173 | } | 170 | } |
174 | 171 | ||
175 | # Add new A and AAAA record based on whether ipv4 and ipv6 address provided | 172 | # Add new A and AAAA record based on whether ipv4 and ipv6 address provided |
176 | - $dnsupdate->push(update=>rr_add("$dnshost. 3600 A $ipv4")) if($ipv4); | ||
177 | - $dnsupdate->push(update=>rr_add("$dnshost. 3600 AAAA $ipv6")) if($ipv6); | 173 | + $dnsupdate->push(update=>rr_add("$dnshost. $ttl A $ipv4")) if($ipv4); |
174 | + $dnsupdate->push(update=>rr_add("$dnshost. $ttl AAAA $ipv6")) if($ipv6); | ||
178 | 175 | ||
179 | - # Always add a new TXT record with the timestamp of the last update | ||
180 | - my $txt = ($ipv4 or $ipv6) ? $UpdateTXT : $DeleteTXT; | ||
181 | - $dnsupdate->push(update=>rr_add($dnshost. '. 3600 TXT "' . $txt . localtime() | ||
182 | - . '"')) if($txt); | 176 | + # Add a TXT record with the timestamp of the last update, if required |
177 | + if (my $txt = ($ipv4 or $ipv6) ? $UpdateTXT : $DeleteTXT) { | ||
178 | + my $timestamp = localtime(); | ||
179 | + $dnsupdate->push(update=>rr_add("$dnshost. $ttl TXT \"$txt $timestamp\"")); | ||
180 | + } | ||
183 | 181 | ||
184 | # Sign the request with the signer and key | 182 | # Sign the request with the signer and key |
185 | $dnsupdate->sign_tsig($signer, $key); | 183 | $dnsupdate->sign_tsig($signer, $key); |
@@ -327,23 +325,58 @@ sub handle_list($$$$$$) { | @@ -327,23 +325,58 @@ sub handle_list($$$$$$) { | ||
327 | my $cgi = CGI->new; | 325 | my $cgi = CGI->new; |
328 | 326 | ||
329 | ############################## | 327 | ############################## |
328 | +# Load configuration, if desired | ||
329 | +if ($ConfigFile cmp 'ignore') { | ||
330 | + my $CFGFile = $0; | ||
331 | + $CFGFile =~ s/(\.pl)?$/.cfg/; | ||
332 | + if (open (CONFIG, $CFGFile)) { | ||
333 | + my %CONFIG = ( | ||
334 | + allow_debug_key => \$AllowDebugKey, dns_server => \$DNSServer, | ||
335 | + expand_cnames => \$ExpandCNAMEs, auth_mode => \$AuthMode, | ||
336 | + static_signer => \$StaticSigner, static_key => \$StaticKey, | ||
337 | + require_rr => \$RequireRR, replace_rr => \@ReplaceRR, | ||
338 | + update_txt => \$UpdateTXT, delete_txt => \$DeleteTXT, | ||
339 | + expire_after => \$ExpireAfter, record_ttl => \$RecordTTL, | ||
340 | + ); | ||
341 | + | ||
342 | + while (<CONFIG>) { | ||
343 | + chomp; s/^\s+//; s/\s*(#.*)?$//; # trim whitespace & comments | ||
344 | + next unless length; # skip empty lines | ||
345 | + my ($key, $value) = split(/\s*=\s*/, $_, 2); # split key and value | ||
346 | + my $dst = $CONFIG{$key}; # get destination for value | ||
347 | + if (ref $dst eq 'SCALAR') { $$dst = $value; } # store scalar value | ||
348 | + elsif (ref $dst eq 'CODE') { &$dst($value); } # call setter function | ||
349 | + elsif (ref $dst cmp 'ARRAY') { die "Invalid $key in $CFGFile\n"; } | ||
350 | + else { @$dst = split(/\s*,\s*/, $value); } # split and store array | ||
351 | + } | ||
352 | + close CONFIG; | ||
353 | + } elsif ($ConfigFile eq 'required') { | ||
354 | + die "unable to load configuration from $CFGFile: $!, aborting\n" | ||
355 | + } | ||
356 | +} | ||
357 | + | ||
358 | +############################## | ||
330 | # Validate Configuration | 359 | # Validate Configuration |
331 | my $CE = 'Configuration Error:'; | 360 | my $CE = 'Configuration Error:'; |
332 | -die "$CE \$AuthMode '$AuthMode' is unsupported must be remote, static or both\n" | 361 | +die "$CE ConfigFile must be optional, required or ignore, not '$ConfigFile'\n" |
362 | + unless $ConfigFile=~/optional|required|ignore/; | ||
363 | +die "$CE AuthMode '$AuthMode' is unsupported must be remote, static or both\n" | ||
333 | unless $AuthMode=~/remote|static|both/; | 364 | unless $AuthMode=~/remote|static|both/; |
334 | -die "$CE \$StaticSigner must be set for \$AuthMode '$AuthMode'\n" | 365 | +die "$CE StaticSigner must be set for \$AuthMode '$AuthMode'\n" |
335 | unless ($StaticSigner or $AuthMode eq 'remote'); | 366 | unless ($StaticSigner or $AuthMode eq 'remote'); |
336 | -die "$CE \$StaticKey must be set for \$AuthMode '$AuthMode'\n" | 367 | +die "$CE StaticKey must be set for \$AuthMode '$AuthMode'\n" |
337 | unless ($StaticKey or $AuthMode eq 'remote'); | 368 | unless ($StaticKey or $AuthMode eq 'remote'); |
338 | -die "$CE \$RequireRR is set to unsupported type '$RequireRR'\n" | 369 | +die "$CE RequireRR is set to unsupported type '$RequireRR'\n" |
339 | if ($RequireRR and not $DNS_label{$RequireRR}); | 370 | if ($RequireRR and not $DNS_label{$RequireRR}); |
340 | -die "$CE \$ExpireAfter '$ExpireAfter' is not supported\n" | ||
341 | - unless ($ExpireAfter=~/^\d+[smhw]$/); | ||
342 | -die "$CE \$UpdateTXT must be set when \$ExpireAfter is set\n" | 371 | +die "$CE RecordTTL '$RecordTTL' is not supported\n" |
372 | + unless ($RecordTTL=~/^\d+[smhw]?$/); | ||
373 | +die "$CE ExpireAfter '$ExpireAfter' is not supported\n" | ||
374 | + unless ($ExpireAfter=~/^\d+[smhw]?$/); | ||
375 | +die "$CE UpdateTXT must be set when \$ExpireAfter is set\n" | ||
343 | if($ExpireAfter and not $UpdateTXT); | 376 | if($ExpireAfter and not $UpdateTXT); |
344 | foreach my $rrtype (@ReplaceRR) { | 377 | foreach my $rrtype (@ReplaceRR) { |
345 | - die "$CE \$ReplaceRR contains unsupported type '$rrtype'\n" | ||
346 | - unless ($DNS_label{$rrtype}); | 378 | + die "$CE ReplaceRR contains unsupported type '$rrtype'\n" |
379 | + unless exists $DNS_label{$rrtype}; | ||
347 | } | 380 | } |
348 | 381 | ||
349 | 382 |