Skip to content

Commit

Permalink
Merge pull request #45 from mrikirill/feature/ipv6-autodetect-upd
Browse files Browse the repository at this point in the history
Updated IP detection and setup dns records logic
  • Loading branch information
mrikirill authored Jul 28, 2024
2 parents 507ae45 + a385c4f commit c4ab924
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 76 deletions.
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

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

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

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)).

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.

Your DNS records should appear (or already be setup as follows) in Cloudflare:

(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)
Expand All @@ -82,7 +84,7 @@ For assistance with vi commands, see:

## How to install

1. **SSH with root privledges on your supported device:**
1. **SSH with root privileges on your supported device:**

a. For DSM Users:
Expand Down Expand Up @@ -131,7 +133,8 @@ For multiple domains: __subdomain.mydomain.com|vpn.mydomain.com__

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

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)
4. Don't forget to deactivate SSH (step 1) if you don't need it. Leaving it active can be a security risk.
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)

## Troubleshooting and known issues

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

```
/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" "" ""
/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"
```

* Check output logs
Expand Down
113 changes: 41 additions & 72 deletions cloudflare.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,20 @@
* @author https://github.com/mrikirill
*/

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

/**
* Note:
* Cloudflare API Key - $argv[2]
* Hostname - $argv[1] we use username as hostname source cause it supports 256 symbols
*/
$cf = new SynologyCloudflareDDNSAgent($argv[2], $argv[1]);
$cf = new SynologyCloudflareDDNSAgent($argv[2], $argv[1], $argv[4]);
$cf->setDnsRecords();
$cf->updateDnsRecords();

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

class Ipify
{
const API_URL = 'https://api64.ipify.org';
const API_URL = 'https://api6.ipify.org';
/**
* Universal: IPv4/IPv6
* Return if external IPv6 address is available
* @link https://www.ipify.org
* @throws Exception
*/
public function getExternalIpAddress()
public function tryGetIpv6()
{
$options = [
CURLOPT_URL => self::API_URL . "/?format=json",
Expand Down Expand Up @@ -231,24 +233,16 @@ class SynologyCloudflareDDNSAgent
private $cloudflareAPI;
private $ipify;

function __construct($apiKey, $hostname)
function __construct($apiKey, $hostname, $ipv4)
{
$this->cloudflareAPI = new CloudflareAPI($apiKey);
$this->ipify = new Ipify();
$this->ipv4 = $ipv4;

try {
$ip = $this->ipify->getExternalIpAddress();
switch ($this->getIpAddressVersion($ip)) {
case 4:
$this->ipv4 = $ip;
break;
case 6:
$this->ipv6 = $ip;
break;
}
$ipv6 = $this->ipify->tryGetIpv6();
} catch (Exception $e) {
echo 'Error: ' . $e->getMessage();
$this->exitWithSynologyMsg();
// IPv6 not available
}

try {
Expand Down Expand Up @@ -283,12 +277,16 @@ public function setDnsRecords()
}

try {
foreach ($this->dnsRecordList as &$dnsRecord) {
foreach ($this->dnsRecordList as $key => $dnsRecord) {
$json = $this->cloudflareAPI->getDnsRecords($dnsRecord->zoneId, $dnsRecord->type, $dnsRecord->hostname);
if (isset($json['result']['0'])) {
$dnsRecord->id = $json['result']['0']['id'];
$dnsRecord->ttl = $json['result']['0']['ttl'];
$dnsRecord->proxied = $json['result']['0']['proxied'];
// If the DNS record exists, update its ID, TTL, and proxied status
$this->dnsRecordList[$key]->id = $json['result']['0']['id'];
$this->dnsRecordList[$key]->ttl = $json['result']['0']['ttl'];
$this->dnsRecordList[$key]->proxied = $json['result']['0']['proxied'];
} else {
// If the DNS record does not exist, remove it from the list
unset($this->dnsRecordList[$key]);
}
}
} catch (Exception $e) {
Expand Down Expand Up @@ -341,15 +339,28 @@ private function matchHostnameWithZone($hostnameList = [])
$zoneName = $zone['name'];
foreach ($hostnameList as $hostname) {
if (strpos($hostname, $zoneName) !== false) {
$this->dnsRecordList[$hostname] = new DnsRecordEntity(
// Add an IPv4 DNS record for each hostname that matches a zone
$this->dnsRecordList[] = new DnsRecordEntity(
'',
$this->getDnsRecordType(),
'A',
$hostname,
$this->getIpAddress(),
$this->ipv4,
$zoneId,
'',
''
);
if (isset($this->ipv6)) {
// Add an IPv6 DNS record if an IPv6 address is available
$this->dnsRecordList[] = new DnsRecordEntity(
'',
'AAAA',
$hostname,
$this->ipv6,
$zoneId,
'',
''
);
}
}
}
}
Expand All @@ -358,28 +369,6 @@ private function matchHostnameWithZone($hostnameList = [])
}
}

/**
* Determines the DNS record type (A or AAAA) based on the IP version.
* Returns 'AAAA' if IPv6 is present, or 'A' if only IPv4 is available.
*
* @return string The DNS record type, either 'A' or 'AAAA'.
*/
private function getDnsRecordType()
{
return $this->ipv6 ? 'AAAA' : 'A';
}

/**
* Retrieves the current external IP address.
* If both IPv4 and IPv6 are available, returns the IPv6 address.
*
* @return string The current external IP address, either IPv4 or IPv6.
*/
private function getIpAddress()
{
return $this->ipv6 ? $this->ipv6 : $this->ipv4;
}

/**
* Extracts valid hostnames from a given string of hostnames separated by pipes (|).
*
Expand Down Expand Up @@ -449,25 +438,5 @@ private function exitWithSynologyMsg($msg = '')
echo $msg;
exit();
}

/**
* Determines the IP address version (IPv4 or IPv6) of a given IP address.
*
* This method checks if the provided IP address is a valid, public IPv6 or IPv4 address.
*
* @param string $ip The IP address to be evaluated.
* @return int Returns 6 if the IP address is a valid IPv6 address, 4 if it is a valid IPv4 address.
* @throws InvalidArgumentException if the IP address is not valid or is not public.
*/
private function getIpAddressVersion($ip)
{
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
return 6;
} elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
return 4;
} else {
throw new InvalidArgumentException('Invalid IP address');
}
}
}
?>

0 comments on commit c4ab924

Please sign in to comment.