Created
June 9, 2021 16:50
-
-
Save Tofandel/7370a1e815de7d49dd8d260fc4293cd5 to your computer and use it in GitHub Desktop.
Synology cloudflare DDNS script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/php -d open_basedir=/usr/syno/bin/ddns | |
<?php | |
if ($argc !== 5) { | |
echo 'badparam'; | |
exit(); | |
} | |
$cf = new updateCFDDNS($argv); | |
$cf->makeUpdateDNS(); | |
/** | |
* DDNS auto updater for Synology NAS | |
* Base on Cloudflare API v4 | |
* Supports multidomains and sundomains | |
*/ | |
class updateCFDDNS | |
{ | |
const API_URL = 'https://api.cloudflare.com'; | |
var $account, $apiKey, $hostList, $ip; | |
function giveHost($host_with_subdomain) { | |
$array = explode(".", $host_with_subdomain); | |
return (array_key_exists(count($array) - 2, $array) ? $array[count($array) - 2] : "").".".$array[count($array) - 1]; | |
} | |
function __construct($argv) | |
{ | |
if (count($argv) != 5) { | |
$this->badParam('wrong parameter count'); | |
} | |
$this->apiKey = (string) $argv[2]; // CF Global API Key | |
$hostname = (string) $argv[3]; // example: example.com.uk---sundomain.example1.com---example2.com | |
$this->ip = (string) $argv[4]; | |
$this->validateIpV4($this->ip); | |
$arHost = explode('---', $hostname); | |
if (empty($arHost)) { | |
$this->badParam('empty host list'); | |
} | |
foreach ($arHost as $value) { | |
$hostname = $this->giveHost($value); | |
$this->hostList[$value] = [ | |
'hostname' => $hostname, | |
'fullname' => $value, | |
'zoneId' => '', | |
'recordId' => '', | |
'proxied' => true, | |
]; | |
} | |
$this->setZones(); | |
foreach ($this->hostList as $arHost) { | |
$this->setRecord($arHost['fullname'], $arHost['zoneId']); | |
} | |
} | |
/** | |
* Update CF DNS records | |
*/ | |
function makeUpdateDNS() | |
{ | |
if(empty($this->hostList)) { | |
$this->badParam('empty host list'); | |
} | |
foreach ($this->hostList as $arHost) { | |
$post = [ | |
'type' => 'A', | |
'name' => $arHost['fullname'], | |
'content' => $this->ip, | |
'ttl' => 1, | |
'proxied' => $arHost['proxied'], | |
]; | |
$json = $this->callCFapi("PUT", "client/v4/zones/" . $arHost['zoneId'] . "/dns_records/" . $arHost['recordId'], $post); | |
if (!$json['success']) { | |
echo 'Update Record failed'; | |
exit(); | |
} | |
} | |
echo "good"; | |
} | |
function badParam($msg = '') | |
{ | |
echo (strlen($msg) > 0) ? $msg : 'badparam'; | |
exit(); | |
} | |
function validateIpV4($ip) | |
{ | |
if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { | |
$this->badParam('invalid ip-address, only ipv4'); | |
} | |
return true; | |
} | |
/** | |
* Set ZoneID for each hosts | |
*/ | |
function setZones() | |
{ | |
$json = $this->callCFapi("GET", "client/v4/zones"); | |
if (!$json['success']) { | |
$this->badParam('getZone unsuccessful response'); | |
} | |
$arZones = []; | |
foreach ($json['result'] as $ar) { | |
$arZones[] = [ | |
'hostname' => $ar['name'], | |
'zoneId' => $ar['id'] | |
]; | |
} | |
foreach ($this->hostList as $hostname => $arHost) { | |
$res = $this->isZonesContainFullname($arZones, $arHost['hostname']); | |
if(!empty($res)) { | |
$this->hostList[$hostname]['zoneId'] = $res['zoneId']; | |
$this->hostList[$hostname]['hostname'] = $res['hostname']; | |
} | |
} | |
} | |
/** | |
* Find hostname for full domain name | |
* example: domain.com.uk --> vpn.domain.com.uk | |
*/ | |
function isZonesContainFullname($arZones, $fullname){ | |
$res = []; | |
foreach($arZones as $arZone) { | |
if (strpos($fullname, $arZone['hostname']) !== false) { | |
$res = $arZone; | |
break; | |
} | |
} | |
return $res; | |
} | |
/** | |
* Set Records for each hosts | |
*/ | |
function setRecord($fullname, $zoneId) | |
{ | |
if (empty($fullname)) { | |
return false; | |
} | |
if (empty($zoneId)) { | |
unset($this->hostList[$fullname]); | |
return false; | |
} | |
$json = $this->callCFapi("GET", "client/v4/zones/${zoneId}/dns_records?type=A&name=${fullname}"); | |
if (!$json['success']) { | |
$this->badParam('unsuccessful response for getRecord host: ' . $fullname); | |
} | |
$this->hostList[$fullname]['recordId'] = $json['result']['0']['id']; | |
$this->hostList[$fullname]['proxied'] = $json['result']['0']['proxied']; | |
} | |
/** | |
* Call CloudFlare v4 API @link https://api.cloudflare.com/#getting-started-endpoints | |
*/ | |
function callCFapi($method, $path, $data = []) { | |
$options = [ | |
CURLOPT_URL => self::API_URL . '/' . $path, | |
CURLOPT_HTTPHEADER => ["Authorization: Bearer $this->apiKey", "Content-Type: application/json"], | |
CURLOPT_RETURNTRANSFER => true, | |
CURLOPT_HEADER => false, | |
CURLOPT_VERBOSE => false, | |
]; | |
if(empty($method)){ | |
$this->badParam('Empty method'); | |
} | |
switch($method) { | |
case "GET": | |
$options[CURLOPT_HTTPGET] = true; | |
break; | |
case "POST": | |
$options[CURLOPT_POST] = true; | |
$options[CURLOPT_HTTPGET] = false; | |
$options[CURLOPT_POSTFIELDS] = json_encode($data); | |
break; | |
case "PUT": | |
$options[CURLOPT_POST] = false; | |
$options[CURLOPT_HTTPGET] = false; | |
$options[CURLOPT_CUSTOMREQUEST] = "PUT"; | |
$options[CURLOPT_POSTFIELDS] = json_encode($data); | |
break; | |
} | |
$req = curl_init(); | |
curl_setopt_array($req, $options); | |
$res = curl_exec($req); | |
curl_close($req); | |
//echo $path; | |
//var_dump($data); | |
//var_dump(json_decode($res, true)); | |
return json_decode($res, true); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment