Skip to content

Commit c4ab924

Browse files
authored
Merge pull request #45 from mrikirill/feature/ipv6-autodetect-upd
Updated IP detection and setup dns records logic
2 parents 507ae45 + a385c4f commit c4ab924

2 files changed

Lines changed: 48 additions & 76 deletions

File tree

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
- 🆕 New hostname input format: `subdomain1.mydomain.com|subdomain2.mydomain.com` (each domain is separated: `|`) used to be with `---` separator
2424
- 🆕 Hostname input uses a new source of data (account) and support 256 symbols limit (DSM UI limit)
25-
- 🆕 Autodetect IPv4 and IPv6 addresses
25+
- 🆕 Autodetect IPv6 addresses via [ipify.org](https://www.ipify.org)
2626
- 🆕 Optimised request to Cloudflare API
2727
- 🆕 Installer script
2828

@@ -61,6 +61,8 @@ Before starting the installation process, make sure you have (and know) the foll
6161

6262
Ensure the DNS A record(s) for the domain/zone(s) you wish to update with this script have been created (More information: [Managing DNS records](https://support.cloudflare.com/hc/en-us/articles/360019093151-Managing-DNS-records-in-Cloudflare)).
6363

64+
Case for if IpV6 is available (check via https://api6.ipify.org), you can create an AAAA record for the domain/zone(s) you wish to update with this script.
65+
6466
Your DNS records should appear (or already be setup as follows) in Cloudflare:
6567

6668
(Note: Having Proxied turned on for your A records isn't necessary, but it will prevent those snooping around from easily finding out your current IP address)
@@ -82,7 +84,7 @@ For assistance with vi commands, see:
8284

8385
## How to install
8486

85-
1. **SSH with root privledges on your supported device:**
87+
1. **SSH with root privileges on your supported device:**
8688

8789
a. For DSM Users:
8890
@@ -131,7 +133,8 @@ For multiple domains: __subdomain.mydomain.com|vpn.mydomain.com__
131133

132134
Finally, press the test connection button to confirm all information is correctly entered, before pressing Ok to save and confirm your details.
133135

134-
4. Enjoy 🍺 and __don't forget to deactivate SSH (step 1) if you don't need it__. OR [![Sponsor](https://img.shields.io/badge/sponsor-GitHub%20Sponsors-brightgreen)](https://github.com/sponsors/mrikirill)
136+
4. Don't forget to deactivate SSH (step 1) if you don't need it. Leaving it active can be a security risk.
137+
5. You're done! Optional, if you're happy with this script you could buy me ☕ or 🍺 here -> [![Sponsor](https://img.shields.io/badge/sponsor-GitHub%20Sponsors-brightgreen)](https://github.com/sponsors/mrikirill)
135138

136139
## Troubleshooting and known issues
137140

@@ -198,7 +201,7 @@ You can run this script directly to see output logs
198201
* Run this command:
199202

200203
```
201-
/usr/bin/php -d open_basedir=/usr/syno/bin/ddns -f /usr/syno/bin/ddns/cloudflare.php "" "domain1.com|vpn.domain2.com" "your-CloudFlare-token" "" ""
204+
/usr/bin/php -d open_basedir=/usr/syno/bin/ddns -f /usr/syno/bin/ddns/cloudflare.php "domain1.com|vpn.domain2.com" "your-CloudFlare-token" "" "your-ip-address"
202205
```
203206

204207
* Check output logs

cloudflare.php

Lines changed: 41 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,20 @@
88
* @author https://github.com/mrikirill
99
*/
1010

11-
// Note: username - $argv[1], password - $argv[2], hostname - $argv[3], ipv4 - $argv[4]
12-
if ($argc !== 5 || count($argv) != 5) {
11+
/**
12+
* Synology passes 5 arguments in order:
13+
* 0 - not in use
14+
* 1 - username - uses for domains: domain1.com|vpn.domain2.com
15+
* 2 - password - Cloudflare API token
16+
* 3 - hostname - the script doesn't use it die to input limits
17+
* 4 - IPv4 - Synology provided IPv4
18+
*/
19+
if ($argc !== 5) {
1320
echo SynologyOutput::BAD_PARAMS;
1421
exit();
1522
}
1623

17-
/**
18-
* Note:
19-
* Cloudflare API Key - $argv[2]
20-
* Hostname - $argv[1] we use username as hostname source cause it supports 256 symbols
21-
*/
22-
$cf = new SynologyCloudflareDDNSAgent($argv[2], $argv[1]);
24+
$cf = new SynologyCloudflareDDNSAgent($argv[2], $argv[1], $argv[4]);
2325
$cf->setDnsRecords();
2426
$cf->updateDnsRecords();
2527

@@ -148,13 +150,13 @@ public function updateDnsRecord($zoneId, $recordId, $body)
148150

149151
class Ipify
150152
{
151-
const API_URL = 'https://api64.ipify.org';
153+
const API_URL = 'https://api6.ipify.org';
152154
/**
153-
* Universal: IPv4/IPv6
155+
* Return if external IPv6 address is available
154156
* @link https://www.ipify.org
155157
* @throws Exception
156158
*/
157-
public function getExternalIpAddress()
159+
public function tryGetIpv6()
158160
{
159161
$options = [
160162
CURLOPT_URL => self::API_URL . "/?format=json",
@@ -231,24 +233,16 @@ class SynologyCloudflareDDNSAgent
231233
private $cloudflareAPI;
232234
private $ipify;
233235

234-
function __construct($apiKey, $hostname)
236+
function __construct($apiKey, $hostname, $ipv4)
235237
{
236238
$this->cloudflareAPI = new CloudflareAPI($apiKey);
237239
$this->ipify = new Ipify();
240+
$this->ipv4 = $ipv4;
238241

239242
try {
240-
$ip = $this->ipify->getExternalIpAddress();
241-
switch ($this->getIpAddressVersion($ip)) {
242-
case 4:
243-
$this->ipv4 = $ip;
244-
break;
245-
case 6:
246-
$this->ipv6 = $ip;
247-
break;
248-
}
243+
$ipv6 = $this->ipify->tryGetIpv6();
249244
} catch (Exception $e) {
250-
echo 'Error: ' . $e->getMessage();
251-
$this->exitWithSynologyMsg();
245+
// IPv6 not available
252246
}
253247

254248
try {
@@ -283,12 +277,16 @@ public function setDnsRecords()
283277
}
284278

285279
try {
286-
foreach ($this->dnsRecordList as &$dnsRecord) {
280+
foreach ($this->dnsRecordList as $key => $dnsRecord) {
287281
$json = $this->cloudflareAPI->getDnsRecords($dnsRecord->zoneId, $dnsRecord->type, $dnsRecord->hostname);
288282
if (isset($json['result']['0'])) {
289-
$dnsRecord->id = $json['result']['0']['id'];
290-
$dnsRecord->ttl = $json['result']['0']['ttl'];
291-
$dnsRecord->proxied = $json['result']['0']['proxied'];
283+
// If the DNS record exists, update its ID, TTL, and proxied status
284+
$this->dnsRecordList[$key]->id = $json['result']['0']['id'];
285+
$this->dnsRecordList[$key]->ttl = $json['result']['0']['ttl'];
286+
$this->dnsRecordList[$key]->proxied = $json['result']['0']['proxied'];
287+
} else {
288+
// If the DNS record does not exist, remove it from the list
289+
unset($this->dnsRecordList[$key]);
292290
}
293291
}
294292
} catch (Exception $e) {
@@ -341,15 +339,28 @@ private function matchHostnameWithZone($hostnameList = [])
341339
$zoneName = $zone['name'];
342340
foreach ($hostnameList as $hostname) {
343341
if (strpos($hostname, $zoneName) !== false) {
344-
$this->dnsRecordList[$hostname] = new DnsRecordEntity(
342+
// Add an IPv4 DNS record for each hostname that matches a zone
343+
$this->dnsRecordList[] = new DnsRecordEntity(
345344
'',
346-
$this->getDnsRecordType(),
345+
'A',
347346
$hostname,
348-
$this->getIpAddress(),
347+
$this->ipv4,
349348
$zoneId,
350349
'',
351350
''
352351
);
352+
if (isset($this->ipv6)) {
353+
// Add an IPv6 DNS record if an IPv6 address is available
354+
$this->dnsRecordList[] = new DnsRecordEntity(
355+
'',
356+
'AAAA',
357+
$hostname,
358+
$this->ipv6,
359+
$zoneId,
360+
'',
361+
''
362+
);
363+
}
353364
}
354365
}
355366
}
@@ -358,28 +369,6 @@ private function matchHostnameWithZone($hostnameList = [])
358369
}
359370
}
360371

361-
/**
362-
* Determines the DNS record type (A or AAAA) based on the IP version.
363-
* Returns 'AAAA' if IPv6 is present, or 'A' if only IPv4 is available.
364-
*
365-
* @return string The DNS record type, either 'A' or 'AAAA'.
366-
*/
367-
private function getDnsRecordType()
368-
{
369-
return $this->ipv6 ? 'AAAA' : 'A';
370-
}
371-
372-
/**
373-
* Retrieves the current external IP address.
374-
* If both IPv4 and IPv6 are available, returns the IPv6 address.
375-
*
376-
* @return string The current external IP address, either IPv4 or IPv6.
377-
*/
378-
private function getIpAddress()
379-
{
380-
return $this->ipv6 ? $this->ipv6 : $this->ipv4;
381-
}
382-
383372
/**
384373
* Extracts valid hostnames from a given string of hostnames separated by pipes (|).
385374
*
@@ -449,25 +438,5 @@ private function exitWithSynologyMsg($msg = '')
449438
echo $msg;
450439
exit();
451440
}
452-
453-
/**
454-
* Determines the IP address version (IPv4 or IPv6) of a given IP address.
455-
*
456-
* This method checks if the provided IP address is a valid, public IPv6 or IPv4 address.
457-
*
458-
* @param string $ip The IP address to be evaluated.
459-
* @return int Returns 6 if the IP address is a valid IPv6 address, 4 if it is a valid IPv4 address.
460-
* @throws InvalidArgumentException if the IP address is not valid or is not public.
461-
*/
462-
private function getIpAddressVersion($ip)
463-
{
464-
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
465-
return 6;
466-
} elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
467-
return 4;
468-
} else {
469-
throw new InvalidArgumentException('Invalid IP address');
470-
}
471-
}
472441
}
473442
?>

0 commit comments

Comments
 (0)