Merge branch 'master' into cdmon
This commit is contained in:
commit
6957c77730
5 changed files with 170 additions and 28 deletions
|
|
@ -25,6 +25,8 @@ repository history](https://github.com/ddclient/ddclient/commits/master).
|
|||
|
||||
* DynDNS2 now uses the newer ipv4/ipv6 syntax's
|
||||
* The OVH provider now ignores extra data returned
|
||||
* Allow to define usev4 and usev6 options per hostname
|
||||
* Merge multiple configs for the same hostname instead of use the last
|
||||
|
||||
## 2022-10-20 v3.10.0
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ EXTRA_DIST = \
|
|||
ChangeLog.md \
|
||||
README.cisco \
|
||||
README.md \
|
||||
README.ssl \
|
||||
autogen \
|
||||
sample-ddclient-wrapper.sh \
|
||||
sample-etc_cron.d_ddclient \
|
||||
|
|
|
|||
|
|
@ -340,15 +340,6 @@ ssl=yes # use ssl-support. Works with
|
|||
# password=mypassword
|
||||
# my-domain.com
|
||||
|
||||
##
|
||||
## Enom (www.enom.com)
|
||||
##
|
||||
# protocol=enom,
|
||||
# login=domain.name,
|
||||
# password=domain-password
|
||||
# my-domain.com
|
||||
|
||||
|
||||
##
|
||||
## cdmon.org (https://dinamico.cdmon.org/)
|
||||
##
|
||||
|
|
@ -357,3 +348,19 @@ ssl=yes # use ssl-support. Works with
|
|||
#login=my-cdmon.org-login, \
|
||||
#password=my-cdmon.org-password \
|
||||
#domain.example.com
|
||||
|
||||
##
|
||||
## Enom (www.enom.com)
|
||||
##
|
||||
# protocol=enom,
|
||||
# login=domain.name,
|
||||
# password=domain-password
|
||||
# my-domain.com
|
||||
|
||||
##
|
||||
## DigitalOcean (www.digitalocean.com)
|
||||
##
|
||||
#protocol=digitalocean, \
|
||||
#zone=example.com, \
|
||||
#password=api-token \
|
||||
#example.com,sub.example.com
|
||||
|
|
|
|||
163
ddclient.in
163
ddclient.in
|
|
@ -614,6 +614,17 @@ my %services = (
|
|||
'password' => setv(T_STRING, 0, 0, 'unused', undef),
|
||||
},
|
||||
},
|
||||
'digitalocean' => {
|
||||
'updateable' => undef,
|
||||
'update' => \&nic_digitalocean_update,
|
||||
'examples' => \&nic_digitalocean_examples,
|
||||
'variables' => {
|
||||
%{$variables{'service-common-defaults'}},
|
||||
'server' => setv(T_FQDNP, 1, 0, 'api.digitalocean.com', undef),
|
||||
'zone' => setv(T_FQDN, 1, 0, '', undef),
|
||||
'login' => setv(T_LOGIN, 0, 0, 'unused', undef),
|
||||
},
|
||||
},
|
||||
'dinahosting' => {
|
||||
'updateable' => undef,
|
||||
'update' => \&nic_dinahosting_update,
|
||||
|
|
@ -940,13 +951,13 @@ my %services = (
|
|||
},
|
||||
},
|
||||
'keysystems' => {
|
||||
'updateable' => undef,
|
||||
'update' => \&nic_keysystems_update,
|
||||
'examples' => \&nic_keysystems_examples,
|
||||
'variables' => merge(
|
||||
'updateable' => undef,
|
||||
'update' => \&nic_keysystems_update,
|
||||
'examples' => \&nic_keysystems_examples,
|
||||
'variables' => merge(
|
||||
$variables{'keysystems-common-defaults'},
|
||||
$variables{'service-common-defaults'},
|
||||
),
|
||||
),
|
||||
},
|
||||
'dnsexit' => {
|
||||
'updateable' => undef,
|
||||
|
|
@ -971,10 +982,10 @@ my %services = (
|
|||
'update' => \&nic_enom_update,
|
||||
'examples' => \&nic_enom_examples,
|
||||
'variables' => {
|
||||
%{$variables{'service-common-defaults'}},
|
||||
'server' => setv(T_FQDNP, 1, 0, 'dynamic.name-services.com', undef),
|
||||
'min-interval' => setv(T_DELAY, 0, 0, 0, interval('5m')),
|
||||
},
|
||||
%{$variables{'service-common-defaults'}},
|
||||
'server' => setv(T_FQDNP, 1, 0, 'dynamic.name-services.com', undef),
|
||||
'min-interval' => setv(T_DELAY, 0, 0, 0, interval('5m')),
|
||||
},
|
||||
},
|
||||
);
|
||||
$variables{'merged'} = {
|
||||
|
|
@ -1380,7 +1391,7 @@ sub write_cache {
|
|||
## merge the updated host entries into the cache.
|
||||
foreach my $h (keys %config) {
|
||||
if (!exists $cache{$h} || $config{$h}{'update'}) {
|
||||
map { defined($config{$h}{$_}) ? ($cache{$h}{$_} = $config{$h}{$_}) : () } @{$config{$h}{'cacheable'}};
|
||||
map { defined($config{$h}{$_}) ? ($cache{$h}{$_} = $config{$h}{$_}) : () } @{$config{$h}{'cacheable'}};
|
||||
} else {
|
||||
map { $cache{$h}{$_} = $config{$h}{$_} } qw(atime wtime status);
|
||||
}
|
||||
|
|
@ -1648,9 +1659,14 @@ sub _read_config {
|
|||
|
||||
## allow {host} to be a comma separated list of hosts
|
||||
foreach my $h (split_by_comma($host)) {
|
||||
## save a copy of the current globals
|
||||
$config{$h} = { %locals };
|
||||
$config{$h}{'host'} = $h;
|
||||
if ($config{$h}) {
|
||||
## host already defined, merging configs
|
||||
$config{$h} = { %{merge($config{$h}, \%locals)} };
|
||||
} else {
|
||||
## save a copy of the current globals
|
||||
$config{$h} = { %locals };
|
||||
$config{$h}{'host'} = $h;
|
||||
}
|
||||
}
|
||||
}
|
||||
%passwords = ();
|
||||
|
|
@ -1799,7 +1815,7 @@ sub init_config {
|
|||
$proto = opt('protocol') if !defined($proto);
|
||||
|
||||
load_sha1_support($proto) if (grep (/^$proto$/, ("freedns", "nfsn")));
|
||||
load_json_support($proto) if (grep (/^$proto$/, ("1984", "cloudflare", "gandi", "godaddy", "hetzner", "yandex", "nfsn", "njalla", "porkbun")));
|
||||
load_json_support($proto) if (grep (/^$proto$/, ("1984", "cloudflare", "digitalocean", "gandi", "godaddy", "hetzner", "yandex", "nfsn", "njalla", "porkbun")));
|
||||
|
||||
if (!exists($services{$proto})) {
|
||||
warning("skipping host: %s: unrecognized protocol '%s'", $h, $proto);
|
||||
|
|
@ -5858,7 +5874,7 @@ sub nic_changeip_update {
|
|||
}
|
||||
|
||||
######################################################################
|
||||
## nic_googledomains_examples
|
||||
## nic_godaddy_examples
|
||||
##
|
||||
## written by awalon
|
||||
##
|
||||
|
|
@ -7989,6 +8005,123 @@ sub nic_enom_update {
|
|||
}
|
||||
}
|
||||
|
||||
sub nic_digitalocean_examples {
|
||||
return <<"EoEXAMPLE";
|
||||
o 'digitalocean'
|
||||
|
||||
The 'digitalocean' protocol updates domains hosted by Digital Ocean (https://www.digitalocean.com/).
|
||||
|
||||
This protocol supports both IPv4 and IPv6. It will only update an existing record; it will not
|
||||
create a new one. So, before using it, make sure there's already one (and at most one) of each
|
||||
record type (A and/or AAAA) you plan to update present in your Digital Ocean zone.
|
||||
|
||||
This protocol implements the API documented here:
|
||||
https://docs.digitalocean.com/reference/api/api-reference/.
|
||||
|
||||
You can get your API token by following these instructions:
|
||||
https://docs.digitalocean.com/reference/api/create-personal-access-token/
|
||||
|
||||
Available configuration variables:
|
||||
* server (optional): API server. Defaults to 'api.digitalocean.com'.
|
||||
* zone (required): DNS zone under which the hostname falls.
|
||||
* password (required): API token from DigitalOcean Control Panel. See instructions linked above.
|
||||
|
||||
Example ${program}.conf file entries:
|
||||
protocol=digitalocean, \\
|
||||
zone=example.com, \\
|
||||
password=api-token \\
|
||||
example.com,sub.example.com
|
||||
EoEXAMPLE
|
||||
}
|
||||
|
||||
sub nic_digitalocean_update_one {
|
||||
my ($h, $ip, $ipv) = @_;
|
||||
|
||||
info("setting %s address to %s for %s", $ipv, $ip, $h);
|
||||
|
||||
my $server = $config{$h}{'server'};
|
||||
my $type = $ipv eq 'ipv6' ? 'AAAA' : 'A';
|
||||
|
||||
my $headers;
|
||||
$headers = "Content-Type: application/json\n";
|
||||
$headers .= "Authorization: Bearer $config{$h}{'password'}\n";
|
||||
|
||||
my $list_url;
|
||||
$list_url = "https://$server/v2/domains/$config{$h}{'zone'}/records";
|
||||
$list_url .= "?name=$h";
|
||||
$list_url .= "&type=$type";
|
||||
|
||||
my $list_resp = geturl(
|
||||
proxy => opt('proxy'),
|
||||
url => $list_url,
|
||||
headers => $headers,
|
||||
);
|
||||
unless ($list_resp && header_ok($h, $list_resp)) {
|
||||
$config{$h}{"status-$ipv"} = 'failed';
|
||||
failed("listing %s %s: Failed connection or bad response from %s.", $h, $ipv, $server);
|
||||
return;
|
||||
}
|
||||
$list_resp =~ s/^.*?\n\n//s; # Strip header
|
||||
|
||||
my $list = eval { decode_json($list_resp) };
|
||||
if ($@) {
|
||||
$config{$h}{"status-$ipv"} = 'failed';
|
||||
failed("listing %s %s: JSON decoding failure", $h, $ipv);
|
||||
return;
|
||||
}
|
||||
|
||||
my $elem = $list;
|
||||
unless ((ref($elem) eq 'HASH') &&
|
||||
(ref ($elem = $elem->{'domain_records'}) eq 'ARRAY') &&
|
||||
(@$elem == 1 && ref ($elem = $elem->[0]) eq 'HASH')) {
|
||||
$config{$h}{"status-$ipv"} = 'failed';
|
||||
failed("listing %s %s: no record, multiple records, or malformed JSON", $h, $ipv);
|
||||
return;
|
||||
}
|
||||
|
||||
my $current_ip = $elem->{'data'};
|
||||
my $record_id = $elem->{'id'};
|
||||
|
||||
if ($current_ip eq $ip) {
|
||||
info("updating %s %s: IP is already %s, no update needed.", $h, $ipv, $ip);
|
||||
} else {
|
||||
my $update_data = encode_json({'type' => $type, 'data' => $ip});
|
||||
my $update_resp = geturl(
|
||||
proxy => opt('proxy'),
|
||||
url => "https://$server/v2/domains/$config{$h}{'zone'}/records/$record_id",
|
||||
method => 'PATCH',
|
||||
headers => $headers,
|
||||
data => $update_data,
|
||||
);
|
||||
unless ($update_resp && header_ok($h, $update_resp)) {
|
||||
$config{$h}{"status-$ipv"} = 'failed';
|
||||
failed("updating %s %s: Failed connection or bad response from %s.", $h, $ipv, $server);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$config{$h}{"status-$ipv"} = 'good';
|
||||
$config{$h}{"ip-$ipv"} = $ip;
|
||||
$config{$h}{"mtime"} = $now;
|
||||
}
|
||||
|
||||
sub nic_digitalocean_update {
|
||||
debug("\nnic_digitalocean_update -------------------");
|
||||
|
||||
foreach my $h (@_) {
|
||||
my $ipv4 = delete $config{$h}{'wantipv4'};
|
||||
my $ipv6 = delete $config{$h}{'wantipv6'};
|
||||
|
||||
if ($ipv4) {
|
||||
nic_digitalocean_update_one($h, $ipv4, 'ipv4');
|
||||
}
|
||||
|
||||
if ($ipv6) {
|
||||
nic_digitalocean_update_one($h, $ipv6, 'ipv6');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Execute main() if this file is run as a script or run via PAR (https://metacpan.org/pod/PAR),
|
||||
# otherwise do nothing. This "modulino" pattern makes it possible to import this file as a module
|
||||
# and test its functions directly; there's no need for test-only command-line arguments or stdout
|
||||
|
|
|
|||
|
|
@ -9,12 +9,13 @@
|
|||
#
|
||||
# All credits for this one liner go to the author of this blog:
|
||||
# http://scytale.name/blog/2010/01/fritzbox-wan-ip
|
||||
# As the author explains its not required to tamper with the provided IP for the FritzBox
|
||||
# as it always binds to that address for UPnP.
|
||||
# Disclaimer: It might be necessary to make the script executable
|
||||
|
||||
# Set default hostname to connect to the FritzBox
|
||||
: ${FRITZ_BOX_HOSTNAME:=fritz.box}
|
||||
|
||||
curl -s -H 'Content-Type: text/xml; charset="utf-8"' \
|
||||
-H 'SOAPAction: urn:schemas-upnp-org:service:WANIPConnection:1#GetExternalIPAddress' \
|
||||
-d '<?xml version="1.0" encoding="utf-8"?><s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <s:Body> <u:GetExternalIPAddress xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1" /></s:Body></s:Envelope>' \
|
||||
'http://fritz.box:49000/igdupnp/control/WANIPConn1' | \
|
||||
"http://$FRITZ_BOX_HOSTNAME:49000/igdupnp/control/WANIPConn1" | \
|
||||
grep -Eo '\<[[:digit:]]{1,3}(\.[[:digit:]]{1,3}){3}\>'
|
||||
|
|
|
|||
Loading…
Reference in a new issue