From 8a4c61f72137f92d26525da0c4e83c64d338a725 Mon Sep 17 00:00:00 2001 From: schreibubi Date: Wed, 21 Apr 2021 09:01:50 +0200 Subject: [PATCH] dns/dyndns: Add hetzner dns console to supported dyndns services (#2201) --- .../src/etc/inc/plugins.inc.d/dyndns.inc | 2 + .../inc/plugins.inc.d/dyndns/phpDynDNS.inc | 103 ++++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/dns/dyndns/src/etc/inc/plugins.inc.d/dyndns.inc b/dns/dyndns/src/etc/inc/plugins.inc.d/dyndns.inc index 865245694..e38fd0a46 100644 --- a/dns/dyndns/src/etc/inc/plugins.inc.d/dyndns.inc +++ b/dns/dyndns/src/etc/inc/plugins.inc.d/dyndns.inc @@ -128,6 +128,8 @@ function dyndns_list() 'he-net' => 'HE.net', 'he-net-tunnelbroker' => 'HE.net Tunnelbroker', 'he-net-v6' => 'HE.net (v6)', + 'hetzner' => 'Hetzner DNS Console', + 'hetzner-v6' => 'Hetzner DNS Console (v6)', 'linode' => 'Linode', 'linode-v6' => 'Linode (v6)', 'loopia' => 'Loopia', diff --git a/dns/dyndns/src/etc/inc/plugins.inc.d/dyndns/phpDynDNS.inc b/dns/dyndns/src/etc/inc/plugins.inc.d/dyndns/phpDynDNS.inc index e86b66e00..329c6db81 100644 --- a/dns/dyndns/src/etc/inc/plugins.inc.d/dyndns/phpDynDNS.inc +++ b/dns/dyndns/src/etc/inc/plugins.inc.d/dyndns/phpDynDNS.inc @@ -214,6 +214,8 @@ class updatedns break; case 'cloudflare-token': case 'cloudflare-token-v6': + case 'hetzner': + case 'hetzner-v6': if (!$dnsPass) { $this->_error(4); } elseif (!$dnsHost) { @@ -244,6 +246,7 @@ class updatedns case 'regfish-v6': case 'route53-v6': case 'cloudflare-token-v6': + case 'hetzner-v6': $this->_useIPv6 = true; break; default: @@ -318,6 +321,8 @@ class updatedns case 'he-net': case 'he-net-tunnelbroker': case 'he-net-v6': + case 'hetzner': + case 'hetzner-v6': case 'hn': case 'linode': case 'linode-v6': @@ -821,6 +826,87 @@ class updatedns } } break; + case 'hetzner': + case 'hetzner-v6': + $baseUrl = 'https://dns.hetzner.com/api/v1'; + $fqdn = str_replace(' ', '', $this->_dnsHost); + $recordType = ($this->_useIPv6) ? 'AAAA' : 'A'; + $ttlData = intval($this->_dnsTTL) < 1 ? 120 : intval($this->_dnsTTL); + $hostData = array( + "value" => "{$this->_dnsIP}", + "type" => $recordType, + "name" => "", + "ttl" => $ttlData, + "zone_id" => "" + ); + + $headerAuth = array( + "Auth-API-Token: {$this->_dnsPass}", + 'Content-Type: application/json' + ); + + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_HTTPHEADER, $headerAuth); + + // Get all zone info + $zonesUrl = "$baseUrl/zones"; + curl_setopt($ch, CURLOPT_URL, $zonesUrl); + $rawoutput = curl_exec($ch); + $output = json_decode($rawoutput); + $zoneId = null; // Set default value + + + // Iterate zone objects, check if $fqdn is equal to or ends with zone name + foreach ($output->zones as $key => $zoneObj) { + + if (preg_match("/^{$zoneObj->name}$|\.{$zoneObj->name}$/", $fqdn)) { + // Found matching zone + $zoneId = $zoneObj->id; + // Get $hostName from $fqdn, set $domainName + // These are only really used for log messages. + $hostName = preg_replace("/\.?{$zoneObj->name}$/", '', $fqdn); + $domainName = $zoneObj->name; + + break; + } + } + + if ($zoneId) { // If zone ID was found get host ID + $dnsRecordsUrl = "$baseUrl/records?zone_id=$zoneId"; + curl_setopt($ch, CURLOPT_URL, $dnsRecordsUrl); + $rawoutput = curl_exec($ch); + $output = json_decode($rawoutput); + $recordId = null; + + + // Iterate zone objects, check if $hostName exist of the same type + foreach ($output->records as $key => $recordObj) { + if (preg_match("/^{$recordObj->name}$/", $hostName)) { + if ($recordObj->type == $recordType) { + // Found matching host + $recordId = $recordObj->id; + break; + } + } + } + + if ($recordId) { // If record ID was found, update record + $setRecordUrl = "$baseUrl/records/$recordId"; + curl_setopt($ch, CURLOPT_URL, $setRecordUrl); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); + } + else { + $setRecordUrl = "$baseUrl/records"; + curl_setopt($ch, CURLOPT_URL, $setRecordUrl); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); + } + + $hostData["zone_id"] = $zoneId; + $hostData["name"] = $hostName; + + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($hostData)); + } + break; case 'eurodns': curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass); @@ -1396,6 +1482,23 @@ class updatedns log_error("Dynamic DNS ({$this->_dnsHost}): PAYLOAD: {$data}"); } break; + case 'hetzner': + case 'hetzner-v6': + $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $output = json_decode($data); + if ($output->record->value === $this->_dnsIP) { + $status = "Dynamic DNS: (Success) {$this->_dnsHost} updated to {$this->_dnsIP}"; + $successful_update = true; + } elseif ($http_code == 401) { + $status = 'Dynamic DNS: (Error) Bad authentication attempt because of a wrong API Key.'; + } elseif ($http_code == 403) { + $status = 'Dynamic DNS: (Error) Access to the resource is denied. Mainly due to a lack of permissions to access it!'; + } else { + $status = 'Dynamic DNS: (Error) "Unknown Response"'; + log_error("Dynamic DNS: HTTP Status: {$http_code} PAYLOAD: {$data}"); + $this->_debug($data); + } + break; case 'digitalocean': $output = json_decode($data); if ($output->domain_record->data === $this->_dnsIP) {