From bc855fdc68ed5ae77eb23579f92bb504b649334d Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Mon, 2 Nov 2026 14:46:19 +0100 Subject: [PATCH] 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 --- .../Api/ServerTransferController.php | 2 +- .../TransferFinishChecklist.php | 21 ++++++++++++++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/Api/ServerTransferController.php b/app/Http/Controllers/Api/ServerTransferController.php index efd8f428b7..0dc0229a30 100644 --- a/app/Http/Controllers/Api/ServerTransferController.php +++ b/app/Http/Controllers/Api/ServerTransferController.php @@ -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) diff --git a/app/Services/ServerTransfer/TransferFinishChecklist.php b/app/Services/ServerTransfer/TransferFinishChecklist.php index d894b49cb3..25d6d19e61 100644 --- a/app/Services/ServerTransfer/TransferFinishChecklist.php +++ b/app/Services/ServerTransfer/TransferFinishChecklist.php @@ -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 + */ + 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]; }