mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Extend DNS validator for concurrency
This commit is contained in:
+1
-1
@@ -55,7 +55,7 @@
|
||||
"utopia-php/detector": "0.2.*",
|
||||
"utopia-php/domains": "0.9.*",
|
||||
"utopia-php/emails": "0.6.*",
|
||||
"utopia-php/dns": "1.1.*",
|
||||
"utopia-php/dns": "1.3.*",
|
||||
"utopia-php/dsn": "0.2.1",
|
||||
"utopia-php/framework": "0.33.*",
|
||||
"utopia-php/fetch": "0.4.*",
|
||||
|
||||
Generated
+7
-7
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "ad28b7155175986191bd19bbcd13d623",
|
||||
"content-hash": "001835bef99a3dc73ab54cf4b22fc5e8",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
@@ -3947,16 +3947,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/dns",
|
||||
"version": "1.1.3",
|
||||
"version": "1.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/dns.git",
|
||||
"reference": "1e6b4bac735329c9e5ec69a6a5d899ec2d050707"
|
||||
"reference": "7644276913f68648515228d68684feca8742a645"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/dns/zipball/1e6b4bac735329c9e5ec69a6a5d899ec2d050707",
|
||||
"reference": "1e6b4bac735329c9e5ec69a6a5d899ec2d050707",
|
||||
"url": "https://api.github.com/repos/utopia-php/dns/zipball/7644276913f68648515228d68684feca8742a645",
|
||||
"reference": "7644276913f68648515228d68684feca8742a645",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3998,9 +3998,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/dns/issues",
|
||||
"source": "https://github.com/utopia-php/dns/tree/1.1.3"
|
||||
"source": "https://github.com/utopia-php/dns/tree/1.3.0"
|
||||
},
|
||||
"time": "2025-11-06T19:08:29+00:00"
|
||||
"time": "2025-11-12T19:24:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/domains",
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Network\Validator;
|
||||
|
||||
use Swoole\Coroutine\WaitGroup;
|
||||
use Utopia\DNS\Message\Record;
|
||||
use Utopia\DNS\Validator\DNS as BaseDNS;
|
||||
|
||||
class DNS extends BaseDNS
|
||||
{
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected array $dnsServers = [];
|
||||
|
||||
/**
|
||||
* @param string $target Expected value for the DNS record
|
||||
* @param int $type Type of DNS record to validate
|
||||
* For value, use const from Record, such as Record::TYPE_A
|
||||
* When using CAA type, you can provide exact match, or just issuer domain as $target
|
||||
* @param string|array<string> $dnsServers DNS server IP(s) or domain(s) to use for validation
|
||||
*/
|
||||
public function __construct(string $target, int $type = Record::TYPE_CNAME, string|array $dnsServers = self::DEFAULT_DNS_SERVER)
|
||||
{
|
||||
parent::__construct($target, $type, is_array($dnsServers) ? $dnsServers[0] : $dnsServers);
|
||||
|
||||
$this->dnsServers = is_array($dnsServers) ? $dnsServers : [$dnsServers];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate DNS record value against multiple DNS servers
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid(mixed $value): bool
|
||||
{
|
||||
if (\count($this->dnsServers) === 1) {
|
||||
return $this->isValidWithDNSServer($value, $this->dnsServers[0]);
|
||||
}
|
||||
|
||||
$wg = new WaitGroup();
|
||||
$failedValidator = null;
|
||||
|
||||
foreach ($this->dnsServers as $dnsServer) {
|
||||
$wg->add();
|
||||
|
||||
\go(function () use ($value, $dnsServer, $wg, &$failedValidator) {
|
||||
try {
|
||||
$validator = new BaseDNS($this->target, $this->type, $dnsServer);
|
||||
$isValid = $validator->isValid($value);
|
||||
|
||||
if (!$isValid) {
|
||||
$failedValidator = $validator;
|
||||
}
|
||||
} finally {
|
||||
$wg->done();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$wg->wait();
|
||||
|
||||
if (!\is_null($failedValidator)) {
|
||||
$this->count = $failedValidator->count;
|
||||
$this->value = $failedValidator->value;
|
||||
$this->reason = $failedValidator->reason;
|
||||
$this->records = $failedValidator->records;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate with a specific DNS server
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param string $dnsServer
|
||||
* @return bool
|
||||
*/
|
||||
protected function isValidWithDNSServer(mixed $value, string $dnsServer): bool
|
||||
{
|
||||
$validator = new BaseDNS($this->target, $this->type, $dnsServer);
|
||||
$result = $validator->isValid($value);
|
||||
|
||||
$this->count = $validator->count;
|
||||
$this->value = $validator->value;
|
||||
$this->reason = $validator->reason;
|
||||
$this->records = $validator->records;
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,10 @@
|
||||
namespace Appwrite\Platform\Modules\Proxy;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Network\Validator\DNS as ValidatorDNS;
|
||||
use Appwrite\Platform\Action as PlatformAction;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\DNS\Message\Record;
|
||||
use Utopia\DNS\Validator\DNS as ValidatorDNS;
|
||||
use Utopia\Domains\Domain;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\System\System;
|
||||
@@ -19,6 +19,18 @@ class Action extends PlatformAction
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse DNS servers from environment variable
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
protected function getDNSServers(): array
|
||||
{
|
||||
$dnsEnv = System::getEnv('_APP_DNS', '8.8.8.8');
|
||||
$servers = \array_map('trim', \explode(',', $dnsEnv));
|
||||
return \array_filter($servers, fn ($server) => !empty($server));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify or re-verify a rule
|
||||
*
|
||||
@@ -31,6 +43,7 @@ class Action extends PlatformAction
|
||||
public function verifyRule(Document $rule, ?Log $log = null, ?string $verificationDomainAPI = null, ?string $verificationDomainFunction = null): void
|
||||
{
|
||||
$dnsValidatorClass = $this->dnsValidatorClass;
|
||||
$dnsServers = $this->getDNSServers();
|
||||
|
||||
$domain = new Domain($rule->getAttribute('domain', ''));
|
||||
|
||||
@@ -46,7 +59,7 @@ class Action extends PlatformAction
|
||||
$caaTarget = System::getEnv('_APP_DOMAIN_TARGET_CAA', '');
|
||||
if (!empty($caaTarget)) {
|
||||
$validationStart = \microtime(true);
|
||||
$validator = new $dnsValidatorClass($caaTarget, Record::TYPE_CAA, System::getEnv('_APP_DNS', '8.8.8.8'));
|
||||
$validator = new $dnsValidatorClass($caaTarget, Record::TYPE_CAA, $dnsServers);
|
||||
if (!$validator->isValid($domain->get())) {
|
||||
if (!\is_null($log)) {
|
||||
$log->addExtra('dnsTimingCaa', \strval(\microtime(true) - $validationStart));
|
||||
@@ -88,7 +101,7 @@ class Action extends PlatformAction
|
||||
$mainValidator = null; // Validator to use for error description
|
||||
|
||||
if (!is_null($targetCNAME)) {
|
||||
$validator = new $dnsValidatorClass($targetCNAME->get(), Record::TYPE_CNAME, System::getEnv('_APP_DNS', '8.8.8.8'));
|
||||
$validator = new $dnsValidatorClass($targetCNAME->get(), Record::TYPE_CNAME, $dnsServers);
|
||||
$validators[] = $validator;
|
||||
|
||||
if (\is_null($mainValidator)) {
|
||||
@@ -98,7 +111,7 @@ class Action extends PlatformAction
|
||||
|
||||
$targetA = System::getEnv('_APP_DOMAIN_TARGET_A', '');
|
||||
if ((new IP(IP::V4))->isValid($targetA)) {
|
||||
$validator = new $dnsValidatorClass($targetA, Record::TYPE_A, System::getEnv('_APP_DNS', '8.8.8.8'));
|
||||
$validator = new $dnsValidatorClass($targetA, Record::TYPE_A, $dnsServers);
|
||||
$validators[] = $validator;
|
||||
|
||||
if (\is_null($mainValidator)) {
|
||||
@@ -108,7 +121,7 @@ class Action extends PlatformAction
|
||||
|
||||
$targetAAAA = System::getEnv('_APP_DOMAIN_TARGET_AAAA', '');
|
||||
if ((new IP(IP::V6))->isValid($targetAAAA)) {
|
||||
$validator = new $dnsValidatorClass($targetAAAA, Record::TYPE_AAAA, System::getEnv('_APP_DNS', '8.8.8.8'));
|
||||
$validator = new $dnsValidatorClass($targetAAAA, Record::TYPE_AAAA, $dnsServers);
|
||||
$validators[] = $validator;
|
||||
|
||||
if (\is_null($mainValidator)) {
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Network\Validators;
|
||||
|
||||
use Appwrite\Network\Validator\DNS;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Utopia\DNS\Message\Record;
|
||||
|
||||
class DNSTest extends TestCase
|
||||
{
|
||||
public function testSingleDNSServer(): void
|
||||
{
|
||||
$validator = new DNS('appwrite.io', Record::TYPE_CNAME, '8.8.8.8');
|
||||
|
||||
$this->assertEquals(false, $validator->isValid(''));
|
||||
$this->assertEquals(false, $validator->isValid(null));
|
||||
$this->assertEquals('string', $validator->getType());
|
||||
}
|
||||
|
||||
public function testMultipleDNSServers(): void
|
||||
{
|
||||
$validator = new DNS('appwrite.io', Record::TYPE_CNAME, ['8.8.8.8', '1.1.1.1']);
|
||||
|
||||
$this->assertEquals(false, $validator->isValid(''));
|
||||
$this->assertEquals(false, $validator->isValid(null));
|
||||
$this->assertEquals('string', $validator->getType());
|
||||
}
|
||||
|
||||
public function testValidationFailure(): void
|
||||
{
|
||||
$validator = new DNS('invalid-target.example.com', Record::TYPE_CNAME, ['8.8.8.8', '1.1.1.1']);
|
||||
|
||||
$result = $validator->isValid('nonexistent-domain-' . \uniqid() . '.com');
|
||||
|
||||
$this->assertEquals(false, $result);
|
||||
$this->assertIsInt($validator->count);
|
||||
$this->assertIsString($validator->value);
|
||||
$this->assertIsArray($validator->records);
|
||||
$this->assertIsString($validator->getDescription());
|
||||
}
|
||||
|
||||
public function testCoreDNSFailure(): void
|
||||
{
|
||||
// CoreDNS is configured to return cname.localhost. for stage.webapp.com
|
||||
$validator = new DNS('cname.localhost.', Record::TYPE_CNAME, ['172.16.238.100', '8.8.8.8']);
|
||||
|
||||
$result = $validator->isValid('stage.webapp.com');
|
||||
$this->assertEquals(false, $result);
|
||||
|
||||
$result = $validator->isValid('stage-wrong-cname.webapp.com');
|
||||
$this->assertEquals(false, $result);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user