1
0
Fork 0
mirror of https://github.com/fernwerker/ownDynDNS.git synced 2025-07-10 06:05:13 +02:00

* rewrite the hole script:

** use .env file for config so we can retrieve updates
** add config and payload DTO with validators
** allow registrable domains as DynDNS
** update IPv4/6 only if changed (or really forced)
** remove obsolete failed_logins counter
** save logs with timestamp
** save only the newest 100 log entries for each domain
This commit is contained in:
Branko Wilhelm 2019-05-16 18:00:21 +02:00
parent 0c58ee7009
commit 05e326abe6
No known key found for this signature in database
GPG key ID: 3BBFB6DDFBC46B0E
9 changed files with 528 additions and 149 deletions

122
src/Config.php Normal file
View file

@ -0,0 +1,122 @@
<?php
namespace netcup\DNS\API;
final class Config
{
/**
* @var string
*/
private $username;
/**
* @var string
*/
private $password;
/**
* @var string
*/
private $apiKey;
/**
* @var string
*/
private $apiPassword;
/**
* @var int
*/
private $customerId;
/**
* @var string
*/
private $logFile;
/**
* @var bool
*/
private $debug;
public function __construct(array $config)
{
foreach (get_object_vars($this) as $key => $val) {
if (isset($config[$key])) {
$this->$key = $config[$key];
}
}
}
/**
* @return bool
*/
public function isValid()
{
return
!empty($this->username) &&
!empty($this->password) &&
!empty($this->apiKey) &&
!empty($this->apiPassword) &&
!empty($this->customerId) &&
!empty($this->logFile);
}
/**
* @return string
*/
public function getUsername()
{
return $this->username;
}
/**
* @return string
*/
public function getPassword()
{
return $this->password;
}
/**
* @return string
*/
public function getApiKey()
{
return $this->apiKey;
}
/**
* @return string
*/
public function getApiPassword()
{
return $this->apiPassword;
}
/**
* @return int
*/
public function getCustomerId()
{
return $this->customerId;
}
/**
* @return string
*/
public function getLogFile()
{
return $this->logFile;
}
/**
* @return bool
*/
public function isDebug()
{
return $this->debug;
}
}

181
src/Handler.php Normal file
View file

@ -0,0 +1,181 @@
<?php
namespace netcup\DNS\API;
use RuntimeException;
final class Handler
{
/**
* @var array
*/
private $log;
/**
* @var Config
*/
private $config;
/**
* @var Payload
*/
private $payload;
public function __construct(array $config, array $payload)
{
$this->config = new Config($config);
if (!$this->config->isValid()) {
throw new RuntimeException('configuration invalid');
}
$this->payload = new Payload($payload);
if (!$this->payload->isValid()) {
throw new RuntimeException('payload invalid');
}
if (
$this->config->getUsername() !== $this->payload->getUser() ||
$this->config->getPassword() !== $this->payload->getPassword()
) {
throw new RuntimeException('credentials wrong');
}
if (is_readable($this->config->getLogFile())) {
$this->log = json_decode(file_get_contents($this->config->getLogFile()), true);
} else {
$this->log[$this->payload->getDomain()] = [];
}
}
public function __destruct()
{
$this->doExit();
}
/**
* @param string $msg
*
* @return self
*/
private function doLog($msg)
{
$this->log[$this->payload->getDomain()][] = sprintf('[%s] %s', date('c'), $msg);
if ($this->config->isDebug()) {
printf('[DEBUG] %s %s', $msg, PHP_EOL);
}
return $this;
}
private function doExit()
{
// save only the newest 100 log entries for each domain
$this->log[$this->payload->getDomain()] = array_reverse(array_slice(array_reverse($this->log[$this->payload->getDomain()]), 0, 100));
if (!is_writable($this->config->getLogFile()) || !file_put_contents($this->config->getLogFile(), json_encode($this->log, JSON_PRETTY_PRINT))) {
printf('[ERROR] unable to write %s %s', $this->config->getLogFile(), PHP_EOL);
}
}
/**
*
* @return self
*/
public function doRun()
{
$clientRequestId = md5($this->payload->getDomain() . time());
$dnsClient = new Soap\DomainWebserviceSoapClient();
$loginHandle = $dnsClient->login(
$this->config->getCustomerId(),
$this->config->getApiKey(),
$this->config->getApiPassword(),
$clientRequestId
);
if (2000 === $loginHandle->statuscode) {
$this->doLog('api login successful');
} else {
$this->doLog(sprintf('api login failed, message: %s', $loginHandle->longmessage));
}
$infoHandle = $dnsClient->infoDnsRecords(
$this->payload->getHostname(),
$this->config->getCustomerId(),
$this->config->getApiKey(),
$loginHandle->responsedata->apisessionid,
$clientRequestId
);
$changes = false;
foreach ($infoHandle->responsedata->dnsrecords as $key => $record) {
$recordHostnameReal = ($record->hostname !== '@') ? $record->hostname . '.' . $this->payload->getHostname() : $this->payload->getHostname();
if ($recordHostnameReal === $this->payload->getDomain()) {
// update A Record if exists and IP has changed
if ('A' === $record->type && $this->payload->getIpv4() &&
(
$this->payload->isForce() ||
$record->destination !== $this->payload->getIpv4()
)
) {
$record->destination = $this->payload->getIpv4();
$this->doLog(sprintf('IPv4 for %s set to %s', $recordHostnameReal, $this->payload->getIpv4()));
$changes = true;
}
// update AAAA Record if exists and IP has changed
if ('AAAA' === $record->type && $this->payload->getIpv6() &&
(
$this->payload->isForce()
|| $record->destination !== $this->payload->getIpv6()
)
) {
$record->destination = $this->payload->getIpv6();
$this->doLog(sprintf('IPv6 for %s set to %s', $recordHostnameReal, $this->payload->getIpv6()));
$changes = true;
}
}
}
if (true === $changes) {
$recordSet = new Soap\Dnsrecordset();
$recordSet->dnsrecords = $infoHandle->responsedata->dnsrecords;
$dnsClient->updateDnsRecords(
$this->payload->getHostname(),
$this->config->getCustomerId(),
$this->config->getApiKey(),
$loginHandle->responsedata->apisessionid,
$clientRequestId,
$recordSet
);
$this->doLog('dns recordset updated');
} else {
$this->doLog('dns recordset NOT updated (no changes)');
}
$logoutHandle = $dnsClient->logout(
$this->config->getCustomerId(),
$this->config->getApiKey(),
$loginHandle->responsedata->apisessionid,
$clientRequestId
);
if (2000 === $logoutHandle->statuscode) {
$this->doLog('api logout successful');
} else {
$this->doLog(sprintf('api logout failed, message: %s', $loginHandle->longmessage));
}
return $this;
}
}

156
src/Payload.php Normal file
View file

@ -0,0 +1,156 @@
<?php
namespace netcup\DNS\API;
final class Payload
{
/**
* @var string
*/
private $user;
/**
* @var string
*/
private $password;
/**
* @var string
*/
private $domain;
/**
* @var string
*/
private $ipv4;
/**
* @var string
*/
private $ipv6;
/**
* @var bool
*/
private $force = false;
public function __construct(array $payload)
{
foreach (get_object_vars($this) as $key => $val) {
if (isset($payload[$key])) {
$this->$key = $payload[$key];
}
}
}
/**
* @return bool
*/
public function isValid()
{
return
!empty($this->user) &&
!empty($this->password) &&
!empty($this->domain) &&
(
(
!empty($this->ipv4) && $this->isValidIpv4()
)
||
(
!empty($this->ipv6) && $this->isValidIpv6()
)
);
}
/**
* @return string
*/
public function getUser()
{
return $this->user;
}
/**
* @return string
*/
public function getPassword()
{
return $this->password;
}
/**
* @return string
*/
public function getDomain()
{
return $this->domain;
}
/**
* there is no good way to get the correct "registrable" Domain without external libs!
*
* @see https://github.com/jeremykendall/php-domain-parser
*
* this method is still tricky, because:
*
* works: nas.tld.com
* works: nas.tld.de
* works: tld.com
* failed: nas.tld.co.uk
* failed: nas.home.tld.de
*
* @return string
*/
public function getHostname()
{
// 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);
}
/**
* @return string
*/
public function getIpv4()
{
return $this->ipv4;
}
/**
* @return bool
*/
public function isValidIpv4()
{
return (bool)filter_var($this->ipv4, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
}
/**
* @return string
*/
public function getIpv6()
{
return $this->ipv6;
}
/**
* @return bool
*/
public function isValidIpv6()
{
return (bool)filter_var($this->ipv6, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
}
/**
* @return bool
*/
public function isForce()
{
return $this->force;
}
}

1377
src/Soap.php Normal file

File diff suppressed because it is too large Load diff