fix(dns): fall back to system resolver (#11811)

This commit is contained in:
Andras Bacsai
2026-09-16 13:38:46 +02:00
committed by GitHub
parent 1b842090ad
commit ac1123ec56
2 changed files with 113 additions and 0 deletions
+35
View File
@@ -66,6 +66,7 @@ class CheckDomainDns
}
$type = dnsRecordTypeForIp($expectedIp) === 'AAAA' ? DNSTypes::NAME_AAAA : DNSTypes::NAME_A;
$receivedAddressRecord = false;
foreach ($dnsServers as $dnsServer) {
$remainingNanoseconds = $deadline - hrtime(true);
@@ -90,6 +91,7 @@ class CheckDomainDns
continue;
}
$receivedAddressRecord = true;
if (isCloudflareIp($record->getData()) || ($expectedIp && $record->getData() === $expectedIp)) {
return $this->result('ok', $this->successMessage($server, $expectedIp), $expectedIp);
}
@@ -99,9 +101,42 @@ class CheckDomainDns
}
}
if (! $receivedAddressRecord && hrtime(true) < $deadline) {
foreach ($this->resolveWithSystemDns($host, $type) as $resolvedIp) {
if (isCloudflareIp($resolvedIp) || ($expectedIp && $resolvedIp === $expectedIp)) {
return $this->result('ok', $this->successMessage($server, $expectedIp), $expectedIp);
}
}
}
return $this->result('failed', dnsMismatchGuidanceMessage($expectedIp, $expectedIp), $expectedIp);
}
/**
* @return array<int, string>
*/
protected function resolveWithSystemDns(string $host, string $type): array
{
$recordType = $type === DNSTypes::NAME_AAAA ? DNS_AAAA : DNS_A;
$addressKey = $type === DNSTypes::NAME_AAAA ? 'ipv6' : 'ip';
try {
$records = @dns_get_record($host, $recordType);
} catch (\Throwable) {
return [];
}
if (! is_array($records)) {
return [];
}
return collect($records)
->pluck($addressKey)
->filter(fn ($address) => is_string($address) && filter_var($address, FILTER_VALIDATE_IP) !== false)
->values()
->all();
}
private function successMessage(Server $server, ?string $expectedIp): string
{
if (
+78
View File
@@ -110,3 +110,81 @@ it('does not start another resolver query after the total dns budget is exhauste
->and($result['example']['message'])->toBe('Could not validate DNS for this domain.')
->and($queryCount)->toHaveCount(0);
});
it('falls back to the system resolver when custom dns servers return no answers', function () {
InstanceSettings::unguarded(fn () => InstanceSettings::query()->updateOrCreate(
['id' => 0],
[
'is_dns_validation_enabled' => true,
'custom_dns_servers' => '192.0.2.1',
]
));
app()->bind(DNSQuery::class, fn () => new class('192.0.2.1') extends DNSQuery
{
public function query(string $question, string $typeName = DNSTypes::NAME_A): false
{
return false;
}
public function hasError(): bool
{
return true;
}
});
$action = new class extends CheckDomainDns
{
protected function resolveWithSystemDns(string $host, string $type): array
{
return ['203.0.113.10'];
}
};
$result = $action->handle(
['example' => 'https://example.com'],
new Server(['ip' => '203.0.113.10']),
'203.0.113.10',
);
expect($result['example']['status'])->toBe('ok');
});
it('does not use the system resolver after a custom dns server returns an address', function () {
InstanceSettings::unguarded(fn () => InstanceSettings::query()->updateOrCreate(
['id' => 0],
[
'is_dns_validation_enabled' => true,
'custom_dns_servers' => '192.0.2.1',
]
));
app()->bind(DNSQuery::class, fn () => new class('192.0.2.1') extends DNSQuery
{
public function query(string $question, string $typeName = DNSTypes::NAME_A): array
{
return [new DNSResult($typeName, 1, 'IN', 60, '198.51.100.20', $question, '', [])];
}
public function hasError(): bool
{
return false;
}
});
$action = new class extends CheckDomainDns
{
protected function resolveWithSystemDns(string $host, string $type): array
{
throw new RuntimeException('The system resolver must not be used.');
}
};
$result = $action->handle(
['example' => 'https://example.com'],
new Server(['ip' => '203.0.113.10']),
'203.0.113.10',
);
expect($result['example']['status'])->toBe('failed');
});