From 5ec8bfe1417df05bb2f2ef69e26b0ad950f98cd2 Mon Sep 17 00:00:00 2001 From: Joel Beckmeyer Date: Mon, 31 Jul 2023 23:06:39 -0400 Subject: [PATCH 1/2] gandi: update logic - allow updating IPv6/AAAA - allow updating A and AAAA records simultaneously - skip updating if record already has same IP --- ddclient.in | 127 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 75 insertions(+), 52 deletions(-) diff --git a/ddclient.in b/ddclient.in index c509aba..58324e9 100755 --- a/ddclient.in +++ b/ddclient.in @@ -7362,68 +7362,91 @@ EoEXAMPLE ###################################################################### sub nic_gandi_update { debug("\nnic_gandi_update -------------------"); - # Update each set configured host. foreach my $h (@_) { - my $ip = delete $config{$h}{'wantip'}; - (my $hostname = $h) =~ s/\.\Q$config{$h}{zone}\E$//; + foreach my $ipv ('ipv4', 'ipv6') { + my $ip = delete $config{$h}{"want$ipv"}; + if(!$ip) { + next; + } + (my $hostname = $h) =~ s/\.\Q$config{$h}{zone}\E$//; + info("%s -- Setting IP address to %s.", $h, $ip); + verbose("UPDATE:", "updating %s", $h); - info("%s -- Setting IP address to %s.", $h, $ip); - verbose("UPDATE:", "updating %s", $h); + my $headers; + $headers = "Content-Type: application/json\n"; + $headers .= "Authorization: Apikey $config{$h}{'password'}\n"; - my $headers; - $headers = "Content-Type: application/json\n"; - $headers .= "Authorization: Apikey $config{$h}{'password'}\n"; - my $data = encode_json({ - defined($config{$h}{'ttl'}) ? (rrset_ttl => $config{$h}{'ttl'}) : (), - rrset_values => [$ip], - }); - my $rrset_type = is_ipv6($ip) ? "AAAA" : "A"; - my $url; - $url = "https://$config{$h}{'server'}$config{$h}{'script'}"; - $url .= "/livedns/domains/$config{$h}{'zone'}/records/$hostname/$rrset_type"; + my $rrset_type = $ipv eq 'ipv6' ? 'AAAA' : 'A'; + my $url; + $url = "https://$config{$h}{'server'}$config{$h}{'script'}"; + $url .= "/livedns/domains/$config{$h}{'zone'}/records/$hostname/$rrset_type"; - my $reply = geturl( - proxy => opt('proxy'), - url => $url, - headers => $headers, - method => 'PUT', - data => $data, - ); - unless ($reply) { - failed("%s -- Could not connect to %s.", $h, $config{$h}{'server'}); - next; - } - my $ok = header_ok($h, $reply); + my $reply = geturl( + proxy => opt('proxy'), + url => $url, + headers => $headers, + method => 'GET' + ); + unless ($reply) { + failed("%s -- Could not connect to %s.", $h, $config{$h}{'server'}); + next; + } + my $ok = header_ok($h, $reply); - $reply =~ s/^.*?\n\n//s; - my $response = eval { decode_json($reply) }; - if (!defined($response)) { - $config{$h}{'status'} = "bad"; + $reply =~ s/^.*?\n\n//s; + my $response = eval { decode_json($reply) }; + if (!defined($response)) { + $config{$h}{"status-$ipv"} = "bad"; - failed("%s -- Unexpected service response.", $h); - next; - } - - if ($ok) { - $config{$h}{'ip'} = $ip; - $config{$h}{'mtime'} = $now; - $config{$h}{'status'} = "good"; - - success("%s -- Updated successfully to %s.", $h, $ip); - } else { - $config{$h}{'status'} = "bad"; - - if (defined($response->{status}) && $response->{status} eq "error") { - my @errors; - for my $err (@{$response->{errors}}) { - push(@errors, $err->{description}); - } - failed("%s -- %s.", $h, join(", ", @errors)); - } else { failed("%s -- Unexpected service response.", $h); + next; + } + if($response->{'rrset_values'}->[0] eq $ip && (!defined($config{$h}{'ttl'}) || + $response->{'rrset_ttl'} eq $config{$h}{'ttl'})) { + $config{$h}{'ip'} = $ip; + $config{$h}{'mtime'} = $now; + $config{$h}{"status-$ipv"} = "good"; + success("updating %s: skipped: address was already set to %s.", $h, $ip); + next; + } + + my $data = encode_json({ + defined($config{$h}{'ttl'}) ? (rrset_ttl => $config{$h}{'ttl'}) : (), + rrset_values => [$ip], + }); + $reply = geturl( + proxy => opt('proxy'), + url => $url, + headers => $headers, + method => 'PUT', + data => $data, + ); + unless ($reply) { + failed("%s -- Could not connect to %s.", $h, $config{$h}{'server'}); + next; + } + $ok = header_ok($h, $reply); + if ($ok) { + $config{$h}{'ip'} = $ip; + $config{$h}{'mtime'} = $now; + $config{$h}{"status-$ipv"} = "good"; + + success("%s -- Updated successfully to %s.", $h, $ip); + } else { + $config{$h}{"status-$ipv"} = "bad"; + + if (defined($response->{status}) && $response->{status} eq "error") { + my @errors; + for my $err (@{$response->{errors}}) { + push(@errors, $err->{description}); + } + failed("%s -- %s.", $h, join(", ", @errors)); + } else { + failed("%s -- Unexpected service response.", $h); + } } } } From 846ab59e6833871ee10688e466d626aa87a1ba2b Mon Sep 17 00:00:00 2001 From: Joel Beckmeyer Date: Wed, 2 Aug 2023 19:49:39 -0400 Subject: [PATCH 2/2] gandi: improve documentation --- ddclient.conf.in | 8 ++++---- ddclient.in | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/ddclient.conf.in b/ddclient.conf.in index fb50ab3..69ddc47 100644 --- a/ddclient.conf.in +++ b/ddclient.conf.in @@ -197,10 +197,10 @@ ssl=yes # use ssl-support. Works with ## Gandi (gandi.net) ## ## Single host update -# protocol=gandi, \ -# zone=example.com, \ -# password=my-gandi-api-key, \ -# ttl=3h \ +# protocol=gandi +# zone=example.com +# password=my-gandi-api-key +# ttl=10800 # optional # myhost.example.com ## diff --git a/ddclient.in b/ddclient.in index 58324e9..514693a 100755 --- a/ddclient.in +++ b/ddclient.in @@ -7339,20 +7339,20 @@ Available configuration variables: Required. * zone: The DNS zone to be updated. Required. * ttl: The time-to-live value associated with the updated DNS record. - Optional; uses Gandi's default (3h) if unset. + Optional; uses Gandi's default (10800) if unset. Example ${program}.conf file entries: ## Single host update. - protocol=gandi, \\ - zone=example.com, \\ - password=my-gandi-api-key, \\ + protocol=gandi + zone=example.com + password=my-gandi-api-key host.example.com ## Multiple host update. - protocol=gandi, \\ - zone=example.com, \\ - password=my-gandi-api-key, \\ - ttl=1h \\ + protocol=gandi + zone=example.com + password=my-gandi-api-key + ttl=3600 # optional hosta.example.com,hostb.sub.example.com EoEXAMPLE }