From 8a6b9d08628f06f55463cc1b3bfe359b12e66375 Mon Sep 17 00:00:00 2001 From: Thomas Hebb Date: Sat, 6 Jun 2020 01:39:09 -0400 Subject: [PATCH 01/32] cloudflare: Remove unused $hostname variable This is no longer used since commit 6c951a039543 ("Add files via upload"), which updated to the Cloudflare API v4. The new API does not require any preprocessing of the domain name. --- ddclient.in | 1 - 1 file changed, 1 deletion(-) diff --git a/ddclient.in b/ddclient.in index 276186c..94f49a3 100755 --- a/ddclient.in +++ b/ddclient.in @@ -5821,7 +5821,6 @@ sub nic_cloudflare_update { # FQDNs for my $domain (@hosts) { - (my $hostname = $domain) =~ s/\.$config{$key}{zone}$//; my $ipv4 = delete $config{$domain}{'wantipv4'}; my $ipv6 = delete $config{$domain}{'wantipv6'}; From c9754a125ce6996682e7400b36afb51ec5523901 Mon Sep 17 00:00:00 2001 From: satrapes Date: Fri, 30 Sep 2022 15:49:57 +0300 Subject: [PATCH 02/32] Add support for njal.la (#459) Co-authored-by: Dimitris Paraskevopoulos --- README.md | 1 + ddclient.conf.in | 19 +++++--- ddclient.in | 116 +++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 126 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index f61608c..c1f7b74 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ Dynamic DNS services currently supported include: Gandi - See https://gandi.net dnsexit - See https://dnsexit.com/ for details 1984.is - See https://www.1984.is/product/freedns/ for details + Njal.la - See https://njal.la/docs/ddns/ `ddclient` now supports many cable and DSL broadband routers. diff --git a/ddclient.conf.in b/ddclient.conf.in index 26c7abc..d6bf96c 100644 --- a/ddclient.conf.in +++ b/ddclient.conf.in @@ -19,12 +19,12 @@ daemon=300 # check every 300 seconds syslog=yes # log update msgs to syslog mail=root # mail all msgs to root -mail-failure=root # mail failed update msgs to root +mail-failure=root # mail failed update msgs to root pid=@runstatedir@/ddclient.pid # record PID in file. ssl=yes # use ssl-support. Works with - # ssl-library -# postscript=script # run script after updating. The - # new IP is added as argument. + # ssl-library +# postscript=script # run script after updating. The + # new IP is added as argument. # #use=watchguard-soho, fw=192.168.111.1:80 # via Watchguard's SOHO FW #use=netopia-r910, fw=192.168.111.1:80 # via Netopia R910 FW @@ -88,7 +88,6 @@ ssl=yes # use ssl-support. Works with # protocol=dyndns2 \ # your-static-host.dyndns.org -## ## ## dyndns.org custom addresses ## @@ -154,7 +153,6 @@ ssl=yes # use ssl-support. Works with # zone=example.com \ # example.com,subdomain.example.com -## ## ## Loopia (loopia.se) ## @@ -302,9 +300,18 @@ ssl=yes # use ssl-support. Works with # password=mypassword \ # myhost.mydomain.com +## ## dnsexit (www.dnsexit.com) ## #protocol=dnsexit, \ #login=myusername, \ #password=mypassword, \ #subdomain-1.domain.com,subdomain-2.domain.com + +## +## Njal.la (http://njal.la/) +## +# protocol=njalla, +# password=mypassword +# quietreply=no|yes +# my-domain.com diff --git a/ddclient.in b/ddclient.in index 94f49a3..99197d8 100755 --- a/ddclient.in +++ b/ddclient.in @@ -762,6 +762,17 @@ my %services = ( 'zone' => setv(T_FQDN, 1, 0, undef, undef), }, }, + 'njalla' => { + 'updateable' => undef, + 'update' => \&nic_njalla_update, + 'examples' => \&nic_njalla_examples, + 'variables' => { + %{$variables{'service-common-defaults'}}, + 'login' => setv(T_STRING, 0, 0, 'unused', undef), + 'server' => setv(T_FQDNP, 1, 0, 'njal.la', undef), + 'quietreply' => setv(T_BOOL, 0, 1, 0, undef) + }, + }, 'noip' => { 'updateable' => undef, 'update' => \&nic_noip_update, @@ -1706,7 +1717,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"))); + load_json_support($proto) if (grep (/^$proto$/, ("1984", "cloudflare", "gandi", "godaddy", "hetzner", "yandex", "nfsn", "njalla"))); if (!exists($services{$proto})) { warning("skipping host: %s: unrecognized protocol '%s'", $h, $proto); @@ -2693,7 +2704,7 @@ sub fetch_via_curl { $curl->setopt(WWW::Curl::Easy->CURLOPT_CAINFO, opt('ssl_ca_file')) if defined(opt('ssl_ca_file')); $curl->setopt(WWW::Curl::Easy->CURLOPT_CAPATH, opt('ssl_ca_dir')) if defined(opt('ssl_ca_dir')); $curl->setopt(WWW::Curl::Easy->CURLOPT_IPRESOLVE, - ($ipversion == 4) ? WWW::Curl::Easy->CURL_IPRESOLVE_V4 : + ($ipversion == 4) ? WWW::Curl::Easy->CURL_IPRESOLVE_V4 : ($ipversion == 6) ? WWW::Curl::Easy->CURL_IPRESOLVE_V6 : WWW::Curl::Easy->CURL_IPRESOLVE_WHATEVER); $curl->setopt(WWW::Curl::Easy->CURLOPT_USERAGENT, "${program}/${version}"); @@ -3172,7 +3183,7 @@ sub get_ip_from_interface { debug("Reply from '%s' :\n------\n%s------", $cmd, $reply); ## IPv6 is more complex than IPv4. Start by filtering on only "inet6" addresses - ## Then remove deprecated or temporary addresses and finally seleect on global or local addresses + ## Then remove deprecated or temporary addresses and finally seleect on global or local addresses my @reply = split(/\n/, $reply); @reply = grep(/\binet6\b/, @reply); # Select only IPv6 entries @reply = grep(!/\bdeprecated\b|\btemporary\b/, @reply); # Remove deprecated and temporary @@ -5041,6 +5052,103 @@ sub nic_nfsn_update { ###################################################################### +###################################################################### +## nic_njalla_examples +###################################################################### +sub nic_njalla_examples { + return <<"EoEXAMPLE"; + +o 'njalla' + +The 'njalla' protocol is used by DNS service offered by njal.la. + +Configuration variables applicable to the 'njalla' protocol are: + protocol=njalla ## + password=service-password ## Generated password for your dynamic DNS record + quietreply=no|yes ## If yes return empty response on success with status 200 but print errors + domain ## subdomain to update, use @ for base domain name, * for catch all + +Example ${program}.conf file entries: + ## single host update + protocol=njalla \\ + password=njal.la-key + quietreply=no + domain.com + +EoEXAMPLE +} +###################################################################### +## nic_njalla_update +## +## written by satrapes +## +## based on https://njal.la/docs/ddns/ +## needs this url to update: +## https://njal.la/update?h=host_name&k=domain_password&a=your_ip +## response contains "code 200" on succesful completion +###################################################################### +sub nic_njalla_update { + debug("\nnic_njalla_update -------------------"); + + foreach my $h (@_) { + # Read input params + my $ipv4 = delete $config{$h}{'wantipv4'}; + my $ipv6 = delete $config{$h}{'wantipv6'}; + my $quietreply = delete $config{$h}{'quietreply'}; + my $ip_output = ''; + + # Build url + my $url = "https://$config{$h}{'server'}/update/?h=$h&k=$config{$h}{'password'}"; + my $auto = 1; + foreach my $ip ($ipv4, $ipv6) { + next if (!$ip); + $auto = 0; + my $ipv = ($ip eq ($ipv6 // '')) ? '6' : '4'; + my $type = ($ip eq ($ipv6 // '')) ? 'aaaa' : 'a'; + $ip_output .= " IP v$ipv: $ip,"; + $url .= "&$type=$ip"; + } + $url .= (($auto eq 1)) ? '&auto' : ''; + $url .= (($quietreply eq 1)) ? '&quiet' : ''; + + info("setting address to%s for %s", ($ip_output eq '') ? ' auto' : $ip_output, $h); + verbose("UPDATE:", "updating %s", $h); + debug("url: %s", $url); + + # Try to get URL + my $reply = geturl(proxy => opt('proxy'), url => $url); + my $response = ''; + if ($quietreply) { + $reply =~ qr/invalid host or key/mp; + $response = ${^MATCH}; + if (!$response) { + success("updating %s: good: IP address set to %s", $h, $ip_output); + } + elsif ($response =~ /invalid host or key/) { + failed("Invalid host or key"); + } else { + failed("Unknown response"); + } + } else { + $reply =~ qr/{(?:[^{}]*|(?R))*}/mp; + $response = eval {decode_json(${^MATCH})}; + # No response, declare as failed + if (!defined($reply) || !$reply) { + failed("updating %s: Could not connect to %s.", $h, $config{$h}{'server'}); + } else { + # Strip header + if ($response->{status} == 401 && $response->{message} =~ /invalid host or key/) { + failed("Invalid host or key"); + } elsif ($response->{status} == 200 && $response->{message} =~ /record updated/) { + success("updating %s: good: IP address set to %s", $h, $response->{value}->{A}); + } else { + failed("Unknown response"); + } + } + } + } +} + ###################################################################### ## nic_sitelutions_examples ###################################################################### @@ -5323,7 +5431,7 @@ sub nic_1984_update { next; } next if !header_ok($host, $reply); - + # Strip header $reply =~ qr/{(?:[^{}]*|(?R))*}/mp; my $response = eval { decode_json(${^MATCH}) }; From 3e2cb0a0dc94db59d6257dd10037fd030254f774 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandro=20J=C3=A4ckel?= Date: Thu, 20 Oct 2022 20:06:35 +0200 Subject: [PATCH 03/32] Cut 3.10.0 release --- ChangeLog.md | 3 ++- ddclient.in | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index cc5b656..881c76a 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -3,10 +3,11 @@ This document describes notable changes. For details, see the [source code repository history](https://github.com/ddclient/ddclient/commits/master). -## Not yet released +## 2022-10-20 v3.10.0 ### New features * Added support for domaindiscount24.com + * Added support for njal.la ## 2022-05-15 v3.10.0_2 diff --git a/ddclient.in b/ddclient.in index 99197d8..5e4759e 100755 --- a/ddclient.in +++ b/ddclient.in @@ -30,7 +30,7 @@ use IO::Socket::INET; use Socket qw(AF_INET AF_INET6 PF_INET PF_INET6); use Sys::Hostname; -use version 0.77; our $VERSION = version->declare('v3.10.0_2'); +use version 0.77; our $VERSION = version->declare('v3.10.0'); (my $version = $VERSION->stringify()) =~ s/^v//; my $programd = $0; $programd =~ s%^.*/%%; From ce5dfe3501e27d13801b1499ad7ca674dfb3d243 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandro=20J=C3=A4ckel?= Date: Tue, 25 Oct 2022 15:46:07 +0200 Subject: [PATCH 04/32] README.md: don't use version number --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c1f7b74..ddaffdd 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# DDCLIENT v3.10.0 +# DDCLIENT `ddclient` is a Perl client used to update dynamic DNS entries for accounts on many dynamic DNS services. @@ -78,8 +78,8 @@ ddclient package. the directory: ```shell - tar xvfa ddclient-3.10.0.tar.gz - cd ddclient-3.10.0 + tar xvfa ddclient-3.XX.X.tar.gz + cd ddclient-3.XX.X ``` (If you are installing from a clone of the Git repository, you From 36744e5b205c676b326e72de5cf81ed7ad437f64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandro=20J=C3=A4ckel?= Date: Tue, 25 Oct 2022 15:46:33 +0200 Subject: [PATCH 05/32] set version number via autoconf --- ddclient.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ddclient.in b/ddclient.in index 5e4759e..05bafc3 100755 --- a/ddclient.in +++ b/ddclient.in @@ -30,8 +30,8 @@ use IO::Socket::INET; use Socket qw(AF_INET AF_INET6 PF_INET PF_INET6); use Sys::Hostname; -use version 0.77; our $VERSION = version->declare('v3.10.0'); -(my $version = $VERSION->stringify()) =~ s/^v//; +use version 0.77; our $VERSION = version->declare('@PACKAGE_VERSION@'); +my $version = $VERSION->stringify(); my $programd = $0; $programd =~ s%^.*/%%; my $program = $programd; From d13469fde532443ec98c24df99505e98cac3b5b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandro=20J=C3=A4ckel?= Date: Tue, 25 Oct 2022 15:46:45 +0200 Subject: [PATCH 06/32] Bump version number in preparation for next release --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 554ea6b..6c58b8f 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ([2.63]) -AC_INIT([ddclient], [3.10.0_2]) +AC_INIT([ddclient], [3.10.1]) AC_CONFIG_SRCDIR([ddclient.in]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) From e8cc636474443fec2886541ab643699bb48ec781 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandro=20J=C3=A4ckel?= Date: Mon, 31 Oct 2022 00:06:56 +0100 Subject: [PATCH 07/32] Use https in more places --- ddclient.in | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ddclient.in b/ddclient.in index 05bafc3..6c60d5a 100755 --- a/ddclient.in +++ b/ddclient.in @@ -116,8 +116,8 @@ my %builtinweb = ( 'myonlineportal' => {'url' => 'https://myonlineportal.net/checkip'}, 'noip-ipv4' => {'url' => 'http://ip1.dynupdate.no-ip.com/'}, 'noip-ipv6' => {'url' => 'http://ip1.dynupdate6.no-ip.com/'}, - 'nsupdate.info-ipv4' => {'url' => 'http://ipv4.nsupdate.info/myip'}, - 'nsupdate.info-ipv6' => {'url' => 'http://ipv6.nsupdate.info/myip'}, + 'nsupdate.info-ipv4' => {'url' => 'https://ipv4.nsupdate.info/myip'}, + 'nsupdate.info-ipv6' => {'url' => 'https://ipv6.nsupdate.info/myip'}, 'zoneedit' => {'url' => 'https://dynamic.zoneedit.com/checkip.html'}, ); my %builtinfw = ( @@ -4263,8 +4263,8 @@ sub nic_noip_update { 'badagent' => 'Invalid user agent', 'nohost' => 'The hostname specified does not exist in the database', '!donator' => 'The offline setting was set, when the user is not a donator', - 'abuse', => 'The hostname specified is blocked for abuse; open a trouble ticket at http://www.no-ip.com', - 'numhost' => 'System error: Too many or too few hosts found. open a trouble ticket at http://www.no-ip.com', + 'abuse', => 'The hostname specified is blocked for abuse; open a trouble ticket at https://www.no-ip.com', + 'numhost' => 'System error: Too many or too few hosts found. open a trouble ticket at https://www.no-ip.com', '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', ); @@ -6402,7 +6402,7 @@ EoEXAMPLE ###################################################################### ## nic_freemyip_update ## by Cadence (reused code from nic_duckdns) -## http://freemyip.com/update?token=ec54b4b64db27fe8873c7f7&domain=myhost +## https://freemyip.com/update?token=ec54b4b64db27fe8873c7f7&domain=myhost ## response contains OK or ERROR ###################################################################### sub nic_freemyip_update { From 3dffe5372a4443bfd0bbadf2097ea8ea68e38b85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandro=20J=C3=A4ckel?= Date: Mon, 31 Oct 2022 00:07:05 +0100 Subject: [PATCH 08/32] Format --- ddclient.in | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ddclient.in b/ddclient.in index 6c60d5a..e5d0d0c 100755 --- a/ddclient.in +++ b/ddclient.in @@ -4280,10 +4280,7 @@ sub nic_noip_update { info("setting IP address to %s for %s", $ip, $hosts); verbose("UPDATE:", "updating %s", $hosts); - my $url = "https://$config{$h}{'server'}/nic/update?system="; - $url .= 'noip'; - $url .= "&hostname=$hosts"; - $url .= "&myip="; + my $url = "https://$config{$h}{'server'}/nic/update?system=noip&hostname=$hosts&myip="; $url .= $ip if $ip; my $reply = geturl( From f01110aedb013cc4918ba7f4f196e02a7ad63e4f Mon Sep 17 00:00:00 2001 From: gertfriend Date: Fri, 18 Nov 2022 08:43:47 +0100 Subject: [PATCH 09/32] Add regfish (#471) --- README.md | 1 + ddclient.conf.in | 7 +++++ ddclient.in | 74 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+) diff --git a/README.md b/README.md index ddaffdd..926dce3 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ Dynamic DNS services currently supported include: dnsexit - See https://dnsexit.com/ for details 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 `ddclient` now supports many cable and DSL broadband routers. diff --git a/ddclient.conf.in b/ddclient.conf.in index d6bf96c..97b6b59 100644 --- a/ddclient.conf.in +++ b/ddclient.conf.in @@ -315,3 +315,10 @@ ssl=yes # use ssl-support. Works with # password=mypassword # quietreply=no|yes # my-domain.com + +## +## regfish.de (www.regfish.de/) +## +# protocol=regfishde, +# password=mypassword +# my-domain.com diff --git a/ddclient.in b/ddclient.in index e5d0d0c..86f8602 100755 --- a/ddclient.in +++ b/ddclient.in @@ -533,6 +533,10 @@ my %variables = ( 'script' => setv(T_STRING, 0, 1, '/RemoteUpdate.sv', undef), 'min-error-interval' => setv(T_DELAY, 0, 0, interval('8m'), 0), }, + 'regfishde-common-defaults' => { + 'server' => setv(T_FQDNP, 1, 0, 'dyndns.regfish.de', undef), + 'login' => setv(T_LOGIN, 0, 0, 0, 'unused', undef), + }, ); my %services = ( '1984' => { @@ -895,6 +899,15 @@ my %services = ( $variables{'service-common-defaults'}, ), }, + 'regfishde' => { + 'updateable' => undef, + 'update' => \&nic_regfishde_update, + 'examples' => \&nic_regfishde_examples, + 'variables' => merge( + $variables{'regfishde-common-defaults'}, + $variables{'service-common-defaults'}, + ), + }, ); $variables{'merged'} = { map({ %{$services{$_}{'variables'}} } keys(%services)), @@ -7159,6 +7172,67 @@ sub nic_keysystems_update { } } +###################################################################### +## nic_regfishde_examples +###################################################################### +sub nic_regfishde_examples { + return < opt('proxy'), url => $url); + + # No response, give error + if (!defined($reply) || !$reply) { + failed("regfish.de updating %s: failed: %s.", $h, $config{$h}{'server'}); + last; + } + last if !header_ok($h, $reply); + + if ($reply =~ /success/) + { + $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: '$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 From 89e30d0aa8ccb99018f163228eed5f9519bbc543 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luca=20Sch=C3=B6neberg?= Date: Mon, 21 Nov 2022 16:17:08 +0100 Subject: [PATCH 10/32] fix hetzner zone problem --- ddclient.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ddclient.in b/ddclient.in index 86f8602..3df624d 100755 --- a/ddclient.in +++ b/ddclient.in @@ -6127,7 +6127,7 @@ sub nic_hetzner_update { $config{$domain}{"status-ipv$ipv"} = 'failed'; # Get DNS 'A' or 'AAAA' record ID - $url = "https://$config{$key}{'server'}/records?$zone_id"; + $url = "https://$config{$key}{'server'}/records"; $reply = geturl(proxy => opt('proxy'), url => $url, headers => $headers @@ -6144,7 +6144,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) ? $_->{id} : ()} @{$response->{records}}; + my ($dns_rec_id) = map { ($_->{name} eq $hostname && $_->{type} eq $type && &zone_id eq $_{zone_id}) ? $_->{id} : ()} @{$response->{records}}; # Set domain my $http_method=""; From 10041d225caa8b978035eb2db5b0bc316772ed3a Mon Sep 17 00:00:00 2001 From: Pekka Nikander Date: Wed, 28 Dec 2022 19:56:49 +0200 Subject: [PATCH 11/32] Add IPv6 support for EasyDNS --- ddclient.in | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/ddclient.in b/ddclient.in index 3df624d..3dd5aaf 100755 --- a/ddclient.in +++ b/ddclient.in @@ -4671,10 +4671,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' @@ -4683,7 +4685,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'}) { @@ -4720,9 +4726,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 From fcd3ef8b5a6d05afeae8f53cae3697c77f393d32 Mon Sep 17 00:00:00 2001 From: ThinkChaos Date: Wed, 28 Dec 2022 17:31:16 -0500 Subject: [PATCH 12/32] fix: nic_ovh_update not ignoring extra data Example bodies I've seen: ``` 0013 good 127.0.0.1 0 ``` ``` 0013 nochg 127.0.0.1 0 ``` ``` 007 nohost 0 ``` Seems like the trailing zero was not there before as the code relied on `pop`. Instead, we find the first line that matches `good`/`nochg`. --- ddclient.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ddclient.in b/ddclient.in index 3df624d..27975ca 100755 --- a/ddclient.in +++ b/ddclient.in @@ -6850,8 +6850,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'; @@ -6862,7 +6862,7 @@ 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); } } } From 3149171aa70a95d0b933eaa97a4dcd4fb5adedef Mon Sep 17 00:00:00 2001 From: bernhardfrenking <58109356+bernhardfrenking@users.noreply.github.com> Date: Fri, 30 Dec 2022 03:23:30 +0100 Subject: [PATCH 13/32] Add DDNS API domeneshop.no (#478) Co-authored-by: Sandro --- README.md | 1 + ddclient.conf.in | 8 +++++ ddclient.in | 89 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+) diff --git a/README.md b/README.md index 926dce3..8b9f0b3 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ 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 `ddclient` now supports many cable and DSL broadband routers. diff --git a/ddclient.conf.in b/ddclient.conf.in index 97b6b59..e24c8bf 100644 --- a/ddclient.conf.in +++ b/ddclient.conf.in @@ -308,6 +308,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= +# password= +# subdomain-1.domain.com,subdomain-2.domain.com + ## ## Njal.la (http://njal.la/) ## diff --git a/ddclient.in b/ddclient.in index 3df624d..22580bb 100755 --- a/ddclient.in +++ b/ddclient.in @@ -630,6 +630,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, @@ -4467,6 +4476,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 ###################################################################### From ff48a0ccb9475c5ddba0a66cf6d7ff9a81638344 Mon Sep 17 00:00:00 2001 From: Mitch Brown Date: Wed, 4 Jan 2023 22:27:21 -0500 Subject: [PATCH 14/32] add IPV6 support to duckdns protocol --- ddclient.in | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ddclient.in b/ddclient.in index 22580bb..7fa4c22 100755 --- a/ddclient.in +++ b/ddclient.in @@ -6445,7 +6445,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; From 8d3a3835870ed5cc83bfbd5cc279f806e24e5572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luca=20Sch=C3=B6neberg?= Date: Fri, 13 Jan 2023 22:30:06 +0100 Subject: [PATCH 15/32] Revert "fix hetzner zone problem", add zone_id to filter (Hetzner) (#491) --- ddclient.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ddclient.in b/ddclient.in index 6b38325..d4183b2 100755 --- a/ddclient.in +++ b/ddclient.in @@ -6223,7 +6223,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 @@ -6240,7 +6240,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=""; From e910204b3da4db8b8fd06c9cc7ba51db0222f919 Mon Sep 17 00:00:00 2001 From: Reuben Thomas Date: Sat, 14 Jan 2023 02:24:03 +0100 Subject: [PATCH 16/32] Add support for Mythic Beasts Dynamic DNS (#312) Co-authored-by: Sandro --- README.md | 1 + ddclient.in | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8b9f0b3..ced99c9 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ Dynamic DNS services currently supported include: 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 `ddclient` now supports many cable and DSL broadband routers. diff --git a/ddclient.in b/ddclient.in index d4183b2..eff10fb 100755 --- a/ddclient.in +++ b/ddclient.in @@ -753,6 +753,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, @@ -3852,7 +3862,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 { @@ -5851,6 +5863,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 ###################################################################### From fa6c95f5110455b6e1ad80d1147086619ddbf7df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20du=20Bo=C3=BFs?= Date: Fri, 27 Jan 2023 17:58:26 +0100 Subject: [PATCH 17/32] Update dyndns2 client to use new IPv4/IPv6 logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas du Boÿs --- ddclient.in | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/ddclient.in b/ddclient.in index eff10fb..744d63e 100755 --- a/ddclient.in +++ b/ddclient.in @@ -4069,10 +4069,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 @@ -4091,7 +4094,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); @@ -4114,7 +4121,6 @@ sub nic_dyndns2_update { my @reply = split /\n/, $reply; my $state = 'header'; - my $returnedip = $ip; foreach my $line (@reply) { if ($state eq 'header') { @@ -4128,22 +4134,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}); From cca4291360ce31aff1ab0d877d2622c11510c1f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20du=20Bo=C3=BFs?= Date: Sat, 28 Jan 2023 10:46:43 +0100 Subject: [PATCH 18/32] fix ipv4 address on message log when address already set --- ddclient.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ddclient.in b/ddclient.in index 744d63e..a5e9c68 100755 --- a/ddclient.in +++ b/ddclient.in @@ -3820,7 +3820,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); From 11a5bd5e7ef0d199c754947e24c0c8a736d18c48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20du=20Bo=C3=BFs?= Date: Sat, 28 Jan 2023 11:34:41 +0100 Subject: [PATCH 19/32] define usev4, usev6 and dependancies as per-host config --- ddclient.in | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/ddclient.in b/ddclient.in index eff10fb..ad7dee5 100755 --- a/ddclient.in +++ b/ddclient.in @@ -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), From d35d62f3e753ffe15b151f7b7c5dea96bfa3ca7a Mon Sep 17 00:00:00 2001 From: drinn Date: Sat, 28 Jan 2023 09:48:51 -0600 Subject: [PATCH 20/32] updated nic_duckdns_update to account for extra lines in duckdns reply --- ddclient.in | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/ddclient.in b/ddclient.in index eff10fb..8797d7d 100755 --- a/ddclient.in +++ b/ddclient.in @@ -6559,16 +6559,28 @@ 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); + } + } } From dbc40557d22b36a6847d0cd11e59185647516f7b Mon Sep 17 00:00:00 2001 From: drinn Date: Sat, 28 Jan 2023 09:59:58 -0600 Subject: [PATCH 21/32] removed empty space --- ddclient.in | 1 - 1 file changed, 1 deletion(-) diff --git a/ddclient.in b/ddclient.in index 8797d7d..b818ea6 100755 --- a/ddclient.in +++ b/ddclient.in @@ -6580,7 +6580,6 @@ sub nic_duckdns_update { if ($state eq 'noresult') { failed("updating %s: Server said: '%s'", $h, $line); } - } } From 5596ee83d16e31372c29cd6b611cbd2db5647e69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20du=20Bo=C3=BFs?= Date: Sat, 28 Jan 2023 11:09:52 +0100 Subject: [PATCH 22/32] set use to disabled if usev4 or usev6 is set --- ddclient.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ddclient.in b/ddclient.in index eff10fb..5ed1184 100755 --- a/ddclient.in +++ b/ddclient.in @@ -1191,6 +1191,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) // ''; @@ -3620,6 +3621,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. From 65a1bcc7d96ad7607a60b58c771dd4cc191e5327 Mon Sep 17 00:00:00 2001 From: Naoya Niwa Date: Wed, 8 Feb 2023 21:56:08 +0900 Subject: [PATCH 23/32] Add support for Porkbun (#490) * Add support for Porkbun * Add IPv6 support for porkbun --- README.md | 1 + ddclient.conf.in | 9 ++ ddclient.in | 280 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 289 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ced99c9..d636b14 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/ddclient.conf.in b/ddclient.conf.in index e24c8bf..cd9df29 100644 --- a/ddclient.conf.in +++ b/ddclient.conf.in @@ -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) ## diff --git a/ddclient.in b/ddclient.in index 60967b8..8cbba46 100755 --- a/ddclient.in +++ b/ddclient.in @@ -858,6 +858,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, @@ -1765,7 +1780,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); @@ -7095,6 +7110,269 @@ sub nic_ovh_update { } } +###################################################################### +## 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); + } + } +} + sub nic_cloudns_examples { return <<"EoEXAMPLE"; o 'cloudns' From c5a956e386ef8ca6163770ff712d75cf0e7a0b0b Mon Sep 17 00:00:00 2001 From: Jontron123 Date: Wed, 8 Feb 2023 07:59:52 -0500 Subject: [PATCH 24/32] Add support for Enom (#508) * Add support for Enom * Add support for Enom * Add support for Enom --- README.md | 1 + ddclient.conf.in | 8 +++++ ddclient.in | 94 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+) diff --git a/README.md b/README.md index d636b14..f5a6c5a 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ Dynamic DNS services currently supported include: 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 `ddclient` now supports many cable and DSL broadband routers. diff --git a/ddclient.conf.in b/ddclient.conf.in index cd9df29..410b356 100644 --- a/ddclient.conf.in +++ b/ddclient.conf.in @@ -339,3 +339,11 @@ ssl=yes # use ssl-support. Works with # protocol=regfishde, # password=mypassword # my-domain.com + +## +## Enom (www.enom.com) +## +# protocol=enom, +# login=domain.name, +# password=domain-password +# my-domain.com diff --git a/ddclient.in b/ddclient.in index 8cbba46..530458f 100755 --- a/ddclient.in +++ b/ddclient.in @@ -957,6 +957,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)), @@ -7740,6 +7750,90 @@ sub nic_regfishde_update { } } +###################################################################### +###################################################################### +## enom +###################################################################### +sub nic_enom_examples { + return < 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 From 8f7ddce2ec124d3900da497c914f9969e1344c27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandro=20J=C3=A4ckel?= Date: Wed, 8 Feb 2023 14:02:02 +0100 Subject: [PATCH 25/32] Remove all init systems that use some variations of `/etc/init.d` --- Makefile.am | 6 -- README.md | 78 +------------------ README.ssl | 13 ---- sample-etc_rc.d_ddclient.freebsd | 31 -------- sample-etc_rc.d_init.d_ddclient | 100 ------------------------- sample-etc_rc.d_init.d_ddclient.alpine | 38 ---------- sample-etc_rc.d_init.d_ddclient.lsb | 64 ---------------- sample-etc_rc.d_init.d_ddclient.redhat | 41 ---------- sample-etc_rc.d_init.d_ddclient.ubuntu | 57 -------------- 9 files changed, 1 insertion(+), 427 deletions(-) delete mode 100644 README.ssl delete mode 100755 sample-etc_rc.d_ddclient.freebsd delete mode 100755 sample-etc_rc.d_init.d_ddclient delete mode 100755 sample-etc_rc.d_init.d_ddclient.alpine delete mode 100755 sample-etc_rc.d_init.d_ddclient.lsb delete mode 100755 sample-etc_rc.d_init.d_ddclient.redhat delete mode 100755 sample-etc_rc.d_init.d_ddclient.ubuntu diff --git a/Makefile.am b/Makefile.am index b829573..a9543ee 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 = diff --git a/README.md b/README.md index f5a6c5a..521297b 100644 --- a/README.md +++ b/README.md @@ -116,82 +116,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`` @@ -251,7 +175,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 diff --git a/README.ssl b/README.ssl deleted file mode 100644 index 3aa579b..0000000 --- a/README.ssl +++ /dev/null @@ -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 diff --git a/sample-etc_rc.d_ddclient.freebsd b/sample-etc_rc.d_ddclient.freebsd deleted file mode 100755 index d8dc341..0000000 --- a/sample-etc_rc.d_ddclient.freebsd +++ /dev/null @@ -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" diff --git a/sample-etc_rc.d_init.d_ddclient b/sample-etc_rc.d_init.d_ddclient deleted file mode 100755 index 5eb9b40..0000000 --- a/sample-etc_rc.d_init.d_ddclient +++ /dev/null @@ -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 diff --git a/sample-etc_rc.d_init.d_ddclient.alpine b/sample-etc_rc.d_init.d_ddclient.alpine deleted file mode 100755 index d20d119..0000000 --- a/sample-etc_rc.d_init.d_ddclient.alpine +++ /dev/null @@ -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 $? -} diff --git a/sample-etc_rc.d_init.d_ddclient.lsb b/sample-etc_rc.d_init.d_ddclient.lsb deleted file mode 100755 index bced239..0000000 --- a/sample-etc_rc.d_init.d_ddclient.lsb +++ /dev/null @@ -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 diff --git a/sample-etc_rc.d_init.d_ddclient.redhat b/sample-etc_rc.d_init.d_ddclient.redhat deleted file mode 100755 index 2e0fd32..0000000 --- a/sample-etc_rc.d_init.d_ddclient.redhat +++ /dev/null @@ -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 diff --git a/sample-etc_rc.d_init.d_ddclient.ubuntu b/sample-etc_rc.d_init.d_ddclient.ubuntu deleted file mode 100755 index 73451c0..0000000 --- a/sample-etc_rc.d_init.d_ddclient.ubuntu +++ /dev/null @@ -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 From d3a353990b4468566e0f1722b0f298c08cfd74f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandro=20J=C3=A4ckel?= Date: Wed, 8 Feb 2023 14:02:33 +0100 Subject: [PATCH 26/32] Update the changelog, bump next version to 3.11.0 because of breaking changes --- ChangeLog.md | 25 +++++++++++++++++++++++++ configure.ac | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index 881c76a..f3919f2 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -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 diff --git a/configure.ac b/configure.ac index 6c58b8f..8a550d0 100644 --- a/configure.ac +++ b/configure.ac @@ -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]) From 611b41e7a696f96955e1c1a88c435b9a10311108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20du=20Bo=C3=BFs?= Date: Tue, 21 Feb 2023 19:33:56 +0100 Subject: [PATCH 27/32] Merge configs for the same hosts instead of using the last one --- ddclient.in | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ddclient.in b/ddclient.in index 530458f..38f68e5 100755 --- a/ddclient.in +++ b/ddclient.in @@ -1639,9 +1639,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 = (); From 36de4e0b88969f9fcf8b6bf9c9d3864705a75822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20du=20Bo=C3=BFs?= Date: Tue, 21 Feb 2023 19:45:41 +0100 Subject: [PATCH 28/32] updating changelog --- ChangeLog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index f3919f2..2496130 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -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 From b225d37528ce00f0785f94c72268756c439ec264 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20du=20Bo=C3=BFs?= Date: Tue, 21 Feb 2023 21:10:28 +0100 Subject: [PATCH 29/32] Remove README.ssl from makefile as it's deleted on master --- Makefile.am | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index a9543ee..99ab507 100644 --- a/Makefile.am +++ b/Makefile.am @@ -6,7 +6,6 @@ EXTRA_DIST = \ ChangeLog.md \ README.cisco \ README.md \ - README.ssl \ autogen \ sample-ddclient-wrapper.sh \ sample-etc_cron.d_ddclient \ From 07fbbeb5bb5b54797efab34e4f3115fdb487a13b Mon Sep 17 00:00:00 2001 From: Thomas Hebb Date: Sat, 25 Feb 2023 23:28:45 -0500 Subject: [PATCH 30/32] Fix a few whitespace issues and a typo --- ddclient.in | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/ddclient.in b/ddclient.in index 530458f..a37bb16 100755 --- a/ddclient.in +++ b/ddclient.in @@ -931,13 +931,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, @@ -962,10 +962,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'} = { @@ -1371,7 +1371,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); } @@ -5703,7 +5703,7 @@ sub nic_changeip_update { } ###################################################################### -## nic_googledomains_examples +## nic_godaddy_examples ## ## written by awalon ## From 4458cceb1b29b4b85fbe4f38f3381a6621048d00 Mon Sep 17 00:00:00 2001 From: Lenard Hess Date: Sun, 26 Feb 2023 11:31:03 +0100 Subject: [PATCH 31/32] Add environment variable to override FRITZ!Box hostname For setups with a different DNS server than the FRITZ!Box, the fritz.box DNS entry may be missing. --- sample-get-ip-from-fritzbox | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sample-get-ip-from-fritzbox b/sample-get-ip-from-fritzbox index 079df07..05c8277 100755 --- a/sample-get-ip-from-fritzbox +++ b/sample-get-ip-from-fritzbox @@ -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 ' ' \ - 'http://fritz.box:49000/igdupnp/control/WANIPConn1' | \ + "http://$FRITZ_BOX_HOSTNAME:49000/igdupnp/control/WANIPConn1" | \ grep -Eo '\<[[:digit:]]{1,3}(\.[[:digit:]]{1,3}){3}\>' From c436a34ede5b0d39133071c8d1a0144d6c0d6c41 Mon Sep 17 00:00:00 2001 From: Thomas Hebb Date: Sat, 25 Feb 2023 23:31:41 -0500 Subject: [PATCH 32/32] Add support for Digital Ocean --- ddclient.conf.in | 8 +++ ddclient.in | 130 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 137 insertions(+), 1 deletion(-) diff --git a/ddclient.conf.in b/ddclient.conf.in index 410b356..6a7a205 100644 --- a/ddclient.conf.in +++ b/ddclient.conf.in @@ -347,3 +347,11 @@ ssl=yes # use ssl-support. Works with # 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 diff --git a/ddclient.in b/ddclient.in index fb2dfec..a4f4c12 100755 --- a/ddclient.in +++ b/ddclient.in @@ -605,6 +605,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, @@ -1795,7 +1806,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); @@ -7839,6 +7850,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