fix(transfer): resolve IPv6-only server host names

The domain check of the transfer checklist resolved the server host name
with gethostbynamel(), which returns IPv4 only, so a domain that points to
an IPv6-only server was reported as pointing to another address. Resolve A
and AAAA records and compare normalized addresses. The Cloud SSH probe now
also resolves AAAA records when a host name has no A record.

Closes #12397

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andras BacsaiandClaude Opus 5.5 committed 2026-11-02 14:46:19 +01:00
1 parent 8dea526098
commit bc855fdc68
2 files changed
+19 -4

No files matched your search

@@ -232,7 +232,7 @@ class ServerTransferController extends Controller
Server::throttleValidation($teamId, $ip);
$probeIp = Server::normalizeIp($ip);
if (isCloud() && ! filter_var($probeIp, FILTER_VALIDATE_IP)) {
$probeIp = gethostbyname($probeIp);
$probeIp = resolveHostToIp($probeIp);
}
$probeHost = filter_var($probeIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? "[{$probeIp}]" : $probeIp;
$probeAllowed = ! isCloud() || (filter_var($probeIp, FILTER_VALIDATE_IP)
@@ -407,14 +407,29 @@ class TransferFinishChecklist
: ['action', 'This instance does not own the record yet.', 'Until it owns the record, this instance does not change or delete it.'];
}
/**
* The IPv4 and IPv6 addresses of a host name, in the normalized form of normalizeIpAddress().
*
* @return array<int, string>
*/
private function resolveIps(string $host): array
{
$records = @dns_get_record($host, DNS_A | DNS_AAAA) ?: [];
return array_values(array_filter(array_map(
fn (array $record): ?string => isset($record['ip']) || isset($record['ipv6']) ? normalizeIpAddress($record['ip'] ?? $record['ipv6']) : null,
$records,
)));
}
/**
* @return array{0: string, 1: string, 2: string|null}
*/
private function checkDomain(Server $server, string $host): array
{
$serverIps = filter_var($server->ip, FILTER_VALIDATE_IP) ? [$server->ip] : (gethostbynamel($server->ip) ?: []);
$records = @dns_get_record($host, DNS_A | DNS_AAAA) ?: [];
$ips = array_values(array_filter(array_map(fn (array $record) => $record['ip'] ?? $record['ipv6'] ?? null, $records)));
$serverIp = normalizeIpAddress($server->ip);
$serverIps = filter_var($serverIp, FILTER_VALIDATE_IP) ? [$serverIp] : $this->resolveIps($serverIp);
$ips = $this->resolveIps($host);
if ($ips === []) {
return ['action', 'The domain does not resolve.', null];
}