Skip to content

Instantly share code, notes, and snippets.

@psct
Created June 18, 2019 14:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save psct/a8d08573e05797775d2b734088281e01 to your computer and use it in GitHub Desktop.
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
--- /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