From 7361b7ca43adab32740817b4dab386be2fdda73c Mon Sep 17 00:00:00 2001 From: Charles Ulrich Date: Fri, 21 Sep 2018 02:17:43 -0400 Subject: [PATCH] dns/dyndns: Add IPv6 support to Cloudflare DDNS and other fixes: (#861) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add IPv6 support * create DNS record if it doesn't exist * better variable naming * saner parsing of FQDN (previous code assumed all FQDNs had exactly three parts) * trimming of whitespace in the fqdn field is actually effective now * warn if multiple records match when getting the record id * removed “proxiable” attribute when updating, it is not an attribute you can set as far as I know * default “proxied” to off, assuming most people don't host websites from their firewalls * complex string interpolation in HTTP headers --- .../src/etc/inc/plugins.inc.d/dyndns.inc | 1 + .../inc/plugins.inc.d/dyndns/phpDynDNS.inc | 66 ++++++++++++------- 2 files changed, 42 insertions(+), 25 deletions(-) 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 bfe345048..5e664daa2 100644 --- a/dns/dyndns/src/etc/inc/plugins.inc.d/dyndns.inc +++ b/dns/dyndns/src/etc/inc/plugins.inc.d/dyndns.inc @@ -99,6 +99,7 @@ function dyndns_list() '3322' => '3322', 'citynetwork' => 'City Network', 'cloudflare' => 'CloudFlare', + 'cloudflare-v6' => 'CloudFlare (v6)', 'custom' => 'Custom', 'custom-v6' => 'Custom (v6)', 'dhs' => 'DHS', 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 75dfff515..6120661c5 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 @@ -28,6 +28,7 @@ * - Custom dynamic DNS (any URL) * - Custom dynamic DNS IPv6 (any URL) * - CloudFlare (www.cloudflare.com) + * - CloudFlare IPv6 (www.cloudflare.com) * - Eurodns (eurodns.com) * - GratisDNS (gratisdns.dk) * - City Network (citynetwork.se) @@ -75,7 +76,8 @@ * SelfHost - Last Tested: 26 December 2011 * Amazon Route53 - Last Tested: 01 April 2012 * DNS-O-Matic - Last Tested: 9 September 2010 - * CloudFlare - Last Tested: 30 May 2013 + * CloudFlare - Last Tested: 19 September 2018 + * CloudFlare IPv6 - Last Tested: 19 September 2018 * Eurodns - Last Tested: 25 July 2018 * GratisDNS - Last Tested: 15 August 2012 * OVH DynHOST - Last Tested: NEVER @@ -236,6 +238,7 @@ class updatedns } switch ($dnsService) { + case 'cloudflare-v6': case 'custom-v6': case 'he-net-v6': case 'regfish-v6': @@ -284,6 +287,7 @@ class updatedns case '3322': case 'citynetwork': case 'cloudflare': + case 'cloudflare-v6': case 'custom': case 'custom-v6': case 'dhs': @@ -705,41 +709,52 @@ class updatedns curl_setopt($ch, CURLOPT_URL, $server); break; case 'cloudflare': - $dnsServer ='api.cloudflare.com'; - $dnsHost = str_replace(' ', '', $this->_dnsHost); - $host_names = explode('.', $dnsHost); - $bottom_host_name = $host_names[count($host_names) - 2] . '.' . $host_names[count($host_names) - 1]; + case 'cloudflare-v6': + $baseUrl = 'https://api.cloudflare.com/client/v4'; + $fqdn = str_replace(' ', '', $this->_dnsHost); + $fqdnParts = explode('.', $fqdn); + $hostName = array_shift($fqdnParts); + $domainName = implode('.', $fqdnParts); + $recordType = ($this->_useIPv6) ? 'AAAA' : 'A'; + $hostData = array( + "content" => "{$this->_dnsIP}", + "type" => $recordType, + "name" => $fqdn + ); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HTTPHEADER, array( - 'X-Auth-Email: '.$this->_dnsUser.'', - 'X-Auth-Key: '.$this->_dnsPass.'', + "X-Auth-Email: {$this->_dnsUser}", + "X-Auth-Key: {$this->_dnsPass}", 'Content-Type: application/json' )); // Get zone ID - $getZoneId = "https://{$dnsServer}/client/v4/zones/?name={$bottom_host_name}"; + $zonesUrl = "$baseUrl/zones"; + $getZoneId = "$zonesUrl/?name=$domainName"; curl_setopt($ch, CURLOPT_URL, $getZoneId); $output = json_decode(curl_exec($ch)); - $zone = $output->result[0]->id; - if ($zone) { // If zone ID was found get host ID - $getHostId = "https://{$dnsServer}/client/v4/zones/{$zone}/dns_records?name={$this->_dnsHost}"; + $zoneId = $output->result[0]->id; + if ($zoneId) { // If zone ID was found get host ID + $dnsRecordsUrl = "$zonesUrl/$zoneId/dns_records"; + $getHostId = "$dnsRecordsUrl?name=$fqdn&type=$recordType"; curl_setopt($ch, CURLOPT_URL, $getHostId); $output = json_decode(curl_exec($ch)); - $host = $output->result[0]->id; - if ($host) { // If host ID was found update host - $hostData = array( - "content" => "{$this->_dnsIP}", - "type" => "A", - "name" => "{$this->_dnsHost}", - "proxiable" => $output->result[0]->proxiable, - "proxied" => $output->result[0]->proxied - ); - $data_json = json_encode($hostData); - $updateHostId = "https://{$dnsServer}/client/v4/zones/{$zone}/dns_records/{$host}"; - curl_setopt($ch, CURLOPT_URL, $updateHostId); + $recordCount = count($output->result); + if ($recordCount === 0) { + // create record + curl_setopt($ch, CURLOPT_URL, $dnsRecordsUrl); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); + } elseif ($recordCount >= 1) { + // update record + $recordId = $output->result[0]->id; + $hostData["proxied"] = $output->result[0]->proxied; + curl_setopt($ch, CURLOPT_URL, "$dnsRecordsUrl/$recordId"); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); - curl_setopt($ch, CURLOPT_POSTFIELDS, $data_json); + } + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($hostData)); + if ($recordCount > 1) { + log_error("Dynamic DNS ($fqdn): Warning: multiple records for $hostName found"); } } break; @@ -1118,6 +1133,7 @@ class updatedns } break; case 'cloudflare': + case 'cloudflare-v6': $output = json_decode($data); if ($output->result->content === $this->_dnsIP) { $status = "Dynamic DNS: (Success) {$this->_dnsHost} updated to {$this->_dnsIP}"; @@ -1125,7 +1141,7 @@ class updatedns } elseif ($output->errors[0]->code === 9103) { $status = "Dynamic DNS ({$this->_dnsHost}): ERROR - Invalid Credentials! Don't forget to use API Key for password field with CloudFlare."; } elseif (($output->success) && (!$output->result[0]->id)) { - $status = "Dynamic DNS ({$this->_dnsHost}): ERROR - Zone or Host ID was not found, check that your hostname is correct and an A record already exists."; + $status = "Dynamic DNS ({$this->_dnsHost}): ERROR - Zone ID was not found."; } else { $status = "Dynamic DNS ({$this->_dnsHost}): UNKNOWN ERROR - {$output->errors[0]->message}"; log_error("Dynamic DNS ({$this->_dnsHost}): PAYLOAD: {$data}");