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 | 26 | use Net::DNS; |
27 | 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 | 33 | my $DNSServer = '192.168.1.1'; # DNS Server to communicate with (use IP!) |
33 | 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 | 36 | # and other values to enable with debug= param. |
36 | 37 | my $AuthMode = 'remote'; # either 'static', 'remote' or 'both' |
37 | 38 | my $StaticSigner = ''; # required for AuthMode 'static' or 'both' |
... | ... | @@ -40,8 +41,9 @@ my $RequireRR = ''; # Require existing record of this type for upd. |
40 | 41 | my $ExpireAfter = '1w'; # Expire time for registrations in minutes, |
41 | 42 | # hours, weeks or seconds. format: [0-9]+[mhws]? |
42 | 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 | 62 | sub periodSeconds($) { |
61 | 63 | my ($number, $units) = ($_[0]=~/^(\d+)([smhdw])?$/); |
62 | 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 | 70 | return $number; |
75 | 71 | } |
... | ... | @@ -163,6 +159,7 @@ sub get_authinfo($$) { |
163 | 159 | sub DNS_Update($$$$$$$) { |
164 | 160 | my ($dnsdomain, $dnshost, $ipv4, $ipv6, $signer, $key, $debug) = @_; |
165 | 161 | my $dnsupdate = Net::DNS::Update->new($dnsdomain); |
162 | + my $ttl = periodSeconds($RecordTTL); | |
166 | 163 | |
167 | 164 | # If $RequireRR is set, ensure an records of specified type exist for the name |
168 | 165 | $dnsupdate->push(pre => yxrrset("$dnshost. $RequireRR")) if($RequireRR); |
... | ... | @@ -173,13 +170,14 @@ sub DNS_Update($$$$$$$) { |
173 | 170 | } |
174 | 171 | |
175 | 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 | 182 | # Sign the request with the signer and key |
185 | 183 | $dnsupdate->sign_tsig($signer, $key); |
... | ... | @@ -327,23 +325,58 @@ sub handle_list($$$$$$) { |
327 | 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 | 359 | # Validate Configuration |
331 | 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 | 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 | 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 | 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 | 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 | 376 | if($ExpireAfter and not $UpdateTXT); |
344 | 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 | |
... | ... |