From 45e36039180ae118e316117b4b10dfcecaa9beb3 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 13 Jul 2024 04:01:22 -0400 Subject: [PATCH 1/5] dyndns2: Simplify response parsing --- ddclient.in | 103 +++++++++++++++++++++++++--------------------------- 1 file changed, 49 insertions(+), 54 deletions(-) diff --git a/ddclient.in b/ddclient.in index a26099d..4098313 100755 --- a/ddclient.in +++ b/ddclient.in @@ -4073,61 +4073,56 @@ sub nic_dyndns2_update { next; } next if !header_ok($hosts, $reply); - my @reply = split /\n/, $reply; - my $state = 'header'; - for my $line (@reply) { - if ($state eq 'header') { - $state = 'body'; - } elsif ($state eq 'body') { - $state = 'results' if $line eq ''; - } elsif ($state =~ /^results/) { - $state = 'results2'; - # bug #10: some dyndns providers does not return the IP so - # we can't use the returned IP - my ($status, $returnedips) = split / /, lc $line; - for my $h (@hosts) { - $config{$h}{'status-ipv4'} = $status if $ipv4; - $config{$h}{'status-ipv6'} = $status if $ipv6; - } - if ($status eq 'good') { - for my $h (@hosts) { - $config{$h}{'ipv4'} = $ipv4 if $ipv4; - $config{$h}{'ipv6'} = $ipv6 if $ipv6; - $config{$h}{'mtime'} = $now; - } - success("updating %s: %s: IPv4 address set to %s", $hosts, $status, $ipv4) if $ipv4; - success("updating %s: %s: IPv6 address set to %s", $hosts, $status, $ipv6) if $ipv6; - } elsif (exists $errors{$status}) { - if ($status eq 'nochg') { - warning("updating %s: %s: %s", $hosts, $status, $errors{$status}); - for my $h (@hosts) { - $config{$h}{'ipv4'} = $ipv4 if $ipv4; - $config{$h}{'ipv6'} = $ipv6 if $ipv6; - $config{$h}{'mtime'} = $now; - $config{$h}{'status-ipv4'} = 'good' if $ipv4; - $config{$h}{'status-ipv6'} = 'good' if $ipv6; - } - } else { - failed("updating %s: %s: %s", $hosts, $status, $errors{$status}); - } - } elsif ($status =~ /w(\d+)(.)/) { - my ($wait, $units) = ($1, lc $2); - my ($sec, $scale) = ($wait, 1); - ($scale, $units) = (1, 'seconds') if $units eq 's'; - ($scale, $units) = (60, 'minutes') if $units eq 'm'; - ($scale, $units) = (60*60, 'hours') if $units eq 'h'; - $sec = $wait * $scale; - for my $h (@hosts) { - $config{$h}{'wtime'} = $now + $sec; - } - warning("updating %s: %s: wait %s %s before further updates", $hosts, $status, $wait, $units); - } else { - failed("updating %s: unexpected status (%s)", $hosts, $line); - } - } + # Some services can return 200 OK even if there is an error (e.g., bad authentication, + # updates too frequent) so the body of the response must also be checked. + (my $body = $reply) =~ s/^.*?\n\n//s; + my ($line) = grep(qr/^results/, split(qr/\n/, $body)); + if (!$line) { + failed("updating %s: Could not connect to %s.", $hosts, $groupcfg{'server'}); + next; + } + # bug #10: some dyndns providers does not return the IP so + # we can't use the returned IP + my ($status, $returnedips) = split / /, lc $line; + for my $h (@hosts) { + $config{$h}{'status-ipv4'} = $status if $ipv4; + $config{$h}{'status-ipv6'} = $status if $ipv6; + } + if ($status eq 'good') { + for my $h (@hosts) { + $config{$h}{'ipv4'} = $ipv4 if $ipv4; + $config{$h}{'ipv6'} = $ipv6 if $ipv6; + $config{$h}{'mtime'} = $now; + } + success("updating %s: %s: IPv4 address set to %s", $hosts, $status, $ipv4) if $ipv4; + success("updating %s: %s: IPv6 address set to %s", $hosts, $status, $ipv6) if $ipv6; + } elsif (exists $errors{$status}) { + if ($status eq 'nochg') { + warning("updating %s: %s: %s", $hosts, $status, $errors{$status}); + for my $h (@hosts) { + $config{$h}{'ipv4'} = $ipv4 if $ipv4; + $config{$h}{'ipv6'} = $ipv6 if $ipv6; + $config{$h}{'mtime'} = $now; + $config{$h}{'status-ipv4'} = 'good' if $ipv4; + $config{$h}{'status-ipv6'} = 'good' if $ipv6; + } + } else { + failed("updating %s: %s: %s", $hosts, $status, $errors{$status}); + } + } elsif ($status =~ /w(\d+)(.)/) { + my ($wait, $units) = ($1, lc $2); + my ($sec, $scale) = ($wait, 1); + ($scale, $units) = (1, 'seconds') if $units eq 's'; + ($scale, $units) = (60, 'minutes') if $units eq 'm'; + ($scale, $units) = (60*60, 'hours') if $units eq 'h'; + $sec = $wait * $scale; + for my $h (@hosts) { + $config{$h}{'wtime'} = $now + $sec; + } + warning("updating %s: %s: wait %s %s before further updates", $hosts, $status, $wait, $units); + } else { + failed("updating %s: unexpected status (%s)", $hosts, $line); } - failed("updating %s: Could not connect to %s.", $hosts, $groupcfg{'server'}) - if $state ne 'results2'; } } From 0882712ec2f79f7ac074902669b31a072aa5ce07 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 13 Jul 2024 04:23:27 -0400 Subject: [PATCH 2/5] dyndns2: Treat `nochg` as `good` to eliminate duplicate code --- ddclient.in | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/ddclient.in b/ddclient.in index 4098313..56897a2 100755 --- a/ddclient.in +++ b/ddclient.in @@ -4084,6 +4084,10 @@ sub nic_dyndns2_update { # bug #10: some dyndns providers does not return the IP so # we can't use the returned IP my ($status, $returnedips) = split / /, lc $line; + if ($status eq 'nochg') { + warning("updating %s: %s: %s", $hosts, $status, $errors{$status}); + $status = 'good'; + } for my $h (@hosts) { $config{$h}{'status-ipv4'} = $status if $ipv4; $config{$h}{'status-ipv6'} = $status if $ipv6; @@ -4097,18 +4101,7 @@ sub nic_dyndns2_update { success("updating %s: %s: IPv4 address set to %s", $hosts, $status, $ipv4) if $ipv4; success("updating %s: %s: IPv6 address set to %s", $hosts, $status, $ipv6) if $ipv6; } elsif (exists $errors{$status}) { - if ($status eq 'nochg') { - warning("updating %s: %s: %s", $hosts, $status, $errors{$status}); - for my $h (@hosts) { - $config{$h}{'ipv4'} = $ipv4 if $ipv4; - $config{$h}{'ipv6'} = $ipv6 if $ipv6; - $config{$h}{'mtime'} = $now; - $config{$h}{'status-ipv4'} = 'good' if $ipv4; - $config{$h}{'status-ipv6'} = 'good' if $ipv6; - } - } else { - failed("updating %s: %s: %s", $hosts, $status, $errors{$status}); - } + failed("updating %s: %s: %s", $hosts, $status, $errors{$status}); } elsif ($status =~ /w(\d+)(.)/) { my ($wait, $units) = ($1, lc $2); my ($sec, $scale) = ($wait, 1); From ad4e3769eb673600aa484e7b1eaa0e1645d0bb2d Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 13 Jul 2024 04:24:55 -0400 Subject: [PATCH 3/5] dyndns2: Invert condition to improve readability --- ddclient.in | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/ddclient.in b/ddclient.in index 56897a2..c981b37 100755 --- a/ddclient.in +++ b/ddclient.in @@ -4092,30 +4092,32 @@ sub nic_dyndns2_update { $config{$h}{'status-ipv4'} = $status if $ipv4; $config{$h}{'status-ipv6'} = $status if $ipv6; } - if ($status eq 'good') { - for my $h (@hosts) { - $config{$h}{'ipv4'} = $ipv4 if $ipv4; - $config{$h}{'ipv6'} = $ipv6 if $ipv6; - $config{$h}{'mtime'} = $now; + if ($status ne 'good') { + if (exists($errors{$status})) { + failed("updating %s: %s: %s", $hosts, $status, $errors{$status}); + } elsif ($status =~ qr/w(\d+)(.)/) { + my ($wait, $units) = ($1, lc $2); + my ($sec, $scale) = ($wait, 1); + ($scale, $units) = (1, 'seconds') if $units eq 's'; + ($scale, $units) = (60, 'minutes') if $units eq 'm'; + ($scale, $units) = (60*60, 'hours') if $units eq 'h'; + $sec = $wait * $scale; + for my $h (@hosts) { + $config{$h}{'wtime'} = $now + $sec; + } + warning("updating %s: %s: wait %s %s before further updates", $hosts, $status, $wait, $units); + } else { + failed("updating %s: unexpected status (%s)", $hosts, $line); } - success("updating %s: %s: IPv4 address set to %s", $hosts, $status, $ipv4) if $ipv4; - success("updating %s: %s: IPv6 address set to %s", $hosts, $status, $ipv6) if $ipv6; - } elsif (exists $errors{$status}) { - failed("updating %s: %s: %s", $hosts, $status, $errors{$status}); - } elsif ($status =~ /w(\d+)(.)/) { - my ($wait, $units) = ($1, lc $2); - my ($sec, $scale) = ($wait, 1); - ($scale, $units) = (1, 'seconds') if $units eq 's'; - ($scale, $units) = (60, 'minutes') if $units eq 'm'; - ($scale, $units) = (60*60, 'hours') if $units eq 'h'; - $sec = $wait * $scale; - for my $h (@hosts) { - $config{$h}{'wtime'} = $now + $sec; - } - warning("updating %s: %s: wait %s %s before further updates", $hosts, $status, $wait, $units); - } else { - failed("updating %s: unexpected status (%s)", $hosts, $line); + next; } + for my $h (@hosts) { + $config{$h}{'ipv4'} = $ipv4 if $ipv4; + $config{$h}{'ipv6'} = $ipv6 if $ipv6; + $config{$h}{'mtime'} = $now; + } + success("updating %s: %s: IPv4 address set to %s", $hosts, $status, $ipv4) if $ipv4; + success("updating %s: %s: IPv6 address set to %s", $hosts, $status, $ipv6) if $ipv6; } } From 203bf12245d689e20d0a6b0cd838eaaf2af3ead6 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 13 Jul 2024 04:33:44 -0400 Subject: [PATCH 4/5] dyndns2: Improve readability of status parsing --- ddclient.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ddclient.in b/ddclient.in index c981b37..291b532 100755 --- a/ddclient.in +++ b/ddclient.in @@ -4081,9 +4081,9 @@ sub nic_dyndns2_update { failed("updating %s: Could not connect to %s.", $hosts, $groupcfg{'server'}); next; } - # bug #10: some dyndns providers does not return the IP so - # we can't use the returned IP - my ($status, $returnedips) = split / /, lc $line; + # The IP address normally comes after the status, but we ignore it. (Some services do not + # return the IP so we can't rely on it anyway.) + (my $status = $line) =~ s/ .*$//; if ($status eq 'nochg') { warning("updating %s: %s: %s", $hosts, $status, $errors{$status}); $status = 'good'; From d489cea344668a85961e35bb176bed5d6185e488 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 13 Jul 2024 04:52:05 -0400 Subject: [PATCH 5/5] dyndns2: Wrap long list of group by attributes --- ddclient.in | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/ddclient.in b/ddclient.in index 291b532..b8428e1 100755 --- a/ddclient.in +++ b/ddclient.in @@ -4013,7 +4013,6 @@ EoEXAMPLE ###################################################################### sub nic_dyndns2_update { debug("\nnic_dyndns2_update -------------------"); - my @groups = group_hosts_by(\@_, qw(login password server script static custom wildcard mx backupmx wantipv4 wantipv6)); my %errors = ( 'badauth' => 'Bad authorization (username or password)', 'badsys' => 'The system parameter given was not valid', @@ -4027,7 +4026,20 @@ sub nic_dyndns2_update { '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', ); - for my $group (@groups) { + my @group_by_attrs = qw( + backupmx + custom + login + mx + password + script + server + static + wantipv4 + wantipv6 + wildcard + ); + for my $group (group_hosts_by(\@_, @group_by_attrs)) { my @hosts = @{$group->{hosts}}; my %groupcfg = %{$group->{cfg}}; my $hosts = join(',', @hosts);