Add support for ionos.com
This commit is contained in:
parent
af4ea14fda
commit
60e3cfeb4c
5 changed files with 145 additions and 0 deletions
|
@ -99,6 +99,8 @@ repository history](https://github.com/ddclient/ddclient/commits/master).
|
||||||
[#726](https://github.com/ddclient/ddclient/pull/726)
|
[#726](https://github.com/ddclient/ddclient/pull/726)
|
||||||
* `porkbun`: The update URL hostname is now configurable via the `server`
|
* `porkbun`: The update URL hostname is now configurable via the `server`
|
||||||
option. [#752](https://github.com/ddclient/ddclient/pull/752)
|
option. [#752](https://github.com/ddclient/ddclient/pull/752)
|
||||||
|
* `ionos`: Added support for updating Ionos records.
|
||||||
|
[#743](https://github.com/ddclient/ddclient/pull/743)
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
|
|
||||||
|
|
|
@ -73,6 +73,7 @@ handwritten_tests = \
|
||||||
t/protocol_directnic.pl \
|
t/protocol_directnic.pl \
|
||||||
t/protocol_dnsexit2.pl \
|
t/protocol_dnsexit2.pl \
|
||||||
t/protocol_dyndns2.pl \
|
t/protocol_dyndns2.pl \
|
||||||
|
t/protocol_ionos.pl \
|
||||||
t/read_recap.pl \
|
t/read_recap.pl \
|
||||||
t/skip.pl \
|
t/skip.pl \
|
||||||
t/ssl-validate.pl \
|
t/ssl-validate.pl \
|
||||||
|
|
|
@ -38,6 +38,7 @@ Dynamic DNS services currently supported include:
|
||||||
* [Hurricane Electric](https://dns.he.net)
|
* [Hurricane Electric](https://dns.he.net)
|
||||||
* [Infomaniak](https://faq.infomaniak.com/2376)
|
* [Infomaniak](https://faq.infomaniak.com/2376)
|
||||||
* [INWX](https://www.inwx.com/)
|
* [INWX](https://www.inwx.com/)
|
||||||
|
* [Ionos](https://ionos.com)
|
||||||
* [Loopia](https://www.loopia.se)
|
* [Loopia](https://www.loopia.se)
|
||||||
* [Mythic Beasts](https://www.mythic-beasts.com/support/api/dnsv2/dynamic-dns)
|
* [Mythic Beasts](https://www.mythic-beasts.com/support/api/dnsv2/dynamic-dns)
|
||||||
* [NameCheap](https://www.namecheap.com)
|
* [NameCheap](https://www.namecheap.com)
|
||||||
|
|
72
ddclient.in
72
ddclient.in
|
@ -1295,6 +1295,15 @@ our %protocols = (
|
||||||
'max-interval' => setv(T_DELAY, 0, 'inf', 0),
|
'max-interval' => setv(T_DELAY, 0, 'inf', 0),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
'ionos' => ddclient::Protocol->new(
|
||||||
|
'update' => \&nic_ionos_update,
|
||||||
|
'examples' => \&nic_ionos_examples,
|
||||||
|
'cfgvars' => {
|
||||||
|
%{$cfgvars{'protocol-common-defaults'}},
|
||||||
|
'server' => setv(T_FQDNP, 0, 'ipv4.api.hosting.ionos.com', undef),
|
||||||
|
'login' => undef,
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
$cfgvars{'merged'} = {
|
$cfgvars{'merged'} = {
|
||||||
map({ %{$protocols{$_}{'cfgvars'}} } keys(%protocols)),
|
map({ %{$protocols{$_}{'cfgvars'}} } keys(%protocols)),
|
||||||
|
@ -7644,6 +7653,69 @@ Example ${program}.conf file entries:
|
||||||
EoEXAMPLE
|
EoEXAMPLE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
## nic_ionos_examples
|
||||||
|
######################################################################
|
||||||
|
sub nic_ionos_examples {
|
||||||
|
my $self = shift;
|
||||||
|
return <<EoEXAMPLE;
|
||||||
|
o 'ionos'
|
||||||
|
|
||||||
|
The 'ionos' protocol is used by DNS service offered by ionos.com.
|
||||||
|
|
||||||
|
Configuration variables applicable to the 'ionos' protocol are:
|
||||||
|
protocol=ionos ##
|
||||||
|
password=dyndns-update-key ## the key created to update the host below
|
||||||
|
fully.qualified.host ## the host registered with the service.
|
||||||
|
|
||||||
|
Example ${program}.conf file entries:
|
||||||
|
## single host update
|
||||||
|
protocol=ionos,
|
||||||
|
password=dyndns-update-key
|
||||||
|
myhost.com
|
||||||
|
|
||||||
|
Getting the DynDNS key (only available via Ionos API):
|
||||||
|
|
||||||
|
1. Create an API key
|
||||||
|
2. Enable DynDNS for your host using the /dns/v1/dyndns API
|
||||||
|
(documentation @ https://developer.hosting.ionos.es/docs/dns)
|
||||||
|
3. In the API response, get the key value. The key is a long string
|
||||||
|
from the "q=" parameter. The URL looks like this:
|
||||||
|
https://ipv4.api.hosting.ionos.com/dns/v1/dyndns?q=XXXXXXXXXXXXXXX
|
||||||
|
(in the example above the key is "XXXXXXXXXXXXXXX").
|
||||||
|
|
||||||
|
Note: Because the key is individual for each host, you cannot update
|
||||||
|
multiple hosts with the same key.
|
||||||
|
EoEXAMPLE
|
||||||
|
}
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
## nic_ionos_update
|
||||||
|
## response contains "code 200" on succesfull completion
|
||||||
|
######################################################################
|
||||||
|
sub nic_ionos_update {
|
||||||
|
my $self = shift;
|
||||||
|
## update each configured host
|
||||||
|
## should improve to update in one pass
|
||||||
|
for my $h (@_) {
|
||||||
|
local $_l = pushlogctx($h);
|
||||||
|
my $ip = delete $config{$h}{'wantipv4'};
|
||||||
|
info("setting IP address to $ip");
|
||||||
|
my $url = opt('server', $h) . "/dns/v1/dyndns?q=" . opt('password', $h);
|
||||||
|
my $reply = geturl(proxy => opt('proxy'), url => $url);
|
||||||
|
|
||||||
|
if (header_ok($reply)) {
|
||||||
|
$recap{$h}{'ipv4'} = $ip;
|
||||||
|
$recap{$h}{'mtime'} = $now;
|
||||||
|
$recap{$h}{'status-ipv4'} = 'good';
|
||||||
|
success("IP address set to $ip");
|
||||||
|
} else {
|
||||||
|
$recap{$h}{'status'} = 'failed';
|
||||||
|
failed("server said: $reply");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# Execute main() if this file is run as a script or run via PAR (https://metacpan.org/pod/PAR),
|
# 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
|
# 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
|
# and test its functions directly; there's no need for test-only command-line arguments or stdout
|
||||||
|
|
69
t/protocol_ionos.pl
Normal file
69
t/protocol_ionos.pl
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
use Test::More;
|
||||||
|
BEGIN { eval { require Test::Warnings; } or skip($@, 1); }
|
||||||
|
BEGIN { eval { require 'ddclient'; } or BAIL_OUT($@); }
|
||||||
|
BEGIN {
|
||||||
|
eval { require ddclient::t::HTTPD; 1; } or plan(skip_all => $@);
|
||||||
|
ddclient::t::HTTPD->import();
|
||||||
|
plan tests => 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
package Logger;
|
||||||
|
use parent qw(-norequire ddclient::Logger);
|
||||||
|
sub new {
|
||||||
|
my ($class, $parent) = @_;
|
||||||
|
my $self = $class->SUPER::new(undef, $parent);
|
||||||
|
$self->{logs} = [];
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
sub _log {
|
||||||
|
my ($self, $args) = @_;
|
||||||
|
push(@{$self->{logs}}, $args)
|
||||||
|
if ($args->{label} // '') =~ qr/^(?:WARNING|FATAL|SUCCESS|FAILED)$/;
|
||||||
|
return $self->SUPER::_log($args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Subtest for the protocol_ionos
|
||||||
|
subtest 'Testing protocol_ionos' => sub {
|
||||||
|
# Mock HTTP server
|
||||||
|
httpd()->run(sub {
|
||||||
|
my ($req) = @_;
|
||||||
|
diag('==============================================================================');
|
||||||
|
diag("Test server received request:\n" . $req->as_string());
|
||||||
|
|
||||||
|
return undef if $req->uri()->path() eq '/control';
|
||||||
|
return [400, $textplain, ['invalid method: ' . $req->method()]] if $req->method() ne 'GET';
|
||||||
|
return undef
|
||||||
|
});
|
||||||
|
|
||||||
|
local $ddclient::globals{debug} = 1;
|
||||||
|
local $ddclient::globals{verbose} = 1;
|
||||||
|
my $l = Logger->new($ddclient::_l);
|
||||||
|
|
||||||
|
local %ddclient::config = (
|
||||||
|
'host.my.example.com' => {
|
||||||
|
'protocol' => 'ionos',
|
||||||
|
'password' => 'mytestingpassword',
|
||||||
|
'server' => httpd()->endpoint(),
|
||||||
|
'wantipv4' => '1.2.3.4',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
httpd()->reset([200, $textplain, []]);
|
||||||
|
|
||||||
|
{
|
||||||
|
local $ddclient::_l = $l;
|
||||||
|
|
||||||
|
ddclient::nic_ionos_update(undef, 'host.my.example.com');
|
||||||
|
}
|
||||||
|
|
||||||
|
my @requests = httpd()->reset();
|
||||||
|
is(scalar(@requests), 1, "Single update request");
|
||||||
|
|
||||||
|
my $req = shift(@requests);
|
||||||
|
is($req->uri()->path(), '/dns/v1/dyndns', "Correct request path");
|
||||||
|
is($req->uri()->query(), 'q=mytestingpassword', "Correct request query");
|
||||||
|
};
|
||||||
|
|
||||||
|
done_testing();
|
Loading…
Reference in a new issue