From 7a15d82a3a4d424ae9b5ff2be6ab37e328c4831b Mon Sep 17 00:00:00 2001 From: tracer Date: Tue, 1 Mar 2022 16:42:34 +0100 Subject: [PATCH] added endpoint /dyndns Signed-off-by: tracer --- src/Controller/RequestController.php | 353 ++++++++++++++++++++------- 1 file changed, 263 insertions(+), 90 deletions(-) diff --git a/src/Controller/RequestController.php b/src/Controller/RequestController.php index d0b727f..885a034 100644 --- a/src/Controller/RequestController.php +++ b/src/Controller/RequestController.php @@ -7,12 +7,12 @@ error_reporting(error_level: E_ALL); use App\Entity\Domain; use App\Repository\ApikeyRepository; use App\Repository\DomainRepository; +use App\Repository\PanelRepository; use DI\Container; use DI\ContainerBuilder; use Monolog\Formatter\LineFormatter; use Monolog\Handler\StreamHandler; use Monolog\Logger; -use OpenApi\Generator; use UnhandledMatchError; use function DI\autowire; use OpenApi\Attributes as OAT; @@ -50,14 +50,17 @@ class RequestController { private Logger $log; + private ApiController $apiController; + private ApikeyRepository $apikeyRepository; private DomainController $domainController; private DomainRepository $domainRepository; - private ApikeyRepository $apikeyRepository; + private PanelRepository $panelRepository; private Container $container; private string $header; private array $result; private string $status; private string $message; + /** * @param array $config @@ -95,9 +98,13 @@ class RequestController ]); $this->container = $containerBuilder->build(); + $this->apiController = $this->container->get(name: ApiController::class); + $this->apikeyRepository = $this->container->get(name: ApikeyRepository::class); $this->domainController = $this->container->get(name: DomainController::class); $this->domainRepository = $this->container->get(name: DomainRepository::class); - $this->apikeyRepository = $this->container->get(name: ApikeyRepository::class); + $this->panelRepository = $this->container->get(name: PanelRepository::class); + // *** + //$this->nameserverRepositoy = $this->container->get(name: NameserverRepository::class); } @@ -131,7 +138,7 @@ class RequestController description: 'Domain not found.' )] )] - public function handleAllDomainsGetRequest(): void + private function handleAllDomainsGetRequest(): void { $domains = $this->domainRepository->findAll(); $resultDomain = []; @@ -146,6 +153,41 @@ class RequestController $this->result = $resultDomain; } + /** + */ + private function handlePing() + { + if ($this->checkPassword()) { + $this->header = '200 OK'; + $this->status = json_encode(value: ['response' => 'pong']); + } else { + $this->header = '401 Unauthorized'; + $this->status = json_encode(value: ['message' => 'API key is missing or invalid']); + } + } + + + /** + * @return void + */ + private function handleDomains(): void + { + if ($this->checkPassword()) { + try { + match ($this->requestMethod) { + 'GET' => $this->handleDomainsGetRequest(), + 'POST' => $this->handleDomainsPostRequest(), + 'PUT' => $this->handleDomainsPutRequest(), + 'DELETE' => $this->handleDomainsDeleteRequest() + }; + } catch (UnhandledMatchError) { + $this->header = '400 Bad Request'; + $this->status = '400 Bad Request'; + $this->message = "unknown request method: $this->requestMethod"; + } + } + } + /** * @OA\Tag(name = "Server") @@ -189,98 +231,77 @@ class RequestController * @return void */ - #[OAT\Get( - path : '/domains/{name}', - operationId: 'getSingleDomain', - description: 'Returns information of a single domain specified by its domain name.', - summary : 'Returns a single domain.', - security : [ - ], - tags : ['Domains'], - parameters : [ - new OAT\Parameter(name: 'name', in: 'path', required: true, schema: new OAT\Schema(type: 'string')), - ], - responses : [ - new OAT\Response( - response : 200, - description: 'OK' - ), - new OAT\Response( - response : 401, - description: 'API key is missing or invalid.' - ), - new OAT\Response( - response : 404, - description: 'Domain not found.' - )] - - )] + #[ + OAT\Get( + path : '/domains/{name}', + operationId: 'getSingleDomain', + description: 'Returns information of a single domain specified by its domain name.', + summary : 'Returns a single domain.', + security : [ + ], + tags : ['Domains'], + parameters : [ + new OAT\Parameter(name: 'name', in: 'path', required: true, schema: new OAT\Schema(type: 'string')), + ], + responses : [ + new OAT\Response( + response : 200, + description: 'OK' + ), + new OAT\Response( + response : 401, + description: 'API key is missing or invalid.' + ), + new OAT\Response( + response : 404, + description: 'Domain not found.' + )] + + )] public function processRequest() { $command = $this->uri[2]; - if (empty($command) || !(($command == 'domains') || ($command == 'ping') || ($command == 'apidoc'))) { + if (empty($command) || !(($command == 'domains') || ($command == 'ping') || ($command == 'apidoc') || ($command == 'dyndns'))) { $this->header = '404 Not Found'; $this->status = "404 Not Found"; $this->message = "Endpoint not found."; } else { - if ($command == 'apidoc') { - $openapi = Generator::scan(sources: [__DIR__ . 'RequestController.php']); - $this->status = 'openapi'; - $this->result[] = $openapi->toJson(); - } else { - if ($this->checkPassword()) { - - if ($this->uri[2] == "ping") { - $this->header = '200 OK'; - $this->status = 'pong'; - } else { - try { - match ($this->requestMethod) { - 'GET' => $this->handleDomainGetRequest(), - 'POST' => $this->handleDomainPostRequest(), - 'PUT' => $this->handleDomainPutRequest(), - 'DELETE' => $this->handleDomainDeleteRequest() - }; - } catch (UnhandledMatchError) { - $this->header = '400 Bad Request'; - $this->status = '400 Bad Request'; - $this->message = "unknown request method: $this->requestMethod"; - } - } - } - } - - if (!empty($this->header)) { - header(header: $_SERVER['SERVER_PROTOCOL'] . ' ' . $this->header); - } - if (!empty($this->result)) { - if (!empty($this->status) && $this->status == 'openapi') { - header(header: 'Content-Type: application/json'); - echo $this->result[0]; - } else { - echo json_encode(value: $this->result); - } - } else { - if (!empty($this->status) && $this->status == 'pong') { - echo json_encode(value: [ - 'response' => $this->status - ]); - } else { - echo json_encode(value: [ - 'status' => $this->status ?? "Error: No status", - 'message' => $this->message ?? "Error: No message." - ]); - } + try { + match ($command) { + 'apidoc' => $this->apiDoc(), + 'dyndns' => $this->handleDynDNS(), + 'ping' => $this->handlePing(), + 'domains' => $this->handleDomains(), + }; + } catch (UnhandledMatchError) { + $this->header = '404 Bad Request'; + $this->status = '404 Bad Request'; + $this->message = 'Unknown path: ' . $command; } } + + if (!empty($this->header)) { + header(header: $_SERVER['SERVER_PROTOCOL'] . ' ' . $this->header); + } + + if (!empty($this->result)) { + echo json_encode(value: $this->result); + } elseif (!empty($this->status)) { + echo $this->status; + } else { + echo json_encode(value: [ + 'status' => $this->status ?? "Error: No status", + 'message' => $this->message ?? "Error: No message." + ]); + } } /** * @return bool */ - public function checkPassword(): bool + private function checkPassword(): bool { $headers = array_change_key_case(array: getallheaders(), case: CASE_UPPER); $apiKey = $headers['X-API-KEY'] ?? ''; @@ -310,12 +331,15 @@ class RequestController return true; } + /** * @return void */ - public function handleDomainGetRequest(): void + private function handleDomainsGetRequest(): void { - if ($this->uri[3] == 'name') { + $name = $this->uri[3] ?? ''; + + if ($name == 'name') { if ($result = $this->domainRepository->findByName(name: $this->uri[4])) { $domain = [ 'id' => $result->getId(), @@ -329,10 +353,10 @@ class RequestController $this->message = "The specified domain was not found."; } } else { - if (empty($this->uri[3])) { + if (empty($name)) { $this->handleAllDomainsGetRequest(); } else { - $id = intval(value: $this->uri['3']); + $id = intval(value: $name); if ($id > 0) { if ($result = $this->domainRepository->findById(id: $id)) { $domain = [ @@ -360,7 +384,7 @@ class RequestController /** * @return void */ - public function handleDomainPostRequest(): void + private function handleDomainsPostRequest(): void { $name = $_POST['name'] ?? ''; $panel = $_POST['panel'] ?? ''; @@ -398,12 +422,12 @@ class RequestController /** * @return void */ - public function handleDomainPutRequest(): void + private function handleDomainsPutRequest(): void { $putData = fopen(filename: 'php://input', mode: 'r'); - $data = fread(stream: $putData, length: 512); + $data = fread(stream: $putData, length: 8192); $params = explode(separator: '&', string: $data); - + foreach ($params as $param) { [$key, $value] = explode(separator: '=', string: $param); $put[$key] = $value; @@ -435,8 +459,7 @@ class RequestController /** * @return void */ - public - function handleDomainDeleteRequest(): void + private function handleDomainsDeleteRequest(): void { $deleteData = fopen(filename: 'php://input', mode: 'r'); $data = fread(stream: $deleteData, length: 512); @@ -468,4 +491,154 @@ class RequestController } } + private function apiDoc() + { + //TODO forward to apidoch … + } + + + /** + * @param String $host + * + * @return string + */ + private function getDomain(String $host): string + { + $host = strtolower(string: trim(string: $host)); + $count = substr_count(haystack: $host, needle: '.'); + if ($count == 2){ + if (strlen(string: explode(separator: '.', string: $host)[1]) > 3) { + $host = explode(separator: '.', string: $host, limit: 2)[1]; + } + } else if ($count > 2) { + $host = $this->getDomain(host: explode(separator: '.', string: $host, limit: 2)[1]); + } + return $host; + } + + + private function handleDynDNS() + { + if ($this->checkPassword()) { + $host = $this->uri[3] ?? ''; + + if (empty($host)) { + $this->header = '400 Bad Request'; + $this->status = '400 Bad Request'; + } else { + $a = $_POST['a'] ?? ''; + $aaaa = $_POST['aaaa'] ?? ''; + + if (empty($a) && empty($aaaa)) { + $address = $_SERVER['REMOTE_ADDR']; + + if (filter_var(value: $address, filter: FILTER_VALIDATE_IP, options: FILTER_FLAG_IPV6)) { + $aaaa = $address; + } else { + $a = $address; + } + } + + $domainName = $this->getDomain(host: $host); + $hostName = str_replace(search: '.' . $domainName, replace: '', subject: $host); + $domain = $this->domainRepository->findByName(name: $domainName); + $panel = $this->panelRepository->findByName(name: $domain->getPanel()); + + if (!empty($panel->getAaaa())) { + $domainData = $this->apiController->sendCommand( + requestType: 'GET', + serverName: $panel->getName(), + versionIP: 6, + apiKey: $panel->getApikey(), + command: 'domains/name/' . $domainName, + serverType: 'panel'); + } else { + $domainData = $this->apiController->sendCommand( + requestType: 'GET', + serverName: $panel->getName(), + versionIP: 4, + apiKey: $panel->getApikey(), + command: 'domains/name/' . $domainName, + serverType: 'panel'); + } + + $domainDecodedData = json_decode(json: $domainData['data']); + $domainID = $domainDecodedData->id; + + if (!empty($panel->getAaaa())) { + $dnsData = $this->apiController->sendCommand( + requestType: 'GET', + serverName: $panel->getName(), + versionIP: 6, + apiKey: $panel->getApikey(), + command: 'dns/' . $domainID, + serverType: 'panel'); + } else { + $dnsData = $this->apiController->sendCommand( + requestType: 'GET', + serverName: $panel->getName(), + versionIP: 4, + apiKey: $panel->getApikey(), + command: 'dns/' . $domainID, + serverType: 'panel'); + } + + $dnsDataDecoded = json_decode(json: $dnsData['data']); + $soa = $dnsDataDecoded->records->soa; + $others = $dnsDataDecoded->records->other; + + $updateHost = function(object $host) use ($hostName, $a, $aaaa) { + if ($host->host == $hostName) { + if ($host->type == 'A') { + if (!empty($a)) { + $host->value = $a; + } + } else { + if (!empty($aaaa)) { + $host->value = $aaaa; + } + } + } + }; + + array_map(callback: $updateHost, array: $others); + + $newDnsData = json_encode(value: [ + 'records' => [ + 'soa' => $soa, + 'other' => $others + ] + ]); + + if (!empty($panel->getAaaa())) { + $result = $this->apiController->sendCommand( + requestType: 'PUT', + serverName: $panel->getName(), + versionIP: 6, + apiKey: $panel->getApikey(), + command: 'dns/' . $domainID, + serverType: 'panel', + body: json_decode(json: $newDnsData, associative: true) + ); + } else { + $result = $this->apiController->sendCommand( + requestType: 'PUT', + serverName: $panel->getName(), + versionIP: 4, + apiKey: $panel->getApikey(), + command: 'dns/' . $domainID, + serverType: 'panel', + body: json_decode(json: $newDnsData, associative: true) + ); + } + if ($result['header'] == 200) { + $this->header = '200 OK'; + $this->status = json_encode(value: ['message' => 'Domain successfully updated']); + } + } + } + + } + + }