Merge branch 'master' into cdmon

This commit is contained in:
Serrallonga25 2023-02-24 14:32:47 +01:00 committed by GitHub
commit 680573372d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 691 additions and 462 deletions

View file

@ -3,7 +3,31 @@
This document describes notable changes. For details, see the [source code
repository history](https://github.com/ddclient/ddclient/commits/master).
## 2023-XX-XX v3.11.0
### Breaking changes
* ddclient no longer ships any example files for init systems that use `/etc/init.d`.
This was done because those files where effectively unmaintained, untested by the developers and only updated by downstream distros.
If you where relying on those files, please copy them into your packaging.
### New features
* Added support for domaindiscount24.com
* Added support for domeneshop.no
* Added support for Enom
* Added support for Mythic Beasts Dynamic DNS
* Added support for njal.la
* Added support for Porkbun
* Added support for IPv6 to the EasyDNS and DuckDNS provider
### Bug fixes
* DynDNS2 now uses the newer ipv4/ipv6 syntax's
* The OVH provider now ignores extra data returned
## 2022-10-20 v3.10.0
### New features
* Added support for domaindiscount24.com
@ -12,6 +36,7 @@ repository history](https://github.com/ddclient/ddclient/commits/master).
## 2022-05-15 v3.10.0_2
### Bug fixes
* Fix version number being unable to parse
## 2022-05-15 v3.10.0_1

View file

@ -13,12 +13,6 @@ EXTRA_DIST = \
sample-etc_dhclient-exit-hooks \
sample-etc_dhcpc_dhcpcd-eth0.exe \
sample-etc_ppp_ip-up.local \
sample-etc_rc.d_ddclient.freebsd \
sample-etc_rc.d_init.d_ddclient \
sample-etc_rc.d_init.d_ddclient.alpine \
sample-etc_rc.d_init.d_ddclient.lsb \
sample-etc_rc.d_init.d_ddclient.redhat \
sample-etc_rc.d_init.d_ddclient.ubuntu \
sample-etc_systemd.service \
sample-get-ip-from-fritzbox
CLEANFILES =

View file

@ -29,6 +29,7 @@ Dynamic DNS services currently supported include:
DonDominio - See https://www.dondominio.com for details
NearlyFreeSpeech.net - See https://www.nearlyfreespeech.net/services/dns for details
OVH - See https://www.ovh.com for details
Porkbun - See https://porkbun.com/
ClouDNS - See https://www.cloudns.net
dinahosting - See https://dinahosting.com
Gandi - See https://gandi.net
@ -36,6 +37,9 @@ Dynamic DNS services currently supported include:
1984.is - See https://www.1984.is/product/freedns/ for details
Njal.la - See https://njal.la/docs/ddns/
regfish.de - See https://www.regfish.de/domains/dyndns/ for details
domenehsop - See https://api.domeneshop.no/docs/#tag/ddns/paths/~1dyndns~1update/get
Mythic Beasts - See https://www.mythic-beasts.com/support/api/dnsv2/dynamic-dns for details
Enom - See https://www.enom.com for details
cdmon.org - See https://ticket.cdmon.com/ca/support/solutions/articles/7000005922-api-d-actualitzaci%C3%B3-de-ip-del-dns-gratis-din%C3%A0mic
`ddclient` now supports many cable and DSL broadband routers.
@ -113,82 +117,6 @@ start the first time by hand
systemctl start ddclient.service
#### Redhat style rc files and daemon-mode
cp sample-etc_rc.d_init.d_ddclient /etc/rc.d/init.d/ddclient
enable automatic startup when booting. also check your distribution
/sbin/chkconfig --add ddclient
start the first time by hand
/etc/rc.d/init.d/ddclient start
#### Alpine style rc files and daemon-mode
cp sample-etc_rc.d_init.d_ddclient.alpine /etc/init.d/ddclient
enable automatic startup when booting
rc-update add ddclient
make sure you have perl installed
apk add perl
start the first time by hand
rc-service ddclient start
#### Ubuntu style rc files and daemon-mode
cp sample-etc_rc.d_init.d_ddclient.ubuntu /etc/init.d/ddclient
enable automatic startup when booting
update-rc.d ddclient defaults
make sure you have perl and the required modules installed
apt-get install perl libdata-validate-ip-perl libio-socket-ssl-perl
if you plan to use cloudflare or feedns you need the perl json module
apt-get install libjson-pp-perl
for IPv6 you also need to instal the perl io-socket-inet6 module
apt install libio-socket-inet6-perl
start the first time by hand
service ddclient start
#### FreeBSD style rc files and daemon mode
mkdir -p /usr/local/etc/rc.d
cp sample-etc_rc.d_ddclient.freebsd /usr/local/etc/rc.d/ddclient
enable automatic startup when booting
sysrc ddclient_enable=YES
make sure you have perl and the required modules installed
pkg install perl5 p5-Data-Validate-IP p5-IO-Socket-SSL
if you plan to use cloudflare or feedns you need the perl json module
pkg install p5-JSON-PP
start the service manually for the first time
service ddclient start
If you are not using daemon-mode, configure cron and dhcp or ppp as described below.
## TROUBLESHOOTING
1. enable debugging and verbose messages: ``$ ddclient -daemon=0 -debug -verbose -noquiet``
@ -248,7 +176,7 @@ not become stale.
cp sample-etc_cron.d_ddclient /etc/cron.d/ddclient
vi /etc/cron.d/ddclient
## USING DDCLIENT WITH `dhcpcd-1.3.17`
## USING DDCLIENT WITH `dhcpcd`
If you are using dhcpcd-1.3.17 or thereabouts, you can easily update
your DynDNS entry automatically every time your lease is obtained

View file

@ -1,13 +0,0 @@
Since 3.7.0, ddclient support ssl-updates
To use ssl, put "ssl=yes" in your configuration and make sure
you have IO::Socket::SSL.
On debian, you need libio-socket-ssl-perl to have IO::Socket::SSL
On alpine, you need perl-io-socket-ssl to have IO::Socket::SSL
ssl support is tested on folowing dynamic dns providers:
- dyndns.com
- freemyip.com
- DNS Made Easy
- dondominio.com

View file

@ -1,5 +1,5 @@
AC_PREREQ([2.63])
AC_INIT([ddclient], [3.10.1])
AC_INIT([ddclient], [3.11.0])
AC_CONFIG_SRCDIR([ddclient.in])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([m4])

View file

@ -285,6 +285,15 @@ ssl=yes # use ssl-support. Works with
# password=your_password
# test.example.com
##
## Porkbun (https://porkbun.com/)
##
# protocol=porkbun
# apikey=APIKey
# secretapikey=SecretAPIKey
# host.example.com,host2.sub.example.com
# on-root-domain=yes example.com,sub.example.com
##
## ClouDNS (https://www.cloudns.net)
##
@ -308,6 +317,14 @@ ssl=yes # use ssl-support. Works with
#password=mypassword, \
#subdomain-1.domain.com,subdomain-2.domain.com
##
## domeneshop (www.domeneshop.no)
##
# protocol=domeneshop
# login=<token>
# password=<secret>
# subdomain-1.domain.com,subdomain-2.domain.com
##
## Njal.la (http://njal.la/)
##
@ -323,6 +340,15 @@ 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/)
##

View file

@ -490,17 +490,32 @@ my %variables = (
'host' => setv(T_STRING,1, 1, '', undef),
'use' => setv(T_USE, 0, 0, 'ip', undef),
'usev4' => setv(T_USEV4, 0, 0, 'disabled', undef),
'usev6' => setv(T_USEV6, 0, 0, 'disabled', undef),
'if' => setv(T_IF, 0, 0, 'ppp0', undef),
'ifv4' => setv(T_IF, 0, 0, 'default', undef),
'ifv6' => setv(T_IF, 0, 0, 'default', undef),
'web' => setv(T_STRING,0, 0, 'dyndns', undef),
'web-skip' => setv(T_STRING,0, 0, '', undef),
'web-ssl-validate' => setv(T_BOOL, 0, 0, 1, undef),
'webv4' => setv(T_STRING,0, 0, 'googledomains', undef),
'webv4-skip' => setv(T_STRING,1, 0, '', undef),
'webv6' => setv(T_STRING,0, 0, 'googledomains', undef),
'webv6-skip' => setv(T_STRING,1, 0, '', undef),
'fw' => setv(T_ANY, 0, 0, '', undef),
'fw-skip' => setv(T_STRING,0, 0, '', undef),
'fw-login' => setv(T_LOGIN, 0, 0, '', undef),
'fw-password' => setv(T_PASSWD,0, 0, '', undef),
'fw-ssl-validate' => setv(T_BOOL, 0, 0, 1, undef),
'fwv4' => setv(T_ANY, 0, 0, '', undef),
'fwv4-skip' => setv(T_STRING,1, 0, '', undef),
'fwv6' => setv(T_ANY, 0, 0, '', undef),
'fwv6-skip' => setv(T_STRING,1, 0, '', undef),
'cmd' => setv(T_PROG, 0, 0, '', undef),
'cmd-skip' => setv(T_STRING,0, 0, '', undef),
'cmdv4' => setv(T_PROG, 0, 0, '', undef),
'cmdv6' => setv(T_PROG, 0, 0, '', undef),
'ip' => setv(T_IP, 0, 1, undef, undef), #TODO remove from cache?
'ipv4' => setv(T_IPV4, 0, 1, undef, undef),
'ipv6' => setv(T_IPV6, 0, 1, undef, undef),
@ -639,6 +654,15 @@ my %services = (
'host' => setv(T_NUMBER, 1, 1, 0, undef),
},
},
'domeneshop' => {
'updateable' => undef,
'update' => \&nic_domeneshop_update,
'examples' => \&nic_domeneshop_examples,
'variables' => {
%{$variables{'service-common-defaults'}},
'server' => setv(T_FQDNP, 1, 0, 'api.domeneshop.no', undef),
},
},
'duckdns' => {
'updateable' => undef,
'update' => \&nic_duckdns_update,
@ -753,6 +777,16 @@ my %services = (
'zone' => setv(T_FQDN, 1, 0, '', undef),
},
},
'mythicdyn' => {
'updateable' => undef,
'update' => \&nic_mythicdyn_update,
'examples' => \&nic_mythicdyn_examples,
'variables' => {
%{$variables{'service-common-defaults'}},
'min-interval' => setv(T_DELAY, 0, 0, interval('5m'), 0),
'server' => setv(T_FQDNP, 1, 0, 'api.mythic-beasts.com', undef),
},
},
'namecheap' => {
'updateable' => undef,
'update' => \&nic_namecheap_update,
@ -833,6 +867,21 @@ my %services = (
'server' => setv(T_FQDNP, 1, 0, 'www.ovh.com', undef),
},
},
'porkbun' => {
'updateable' => undef,
'update' => \&nic_porkbun_update,
'examples' => \&nic_porkbun_examples,
'variables' => {
'apikey' => setv(T_PASSWD, 1, 0, '', undef),
'secretapikey' => setv(T_PASSWD, 1, 0, '', undef),
'on-root-domain' => setv(T_BOOL, 0, 0, 0, undef),
'login' => setv(T_LOGIN, 0, 0, 'unused', undef),
'password' => setv(T_PASSWD, 0, 0, 'unused', undef),
'use' => setv(T_USE, 0, 0, 'disabled', undef),
'usev4' => setv(T_USEV4, 0, 0, 'disabled', undef),
'usev6' => setv(T_USEV6, 0, 0, 'disabled', undef),
},
},
'sitelutions' => {
'updateable' => undef,
'update' => \&nic_sitelutions_update,
@ -917,6 +966,16 @@ my %services = (
$variables{'service-common-defaults'},
),
},
'enom' => {
'updateable' => undef,
'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{'merged'} = {
map({ %{$services{$_}{'variables'}} } keys(%services)),
@ -1181,6 +1240,7 @@ sub update_nics {
my $usev6 = opt('usev6', $h) // 'disabled';
$use = 'disabled' if ($use eq 'no'); # backward compatibility
$usev6 = 'disabled' if ($usev6 eq 'no'); # backward compatibility
$use = 'disabled' if ($usev4 ne 'disabled') || ($usev6 ne 'disabled');
my $arg_ip = opt('ip', $h) // '';
my $arg_ipv4 = opt('ipv4', $h) // '';
my $arg_ipv6 = opt('ipv6', $h) // '';
@ -1739,7 +1799,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")));
load_json_support($proto) if (grep (/^$proto$/, ("1984", "cloudflare", "gandi", "godaddy", "hetzner", "yandex", "nfsn", "njalla", "porkbun")));
if (!exists($services{$proto})) {
warning("skipping host: %s: unrecognized protocol '%s'", $h, $proto);
@ -3610,6 +3670,7 @@ sub nic_updateable {
my $usev6 = opt('usev6', $host) // 'disabled';
$use = 'disabled' if ($use eq 'no'); # backward compatibility
$usev6 = 'disabled' if ($usev6 eq 'no'); # backward compatibility
$use = 'disabled' if ($usev4 ne 'disabled') || ($usev6 ne 'disabled');
# If we have a valid IP address and we have previously warned that it was invalid.
# reset the warning count back to zero.
@ -3810,7 +3871,7 @@ sub nic_updateable {
success("%s: skipped: IP address was already set to %s.", $host, $ip);
}
if ($usev4 ne 'disabled') {
success("%s: skipped: IPv4 address was already set to %s.", $host, $ipv6);
success("%s: skipped: IPv4 address was already set to %s.", $host, $ipv4);
}
if ($usev6 ne 'disabled') {
success("%s: skipped: IPv6 address was already set to %s.", $host, $ipv6);
@ -3852,7 +3913,9 @@ sub header_ok {
$ok = 1;
} elsif ($result eq '401') {
failed("updating %s: authorization failed (%s)", $host, $line);
failed("updating %s: authentication failed (%s)", $host, $line);
} elsif ($result eq '403') {
failed("updating %s: not authorized (%s)", $host, $line);
}
} else {
@ -4057,10 +4120,13 @@ sub nic_dyndns2_update {
my @hosts = @{$groups{$sig}};
my $hosts = join(',', @hosts);
my $h = $hosts[0];
my $ip = $config{$h}{'wantip'};
delete $config{$_}{'wantip'} foreach @hosts;
my $ipv4 = $config{$h}{'wantipv4'};
my $ipv6 = $config{$h}{'wantipv6'};
delete $config{$_}{'wantipv4'} foreach @hosts;
delete $config{$_}{'wantipv6'} foreach @hosts;
info("setting IP address to %s for %s", $ip, $hosts);
info("setting IPv4 address to %s for %s", $ipv4, $hosts) if $ipv4;
info("setting IPv6 address to %s for %s", $ipv6, $hosts) if $ipv6;
verbose("UPDATE:", "updating %s", $hosts);
## Select the DynDNS system to update
@ -4079,7 +4145,11 @@ sub nic_dyndns2_update {
$url .= "&hostname=$hosts";
$url .= "&myip=";
$url .= $ip if $ip;
$url .= $ipv4 if $ipv4;
if ($ipv6) {
$url .= "," if $ipv4;
$url .= $ipv6;
}
## some args are not valid for a custom domain.
$url .= "&wildcard=ON" if ynu($config{$h}{'wildcard'}, 1, 0, 0);
@ -4102,7 +4172,6 @@ sub nic_dyndns2_update {
my @reply = split /\n/, $reply;
my $state = 'header';
my $returnedip = $ip;
foreach my $line (@reply) {
if ($state eq 'header') {
@ -4116,22 +4185,28 @@ sub nic_dyndns2_update {
# 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 ($status, $returnedips) = split / /, lc $line;
my $h = shift @hosts;
$config{$h}{'status'} = $status;
$config{$h}{'status-ipv4'} = $status if $ipv4;
$config{$h}{'status-ipv6'} = $status if $ipv6;
if ($status eq 'good') {
$config{$h}{'ip'} = $ip;
$config{$h}{'ipv4'} = $ipv4 if $ipv4;
$config{$h}{'ipv6'} = $ipv6 if $ipv6;
$config{$h}{'mtime'} = $now;
success("updating %s: %s: IP address set to %s", $h, $status, $ip);
success("updating %s: %s: IPv4 address set to %s", $h, $status, $ipv4) if $ipv4;
success("updating %s: %s: IPv6 address set to %s", $h, $status, $ipv6) if $ipv6;
} elsif (exists $errors{$status}) {
if ($status eq 'nochg') {
warning("updating %s: %s: %s", $h, $status, $errors{$status});
$config{$h}{'ip'} = $ip;
$config{$h}{'ipv4'} = $ipv4 if $ipv4;
$config{$h}{'ipv6'} = $ipv6 if $ipv6;
$config{$h}{'mtime'} = $now;
$config{$h}{'status'} = 'good';
$config{$h}{'status-ipv4'} = 'good' if $ipv4;
$config{$h}{'status-ipv6'} = 'good' if $ipv6;
} else {
failed("updating %s: %s: %s", $h, $status, $errors{$status});
@ -4476,6 +4551,86 @@ sub nic_dslreports1_update {
}
}
######################################################################
## nic_domeneshop_examples
######################################################################
sub nic_domeneshop_examples {
return <<"EoEXAMPLE";
o 'domeneshop'
API is documented here: https://api.domeneshop.no/docs/
To generate credentials, visit https://www.domeneshop.no/admin?view=api after logging in to the control panel at
https://www.domeneshop.no/admin?view=api
Configuration variables applicable to the 'domeneshop' api are:
protocol=domeneshop ##
login=token ## api-token
password=secret ## api-secret
domain.example.com ## the host registered with the service. ## the host registered with the service.
Example ${program}.conf file entries:
## single host update
protocol=domeneshop
login=username
password=your-password
my.example.com
EoEXAMPLE
}
######################################################################
## nic_domeneshop_update
######################################################################
sub nic_domeneshop_update {
debug("\nnic_domeneshop_update -------------------");
my $endpointPath = "/v0/dyndns/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 = $globals{'ssl'} ? "https://" : "http://";
$url .= "$config{$h}{'server'}$endpointPath?hostname=$h&myip=$ip";
# Try to get URL
my $reply = geturl(
proxy => opt('proxy'),
url => $url,
login => $config{$h}{'login'},
password => $config{$h}{'password'},
);
# No response, declare as failed
if (!defined($reply) || !$reply) {
failed("Updating %s: Could not connect to %s.", $h, $config{$h}{'server'});
next;
}
next if !header_ok($h, $reply);
# evaluate response
my @reply = split /\n/, $reply;
my $status = shift(@reply);
my $message = pop(@reply);
if ($status =~ /204/) {
$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: '%s' '%s'", $h, $status, $message);
}
}
}
######################################################################
## nic_zoneedit1_examples
######################################################################
@ -4680,10 +4835,12 @@ sub nic_easydns_update {
my @hosts = @{$groups{$sig}};
my $hosts = join(',', @hosts);
my $h = $hosts[0];
my $ip = $config{$h}{'wantip'};
delete $config{$_}{'wantip'} foreach @hosts;
my $ipv4 = $config{$h}{'wantipv4'};
my $ipv6 = $config{$h}{'wantipv6'};
delete $config{$_}{'wantipv4'} foreach @hosts;
delete $config{$_}{'wantipv6'} foreach @hosts;
info("setting IP address to %s for %s", $ip, $hosts);
info("setting IP address to %s %s for %s", $ipv4, $ipv6, $hosts);
verbose("UPDATE:", "updating %s", $hosts);
#'https://api.cp.easydns.com/dyn/generic.php?hostname=test.burry.ca&myip=10.20.30.40&wildcard=ON'
@ -4692,7 +4849,11 @@ sub nic_easydns_update {
$url = "https://$config{$h}{'server'}$config{$h}{'script'}?";
$url .= "hostname=$hosts";
$url .= "&myip=";
$url .= $ip if $ip;
$url .= $ipv4 if $ipv4;
foreach my $ipv6a ($ipv6) {
$url .= "&myip=";
$url .= $ipv6a
}
$url .= "&wildcard=" . ynu($config{$h}{'wildcard'}, 'ON', 'OFF', 'OFF') if defined $config{$h}{'wildcard'};
if ($config{$h}{'mx'}) {
@ -4729,9 +4890,10 @@ sub nic_easydns_update {
$config{$h}{'status'} = $status;
if ($status eq 'NOERROR') {
$config{$h}{'ip'} = $ip;
$config{$h}{'ipv4'} = $ipv4;
$config{$h}{'ipv6'} = $ipv6;
$config{$h}{'mtime'} = $now;
success("updating %s: %s: IP address set to %s", $h, $status, $ip);
success("updating %s: %s: IP address set to %s %s", $h, $status, $ipv4, $ipv6);
} elsif ($status =~ /TOOSOON/) {
## make sure we wait at least a little
@ -5910,6 +6072,82 @@ sub nic_googledomains_update {
}
}
######################################################################
## nic_mythicdyn_examples
##
## written by Reuben Thomas
##
######################################################################
sub nic_mythicdyn_examples {
return <<"EoEXAMPLE";
o 'mythicdyn'
The 'mythicdyn' protocol is used by the Dynamic DNS service offered by
www.mythic-beasts.com.
Configuration variables applicable to the 'mythicdyn' protocol are:
protocol=mythicdyn ##
ipv6=no|yes ## whether to set an A record (default, ipv6=no)
## or AAAA record (ipv6=yes).
login=service-login ## the user name provided by the admin interface
password=service-password ## the password provided by the admin interface
fully.qualified.host ## the host registered with the service
Note: this service automatically sets the IP address to that from which the
request comes, so the IP address detected by ddclient is only used to keep
track of when it needs updating.
Example ${program}.conf file entries:
## Single host update.
protocol=mythicdyn, \\
login=service-login \\
password=service-password, \\
host.example.com
## Multiple host update.
protocol=mythicdyn, \\
login=service-login \\
password=service-password, \\
hosta.example.com,hostb.sub.example.com
EoEXAMPLE
}
######################################################################
## nic_mythicdyn_update
######################################################################
sub nic_mythicdyn_update {
debug("\nnic_mythicdyn_update --------------------");
# Update each set configured host.
foreach my $h (@_) {
info("%s -- Setting IP address.", $h);
my $ipversion = $config{$h}{'ipv6'} ? '6' : '4';
my $reply = geturl(
proxy => opt('proxy'),
url => "https://ipv$ipversion.$config{$h}{'server'}/dns/v2/dynamic/$h",
method => 'POST',
login => $config{$h}{'login'},
password => $config{$h}{'password'},
ipversion => $ipversion,
);
unless ($reply) {
failed("Updating service %s failed: %s", $h, $config{$h}{'server'});
next;
}
my $ok = header_ok($h, $reply);
if ($ok) {
$config{$h}{'mtime'} = $now;
$config{$h}{'status'} = "good";
success("%s -- Updated successfully.", $h);
} else {
failed("%s -- Failed to update.", $h);
}
}
}
######################################################################
## nic_nsupdate_examples
######################################################################
@ -6282,7 +6520,7 @@ sub nic_hetzner_update {
$config{$domain}{"status-ipv$ipv"} = 'failed';
# Get DNS 'A' or 'AAAA' record ID
$url = "https://$config{$key}{'server'}/records";
$url = "https://$config{$key}{'server'}/records?zone_id=$zone_id";
$reply = geturl(proxy => opt('proxy'),
url => $url,
headers => $headers
@ -6299,7 +6537,7 @@ sub nic_hetzner_update {
next;
}
# Pull the ID out of the json, messy
my ($dns_rec_id) = map { ($_->{name} eq $hostname && $_->{type} eq $type && &zone_id eq $_{zone_id}) ? $_->{id} : ()} @{$response->{records}};
my ($dns_rec_id) = map { ($_->{name} eq $hostname && $_->{type} eq $type) ? $_->{id} : ()} @{$response->{records}};
# Set domain
my $http_method="";
@ -6511,7 +6749,11 @@ sub nic_duckdns_update {
$url .= $h;
$url .= "&token=";
$url .= $config{$h}{'password'};
$url .= "&ip=";
if (is_ipv6($ip)) {
$url .= "&ipv6=";
} else {
$url .= "&ip=";
}
$url .= $ip;
@ -6526,15 +6768,26 @@ sub nic_duckdns_update {
next 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: '%s'", $h, $returned);
my $state = 'noresult';
my $line = '';
foreach $line (@reply) {
if ($line eq 'OK') {
$config{$h}{'ip'} = $ip;
$config{$h}{'mtime'} = $now;
$config{$h}{'status'} = 'good';
$state = 'result';
success("updating %s: good: IP address set to %s", $h, $ip);
} elsif ($line eq 'KO') {
$config{$h}{'status'} = 'failed';
$state = 'result';
failed("updating %s: Server said: '%s'", $h, $line);
}
}
if ($state eq 'noresult') {
failed("updating %s: Server said: '%s'", $h, $line);
}
}
}
@ -7005,8 +7258,8 @@ sub nic_ovh_update {
}
my @reply = split /\n/, $reply;
my $returned = pop(@reply);
if ($returned =~ /good/ || $returned =~ /nochg/) {
my $returned = List::Util::first { $_ =~ /good/ || $_ =~ /nochg/ } @reply;
if ($returned) {
$config{$h}{'ip'} = $ip;
$config{$h}{'mtime'} = $now;
$config{$h}{'status'} = 'good';
@ -7017,7 +7270,270 @@ sub nic_ovh_update {
}
} else {
$config{$h}{'status'} = 'failed';
failed("updating %s: Server said: '%s'", $h, $returned);
failed("updating %s: Server said: '%s'", $h, $reply);
}
}
}
######################################################################
## nic_porkbun_examples
######################################################################
sub nic_porkbun_examples {
return <<"EoEXAMPLE";
o 'porkbun'
The 'porkbun' protocol is used for porkbun (https://porkbun.com/).
The API is documented here: https://porkbun.com/api/json/v3/documentation
Before setting up, it is necessary to create your API Key by referring to the following page.
https://kb.porkbun.com/article/190-getting-started-with-the-porkbun-api
Available configuration variables:
* apikey (required): API Key of Porkbun API
* secretapikey (required): Secret API Key of Porkbun API
* on-root-domain=yes or no (default: no): Indicates whether the specified domain name (FQDN) is
an unnamed record (Zone APEX) in a zone.
It is useful to specify it as a local variable as shown in the example.
* usev4, usev6 : These configuration variables can be specified as local variables to override
the global settings. It is useful to finely control IPv4 or IPv6 as shown in the example.
* use (deprecated) : This parameter is deprecated but can be overridden like the above parameters.
Limitations:
* Multiple same name records (for round robin) are not supported.
The same IP address is set for all, creating meaningless extra records.
Example ${program}.conf file entry:
protocol=porkbun
apikey=APIKey
secretapikey=SecretAPIKey
host.example.com,host2.sub.example.com
on-root-domain=yes example.com,sub.example.com
Additional example to finely control IPv4 or IPv6 :
# Example 01 : Global enable both IPv4 and IPv6, and update both records.
usev4=webv4
usev6=ifv6, ifv6=enp1s0
protocol=porkbun
apikey=APIKey
secretapikey=SecretAPIKey
host.example.com,host2.sub.example.com
# Example 02 : Global enable only IPv4, and update only IPv6 record.
usev4=webv4
protocol=porkbun
apikey=APIKey
secretapikey=SecretAPIKey
usev6=ifv6, ifv6=enp1s0, usev4=disabled ipv6.example.com
EoEXAMPLE
}
######################################################################
## nic_porkbun_update
######################################################################
sub nic_porkbun_update {
debug("\nnic_porkbun_update -------------------");
## update each configured host
## should improve to update in one pass
foreach my $host (@_) {
my ($sub_domain, $domain);
if ($config{$host}{'on-root-domain'}) {
$sub_domain = '';
$domain = $host;
} else {
($sub_domain, $domain) = split(/\./, $host, 2);
}
my $ipv4 = delete $config{$host}{'wantipv4'};
my $ipv6 = delete $config{$host}{'wantipv6'};
if (is_ipv4($ipv4)) {
info("setting IPv4 address to %s for %s", $ipv4, $host);
verbose("UPDATE:","updating %s", $host);
my $url = "https://porkbun.com/api/json/v3/dns/retrieveByNameType/$domain/A/$sub_domain";
my $data = encode_json({
secretapikey => $config{$host}{'secretapikey'},
apikey => $config{$host}{'apikey'},
});
my $header = "Content-Type: application/json\n";
my $reply = geturl(
proxy => opt('proxy'),
url => $url,
headers => $header,
method => 'POST',
data => $data,
);
# No response, declare as failed
if (!defined($reply) || !$reply) {
$config{$host}{'status'} = "bad";
failed("updating %s: Could not connect to porkbun.com.", $host);
next;
}
if (!header_ok($host, $reply)) {
$config{$host}{'status'} = "bad";
failed("updating %s: failed (%s)", $host, $reply);
next;
}
# Strip header
# Porkbun sends data in chunks, so it is assumed to be one chunk and parsed forcibly.
$reply =~ qr/{(?:[^{}]*|(?R))*}/mp;
my $response = eval { decode_json(${^MATCH}) };
if (!defined($response)) {
$config{$host}{'status'} = "bad";
failed("%s -- Unexpected service response.", $host);
next;
}
if ($response->{status} ne 'SUCCESS') {
$config{$host}{'status'} = "bad";
failed("%s -- Unexpected status. (status = %s)", $host, $response->{status});
next;
}
my $records = $response->{records};
if (ref($records) eq 'ARRAY' && defined $records->[0]->{'id'}) {
my $count = scalar(@{$records});
if ($count > 1) {
warning("updating %s: There are multiple applicable records. Only first record is used. Overwrite all with the same content.");
}
my $current_content = $records->[0]->{'content'};
if ($current_content eq $ipv4) {
$config{$host}{'status'} = "good";
success("updating %s: skipped: IPv4 address was already set to %s.", $host, $ipv4);
next;
}
my $ttl = $records->[0]->{'ttl'};
my $notes = $records->[0]->{'notes'};
debug("ttl = %s", $ttl);
debug("notes = %s", $notes);
$url = "https://porkbun.com/api/json/v3/dns/editByNameType/$domain/A/$sub_domain";
$data = encode_json({
secretapikey => $config{$host}{'secretapikey'},
apikey => $config{$host}{'apikey'},
content => $ipv4,
ttl => $ttl,
notes => $notes,
});
$reply = geturl(
proxy => opt('proxy'),
url => $url,
headers => $header,
method => 'POST',
data => $data,
);
# No response, declare as failed
if (!defined($reply) || !$reply) {
failed("updating %s: Could not connect to porkbun.com.", $host);
next;
}
if (!header_ok($host, $reply)) {
failed("updating %s: failed (%s)", $host, $reply);
next;
}
$config{$host}{'status'} = "good";
success("updating %s: good: IPv4 address set to %s", $host, $ipv4);
next;
} else {
$config{$host}{'status'} = "bad";
failed("updating %s: No applicable existing records.", $host);
next;
}
} else {
info("No IPv4 address for %s", $host);
}
if (is_ipv6($ipv6)) {
info("setting IPv6 address to %s for %s", $ipv6, $host);
verbose("UPDATE:","updating %s", $host);
my $url = "https://porkbun.com/api/json/v3/dns/retrieveByNameType/$domain/AAAA/$sub_domain";
my $data = encode_json({
secretapikey => $config{$host}{'secretapikey'},
apikey => $config{$host}{'apikey'},
});
my $header = "Content-Type: application/json\n";
my $reply = geturl(
proxy => opt('proxy'),
url => $url,
headers => $header,
method => 'POST',
data => $data,
);
# No response, declare as failed
if (!defined($reply) || !$reply) {
$config{$host}{'status'} = "bad";
failed("updating %s: Could not connect to porkbun.com.", $host);
next;
}
if (!header_ok($host, $reply)) {
$config{$host}{'status'} = "bad";
failed("updating %s: failed (%s)", $host, $reply);
next;
}
# Strip header
# Porkbun sends data in chunks, so it is assumed to be one chunk and parsed forcibly.
$reply =~ qr/{(?:[^{}]*|(?R))*}/mp;
my $response = eval { decode_json(${^MATCH}) };
if (!defined($response)) {
$config{$host}{'status'} = "bad";
failed("%s -- Unexpected service response.", $host);
next;
}
if ($response->{status} ne 'SUCCESS') {
$config{$host}{'status'} = "bad";
failed("%s -- Unexpected status. (status = %s)", $host, $response->{status});
next;
}
my $records = $response->{records};
if (ref($records) eq 'ARRAY' && defined $records->[0]->{'id'}) {
my $count = scalar(@{$records});
if ($count > 1) {
warning("updating %s: There are multiple applicable records. Only first record is used. Overwrite all with the same content.");
}
my $current_content = $records->[0]->{'content'};
if ($current_content eq $ipv6) {
$config{$host}{'status'} = "good";
success("updating %s: skipped: IPv6 address was already set to %s.", $host, $ipv6);
next;
}
my $ttl = $records->[0]->{'ttl'};
my $notes = $records->[0]->{'notes'};
debug("ttl = %s", $ttl);
debug("notes = %s", $notes);
$url = "https://porkbun.com/api/json/v3/dns/editByNameType/$domain/AAAA/$sub_domain";
$data = encode_json({
secretapikey => $config{$host}{'secretapikey'},
apikey => $config{$host}{'apikey'},
content => $ipv6,
ttl => $ttl,
notes => $notes,
});
$reply = geturl(
proxy => opt('proxy'),
url => $url,
headers => $header,
method => 'POST',
data => $data,
);
# No response, declare as failed
if (!defined($reply) || !$reply) {
failed("updating %s: Could not connect to porkbun.com.", $host);
next;
}
if (!header_ok($host, $reply)) {
failed("updating %s: failed (%s)", $host, $reply);
next;
}
$config{$host}{'status'} = "good";
success("updating %s: good: IPv6 address set to %s", $host, $ipv4);
next;
} else {
$config{$host}{'status'} = "bad";
failed("updating %s: No applicable existing records.", $host);
next;
}
} else {
info("No IPv6 address for %s", $host);
}
}
}
@ -7389,6 +7905,90 @@ sub nic_regfishde_update {
}
}
######################################################################
######################################################################
## enom
######################################################################
sub nic_enom_examples {
return <<EoEXAMPLE;
o 'enom'
The 'enom' protocol is used by DNS services offered by www.enom.com and their resellers.
Configuration variables applicable to the 'enom' protocol are:
protocol=enom ##
server=fqdn.of.service ## defaults to dynamic.name-services.com
login=domain.name ## base domain name
password=domain-password ## the domain password registered with the service
A_record ## the A record(s) registered with the service
Example ${program}.conf file entries:
## single host update
protocol=enom, \\
login=mydomain.com, \\
password=mydomain.com-password \\
www
## multiple host update
protocol=enom, \\
login=mydomain.com, \\
password=mydomain.com-password \\
www,mail,*
EoEXAMPLE
}
######################################################################
## enom_update
##
## written by Lars Fredriksson
## modified by Leonidas Arvanitis
##
## based on http://www.edoceo.com/creo/enomddu.php
##
## needs this url to update:
## http://dynamic.name-services.com/interface.asp?Command=SetDNSHost&HostName=www
## &Zone=mydomain.com&DomainPassword=MyD0mainPa55w0rD&Address=123.45.678.90
##
######################################################################
sub nic_enom_update {
debug("\nenom_update -------------------");
## update each configured host
foreach my $h (@_) {
my $ip = delete $config{$h}{'wantip'};
info("setting IP address to %s for %s", $ip, $h);
verbose("UPDATE:","updating %s", $h);
my $url;
$url = "https://$config{$h}{'server'}/interface.asp?Command=SetDNSHost";
$url .= "&HostName=$h";
$url .= "&Zone=$config{$h}{'login'}";
$url .= "&DomainPassword=$config{$h}{'password'}";
$url .= "&Address=";
$url .= $ip if $ip;
my $reply = geturl(
proxy => opt('proxy'),
url => $url
);
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;
if (grep /Done=true/i, @reply) {
$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';
warning("SENT: %s", $url) unless opt('verbose');
warning("REPLIED: %s", $reply);
failed("updating %s: Invalid reply.", $h);
}
}
}
# 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

View file

@ -1,31 +0,0 @@
#!/bin/sh
# PROVIDE: ddclient
# REQUIRE: LOGIN
# KEYWORD: shutdown
#
# Add the following lines to /etc/rc.conf.local or /etc/rc.conf
# to enable this service:
#
# ddclient_enable (bool): Set to NO by default.
# Set it to YES to enable ddclient.
. /etc/rc.subr
name=ddclient
rcvar=ddclient_enable
ddclient_conf="/etc/ddclient/ddclient.conf"
command="/usr/local/sbin/${name}"
load_rc_config $name
delay=$(grep -v '^\s*#' "${ddclient_conf}" | grep -i -m 1 "daemon" | awk -F '=' '{print $2}')
if [ -z "${delay}" ]
then
ddclient_flags="-daemon 300"
else
ddclient_flags=""
fi
run_rc_command "$1"

View file

@ -1,100 +0,0 @@
#!/bin/bash
#
# ddclient This shell script takes care of starting and stopping
# ddclient.
#
# chkconfig: 2345 65 35
# description: ddclient provides support for updating dynamic DNS services.
CONF=/etc/ddclient/ddclient.conf
program=ddclient
[ -f $CONF ] || exit 0
system=unknown
if [ -f /etc/fedora-release ]; then
system=fedora
elif [ -f /etc/redhat-release ]; then
system=redhat
elif [ -f /etc/debian_version ]; then
system=debian
fi
PID=''
if [ "$system" = "fedora" ] || [ "$system" = "redhat" ]; then
. /etc/init.d/functions
PID=`pidofproc $program`
else
PID=`ps -aef | grep "$program - sleep" | grep -v grep | awk '{print $2}'`
fi
PATH=/usr/bin:/usr/local/bin:${PATH}
export PATH
# See how we were called.
case "$1" in
start)
# See if daemon=value is specified in the config file.
# Assumptions:
# * there are no quoted "#" characters before "daemon="
# (if there is a "#" it starts a comment)
# * "daemon=" does not appear in a password or value
# * if the interval value is 0, it is not quoted
INTERVAL=$(sed -e '
s/^\([^#]*[,[:space:]]\)\{0,1\}daemon=\([^,[:space:]]*\).*$/\2/
t quit
d
:quit
q
' "$CONF")
if [ -z "$DELAY" ] || [ "$DELAY" = "0" ]; then
DELAY="-daemon 300"
else
# use the interval specified in the config file
DELAY=''
fi
echo -n "Starting ddclient: "
if [ "$system" = "fedora" ] || [ "$system" = "redhat" ]; then
daemon $program $DELAY
else
ddclient $DELAY
fi
echo
;;
stop)
# Stop daemon.
echo -n "Shutting down ddclient: "
if [ -n "$PID" ]; then
if [ "$system" = "fedora" ] || [ "$system" = "redhat" ]; then
killproc $program
else
kill $PID
fi
else
echo "ddclient is not running"
fi
echo
;;
restart)
$0 stop
$0 start
;;
status)
if [ "$system" = "fedora" ] || [ "$system" = "redhat" ]; then
status $program
else
if test "$PID"; then
for p in $PID; do
echo "$program (pid $p) is running"
done
else
echo "$program is stopped"
fi
fi
;;
*)
echo "Usage: ddclient {start|stop|restart|status}"
exit 1
esac
exit 0

View file

@ -1,38 +0,0 @@
#!/sbin/openrc-run
description="ddclient Daemon for Alpine"
command="/usr/bin/ddclient"
config_file="/etc/ddclient/ddclient.conf"
command_args=""
pidfile=$(grep -v '^\s*#' "${config_file}" | grep -i -m 1 pid= | awk -F '=' '{print $2}')
delay=$(grep -v '^\s*#' "${config_file}" | grep -i -m 1 "daemon" | awk -F '=' '{print $2}')
if [ -z "${delay}" ]
then
command_args="-daemon 300"
else
command_args=""
fi
depend() {
use logger
need net
after firewall
}
start() {
ebegin "Starting ddclient"
start-stop-daemon --start \
--exec "${command}" \
--pidfile "${pidfile}" \
-- \
${command_args}
eend $?
}
stop() {
ebegin "Stopping ddclient"
start-stop-daemon --stop --exec "${command}" \
--pidfile "${pidfile}"
eend $?
}

View file

@ -1,64 +0,0 @@
#!/bin/sh
#
# ddclient This shell script takes care of starting and stopping
# ddclient.
#
# chkconfig: 2345 65 35
# description: ddclient provides support for updating dynamic DNS services.
#
# Above is for RedHat and now the LSB part
### BEGIN INIT INFO
# Provides: ddclient
# Required-Start: $syslog $remote_fs
# Should-Start: $time ypbind sendmail
# Required-Stop: $syslog $remote_fs
# Should-Stop: $time ypbind sendmail
# Default-Start: 3 5
# Default-Stop: 0 1 2 6
# Short-Description: ddclient provides support for updating dynamic DNS services
# Description: ddclient is a Perl client used to update dynamic DNS
# entries for accounts on many dynamic DNS services and
# can be used on many types of firewalls
### END INIT INFO
#
###
[ -f /etc/ddclient/ddclient.conf ] || exit 0
DDCLIENT_BIN=/usr/bin/ddclient
#
# LSB Standard (SuSE,RedHat,...)
#
if [ -f /lib/lsb/init-functions ] ; then
. /lib/lsb/init-functions
fi
# See how we were called.
case "$1" in
start)
echo -n "Starting ddclient "
start_daemon $DDCLIENT_BIN -daemon 300
rc_status -v
;;
stop)
echo -n "Shutting down ddclient "
killproc -TERM `basename $DDCLIENT_BIN`
rc_status -v
;;
restart)
$0 stop
$0 start
rc_status
;;
status)
echo -n "Checking for service ddclient "
checkproc `basename $DDCLIENT_BIN`w
rc_status -v
;;
*)
echo "Usage: ddclient {start|stop|restart|status}"
exit 1
esac
exit 0

View file

@ -1,41 +0,0 @@
#!/bin/sh
#
# ddclient This shell script takes care of starting and stopping
# ddclient.
#
# chkconfig: 2345 65 35
# description: ddclient provides support for updating dynamic DNS services.
[ -f /etc/ddclient/ddclient.conf ] || exit 0
. /etc/rc.d/init.d/functions
# See how we were called.
case "$1" in
start)
# Start daemon.
echo -n "Starting ddclient: "
touch /var/lock/subsys/ddclient
daemon ddclient -daemon 300
echo
;;
stop)
# Stop daemon.
echo -n "Shutting down ddclient: "
killproc ddclient
echo
rm -f /var/lock/subsys/ddclient
;;
restart)
$0 stop
$0 start
;;
status)
status ddclient
;;
*)
echo "Usage: ddclient {start|stop|restart|status}"
exit 1
esac
exit 0

View file

@ -1,57 +0,0 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: ddclient
# Required-Start: $remote_fs $syslog $network
# Required-Stop: $remote_fs $syslog $network
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start ddclient daemon at boot time
# Description: Start ddclient that provides support for updating dynamic DNS services. Originally submitted by paolo martinelli, updated by joe passavanti
### END INIT INFO
DDCLIENT=/usr/bin/ddclient
CONF=/etc/ddclient/ddclient.conf
PIDFILE=/var/run/ddclient.pid
test -x $DDCLIENT || exit 0
test -f $CONF || exit 0
. /lib/lsb/init-functions
case "$1" in
start)
if [ ! -f $PIDFILE ]; then
log_begin_msg "Starting ddclient..."
DELAY=`grep -v '^\s*#' $CONF | grep -i -m 1 "daemon" | awk -F '=' '{print $2}'`
if [ -z "$DELAY" ] ; then
DELAY="-daemon 300"
else
DELAY=''
fi
start-stop-daemon -S -q -p $PIDFILE -x $DDCLIENT -- $DELAY
log_end_msg $?
else
log_warning_msg "Service ddclient already running..."
fi
;;
stop)
if [ -f $PIDFILE ] ; then
log_begin_msg "Stopping ddclient..."
start-stop-daemon -K -q -p $PIDFILE
log_end_msg $?
rm -f $PIDFILE
else
log_warning_msg "No ddclient running..."
fi
;;
restart|reload|force-reload)
$0 stop
$0 start
;;
*)
log_success_msg "Usage: $0 {start|stop|restart|reload|force-reload}"
exit 1
;;
esac
exit 0