Created
June 18, 2019 14:39
-
-
Save psct/a8d08573e05797775d2b734088281e01 to your computer and use it in GitHub Desktop.
Fügt Debian Stretch ddclient Änderungen aus modernerem ddclient hinzu und ergänzt dynv6.com als Anbieter
This file contains 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
--- /usr/sbin/ddclient.stretch 2016-07-02 13:16:57.000000000 +0200 | |
+++ /usr/sbin/ddclient 2019-06-18 16:29:09.370194169 +0200 | |
@@ -1,7 +1,6 @@ | |
#!/usr/bin/perl -w | |
#!/usr/local/bin/perl -w | |
###################################################################### | |
-# $Id: ddclient 184 2015-05-28 19:59:34Z wimpunk $ | |
# | |
# DDCLIENT - a Perl client for updating DynDNS information | |
# | |
@@ -25,17 +24,16 @@ | |
use Getopt::Long; | |
use Sys::Hostname; | |
use IO::Socket; | |
+use Data::Validate::IP; | |
-# my ($VERSION) = q$Revision: 184 $ =~ /(\d+)/; | |
- | |
-my $version = "3.8.3"; | |
+my $version = "3.9.0"; | |
my $programd = $0; | |
$programd =~ s%^.*/%%; | |
my $program = $programd; | |
$program =~ s/d$//; | |
my $now = time; | |
my $hostname = hostname(); | |
-my $etc = ($program =~ /test/i) ? './' : '/etc/'; | |
+my $etc = ($program =~ /test/i) ? './' : '/etc/ddclient/'; | |
my $cachedir = ($program =~ /test/i) ? './' : '/var/cache/ddclient/'; | |
my $savedir = ($program =~ /test/i) ? 'URL/' : '/tmp/'; | |
my $msgs = ''; | |
@@ -95,11 +93,6 @@ | |
'url' => '/status.HTM', | |
'skip' => 'WAN IP', | |
}, | |
- 'smc-barricade-alt' => { | |
- 'name' => 'SMC Barricade FW (alternate config)', | |
- 'url' => '/status.HTM', | |
- 'skip' => 'WAN IP', | |
- }, | |
'smc-barricade-7401bra' => { | |
'name' => 'SMC Barricade 7401BRA FW', | |
'url' => '/admin/wan1.htm', | |
@@ -345,6 +338,7 @@ | |
'web-skip' => setv(T_STRING,1, 0, 1, '', undef), | |
'fw' => setv(T_ANY, 0, 0, 1, '', undef), | |
'fw-skip' => setv(T_STRING,1, 0, 1, '', undef), | |
+ 'fw-banlocal' => setv(T_BOOL, 0, 0, 1, 0, undef), | |
'fw-login' => setv(T_LOGIN, 1, 0, 1, '', undef), | |
'fw-password' => setv(T_PASSWD,1, 0, 1, '', undef), | |
'cmd' => setv(T_PROG, 0, 0, 1, '', undef), | |
@@ -354,7 +348,7 @@ | |
'retry' => setv(T_BOOL, 0, 0, 0, 0, undef), | |
'force' => setv(T_BOOL, 0, 0, 0, 0, undef), | |
'ssl' => setv(T_BOOL, 0, 0, 0, 0, undef), | |
- | |
+ 'ipv6' => setv(T_BOOL, 0, 0, 0, 0, undef), | |
'syslog' => setv(T_BOOL, 0, 0, 1, 0, undef), | |
'facility' => setv(T_STRING,0, 0, 1, 'daemon', undef), | |
'priority' => setv(T_STRING,0, 0, 1, 'notice', undef), | |
@@ -385,18 +379,19 @@ | |
'web-skip' => setv(T_STRING,0, 0, 1, '', undef), | |
'fw' => setv(T_ANY, 0, 0, 1, '', undef), | |
'fw-skip' => setv(T_STRING,0, 0, 1, '', undef), | |
+ 'fw-banlocal' => setv(T_BOOL, 0, 0, 1, 0, undef), | |
'fw-login' => setv(T_LOGIN, 0, 0, 1, '', undef), | |
'fw-password' => setv(T_PASSWD,0, 0, 1, '', undef), | |
'cmd' => setv(T_PROG, 0, 0, 1, '', undef), | |
'cmd-skip' => setv(T_STRING,0, 0, 1, '', undef), | |
- | |
+ 'ipv6' => setv(T_BOOL, 0, 0, 0, 0, undef), | |
'ip' => setv(T_IP, 0, 1, 0, undef, undef), | |
'wtime' => setv(T_DELAY, 0, 1, 1, 0, interval('30s')), | |
'mtime' => setv(T_NUMBER, 0, 1, 0, 0, undef), | |
'atime' => setv(T_NUMBER, 0, 1, 0, 0, undef), | |
'status' => setv(T_ANY, 0, 1, 0, '', undef), | |
'min-interval' => setv(T_DELAY, 0, 0, 1, interval('30s'), 0), | |
- 'max-interval' => setv(T_DELAY, 0, 0, 1, interval('30d'), 0), | |
+ 'max-interval' => setv(T_DELAY, 0, 0, 1, interval('25d'), 0), | |
'min-error-interval' => setv(T_DELAY, 0, 0, 1, interval('5m'), 0), | |
'warned-min-interval' => setv(T_ANY, 0, 1, 0, 0, undef), | |
@@ -446,14 +441,16 @@ | |
'nsupdate-common-defaults' => { | |
'ttl' => setv(T_NUMBER, 0, 1, 0, 600, undef), | |
'zone' => setv(T_STRING, 1, 1, 1, '', undef), | |
+ 'tcp' => setv(T_BOOL, 0, 1, 1, 0, undef), | |
}, | |
'cloudflare-common-defaults' => { | |
- 'server' => setv(T_FQDNP, 1, 0, 1, 'www.cloudflare.com', undef), | |
+ 'server' => setv(T_FQDNP, 1, 0, 1, 'api.cloudflare.com/client/v4', undef), | |
'zone' => setv(T_FQDN, 1, 0, 1, '', undef), | |
'static' => setv(T_BOOL, 0, 1, 1, 0, undef), | |
'wildcard' => setv(T_BOOL, 0, 1, 1, 0, undef), | |
'mx' => setv(T_OFQDN, 0, 1, 1, '', undef), | |
'backupmx' => setv(T_BOOL, 0, 1, 1, 0, undef), | |
+ 'ttl' => setv(T_NUMBER, 1, 0, 1, 1, undef), | |
}, | |
'googledomains-common-defaults' => { | |
'server' => setv(T_FQDNP, 1, 0, 1, 'domains.google.com', undef), | |
@@ -462,6 +459,37 @@ | |
'server' => setv(T_FQDNP, 1, 0, 1, 'www.duckdns.org', undef), | |
'login' => setv(T_LOGIN, 0, 0, 0, 'unused', undef), | |
}, | |
+ 'freemyip-common-defaults' => { | |
+ 'server' => setv(T_FQDNP, 1, 0, 1, 'freemyip.com', undef), | |
+ 'login' => setv(T_LOGIN, 0, 0, 0, 'unused', undef), | |
+ }, | |
+ 'woima-common-defaults' => { | |
+ 'static' => setv(T_BOOL, 0, 1, 1, 0, undef), | |
+ 'wildcard' => setv(T_BOOL, 0, 1, 1, 0, undef), | |
+ 'mx' => setv(T_OFQDN, 0, 1, 1, '', undef), | |
+ 'backupmx' => setv(T_BOOL, 0, 1, 1, 0, undef), | |
+ 'custom' => setv(T_BOOL, 0, 1, 1, 0, undef), | |
+ 'script' => setv(T_STRING, 1, 1, 1, '/nic/update', undef), | |
+ }, | |
+ 'woima-service-common-defaults' => { | |
+ 'server' => setv(T_FQDNP, 1, 0, 1, 'dyn.woima.fi', undef), | |
+ 'login' => setv(T_LOGIN, 1, 0, 1, '', undef), | |
+ 'password' => setv(T_PASSWD, 1, 0, 1, '', undef), | |
+ 'ip' => setv(T_IP, 0, 1, 0, undef, undef), | |
+ 'wtime' => setv(T_DELAY, 0, 1, 1, 0, interval('30s')), | |
+ 'mtime' => setv(T_NUMBER, 0, 1, 0, 0, undef), | |
+ 'atime' => setv(T_NUMBER, 0, 1, 0, 0, undef), | |
+ 'status' => setv(T_ANY, 0, 1, 0, '', undef), | |
+ 'min-interval' => setv(T_DELAY, 0, 0, 1, interval('30s'), 0), | |
+ 'max-interval' => setv(T_DELAY, 0, 0, 1, interval('25d'), 0), | |
+ 'min-error-interval' => setv(T_DELAY, 0, 0, 1, interval('5m'), 0), | |
+ 'warned-min-interval' => setv(T_ANY, 0, 1, 0, 0, undef), | |
+ 'warned-min-error-interval' => setv(T_ANY, 0, 1, 0, 0, undef), | |
+ }, | |
+ 'dynv6-common-defaults' => { | |
+ 'server' => setv(T_FQDNP, 1, 0, 1, 'dynv6.com/api/update', undef), | |
+ 'login' => setv(T_LOGIN, 0, 0, 0, 'unused', undef), | |
+ }, | |
); | |
my %services = ( | |
'dyndns1' => { | |
@@ -621,7 +649,7 @@ | |
'update' => \&nic_cloudflare_update, | |
'examples' => \&nic_cloudflare_examples, | |
'variables' => merge( | |
- { 'server' => setv(T_FQDNP, 1, 0, 1, 'www.cloudflare.com', undef) }, | |
+ { 'server' => setv(T_FQDNP, 1, 0, 1, 'api.cloudflare.com/client/v4', undef) }, | |
{ 'min-interval' => setv(T_DELAY, 0, 0, 1, interval('5m'), 0),}, | |
$variables{'cloudflare-common-defaults'}, | |
$variables{'service-common-defaults'}, | |
@@ -646,6 +674,33 @@ | |
$variables{'service-common-defaults'}, | |
), | |
}, | |
+ 'freemyip' => { | |
+ 'updateable' => undef, | |
+ 'update' => \&nic_freemyip_update, | |
+ 'examples' => \&nic_freemyip_examples, | |
+ 'variables' => merge( | |
+ $variables{'freemyip-common-defaults'}, | |
+ $variables{'service-common-defaults'}, | |
+ ), | |
+ }, | |
+ 'woima' => { | |
+ 'updateable' => undef, | |
+ 'update' => \&nic_woima_update, | |
+ 'examples' => \&nic_woima_examples, | |
+ 'variables' => merge( | |
+ $variables{'woima-common-defaults'}, | |
+ $variables{'woima-service-common-defaults'}, | |
+ ), | |
+ }, | |
+ 'dynv6' => { | |
+ 'updateable' => undef, | |
+ 'update' => \&nic_dynv6_update, | |
+ 'examples' => \&nic_dynv6_examples, | |
+ 'variables' => merge( | |
+ $variables{'dynv6-common-defaults'}, | |
+ $variables{'service-common-defaults'}, | |
+ ), | |
+ }, | |
); | |
$variables{'merged'} = merge($variables{'global-defaults'}, | |
$variables{'service-common-defaults'}, | |
@@ -679,6 +734,7 @@ | |
"", | |
[ "fw", "=s", "-fw address|url : obtain IP address from firewall at 'address'" ], | |
[ "fw-skip", "=s", "-fw-skip pattern : skip any IP addresses before 'pattern' on the firewall address|url" ], | |
+ [ "fw-banlocal", "!", "-fw-banlocal : ignore local IP addresses on the firewall address|url" ], | |
[ "fw-login", "=s", "-fw-login login : use 'login' when getting IP from fw" ], | |
[ "fw-password", "=s", "-fw-password secret : use password 'secret' when getting IP from fw" ], | |
"", | |
@@ -705,6 +761,7 @@ | |
[ "debug", "!", "-{no}debug : print {no} debugging information" ], | |
[ "verbose", "!", "-{no}verbose : print {no} verbose information" ], | |
[ "quiet", "!", "-{no}quiet : print {no} messages for unnecessary updates" ], | |
+ [ "ipv6", "!", "-{no}ipv6 : use ipv6" ], | |
[ "help", "", "-help : this message" ], | |
[ "postscript", "", "-postscript : script to run after updating ddclient, has new IP as param" ], | |
@@ -1886,6 +1943,24 @@ | |
import IO::Socket::SSL; | |
{ no warnings; $IO::Socket::SSL::DEBUG = 0; } | |
} | |
+ | |
+###################################################################### | |
+## load_ipv6_support | |
+###################################################################### | |
+sub load_ipv6_support { | |
+ my $ipv6_loaded = eval {require IO::Socket::INET6}; | |
+ unless ($ipv6_loaded) { | |
+ fatal(<<"EOM"); | |
+Error loading the Perl module IO::Socket::INET6 needed for ipv6 connect. | |
+On Debian, the package libio-socket-inet6-perl must be installed. | |
+On Red Hat, the package perl-IO-Socket-INET6 must be installed. | |
+On Alpine, the package perl-io-socket-inet6 must be installed. | |
+EOM | |
+ } | |
+ import IO::Socket::INET6; | |
+ { no warnings; $IO::Socket::INET6::DEBUG = 0; } | |
+} | |
+ | |
###################################################################### | |
## load_sha1_support | |
###################################################################### | |
@@ -1908,13 +1983,13 @@ | |
## load_json_support | |
###################################################################### | |
sub load_json_support { | |
- my $json_loaded = eval {require JSON::Any}; | |
+ my $json_loaded = eval {require JSON::PP}; | |
unless ($json_loaded) { | |
fatal(<<"EOM"); | |
-Error loading the Perl module JSON::Any needed for cloudflare update. | |
+Error loading the Perl module JSON::PP needed for cloudflare update. | |
EOM | |
} | |
- import JSON::Any; | |
+ import JSON::PP (qw/decode_json/); | |
} | |
###################################################################### | |
## geturl | |
@@ -1924,6 +1999,9 @@ | |
my $url = shift || ''; | |
my $login = shift || ''; | |
my $password = shift || ''; | |
+ my $headers = shift || ''; | |
+ my $method = shift || 'GET'; | |
+ my $data = shift || ''; | |
my ($peer, $server, $port, $default_port, $use_ssl); | |
my ($sd, $rq, $request, $reply); | |
@@ -1964,7 +2042,7 @@ | |
my $to = sprintf "%s%s", $server, $proxy ? " via proxy $peer:$port" : ""; | |
verbose("CONNECT:", "%s", $to); | |
- $request = "GET "; | |
+ $request = "$method "; | |
$request .= "http://$server" if $proxy; | |
$request .= "/$url HTTP/1.0\n"; | |
$request .= "Host: $server\n"; | |
@@ -1973,7 +2051,10 @@ | |
$request .= "Authorization: Basic $auth\n" if $login || $password; | |
$request .= "User-Agent: ${program}/${version}\n"; | |
$request .= "Connection: close\n"; | |
+ $request .= "$headers\n"; | |
+ $request .= "Content-Length: ".length($data)."\n" if $data; | |
$request .= "\n"; | |
+ $request .= $data; | |
## make sure newlines are <cr><lf> for some pedantic proxy servers | |
($rq = $request) =~ s/\n/\r\n/g; | |
@@ -1992,6 +2073,16 @@ | |
Timeout => opt('timeout'), | |
); | |
defined $sd or warning("cannot connect to $peer:$port socket: $@ " . IO::Socket::SSL::errstr()); | |
+ } elsif ($globals{'ipv6'}) { | |
+ load_ipv6_support; | |
+ $sd = IO::Socket::INET6->new( | |
+ PeerAddr => $peer, | |
+ PeerPort => $port, | |
+ Proto => 'tcp', | |
+ MultiHomed => 1, | |
+ Timeout => opt('timeout'), | |
+ ); | |
+ defined $sd or warning("cannot connect to $peer:$port socket: $@"); | |
} else { | |
$sd = IO::Socket::INET->new( | |
PeerAddr => $peer, | |
@@ -2052,6 +2143,48 @@ | |
return $reply; | |
} | |
###################################################################### | |
+## un_zero_pad | |
+###################################################################### | |
+sub un_zero_pad { | |
+ my $in_str = shift(@_); | |
+ my @out_str = (); | |
+ | |
+ if ($in_str eq '0.0.0.0') { | |
+ return $in_str; | |
+ } | |
+ | |
+ foreach my $block (split /\./, $in_str) { | |
+ $block =~ s/^0+//; | |
+ if ($block eq '') { | |
+ $block = '0'; | |
+ } | |
+ push @out_str, $block; | |
+ } | |
+ return join('.', @out_str); | |
+} | |
+###################################################################### | |
+## filter_local | |
+###################################################################### | |
+sub filter_local { | |
+ my $in_ip = shift(@_); | |
+ | |
+ if ($in_ip eq '0.0.0.0') { | |
+ return $in_ip; | |
+ } | |
+ | |
+ my @guess_local = ( | |
+ '^10\.', | |
+ '^172\.(?:1[6-9]|2[0-9]|3[01])\.', | |
+ '^192\.168' | |
+ ); | |
+ foreach my $block (@guess_local) { | |
+ if ($in_ip =~ /$block/) { | |
+ return '0.0.0.0'; | |
+ } | |
+ } | |
+ return $in_ip; | |
+} | |
+###################################################################### | |
## get_ip | |
###################################################################### | |
sub get_ip { | |
@@ -2148,6 +2281,13 @@ | |
} | |
if ($reply =~ /^.*?\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\b.*/is) { | |
$ip = $1; | |
+ $ip = un_zero_pad($ip); | |
+ $ip = filter_local($ip) if opt('fw-banlocal', $h); | |
+ } elsif ( $ip = ipv6_match($reply) ) { | |
+ $ip = un_zero_pad($ip); | |
+ $ip = filter_local($ip) if opt('fw-banlocal', $h); | |
+ } else { | |
+ warning("found neither ipv4 nor ipv6 address"); | |
} | |
if (($use ne 'ip') && (define($ip,'') eq '0.0.0.0')) { | |
$ip = undef; | |
@@ -2194,6 +2334,34 @@ | |
} | |
###################################################################### | |
+## ipv6_match determine ipv6 address from given string and return them | |
+###################################################################### | |
+sub ipv6_match { | |
+ my $content = shift; | |
+ my $omits; | |
+ my $ip = ""; | |
+ my $linenumbers = 0; | |
+ | |
+ my @values = split('\n', $content); | |
+ foreach my $val (@values) { | |
+ next unless $val =~ /((:{0,2}[A-F0-9]{1,4}){0,7}:{1,2}[A-F0-9]{1,4})/ai; # invalid char | |
+ my $parsed = $1; | |
+ | |
+ # check for at least 7 colons | |
+ my $count_colon = () = $parsed =~ /:/g; | |
+ if ($count_colon != 7) { | |
+ # or one double colon | |
+ my $count_double_colon = () = $parsed =~ /::/g; | |
+ if ($count_double_colon != 1) { | |
+ next | |
+ } | |
+ } | |
+ return $parsed; | |
+ } | |
+ return; | |
+} | |
+ | |
+###################################################################### | |
## group_hosts_by | |
###################################################################### | |
sub group_hosts_by { | |
@@ -3502,14 +3670,14 @@ | |
server=fqdn.of.service ## defaults to dynamicdns.park-your-domain.com | |
login=service-login ## login name and password registered with the service | |
password=service-password ## | |
- fully.qualified.host ## the host registered with the service. | |
+ fully.qualified.host ## the hostname to update. | |
Example ${program}.conf file entries: | |
## single host update | |
- protocol=namecheap, \\ | |
- login=my-namecheap.com-login, \\ | |
+ protocol=namecheap \\ | |
+ login=my-namecheap.com-login \\ | |
password=my-namecheap.com-password \\ | |
- myhost.namecheap.com | |
+ myhost | |
EoEXAMPLE | |
} | |
@@ -3518,9 +3686,10 @@ | |
## | |
## written by Dan Boardman | |
## | |
-## based on http://www.namecheap.com/resources/help/index.asp?t=dynamicdns | |
+## based on https://www.namecheap.com/support/knowledgebase/ | |
+## article.aspx/29/11/how-to-use-the-browser-to-dynamically-update-hosts-ip | |
## needs this url to update: | |
-## http://dynamicdns.park-your-domain.com/update?host=host_name& | |
+## https://dynamicdns.park-your-domain.com/update?host=host_name& | |
## domain=domain.com&password=domain_password[&ip=your_ip] | |
## | |
###################################################################### | |
@@ -3536,7 +3705,7 @@ | |
verbose("UPDATE:","updating %s", $h); | |
my $url; | |
- $url = "http://$config{$h}{'server'}/update"; | |
+ $url = "https://$config{$h}{'server'}/update"; | |
my $domain = $config{$h}{'login'}; | |
my $host = $h; | |
$host =~ s/(.*)\.$domain(.*)/$1$2/; | |
@@ -4011,6 +4180,10 @@ | |
zone=dyn.example.com ## forward zone that is to be updated | |
ttl=600 ## time to live of the record; | |
## defaults to 600 seconds | |
+ tcp=off|on ## nsupdate uses UDP by default, and switches to | |
+ ## TCP if the update is too large to fit in a | |
+ ## UDP datagram; this setting forces TCP; | |
+ ## defaults to off | |
login=/usr/bin/nsupdate ## path and name of nsupdate binary; | |
## defaults to '/usr/bin/nsupdate' | |
<hostname> ## fully qualified hostname to update | |
@@ -4045,8 +4218,16 @@ | |
my $binary = $config{$h}{'login'}; | |
my $keyfile = $config{$h}{'password'}; | |
my $server = $config{$h}{'server'}; | |
+ ## nsupdate requires a port number to be separated by whitepace, not colon | |
+ $server =~ s/:/ /; | |
my $zone = $config{$h}{'zone'}; | |
my $ip = $config{$h}{'wantip'}; | |
+ my $recordtype = ''; | |
+ if (is_ipv6($ip)) { | |
+ $recordtype = 'AAAA'; | |
+ } else { | |
+ $recordtype = 'A'; | |
+ } | |
delete $config{$_}{'wantip'} foreach @hosts; | |
info("setting IP address to %s for %s", $ip, $hosts); | |
@@ -4059,14 +4240,15 @@ | |
EoINSTR1 | |
foreach (@hosts) { | |
$instructions .= <<EoINSTR2; | |
-update delete $_. A | |
-update add $_. $config{$_}{'ttl'} A $ip | |
+update delete $_. $recordtype | |
+update add $_. $config{$_}{'ttl'} $recordtype $ip | |
EoINSTR2 | |
} | |
$instructions .= <<EoINSTR3; | |
send | |
EoINSTR3 | |
my $command = "$binary -k $keyfile"; | |
+ $command .= " -v" if ynu($config{$h}{'tcp'}, 1, 0, 0); | |
$command .= " -d" if (opt('debug')); | |
verbose("UPDATE:", "nsupdate command is: %s", $command); | |
verbose("UPDATE:", "nsupdate instructions are:\n%s", $instructions); | |
@@ -4102,7 +4284,7 @@ | |
Configuration variables applicable to the 'cloudflare' protocol are: | |
protocol=cloudflare ## | |
- server=fqdn.of.service ## defaults to www.cloudflare.com | |
+ server=fqdn.of.service ## defaults to api.cloudflare.com/client/v4 | |
login=service-login ## login name and password registered with the service | |
password=service-password ## | |
fully.qualified.host ## the host registered with the service. | |
@@ -4139,6 +4321,10 @@ | |
my $key = $hosts[0]; | |
my $ip = $config{$key}{'wantip'}; | |
+ my $headers = "X-Auth-Email: $config{$key}{'login'}\n"; | |
+ $headers .= "X-Auth-Key: $config{$key}{'password'}\n"; | |
+ $headers .= "Content-Type: application/json"; | |
+ | |
# FQDNs | |
for my $domain (@hosts) { | |
(my $hostname = $domain) =~ s/\.$config{$key}{zone}$//; | |
@@ -4147,13 +4333,11 @@ | |
info("setting IP address to %s for %s", $ip, $domain); | |
verbose("UPDATE:","updating %s", $domain); | |
- # Get domain ID | |
- my $url = "https://$config{$key}{'server'}/api_json.html?a=rec_load_all"; | |
- $url .= "&z=".$config{$key}{'zone'}; | |
- $url .= "&email=".$config{$key}{'login'}; | |
- $url .= "&tkn=".$config{$key}{'password'}; | |
+ # Get zone ID | |
+ my $url = "https://$config{$key}{'server'}/zones?"; | |
+ $url .= "name=".$config{$key}{'zone'}; | |
- my $reply = geturl(opt('proxy'), $url); | |
+ my $reply = geturl(opt('proxy'), $url, undef, undef, $headers); | |
unless ($reply) { | |
failed("updating %s: Could not connect to %s.", $domain, $config{$key}{'server'}); | |
last; | |
@@ -4162,30 +4346,55 @@ | |
# Strip header | |
$reply =~ s/^.*?\n\n//s; | |
- my $response = JSON::Any->jsonToObj($reply); | |
- if ($response->{result} eq 'error') { | |
- failed ("%s", $response->{msg}); | |
+ my $response = eval {decode_json($reply)}; | |
+ if (!defined $response || !defined $response->{result}) { | |
+ failed ("invalid json or result."); | |
next; | |
} | |
# Pull the ID out of the json, messy | |
- my ($id) = map { $_->{name} eq $domain ? $_->{rec_id} : () } @{ $response->{response}->{recs}->{objs} }; | |
- unless($id) { | |
- failed("updating %s: No domain ID found.", $domain); | |
+ my ($zone_id) = map { $_->{name} eq $config{$key}{'zone'} ? $_->{id} : () } @{ $response->{result} }; | |
+ unless($zone_id) { | |
+ failed("updating %s: No zone ID found.", $config{$key}{'zone'}); | |
next; | |
} | |
+ info("zone ID is $zone_id"); | |
- # Set domain | |
- $url = "https://$config{$key}{'server'}/api_json.html?a=rec_edit&type=A&ttl=1"; | |
- $url .= "&name=$hostname"; | |
- $url .= "&z=".$config{$key}{'zone'}; | |
- $url .= "&id=".$id; | |
- $url .= "&email=".$config{$key}{'login'}; | |
- $url .= "&tkn=".$config{$key}{'password'}; | |
- $url .= "&content="; | |
- $url .= "$ip" if $ip; | |
+ # Get DNS record ID | |
+ $url = "https://$config{$key}{'server'}/zones/$zone_id/dns_records?"; | |
+ if (is_ipv6($ip)) { | |
+ $url .= "type=AAAA&name=$domain"; | |
+ } else { | |
+ $url .= "type=A&name=$domain"; | |
+ } | |
+ | |
+ $reply = geturl(opt('proxy'), $url, undef, undef, $headers); | |
+ unless ($reply) { | |
+ failed("updating %s: Could not connect to %s.", $domain, $config{$key}{'server'}); | |
+ last; | |
+ } | |
+ last if !header_ok($domain, $reply); | |
+ | |
+ # Strip header | |
+ $reply =~ s/^.*?\n\n//s; | |
+ $response = eval {decode_json($reply)}; | |
+ if (!defined $response || !defined $response->{result}) { | |
+ failed ("invalid json or result."); | |
+ next; | |
+ } | |
+ | |
+ # Pull the ID out of the json, messy | |
+ my ($dns_rec_id) = map { $_->{name} eq $domain ? $_->{id} : () } @{ $response->{result} }; | |
+ unless($dns_rec_id) { | |
+ failed("updating %s: No DNS record ID found.", $domain); | |
+ next; | |
+ } | |
+ info("DNS record ID is $dns_rec_id"); | |
- $reply = geturl(opt('proxy'), $url); | |
+ # Set domain | |
+ $url = "https://$config{$key}{'server'}/zones/$zone_id/dns_records/$dns_rec_id"; | |
+ my $data = "{\"content\":\"$ip\"}"; | |
+ $reply = geturl(opt('proxy'), $url, undef, undef, $headers, "PATCH", $data); | |
unless ($reply) { | |
failed("updating %s: Could not connect to %s.", $domain, $config{$domain}{'server'}); | |
last; | |
@@ -4194,9 +4403,9 @@ | |
# Strip header | |
$reply =~ s/^.*?\n\n//s; | |
- $response = JSON::Any->jsonToObj($reply); | |
- if ($response->{result} eq 'error') { | |
- failed ("%s", $response->{msg}); | |
+ $response = eval {decode_json($reply)}; | |
+ if (!defined $response || !defined $response->{result}) { | |
+ failed ("invalid json or result."); | |
} else { | |
success ("%s -- Updated Successfully to %s", $domain, $ip); | |
@@ -4210,6 +4419,94 @@ | |
} | |
} | |
+ | |
+###################################################################### | |
+## nic_dynv6_examples | |
+###################################################################### | |
+sub nic_dynv6_examples { | |
+ return <<EoEXAMPLE; | |
+o 'dynv6' | |
+ | |
+The 'dynv6' protocol is used by the free | |
+dynamic DNS service offered by www.dynv6.com. | |
+Check https://dynv6.com/docs/apis for API | |
+ | |
+Configuration variables applicable to the 'dynv6' protocol are: | |
+ protocol=dynv6 ## | |
+ server=www.fqdn.of.service ## defaults to dynv6.com | |
+ password=service-token ## token registered with the service | |
+ fully.qualified.host ## the host registered with the service. | |
+ | |
+Example ${program}.conf file entries: | |
+ ## single host update | |
+ protocol=dynv6, \\ | |
+ password=z0mgs3cjur3p4ss \\ | |
+ myhost.dynv6.net | |
+ | |
+EoEXAMPLE | |
+} | |
+ | |
+###################################################################### | |
+## nic_dynv6_update | |
+## by Peter Siering (copypasta from nic_duckdns_update) | |
+## https://dynv6.com/api/update?hostname=&token=&ipv4=&ipv6= | |
+## response contains OK or KO | |
+###################################################################### | |
+sub nic_dynv6_update { | |
+ debug("\nnic_dynv6_update -------------------"); | |
+ | |
+ ## update each configured host | |
+ ## should improve to update in one pass | |
+ foreach my $h (@_) { | |
+ my $ip = delete $config{$h}{'wantip'}; | |
+ info("setting IP address to %s for %s", $ip, $h); | |
+ verbose("UPDATE:","updating %s", $h); | |
+ | |
+ # Set the URL that we're going to to update | |
+ my $url; | |
+ $url = "http://$config{$h}{'server'}"; | |
+ $url .= "?hostname="; | |
+ $url .= $h; | |
+ $url .= "&token="; | |
+ $url .= $config{$h}{'password'}; | |
+ if (is_ipv6($ip)) { | |
+ $url .= "&ipv6="; | |
+ } else { | |
+ $url .= "&ipv4="; | |
+ } | |
+ $url .= $ip; | |
+ | |
+ # Try to get URL | |
+ my $reply = geturl(opt('proxy'), $url); | |
+ | |
+ # No response, declare as failed | |
+ if (!defined($reply) || !$reply) { | |
+ failed("updating %s: Could not connect to %s.", $h, $config{$h}{'server'}); | |
+ last; | |
+ } | |
+ last if !header_ok($h, $reply); | |
+ | |
+ my @reply = split /\n/, $reply; | |
+ my $returned = pop(@reply); | |
+ if ($returned =~ /addresses updated/ || $returned =~ /addresses unchanged/) | |
+ { | |
+ $config{$h}{'ip'} = $ip; | |
+ $config{$h}{'mtime'} = $now; | |
+ $config{$h}{'status'} = 'good'; | |
+ if ($returned =~ /addresses updated/) | |
+ { | |
+ success("updating %s: good: IP address set to %s", $h, $ip); | |
+ } else { | |
+ warning("redundant update %s: IP address set to %s", $h, $ip); | |
+ } | |
+ } | |
+ else | |
+ { | |
+ $config{$h}{'status'} = 'failed'; | |
+ failed("updating %s: Server said: '$returned'", $h); | |
+ } | |
+ } | |
+} | |
###################################################################### | |
## nic_duckdns_examples | |
###################################################################### | |
@@ -4291,6 +4588,256 @@ | |
} | |
###################################################################### | |
+## nic_freemyip_examples | |
+###################################################################### | |
+sub nic_freemyip_examples { | |
+ return <<EoEXAMPLE; | |
+o 'freemyip' | |
+ | |
+The 'freemyip' protocol is used by the free | |
+dynamic DNS service available at freemyip.com. | |
+API is documented here: https://freemyip.com/help.py | |
+ | |
+Configuration variables applicable to the 'freemyip' protocol are: | |
+ protocol=freemyip ## | |
+ password=service-token ## token for your domain | |
+ non-fully.qualified.host ## the host registered with the service. | |
+ | |
+Example ${program}.conf file entries: | |
+ ## single host update | |
+ protocol=freemyip, \\ | |
+ password=35a6b8d65c6e67c7f78cca65cd \\ | |
+ myhost | |
+ | |
+EoEXAMPLE | |
+} | |
+ | |
+###################################################################### | |
+## nic_freemyip_update | |
+## by Cadence (reused code from nic_duckdns) | |
+## http://freemyip.com/update?token=ec54b4b64db27fe8873c7f7&domain=myhost | |
+## response contains OK or ERROR | |
+###################################################################### | |
+sub nic_freemyip_update { | |
+ debug("\nnic_freemyip_update -------------------"); | |
+ | |
+ foreach my $h (@_) { | |
+ my $ip = delete $config{$h}{'wantip'}; | |
+ info("setting IP address to %s for %s", $ip, $h); | |
+ verbose("UPDATE:","updating %s", $h); | |
+ | |
+ # Set the URL that we're going to to update | |
+ my $url; | |
+ $url = "http://$config{$h}{'server'}/update"; | |
+ $url .= "?token="; | |
+ $url .= $config{$h}{'password'}; | |
+ $url .= "&domain="; | |
+ $url .= $h; | |
+ | |
+ # Try to get URL | |
+ my $reply = geturl(opt('proxy'), $url); | |
+ | |
+ # No response, declare as failed | |
+ if (!defined($reply) || !$reply) { | |
+ failed("updating %s: Could not connect to %s.", $h, $config{$h}{'server'}); | |
+ last; | |
+ } | |
+ last if !header_ok($h, $reply); | |
+ | |
+ my @reply = split /\n/, $reply; | |
+ my $returned = pop(@reply); | |
+ if ($returned =~ /OK/) | |
+ { | |
+ $config{$h}{'ip'} = $ip; | |
+ $config{$h}{'mtime'} = $now; | |
+ $config{$h}{'status'} = 'good'; | |
+ success("updating %s: good: IP address set to %s", $h, $ip); | |
+ } | |
+ else | |
+ { | |
+ $config{$h}{'status'} = 'failed'; | |
+ failed("updating %s: Server said: '$returned'", $h); | |
+ } | |
+ } | |
+} | |
+ | |
+###################################################################### | |
+## nic_woima_examples | |
+###################################################################### | |
+sub nic_woima_examples { | |
+ return <<EoEXAMPLE; | |
+o 'woima' | |
+ | |
+The 'woima' protocol is used by the free | |
+dynamic DNS service offered by woima.fi. | |
+It offers also nameservers for own domains for free. | |
+Dynamic DNS service for own domains is not free. | |
+ | |
+Configuration variables applicable to the 'woima' protocol are: | |
+ protocol=woima ## | |
+ server=fqdn.of.service ## defaults to dyn.woima.fi | |
+ script=/path/to/script ## defaults to /nic/update | |
+ backupmx=no|yes ## indicates that this host is the primary MX for the domain. | |
+ static=no|yes ## indicates that this host has a static IP address. | |
+ custom=no|yes ## indicates that this host is a 'custom' top-level domain name. | |
+ mx=any.host.domain ## a host MX'ing for this host definition. | |
+ wildcard=no|yes ## add a DNS wildcard CNAME record that points to {host} | |
+ login=service-login ## login name and password registered with the service | |
+ password=service-password ## | |
+ fully.qualified.host ## the host registered with the service. | |
+ | |
+Example ${program}.conf file entries: | |
+ ## single host update | |
+ protocol=woima, \\ | |
+ login=my-dyndns.org-login, \\ | |
+ password=my-dyndns.org-password \\ | |
+ myhost.dyndns.org | |
+ | |
+ ## multiple host update with wildcard'ing mx, and backupmx | |
+ protocol=woima, \\ | |
+ login=my-dyndns.org-login, \\ | |
+ password=my-dyndns.org-password, \\ | |
+ mx=a.host.willing.to.mx.for.me,backupmx=yes,wildcard=yes \\ | |
+ myhost.dyndns.org,my2ndhost.dyndns.org | |
+ | |
+ ## multiple host update to the custom DNS service | |
+ protocol=woima, \\ | |
+ login=my-dyndns.org-login, \\ | |
+ password=my-dyndns.org-password \\ | |
+ my-toplevel-domain.com,my-other-domain.com | |
+EoEXAMPLE | |
+} | |
+###################################################################### | |
+## nic_woima_update | |
+###################################################################### | |
+sub nic_woima_update { | |
+ debug("\nnic_woima_update -------------------"); | |
+ | |
+ my %errors = ( | |
+ 'badauth' => 'Bad authorization (username or password)', | |
+ 'badsys' => 'The system parameter given was not valid', | |
+ | |
+ 'notfqdn' => 'A Fully-Qualified Domain Name was not provided', | |
+ 'nohost' => 'The hostname specified does not exist in the database', | |
+ '!yours' => 'The hostname specified exists, but not under the username currently being used', | |
+ '!donator' => 'The offline setting was set, when the user is not a donator', | |
+ '!active' => 'The hostname specified is in a Custom DNS domain which has not yet been activated.', | |
+ 'abuse', => 'The hostname specified is blocked for abuse; you should receive an email notification ' . | |
+ 'which provides an unblock request link. More info can be found on ' . | |
+ 'https://www.dyndns.com/support/abuse.html', | |
+ | |
+ 'numhost' => 'System error: Too many or too few hosts found. Contact support@dyndns.org', | |
+ 'dnserr' => 'System error: DNS error encountered. Contact support@dyndns.org', | |
+ | |
+ 'nochg' => 'No update required; unnecessary attempts to change to the current address are considered abusive', | |
+ ); | |
+ | |
+ my @hosts = @_; | |
+ foreach my $key (keys @hosts) { | |
+ my $h = $hosts[$key]; | |
+ my $ip = $config{$h}{'wantip'}; | |
+ delete $config{$h}{'wantip'}; | |
+ | |
+ info("setting IP address to %s for %s", $ip, $h); | |
+ verbose("UPDATE:","updating %s", $h); | |
+ | |
+ ## Select the DynDNS system to update | |
+ my $url = "http://$config{$h}{'server'}$config{$h}{'script'}?system="; | |
+ if ($config{$h}{'custom'}) { | |
+ warning("updating %s: 'custom' and 'static' may not be used together. ('static' ignored)", $h) | |
+ if $config{$h}{'static'}; | |
+ # warning("updating %s: 'custom' and 'offline' may not be used together. ('offline' ignored)", $h) | |
+ # if $config{$h}{'offline'}; | |
+ $url .= 'custom'; | |
+ | |
+ } elsif ($config{$h}{'static'}) { | |
+ # warning("updating %s: 'static' and 'offline' may not be used together. ('offline' ignored)", $h) | |
+ # if $config{$h}{'offline'}; | |
+ $url .= 'statdns'; | |
+ | |
+ } else { | |
+ $url .= 'dyndns'; | |
+ } | |
+ | |
+ $url .= "&hostname=$h"; | |
+ $url .= "&myip="; | |
+ $url .= $ip if $ip; | |
+ | |
+ ## some args are not valid for a custom domain. | |
+ $url .= "&wildcard=ON" if ynu($config{$h}{'wildcard'}, 1, 0, 0); | |
+ if ($config{$h}{'mx'}) { | |
+ $url .= "&mx=$config{$h}{'mx'}"; | |
+ $url .= "&backmx=" . ynu($config{$h}{'backupmx'}, 'YES', 'NO'); | |
+ } | |
+ | |
+ my $reply = geturl(opt('proxy'), $url, $config{$h}{'login'}, $config{$h}{'password'}); | |
+ if (!defined($reply) || !$reply) { | |
+ failed("updating %s: Could not connect to %s.", $h, $config{$h}{'server'}); | |
+ last; | |
+ } | |
+ last if !header_ok($h, $reply); | |
+ | |
+ my @reply = split /\n/, $reply; | |
+ my $state = 'header'; | |
+ my $returnedip = $ip; | |
+ | |
+ foreach my $line (@reply) { | |
+ if ($state eq 'header') { | |
+ $state = 'body'; | |
+ | |
+ } elsif ($state eq 'body') { | |
+ $state = 'results' if $line eq ''; | |
+ | |
+ } elsif ($state =~ /^results/) { | |
+ $state = 'results2'; | |
+ | |
+ # bug #10: some dyndns providers does not return the IP so | |
+ # we can't use the returned IP | |
+ my ($status, $returnedip) = split / /, lc $line; | |
+ $ip = $returnedip if (not $ip); | |
+ #my $h = shift @hosts; | |
+ | |
+ $config{$h}{'status'} = $status; | |
+ if ($status eq 'good') { | |
+ $config{$h}{'ip'} = $ip; | |
+ $config{$h}{'mtime'} = $now; | |
+ success("updating %s: %s: IP address set to %s", $h, $status, $ip); | |
+ | |
+ } elsif (exists $errors{$status}) { | |
+ if ($status eq 'nochg') { | |
+ warning("updating %s: %s: %s", $h, $status, $errors{$status}); | |
+ $config{$h}{'ip'} = $ip; | |
+ $config{$h}{'mtime'} = $now; | |
+ $config{$h}{'status'} = 'good'; | |
+ | |
+ } else { | |
+ failed("updating %s: %s: %s", $h, $status, $errors{$status}); | |
+ } | |
+ | |
+ } elsif ($status =~ /w(\d+)(.)/) { | |
+ my ($wait, $units) = ($1, lc $2); | |
+ my ($sec, $scale) = ($wait, 1); | |
+ | |
+ ($scale, $units) = (1, 'seconds') if $units eq 's'; | |
+ ($scale, $units) = (60, 'minutes') if $units eq 'm'; | |
+ ($scale, $units) = (60*60, 'hours') if $units eq 'h'; | |
+ | |
+ $sec = $wait * $scale; | |
+ $config{$h}{'wtime'} = $now + $sec; | |
+ warning("updating %s: %s: wait $wait $units before further updates", $h, $status, $ip); | |
+ | |
+ } else { | |
+ failed("updating %s: %s: unexpected status (%s)", $h, $line); | |
+ } | |
+ } | |
+ } | |
+ failed("updating %s: Could not connect to %s.", $h, $config{$h}{'server'}) | |
+ if $state ne 'results2'; | |
+ } | |
+} | |
+ | |
+ | |
+###################################################################### | |
# vim: ai ts=4 sw=4 tw=78 : | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment