From 9573051e3e5b6d171a50a031a100f808daa8f4cf Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 18 May 2024 03:01:21 -0400 Subject: [PATCH 1/8] njalla: Update cached status and IP on success --- ddclient.in | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ddclient.in b/ddclient.in index 84570f2..d7044f6 100755 --- a/ddclient.in +++ b/ddclient.in @@ -5274,10 +5274,12 @@ sub nic_njalla_update { # Try to get URL my $reply = geturl(proxy => opt('proxy'), url => $url); my $response = ''; + my $status = 'bad'; if ($quietreply) { $reply =~ qr/invalid host or key/mp; $response = ${^MATCH}; if (!$response) { + $status = 'good'; success("updating %s: good: IP address set to %s", $h, $ip_output); } elsif ($response =~ /invalid host or key/) { @@ -5296,12 +5298,19 @@ sub nic_njalla_update { if ($response->{status} == 401 && $response->{message} =~ /invalid host or key/) { failed("Invalid host or key"); } elsif ($response->{status} == 200 && $response->{message} =~ /record updated/) { + $status = 'good'; success("updating %s: good: IP address set to %s", $h, $response->{value}->{A}); } else { failed("Unknown response"); } } } + if ($status eq 'good') { + $config{$h}{'ipv4'} = $ipv4 if $ipv4; + $config{$h}{'ipv6'} = $ipv6 if $ipv6; + } + $config{$h}{'status-ipv4'} = $status if $ipv4; + $config{$h}{'status-ipv6'} = $status if $ipv6; } } From 8b0c038d63cc0be658239899b0167f165d1ae933 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 18 May 2024 03:04:23 -0400 Subject: [PATCH 2/8] 1984: Fix missing `next` on failure --- ddclient.in | 1 + 1 file changed, 1 insertion(+) diff --git a/ddclient.in b/ddclient.in index d7044f6..0785eb6 100755 --- a/ddclient.in +++ b/ddclient.in @@ -5606,6 +5606,7 @@ sub nic_1984_update { } unless ($response->{ok}) { failed("%s", $response->{msg}); + next; } if ($response->{msg} =~ /unaltered/) { From baec50d1342ee86db7c02aae11431667b1a6c3ea Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 18 May 2024 03:05:24 -0400 Subject: [PATCH 3/8] 1984: Update cached status and IP on success --- ddclient.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ddclient.in b/ddclient.in index 0785eb6..5ef3b51 100755 --- a/ddclient.in +++ b/ddclient.in @@ -5609,6 +5609,8 @@ sub nic_1984_update { next; } + $config{$host}{'status'} = 'good'; + $config{$host}{'ip'} = $ip; if ($response->{msg} =~ /unaltered/) { success("Updating %s: skipped: IP was already set to %s", $host, $response->{ip}); } else { From dc92f16eb20d8d1376afc308371d31036cdc23c4 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 18 May 2024 03:17:14 -0400 Subject: [PATCH 4/8] dnsexit2: Update new `status-ipv*` vars, not legacy `status` The `dnsexit2` protocol reads the IP addresses from the new `ipv4` and `ipv6` variables, so it should update the `status-ipv4` and `status-ipv6` variables. --- ddclient.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ddclient.in b/ddclient.in index 5ef3b51..29bc1c0 100755 --- a/ddclient.in +++ b/ddclient.in @@ -4269,7 +4269,8 @@ sub nic_dnsexit2_update { my ($status, $message, $srv_message, $srv_details) = @{ $status {$response->{'code'} } }; info("Status: %s -- Message: %s", $status, $message); info("Server Message: %s -- Server Details: %s", $srv_message, $srv_details); - $config{$h}{'status'} = $status; + $config{$h}{'status-ipv4'} = $status if $ipv4; + $config{$h}{'status-ipv6'} = $status if $ipv6; # Handle statuses if ($status eq 'good') { From c60aa225a1d66f0d3eb6a36ad3d1b4c349a85a57 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 18 May 2024 00:34:35 -0400 Subject: [PATCH 5/8] godaddy: Rename `$status` to `$code` --- ddclient.in | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/ddclient.in b/ddclient.in index 29bc1c0..2e0c65c 100755 --- a/ddclient.in +++ b/ddclient.in @@ -5790,12 +5790,12 @@ sub nic_godaddy_update { next; } - (my $status) = ($reply =~ m%^s*HTTP/.*\s+(\d+)%i); + (my $code) = ($reply =~ m%^s*HTTP/.*\s+(\d+)%i); my $ok = header_ok($host, $reply); my $msg; $reply =~ s/^.*?\n\n//s; # extract payload my $response = eval {decode_json($reply)}; - if (!defined($response) && $status != "200") { + if (!defined($response) && $code != "200") { $config{$host}{'status'} = "bad"; failed("%s.%s -- Unexpected or empty service response, cannot parse data.", $hostname, $zone); @@ -5808,26 +5808,26 @@ sub nic_godaddy_update { $config{$host}{'mtime'} = $now; $config{$host}{"status-ipv$ipversion"} = 'good'; - success("%s.%s -- Updated successfully to %s (status: %s).", $hostname, $zone, $ip, $status); + success("%s.%s -- Updated successfully to %s (status: %s).", $hostname, $zone, $ip, $code); next; - } elsif ($status == "400") { + } elsif ($code == "400") { $msg = 'GoDaddy API URL ($url) was malformed.'; - } elsif ($status == "401") { # authentication error + } elsif ($code == "401") { # authentication error if ($config{$host}{'login'} && $config{$host}{'login'}) { $msg = 'login or password option incorrect.'; } else { $msg = 'login or password option missing.'; } $msg .= ' Correct values can be obtained from from https://developer.godaddy.com/keys/.'; - } elsif ($status == "403") { + } elsif ($code == "403") { $msg = 'Customer identified by login and password options denied permission.'; - } elsif ($status == "404") { + } elsif ($code == "404") { $msg = "\"${hostname}.${zone}\" not found at GoDaddy, please check zone option and login/password."; - } elsif ($status == "422") { + } elsif ($code == "422") { $msg = "\"${hostname}.${zone}\" has invalid domain or lacks A/AAAA record."; - } elsif ($status == "429") { + } elsif ($code == "429") { $msg = 'Too many requests to GoDaddy within brief period.'; - } elsif ($status == "503") { + } elsif ($code == "503") { $msg = "\"${hostname}.${zone}\" is unavailable."; } else { $msg = 'Unexpected service response.'; From f21261352623ceb06c654e8a404cb0fcde222410 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 18 May 2024 00:36:04 -0400 Subject: [PATCH 6/8] godaddy: Fix status field name This shouldn't matter in practice because the `status-ipv$ipversion` field is initialized to a non-`good` value so failing to set it to `bad` doesn't turn it `good`, but it improves readability. --- ddclient.in | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ddclient.in b/ddclient.in index 2e0c65c..2bee75a 100755 --- a/ddclient.in +++ b/ddclient.in @@ -5764,6 +5764,7 @@ sub nic_godaddy_update { verbose("UPDATE:", "updating %s.%s", $hostname, $zone); my $ipversion = ($ip eq ($ipv6 // '')) ? '6' : '4'; + my $status = \$config{$host}{"status-ipv$ipversion"}; my $rrset_type = ($ipversion eq '6') ? 'AAAA' : 'A'; my $data = encode_json([ { data => $ip, @@ -5796,7 +5797,7 @@ sub nic_godaddy_update { $reply =~ s/^.*?\n\n//s; # extract payload my $response = eval {decode_json($reply)}; if (!defined($response) && $code != "200") { - $config{$host}{'status'} = "bad"; + $$status = "bad"; failed("%s.%s -- Unexpected or empty service response, cannot parse data.", $hostname, $zone); } elsif (defined($response->{code})) { @@ -5806,7 +5807,7 @@ sub nic_godaddy_update { # read data $config{$host}{"ipv$ipversion"} = $ip; $config{$host}{'mtime'} = $now; - $config{$host}{"status-ipv$ipversion"} = 'good'; + $$status = 'good'; success("%s.%s -- Updated successfully to %s (status: %s).", $hostname, $zone, $ip, $code); next; @@ -5833,7 +5834,7 @@ sub nic_godaddy_update { $msg = 'Unexpected service response.'; } - $config{$host}{"status-ipv$ipversion"} = 'bad'; + $$status = 'bad'; failed("%s.%s -- %s", $hostname, $zone, $msg); } } From ba18535c51c81014291a14f790bfcbd21e9952db Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 18 May 2024 03:09:56 -0400 Subject: [PATCH 7/8] Fix broken legacy `status` handling `$config{$h}{'status'}` was always initialized to a non-`undef` value, so the `//` fallbacks never did anything. Instead, any protocol that does not explicitly update the legacy `status` variable (such as `godaddy`) would always appear to have failed even if it had succeeded. Change the `status*` variables to `undef` by default, and only set them when an attempt is made so that the legacy `//` fallback works as expected. --- ChangeLog.md | 2 ++ ddclient.in | 38 ++++++++++++++++++-------------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 513f988..b8e2766 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -66,6 +66,8 @@ repository history](https://github.com/ddclient/ddclient/commits/master). [#664](https://github.com/ddclient/ddclient/pull/664) * Fixed "Scalar value better written as" Perl warning. [#667](https://github.com/ddclient/ddclient/pull/667) + * Fixed unnecessary repeated updates for some services. + [#670](https://github.com/ddclient/ddclient/pull/670) ## 2023-11-23 v3.11.2 diff --git a/ddclient.in b/ddclient.in index 2bee75a..9c79d5e 100755 --- a/ddclient.in +++ b/ddclient.in @@ -639,9 +639,9 @@ my %variables = ( 'wtime' => setv(T_DELAY, 0, 1, 0, interval('30s')), 'mtime' => setv(T_NUMBER,0, 1, 0, undef), 'atime' => setv(T_NUMBER,0, 1, 0, undef), - 'status' => setv(T_ANY, 0, 1, '', undef), #TODO remove from cache? - 'status-ipv4' => setv(T_ANY, 0, 1, '', undef), - 'status-ipv6' => setv(T_ANY, 0, 1, '', undef), + 'status' => setv(T_ANY, 0, 1, undef, undef), #TODO remove from cache? + 'status-ipv4' => setv(T_ANY, 0, 1, undef, undef), + 'status-ipv6' => setv(T_ANY, 0, 1, undef, undef), 'min-interval' => setv(T_DELAY, 0, 0, interval('30s'), 0), 'max-interval' => setv(T_DELAY, 0, 0, interval('25d'), 0), 'min-error-interval' => setv(T_DELAY, 0, 0, interval('5m'), 0), @@ -1452,10 +1452,8 @@ sub update_nics { # To allow gradual transition, we make sure both the old 'status' and 'ip' are being set # accordingly to what new providers returned in the new 'status-ipv*' and 'ipv*' fields respectively. foreach my $h (@hosts) { - $config{$h}{'status'} //= $config{$h}{'status-ipv4'}; - $config{$h}{'status'} //= $config{$h}{'status-ipv6'}; - $config{$h}{'ip'} //= $config{$h}{'ipv4'}; - $config{$h}{'ip'} //= $config{$h}{'ipv6'}; + $config{$h}{'status'} //= $config{$h}{'status-ipv4'} // $config{$h}{'status-ipv6'}; + $config{$h}{'ip'} //= $config{$h}{'ipv4'} // $config{$h}{'ipv6'}; } runpostscript(join ' ', keys %ipsv4, keys %ipsv6); @@ -1919,7 +1917,7 @@ sub init_config { @hosts = split_by_comma($opt{'host'}); } if (opt('retry')) { - @hosts = map { $_ if $cache{$_}{'status'} ne 'good' } keys %cache; + @hosts = map { $_ if ($cache{$_}{'status'} // '') ne 'good' } keys %cache; } ## remove any other hosts @@ -3568,7 +3566,7 @@ sub nic_updateable { } elsif ( ($use ne 'disabled') && ((!exists($cache{$host}{'ip'})) || ("$cache{$host}{'ip'}" ne "$ip"))) { ## Check whether to update IP address for the "--use" method" - if (($cache{$host}{'status'} eq 'good') && + if ((($cache{$host}{'status'} // '') eq 'good') && !interval_expired($host, 'mtime', 'min-interval')) { warning("skipping update of %s from %s to %s.\nlast updated %s.\nWait at least %s between update attempts.", @@ -3582,7 +3580,7 @@ sub nic_updateable { $cache{$host}{'warned-min-interval'} = $now; - } elsif (($cache{$host}{'status'} ne 'good') && + } elsif ((($cache{$host}{'status'} // '') ne 'good') && !interval_expired($host, 'atime', 'min-error-interval')) { if ( opt('verbose') @@ -3613,7 +3611,7 @@ sub nic_updateable { } elsif ( ($usev4 ne 'disabled') && ((!exists($cache{$host}{'ipv4'})) || ("$cache{$host}{'ipv4'}" ne "$ipv4"))) { ## Check whether to update IPv4 address for the "--usev4" method" - if (($cache{$host}{'status-ipv4'} eq 'good') && + if ((($cache{$host}{'status-ipv4'} // '') eq 'good') && !interval_expired($host, 'mtime', 'min-interval')) { warning("skipping update of %s from %s to %s.\nlast updated %s.\nWait at least %s between update attempts.", @@ -3627,7 +3625,7 @@ sub nic_updateable { $cache{$host}{'warned-min-interval'} = $now; - } elsif (($cache{$host}{'status-ipv4'} ne 'good') && + } elsif ((($cache{$host}{'status-ipv4'} // '') ne 'good') && !interval_expired($host, 'atime', 'min-error-interval')) { if ( opt('verbose') @@ -3658,7 +3656,7 @@ sub nic_updateable { } elsif ( ($usev6 ne 'disabled') && ((!exists($cache{$host}{'ipv6'})) || ("$cache{$host}{'ipv6'}" ne "$ipv6"))) { ## Check whether to update IPv6 address for the "--usev6" method" - if (($cache{$host}{'status-ipv6'} eq 'good') && + if ((($cache{$host}{'status-ipv6'} // '') eq 'good') && !interval_expired($host, 'mtime', 'min-interval')) { warning("skipping update of %s from %s to %s.\nlast updated %s.\nWait at least %s between update attempts.", @@ -3672,7 +3670,7 @@ sub nic_updateable { $cache{$host}{'warned-min-interval'} = $now; - } elsif (($cache{$host}{'status-ipv6'} ne 'good') && + } elsif ((($cache{$host}{'status-ipv6'} // '') ne 'good') && !interval_expired($host, 'atime', 'min-error-interval')) { if ( opt('verbose') @@ -3727,14 +3725,14 @@ sub nic_updateable { } } - $config{$host}{'status'} = $cache{$host}{'status'} // ''; - $config{$host}{'status-ipv4'} = $cache{$host}{'status-ipv4'} // ''; - $config{$host}{'status-ipv6'} = $cache{$host}{'status-ipv6'} // ''; + $config{$host}{'status'} = $cache{$host}{'status'}; + $config{$host}{'status-ipv4'} = $cache{$host}{'status-ipv4'}; + $config{$host}{'status-ipv6'} = $cache{$host}{'status-ipv6'}; $config{$host}{'update'} = $update; if ($update) { - $config{$host}{'status'} = 'noconnect'; - $config{$host}{'status-ipv4'} = 'noconnect'; - $config{$host}{'status-ipv6'} = 'noconnect'; + $config{$host}{'status'} = undef; + $config{$host}{'status-ipv4'} = undef; + $config{$host}{'status-ipv6'} = undef; $config{$host}{'atime'} = $now; $config{$host}{'wtime'} = 0; $config{$host}{'warned-min-interval'} = 0; From 8ef7b13cb06615daadb0e84984a91776760645d9 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 18 May 2024 03:16:04 -0400 Subject: [PATCH 8/8] Don't set legacy `status` in protocols if IPv6-aware ddclient infrastructure will update the legacy `status` variable if necessary. --- ddclient.in | 6 ------ 1 file changed, 6 deletions(-) diff --git a/ddclient.in b/ddclient.in index 9c79d5e..84f7863 100755 --- a/ddclient.in +++ b/ddclient.in @@ -4053,7 +4053,6 @@ sub nic_dyndns2_update { my ($status, $returnedips) = split / /, lc $line; foreach my $h (@hosts) { - $config{$h}{'status'} = $status; $config{$h}{'status-ipv4'} = $status if $ipv4; $config{$h}{'status-ipv6'} = $status if $ipv6; } @@ -4076,7 +4075,6 @@ sub nic_dyndns2_update { $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; } @@ -6634,7 +6632,6 @@ sub nic_duckdns_update { $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; $state = 'result'; @@ -6642,7 +6639,6 @@ sub nic_duckdns_update { success("updating %s: good: IPv6 address set to %s", $h, $ipv6) if $ipv6; } elsif ($line eq 'KO') { - $config{$h}{'status'} = 'failed'; $config{$h}{'status-ipv4'} = 'failed' if $ipv4; $config{$h}{'status-ipv6'} = 'failed' if $ipv6; $state = 'result'; @@ -8061,7 +8057,6 @@ sub nic_infomaniak_update { info($msg); $config{$h}{"ipv$v"} = $ip; $config{$h}{'mtime'} = $config{$h}{'mtime'} // $now; - $config{$h}{'status'} = 'good'; $config{$h}{"status-ipv$v"} = 'good'; next INFOMANIAK_IP_LOOP; } @@ -8070,7 +8065,6 @@ sub nic_infomaniak_update { } } - $config{$h}{'status'} = $config{$h}{'status'} // 'failed'; $config{$h}{"status-ipv$v"} = 'failed'; failed("updating %s: could not update IP on Infomaniak", $h); }