DNSExit update for ipv6.

- Support wantipv4 and wantipv6 configs automatically
- Remove manual record type setting
- Add support for hosts on your zone (subdomain)
- Update config examples
- Code/logic improvements
This commit is contained in:
jortkoopmans 2023-10-17 23:01:34 +02:00 committed by Reuben Thomas
parent 8ed2963b79
commit 9913e0ec29

View file

@ -535,9 +535,9 @@ my %variables = (
'dnsexit2-common-defaults' => { 'dnsexit2-common-defaults' => {
'ssl' => setv(T_BOOL, 0, 0, 1, undef), 'ssl' => setv(T_BOOL, 0, 0, 1, undef),
'server' => setv(T_FQDNP, 1, 0, 'api.dnsexit.com', undef), 'server' => setv(T_FQDNP, 1, 0, 'api.dnsexit.com', undef),
'path' => setv(T_STRING, 0, 1, '/dns/', undef), 'path' => setv(T_STRING, 0, 0, '/dns/', undef),
'record-type' => setv(T_STRING, 1, 0, 'A', undef),
'ttl' => setv(T_NUMBER, 1, 0, 5, 0), 'ttl' => setv(T_NUMBER, 1, 0, 5, 0),
'zone' => setv(T_STRING, 0, 0, undef, undef)
}, },
'regfishde-common-defaults' => { 'regfishde-common-defaults' => {
'server' => setv(T_FQDNP, 1, 0, 'dyndns.regfish.de', undef), 'server' => setv(T_FQDNP, 1, 0, 'dyndns.regfish.de', undef),
@ -4050,24 +4050,29 @@ sub nic_dnsexit2_examples {
return <<"EoEXAMPLE"; return <<"EoEXAMPLE";
o 'dnsexit2' o 'dnsexit2'
The 'dnsexit2' protocol is the new API protocol used by the dynamic hostname services The 'dnsexit2' protocol is the updated protocol for the (free) dynamic hostname services
of the 'DNSExit' dns services. This is currently used by the free of 'DNSExit' (www.dnsexit.com). Their API is accepting JSON payload.
dynamic DNS service offered by www.dnsexit.com.
Configuration variables applicable to the 'dnsexit2' protocol are: Configuration variables applicable to the 'dnsexit2' protocol are:
protocol=dnsexit2 ## protocol=dnsexit2 ##
password=YourAPIKey ## API Key of your account. password=YourAPIKey ## API Key of your account.
server=api.dnsexit.com ## defaults to api.dnsexit.com. server=api.dnsexit.com ## defaults to api.dnsexit.com.
path=/dns/ ## defaults to /dns/. path=/dns/ ## defaults to /dns/.
record-type=A ## defaults to A record.
ttl=5 ## defaults to 5 minutes. ttl=5 ## defaults to 5 minutes.
zone='' ## defaults to empty, which assumes the zone is equal to the fully.qualified.host (is root of your DNSExit domain).
fully.qualified.host ## the host registered with the service. fully.qualified.host ## the host registered with the service.
Example ${program}.conf file entries: Example ${program}.conf file entries:
## single host update ## single host update
protocol=dnsexit2 protocol=dnsexit2
password=YourAPIKey password=YourAPIKey
fully.qualified.host yourown.publicvm.com
## two hosts (which must be) on the same zone
protocol=dnsexit2
password=YourAPIKey
zone=yourown.publicvm.com
host1.yourown.publicvm.com,host2.yourown.publicvm.com
EoEXAMPLE EoEXAMPLE
} }
@ -4081,7 +4086,7 @@ EoEXAMPLE
sub nic_dnsexit2_update { sub nic_dnsexit2_update {
debug("\nnic_dnsexit2_update -------------------"); debug("\nnic_dnsexit2_update -------------------");
## Update each configured host ## Update each configured host (hosts cannot be grouped on this API)
foreach my $h (@_) { foreach my $h (@_) {
# All the known status # All the known status
my %status = ( my %status = (
@ -4094,58 +4099,73 @@ sub nic_dnsexit2_update {
'6' => [ 'error', 'System Error. Our system problem. May not be your problem. Contact our support if you got such error.' ], '6' => [ 'error', 'System Error. Our system problem. May not be your problem. Contact our support if you got such error.' ],
'7' => [ 'error', 'Error getting post data. Our server has problem to receive your JSON posting.' ], '7' => [ 'error', 'Error getting post data. Our server has problem to receive your JSON posting.' ],
); );
my $ip = delete $config{$h}{'wantip'}; my $ipv4 = delete $config{$h}{'wantipv4'};
info("Going to update IP address to %s for %s.", $ip, $h); my $ipv6 = delete $config{$h}{'wantipv6'};
# Updates for ipv4 and ipv6 need to be combined in a single API call, create Hash of Arrays for tracking
my %total_payload;
foreach my $ip ($ipv4, $ipv6){
next if (!$ip);
my $ipv = ($ip eq ($ipv6 // '')) ? '6' : '4';
my $type = ($ip eq ($ipv6 // '')) ? 'AAAA' : 'A';
info("Going to update IPv$ipv address to %s for %s.", $ip, $h);
$config{$h}{'status-ipv$ipv'} = 'failed';
# One key per ipv (4 or 6)
my %payload = (name => $h, type => $type, content => $ip, ttl => $config{$h}{'ttl'});
@total_payload{$ipv} = \%payload;
};
# Set the URL of the API endpoint # Set the URL of the API endpoint
my $url = "https://$config{$h}{'server'}$config{$h}{'path'}"; my $url = "https://$config{$h}{'server'}$config{$h}{'path'}";
# Set JSON payload # Set additional headers
my $header = "Content-Type: application/json\nAccept: application/json";
# Set the zone if empty
if ( not defined $config{$h}{'zone'}){
debug("Zone not defined, setting to default hostname: %s", $h);
$config{$h}{'zone'} = $h
} else {
debug("Zone is: %s", $config{$h}{'zone'});
}
# Build total JSON payload
my @payload_values = values %total_payload;
my $data = encode_json({ my $data = encode_json({
apikey => $config{$h}{'password'}, apikey => $config{$h}{'password'},
domain => $h, domain => $config{$h}{'zone'},
update => { update => \@payload_values
type => $config{$h}{'record-type'},
name => $h,
content => $ip,
ttl => $config{$h}{'ttl'}},
}); });
# Set additional headers
my $header = "Content-Type: application/json\n";
$header .= "Accept: application/json";
# Make the call # Make the call
my $reply = geturl( my $reply = geturl(
proxy => opt('proxy'), proxy => opt('proxy'),
url => $url, url => $url,
headers => $header, headers => $header,
method => 'POST', method => 'POST',
data => $data, data => $data
); );
# No reply, declare as failed # No reply, declare as failed
if (!defined($reply) || !$reply) { unless ($reply && header_ok($h, $reply)){
failed("updating %s: Could not connect to %s%s.", $h, $config{$h}{'server'}, $config{$h}{'path'}); failed("updating %s: Could not connect to %s%s.", $h, $config{$h}{'server'}, $config{$h}{'path'});
$config{$h}{'status'} = 'failed';
last; last;
}; };
# Reply found # Reply found
debug("%s", $reply); debug("%s", $reply);
# $ok is mandatory?
my $ok = header_ok($h, $reply);
# Extract the HTTP response code # Extract the HTTP response code
(my $http_status) = ($reply =~ m%^s*HTTP/.*\s+(\d+)%i); (my $http_status) = ($reply =~ m%^s*HTTP/.*\s+(\d+)%i);
debug("HTTP response code: %s", $http_status); debug("HTTP response code: %s", $http_status);
# If not 200, bail # If not 200, bail
if ( $http_status != "200"){ if ( $http_status ne '200' ){
failed("Failed to update Host\n%s to IP:%s", $h, $ip); failed("Failed to update Host\n%s", $h);
failed("HTTP response code\n%s", $http_status); failed("HTTP response code\n%s", $http_status);
failed("Full reply\n%s", $reply) unless opt('verbose'); failed("Full reply\n%s", $reply) unless opt('verbose');
$config{$h}{'status'} = 'failed'; next;
last;
} }
# Strip HTTP response headers # Strip HTTP response headers
@ -4182,31 +4202,30 @@ sub nic_dnsexit2_update {
# Handle statuses # Handle statuses
if ($status eq 'good') { if ($status eq 'good') {
$config{$h}{'ip'} = $ip;
$config{$h}{'mtime'} = $now; $config{$h}{'mtime'} = $now;
$config{$h}{'status'} = 'good'; my $tracked_ipv;
foreach $tracked_ipv ( keys %total_payload ){
$config{$h}{"ipv$tracked_ipv"} = $total_payload{$tracked_ipv}{content};
$config{$h}{"status-ipv$tracked_ipv"} = 'good';
success("%s", $message); success("%s", $message);
success("Updated %s successfully to IP address %s at time %s", $h, $ip, prettytime($config{$h}{'mtime'})); success("Updated %s successfully to IPv$tracked_ipv address %s at time %s", $h, $total_payload{$tracked_ipv}{content}, prettytime($config{$h}{'mtime'}));
}
} elsif ($status eq 'warning') { } elsif ($status eq 'warning') {
warning("%s", $message); warning("%s", $message);
warning("Server response: %s", $srv_message); warning("Server response: %s", $srv_message);
} elsif ($status =~ m'^(badauth|error)$') { } elsif ($status =~ m'^(badauth|error)$') {
failed("%s", $message); failed("%s", $message);
failed("Server response: %s", $srv_message); failed("Server response: %s", $srv_message);
$config{$h}{'status'} = 'failed';
} else { } else {
failed("This should not be possible"); failed("This should not be possible");
$config{$h}{'status'} = 'failed';
} }
} else { } else {
failed("Status code %s is unknown!", $response->{'code'}); failed("Status code %s is unknown!", $response->{'code'});
$config{$h}{'status'} = 'failed';
} }
} else { } else {
failed("Did not receive expected \"code\" and \"message\" keys in server response."); failed("Did not receive expected \"code\" and \"message\" keys in server response.");
failed("Response:"); failed("Response:");
failed("%s", $response); failed("%s", $response);
$config{$h}{'status'} = 'failed';
} }
} }
} }