diff --git a/README.md b/README.md
index 7e53618..28c52c3 100644
--- a/README.md
+++ b/README.md
@@ -10,21 +10,28 @@ Self-hosted dynamic DNS php script to update netcup DNS API from Router like AVM
## Usage
### Installation
* Copy all files to your webspace
+* If you want multiple endpoints that each can only update one domain look at the mydomain folder.
+The update URL would be https://`url`/mydomain/update.php?(...)
* create a copy of `.env.dist` as `.env` and configure:
- * `username` -> The username for your Router to authenticate (so not everyone can update your DNS)
- * `password` -> password for your Router
- * `apiKey` -> API key which is generated in netcup CCP
- * `apiPassword` -> API password which is generated in netcup CCP
- * `customerId` -> your netcup Customer ID
- * `log` -> true|false enables logging
- * `logFile` -> configures logfile location if enabled
- * `debug` -> true|false enables debug mode and generates more output from update.php (normal operation has no output). Needed to receive stack traces from errors.
- * `returnIp` -> true|false enables return of result if a record was changed
- * `allowCreate` -> true|false allows creation of entries if parameter `create=true` in URL
+
+Parameter | Example | Explanation
+---: | :--- | :---
+`username` | dnsupdater | The username for your Router to authenticate (so not everyone can update your DNS)
+`password` | secretpleasechange | password for your Router
+`apiKey` | 18neqwd91e21onei1p23841 | API key which is generated in netcup CCP
+`apiPassword` | 82jewqde9m30 | API password which is generated in netcup CCP
+`customerId` | 12345 | your netcup Customer ID
+`log` | `true` / false | enables logging
+`logFile` | log.json | configures logfile location if enabled
+`debug` | true / `false` | enables debug mode and generates more output from update.php (normal operation has no output). Needed to receive stack traces from errors.
+`returnIp` | `true` / false | enables return of result if a record was changed
+`allowCreate` | true/`false` | allows creation of entries if parameter `create=true` in URL
+`restrictDomain` | true / `false` | allows admin to restrict the domain to update to a given value `domain` and/or `host`. See URL parameters for host parameter explanation
* alternatively you can use .configure.sh to create your .env file for you (if you are on a *NIX system)
-* Create each host record in your netcup CCP (DNS settings) before using the script. The script does not create any missing records.
+* Create each host record in your netcup CCP (DNS settings) before using the script. The script does not create any missing records.
+You can now set `allowCreate=true` in .env and pass `create=true` as URL parameter to create entries on the fly.
## URL possible uses:
diff --git a/examples/multisite.md b/examples/multisite.md
new file mode 100644
index 0000000..fcbf073
--- /dev/null
+++ b/examples/multisite.md
@@ -0,0 +1,67 @@
+# Multiple Endpoints with separate credentials
+For advanced use you might want to have separate users that can each only update one domain entry.
+
+In that case it might be beneficial to habe multiple endpoints, e.g. `https://dyndns.example.com/endpointN/update.php` where endpointN is any directory name you wish.
+
+## Setting up multiple endpoints
+The directory structure of your webroot might look like this:
+
+├── index.html +├── src +│ ├── Config.php +│ ├── Handler.php +│ ├── Payload.php +│ └── Soap.php +├── fritzbox # this is a subdomain +│ ├── .env +│ └── update.php +├── nas # this is another +│ ├── .env +│ └── update.php +├── examplenet # uses another netcup account +│ ├── .env +│ └── update.php +└── subdomain1 # and another subdomain + ├── .env + └── update.php ++ +Here the update.php files are copied from the mydomain example directory. All .env files contain different user credentials and may even use different netcup credentials. + +## Setting up domain restrictions per .env file +It is nice to have multiple sets of credentials, but if anyone can update any entry of any domain this defeats the purpose. + +That is why you can enable domain restriction per .env file and thereby per set of user credentials. + +In these cases you the domain you send in your url will be ignored in favour of the one configured in the .env file. You still need to send a placeholder for validation purposes. + +Example .env file for fritzbox.example.com.
+username="fritzbox" +password="changemeplease" +apiKey="j1meo213em823jd2q9" +apiPassword="12345secret" +customerId="12345" +debug=false +log=true +logFile=/var/log/dnsupdater/fritzbox.json +restrictDomain=true +domain=fritzbox.example.com ++ +Example .env file for nas.home.example.com.
+username="nas" +password="changemeplease" +apiKey="j1meo213em823jd2q9" +apiPassword="12345secret" +customerId="12345" +debug=false +log=true +logFile=/var/log/dnsupdater/nas.json +restrictDomain=true +domain=example.com # for explicit use of third-level domain +host=nas.home # we use the optional host parameter +\ No newline at end of file diff --git a/examples/mydomain/.env.dist b/examples/mydomain/.env.dist new file mode 100644 index 0000000..9162fbe --- /dev/null +++ b/examples/mydomain/.env.dist @@ -0,0 +1,12 @@ +username="only-mydomain" +password="changemeplease" +apiKey="netcup DNS API Key" +apiPassword="netcup DNS API Password" +customerId="netcup customer ID" +debug=false +log=true +logFile=mydomain.json +returnIp=true +allowCreate=false +restrictDomain=true +domain="mydomain.example.com" diff --git a/examples/mydomain/update.php b/examples/mydomain/update.php new file mode 100755 index 0000000..bbb8b8e --- /dev/null +++ b/examples/mydomain/update.php @@ -0,0 +1,20 @@ +doRun(); diff --git a/src/Config.php b/src/Config.php index 12e4d24..aa85c5c 100644 --- a/src/Config.php +++ b/src/Config.php @@ -55,6 +55,21 @@ final class Config */ private $allowCreate = false; + /** + * @var bool + */ + private $restrictDomain = false; + + /** + * @var string + */ + private $domain; + + /** + * @var string + */ + private $host; + public function __construct(array $config) { @@ -158,4 +173,58 @@ final class Config { return $this->allowCreate; } + + /** + * @return bool + */ + public function isRestrictDomain() + { + return $this->restrictDomain; + } + + /** + * @return string + */ + public function getDomain() + { + if (empty($this->host)) + { + return $this->domain; + } + else + { + return $this->host . "." . $this->domain; + } + } + + /** + * @return string + */ + public function getHost() + { + if (!empty($this->host)) + { + return $this->host; + } + else + { + $domainParts = explode('.', $this->domain); + return $domainParts[0]; + } + } + + /** + * @return string + */ + public function getDomainName() + { + // hack if top level domain are used for dynDNS + if (1 === substr_count($this->domain, '.')) { + return $this->domain; + } + + $domainParts = explode('.', $this->domain); + array_shift($domainParts); // remove sub domain + return implode('.', $domainParts); + } } diff --git a/src/Handler.php b/src/Handler.php index 40117d5..9882782 100644 --- a/src/Handler.php +++ b/src/Handler.php @@ -103,7 +103,6 @@ final class Handler } /** - * * @return self */ public function doRun() @@ -125,34 +124,36 @@ final class Handler $this->doLog(sprintf('api login failed, message: %s', $loginHandle->longmessage)); } + // check if domain is restricted in config, force use of config values for domain and host + if ($this->config->isRestrictDomain()) { + $this->doLog('domain is restricted by .env file'); + $updateDomain = $this->config->getDomain(); + $updateDomainName = $this->config->getDomainName(); + $updateHost = $this->config->getHost(); + $this->doLog(sprintf('ignoring received domain, using configured domain: %s', $updateDomain)); + } else { + $updateDomain = $this->payload->getDomain(); + $updateDomainName = $this->payload->getDomainName(); + $updateHost = $this->payload->getHost(); + } + $infoHandle = $dnsClient->infoDnsRecords( - $this->payload->getDomainName(), + $updateDomainName, $this->config->getCustomerId(), $this->config->getApiKey(), $loginHandle->responsedata->apisessionid, $clientRequestId ); - // test: create new entry if it does not exist - $createnewentry = true; $exists = false; - $testing = true; - $ipv4changes = false; $ipv6changes = false; $txtchanges = false; - - // TODO: delete, testing - // echo "--- EXISTING ENTRIES BELOW ---", PHP_EOL; - // $teststring = print_r($infoHandle->responsedata->dnsrecords, true); - // echo $teststring, PHP_EOL; - foreach ($infoHandle->responsedata->dnsrecords as $key => $record) { - $recordHostnameReal = (!in_array($record->hostname, $this->payload->getMatcher())) ? $record->hostname . '.' . $this->payload->getDomainName() : $this->payload->getDomainName(); + $recordHostnameReal = (!in_array($record->hostname, $this->payload->getMatcher())) ? $record->hostname . '.' . $updateDomainName : $updateDomainName; - - if ($recordHostnameReal === $this->payload->getDomain()) { + if ($recordHostnameReal === $updateDomain) { // found matching entry, no need to create one $exists = true; @@ -165,7 +166,7 @@ final class Handler ) ) { $record->destination = $this->payload->getIpv4(); - $this->doLog(sprintf('IPv4 for %s set to %s', $record->hostname . '.' . $this->payload->getDomainName(), $this->payload->getIpv4())); + $this->doLog(sprintf('IPv4 for %s set to %s', $record->hostname . '.' . $updateDomainName, $this->payload->getIpv4())); $ipv4changes = true; } @@ -177,7 +178,7 @@ final class Handler ) ) { $record->destination = $this->payload->getIpv6(); - $this->doLog(sprintf('IPv6 for %s set to %s', $record->hostname . '.' . $this->payload->getDomainName(), $this->payload->getIpv6())); + $this->doLog(sprintf('IPv6 for %s set to %s', $record->hostname . '.' . $updateDomainName, $this->payload->getIpv6())); $ipv6changes = true; } @@ -189,16 +190,13 @@ final class Handler ) ) { $record->destination = $this->payload->getTxt(); - $this->doLog(sprintf('TXT for %s set to %s', $record->hostname . '.' . $this->payload->getDomainName(), $this->payload->getTxt())); + $this->doLog(sprintf('TXT for %s set to %s', $record->hostname . '.' . $updateDomainName, $this->payload->getTxt())); $txtchanges = true; } } } - // echo "--- Exists ---", $exists, PHP_EOL; - // echo "--- Createnewentry ---", $createnewentry, PHP_EOL; - - // TODO: if entry does not exist and createnewentry is true: + // if entry does not exist and createnewentry is true: if ( !$exists && $this->payload->getCreate() && $this->config->isAllowCreate() ) { // init new record set containing empty array @@ -208,18 +206,13 @@ final class Handler foreach ($this->payload->getTypes() as $key => $type) { $record = new Soap\Dnsrecord(); - - // echo "getDomain: ", $this->payload->getDomain(), PHP_EOL; - // echo "getDomainName: ", $this->payload->getDomainName(), PHP_EOL; - // echo "getHost: ", $this->payload->getHost(), PHP_EOL; - $record->hostname = $this->payload->getHost(); + $record->hostname = $updateHost; $record->type = $type; $record->priority = "0"; // only for MX, can possibly be removed switch ($type) { case 'A': - // echo "A record set: ", $this->payload->getIpv4(), PHP_EOL; $record->destination = $this->payload->getIpv4(); break; @@ -231,20 +224,13 @@ final class Handler $record->destination = $this->payload->getTxt(); break; } - // echo "destination: ", $record->destination, PHP_EOL; - // echo "--- NEW ENTRY BELOW ---", PHP_EOL; - // $teststring = print_r($record, true); - // echo $teststring, PHP_EOL; array_push($newRecordSet->dnsrecords, $record); // push new record into array } - // echo "--- newRecordSet ---", PHP_EOL; - // $teststring = print_r($newRecordSet, true); - // echo $teststring, PHP_EOL; $dnsClient->updateDnsRecords( - $this->payload->getDomainName(), + $updateDomainName, $this->config->getCustomerId(), $this->config->getApiKey(), $loginHandle->responsedata->apisessionid, @@ -261,7 +247,7 @@ final class Handler $recordSet->dnsrecords = $infoHandle->responsedata->dnsrecords; $dnsClient->updateDnsRecords( - $this->payload->getDomainName(), + $updateDomainName, $this->config->getCustomerId(), $this->config->getApiKey(), $loginHandle->responsedata->apisessionid, diff --git a/src/Payload.php b/src/Payload.php index 7cca9b5..3e1776d 100644 --- a/src/Payload.php +++ b/src/Payload.php @@ -255,12 +255,4 @@ final class Payload return $this->force; } - /** - * @return string - */ - public function getType() - { - // TODO: - return "A"; - } }