Description:
When initiating a domain transfer where the domain's delegation uses external nameservers (i.e. not registered in the OpenProvider nameserver registry), APITools::createNameserversArray() silently discards the provided nameservers and substitutes the module's configured default nameservers instead.
Root cause:
In OpenProvider/API/APITools.php, createNameserversArray() calls $apiHelper->getNameserverList($nsName) for each NS to validate it against OpenProvider's registry. Nameservers that are not registered as glue records in OP's system return no results, causing them to be flagged as invalid ($invalidNameServer = true). When fewer than 2 nameservers pass this check, the function falls back to the module defaults from APIConfig::getDefaultNameservers() — with no exception thrown and no warning logged to WHMCS.
Impact:
Any domain transferred via WHMCS where nameservers are hosted externally (the vast majority of real-world cases) will have its DNS delegation silently changed to the reseller's default nameservers at the moment of transfer initiation. This causes immediate DNS outage for the domain if the client has custom delegation.
Steps to reproduce:
Have a domain in WHMCS with ns1.example-external.com / ns2.example-external.com in tbldomains
Trigger DomainTransfer via WHMCS local API
Observe transferDomainRequest sent to OP API — name_servers will contain module defaults, not the domain's actual nameservers
Expected behavior:
If $params['ns1'] and $params['ns2'] contain valid hostnames, they should be used as-is without validation against OP's nameserver registry. Registry lookup should only be performed when adding glue records (i.e. NS is a subdomain of the domain being transferred). Fallback to defaults should only apply when $params contains no NS entries at all.
Suggested fix:
// In createNameserversArray(), before registry lookup:
$ns = trim((string)($params["ns{$i}"] ?? ''));
if ($ns === '') continue;
// Only validate against OP registry if NS is a child of this domain
// (glue record scenario). Otherwise accept it directly.
$isGlue = str_ends_with(strtolower($ns), '.' . strtolower($params['sld'] . '.' . $params['tld']));
if (!$isGlue) {
$nameServers[] = new \OpenProvider\API\DomainNameServer(['name' => $ns]);
continue;
}
// ... existing registry lookup for glue records only
Description:
When initiating a domain transfer where the domain's delegation uses external nameservers (i.e. not registered in the OpenProvider nameserver registry), APITools::createNameserversArray() silently discards the provided nameservers and substitutes the module's configured default nameservers instead.
Root cause:
In OpenProvider/API/APITools.php, createNameserversArray() calls $apiHelper->getNameserverList($nsName) for each NS to validate it against OpenProvider's registry. Nameservers that are not registered as glue records in OP's system return no results, causing them to be flagged as invalid ($invalidNameServer = true). When fewer than 2 nameservers pass this check, the function falls back to the module defaults from APIConfig::getDefaultNameservers() — with no exception thrown and no warning logged to WHMCS.
Impact:
Any domain transferred via WHMCS where nameservers are hosted externally (the vast majority of real-world cases) will have its DNS delegation silently changed to the reseller's default nameservers at the moment of transfer initiation. This causes immediate DNS outage for the domain if the client has custom delegation.
Steps to reproduce:
Have a domain in WHMCS with ns1.example-external.com / ns2.example-external.com in tbldomains
Trigger DomainTransfer via WHMCS local API
Observe transferDomainRequest sent to OP API — name_servers will contain module defaults, not the domain's actual nameservers
Expected behavior:
If $params['ns1'] and $params['ns2'] contain valid hostnames, they should be used as-is without validation against OP's nameserver registry. Registry lookup should only be performed when adding glue records (i.e. NS is a subdomain of the domain being transferred). Fallback to defaults should only apply when $params contains no NS entries at all.
Suggested fix:
// In createNameserversArray(), before registry lookup:
$ns = trim((string)($params["ns{$i}"] ?? ''));
if ($ns === '') continue;
// Only validate against OP registry if NS is a child of this domain
// (glue record scenario). Otherwise accept it directly.
$isGlue = str_ends_with(strtolower($ns), '.' . strtolower($params['sld'] . '.' . $params['tld']));
if (!$isGlue) {
$nameServers[] = new \OpenProvider\API\DomainNameServer(['name' => $ns]);
continue;
}
// ... existing registry lookup for glue records only