2025-10-08 14:23:07 +03:00
< ? php
namespace App\Controllers ;
use Core\Controller ;
use App\Services\WhoisService ;
class DebugController extends Controller
{
/**
* Show raw WHOIS data for a domain
*/
public function whois ()
{
$domain = $_GET [ 'domain' ] ? ? '' ;
if ( empty ( $domain )) {
$this -> view ( 'debug/whois' , [
'domain' => '' ,
'title' => 'WHOIS Debug Tool'
]);
return ;
}
2025-10-21 15:16:56 +03:00
// Log debug tool usage
$logger = new \App\Services\Logger ( 'debug' );
$logger -> info ( 'WHOIS debug tool accessed' , [
'domain' => $domain ,
'user_id' => \Core\Auth :: id (),
'ip_address' => $_SERVER [ 'REMOTE_ADDR' ] ? ? 'unknown' ,
'user_agent' => $_SERVER [ 'HTTP_USER_AGENT' ] ? ? 'unknown'
]);
2025-10-08 14:23:07 +03:00
// Get TLD
$parts = explode ( '.' , $domain );
$tld = $parts [ count ( $parts ) - 1 ];
// Use reflection to access the WhoisService's discovery methods
$whoisService = new WhoisService ();
// Use reflection to call private discoverTldServers method
$reflection = new \ReflectionClass ( $whoisService );
$discoverMethod = $reflection -> getMethod ( 'discoverTldServers' );
$discoverMethod -> setAccessible ( true );
// Handle double TLDs
$doubleTld = null ;
if ( count ( $parts ) >= 3 ) {
$doubleTld = $parts [ count ( $parts ) - 2 ] . '.' . $tld ;
}
// Try double TLD first, then single TLD
$discoveryDebug = [];
$discoveryDebug [] = " === IANA DISCOVERY PROCESS === " ;
$discoveryDebug [] = " " ;
$discoveryDebug [] = " Step 1: Querying IANA WHOIS (whois.iana.org) for TLD information " ;
$discoveryDebug [] = " Step 2: Querying IANA RDAP Bootstrap (https://data.iana.org/rdap/dns.json) " ;
$discoveryDebug [] = " Step 3: Fallback to IANA HTML page if needed " ;
$discoveryDebug [] = " " ;
if ( $doubleTld ) {
$discoveryDebug [] = " Trying double TLD: { $doubleTld } " ;
$servers = $discoverMethod -> invoke ( $whoisService , $doubleTld );
$discoveryDebug [] = " -> RDAP: " . ( $servers [ 'rdap_url' ] ? ? 'Not found' );
$discoveryDebug [] = " -> WHOIS: " . ( $servers [ 'whois_server' ] ? ? 'Not found' );
if ( ! $servers [ 'rdap_url' ] && ! $servers [ 'whois_server' ]) {
$discoveryDebug [] = " " ;
$discoveryDebug [] = " Double TLD failed, trying single TLD: { $tld } " ;
$servers = $discoverMethod -> invoke ( $whoisService , $tld );
$discoveryDebug [] = " -> RDAP: " . ( $servers [ 'rdap_url' ] ? ? 'Not found' );
$discoveryDebug [] = " -> WHOIS: " . ( $servers [ 'whois_server' ] ? ? 'Not found' );
}
} else {
$discoveryDebug [] = " Trying single TLD: { $tld } " ;
$servers = $discoverMethod -> invoke ( $whoisService , $tld );
$discoveryDebug [] = " -> RDAP: " . ( $servers [ 'rdap_url' ] ? ? 'Not found' );
$discoveryDebug [] = " -> WHOIS: " . ( $servers [ 'whois_server' ] ? ? 'Not found' );
}
$rdapUrl = $servers [ 'rdap_url' ];
$whoisServer = $servers [ 'whois_server' ] ? ? 'whois.iana.org' ;
$discoveryDebug [] = " " ;
$discoveryDebug [] = " === FINAL RESULTS === " ;
$discoveryDebug [] = " RDAP URL: " . ( $rdapUrl ? ? 'Not available - will use WHOIS fallback' );
$discoveryDebug [] = " WHOIS Server: { $whoisServer } " ;
$discoveryDebug [] = " " ;
if ( ! $rdapUrl ) {
$discoveryDebug [] = " NOTE: No RDAP server found in IANA sources. Will use traditional WHOIS. " ;
}
// Get raw response - try RDAP first, then WHOIS
$response = '' ;
$parsedData = [];
$server = $whoisServer ;
$rdapSucceeded = false ;
// Add discovery debug info
$response .= " === TLD DISCOVERY DEBUG === \n \n " ;
foreach ( $discoveryDebug as $debug ) {
$response .= $debug . " \n " ;
}
$response .= " \n " ;
// Try RDAP first if available
if ( $rdapUrl ) {
$server = parse_url ( $rdapUrl , PHP_URL_HOST ) . ' (RDAP)' ;
// Construct full RDAP URL
// RDAP standard format: {base_url}domain/{domain_name}
if ( ! preg_match ( '/domain\/$/' , $rdapUrl )) {
$fullRdapUrl = rtrim ( $rdapUrl , '/' ) . '/domain/' . strtolower ( $domain );
} else {
$fullRdapUrl = rtrim ( $rdapUrl , '/' ) . '/' . strtolower ( $domain );
}
// Query RDAP
$ch = curl_init ();
curl_setopt ( $ch , CURLOPT_URL , $fullRdapUrl );
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , true );
curl_setopt ( $ch , CURLOPT_TIMEOUT , 10 );
curl_setopt ( $ch , CURLOPT_FOLLOWLOCATION , true );
curl_setopt ( $ch , CURLOPT_SSL_VERIFYPEER , true );
2025-10-21 15:40:54 +03:00
curl_setopt ( $ch , CURLOPT_HTTPHEADER , [
'Accept: application/rdap+json, application/json, */*' ,
'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
]);
2025-10-08 14:23:07 +03:00
$rdapResponse = curl_exec ( $ch );
$httpCode = curl_getinfo ( $ch , CURLINFO_HTTP_CODE );
$curlError = curl_error ( $ch );
$curlInfo = curl_getinfo ( $ch );
curl_close ( $ch );
2025-10-21 15:40:54 +03:00
// Debug: Log the actual cURL request details
$logger -> debug ( 'RDAP cURL request details' , [
'url' => $fullRdapUrl ,
'http_code' => $httpCode ,
'curl_error' => $curlError ,
'response_length' => strlen ( $rdapResponse ),
'total_time' => $curlInfo [ 'total_time' ] ? ? 'unknown' ,
'primary_ip' => $curlInfo [ 'primary_ip' ] ? ? 'unknown' ,
'response_preview' => substr ( $rdapResponse , 0 , 200 ) . ( strlen ( $rdapResponse ) > 200 ? '...' : '' )
]);
if (( $httpCode === 200 || $httpCode === 404 ) && $rdapResponse ) {
2025-10-08 14:23:07 +03:00
// Pretty print JSON
$rdapData = json_decode ( $rdapResponse , true );
2025-10-21 15:12:19 +03:00
// Check if RDAP returned an error in the JSON or if domain is available
$isDomainAvailable = false ;
2025-10-08 14:23:07 +03:00
if ( $rdapData && isset ( $rdapData [ 'errorCode' ])) {
$rdapSucceeded = true ; // HTTP succeeded, but domain not found
$response .= " \n === RDAP QUERY SUCCESS (Domain Not Found) === \n \n " ;
$response .= " RDAP URL: { $fullRdapUrl } \n " ;
$response .= " HTTP Status: { $httpCode } \n " ;
$response .= " RDAP Error Code: { $rdapData [ 'errorCode' ] } \n " ;
$response .= " Title: " . ( $rdapData [ 'title' ] ? ? 'N/A' ) . " \n " ;
$response .= " Description: " . ( isset ( $rdapData [ 'description' ]) ? implode ( ', ' , ( array ) $rdapData [ 'description' ]) : 'N/A' ) . " \n \n " ;
if ( $rdapData [ 'errorCode' ] == 404 ) {
$response .= " ✓ Domain is AVAILABLE (not registered) \n \n " ;
$parsedData [] = [ 'key' => 'Status' , 'value' => 'AVAILABLE' ];
$parsedData [] = [ 'key' => 'Registrar' , 'value' => 'Not Registered' ];
2025-10-21 15:12:19 +03:00
$isDomainAvailable = true ;
2025-10-08 14:23:07 +03:00
}
$response .= " --- RDAP JSON RESPONSE --- \n \n " ;
$response .= json_encode ( $rdapData , JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES );
2025-10-21 15:12:19 +03:00
} elseif ( $rdapData && isset ( $rdapData [ 'status' ]) && is_array ( $rdapData [ 'status' ])) {
// Check if domain status indicates it's available
foreach ( $rdapData [ 'status' ] as $status ) {
if ( stripos ( $status , 'free' ) !== false || stripos ( $status , 'available' ) !== false ) {
$rdapSucceeded = true ;
$response .= " \n === RDAP QUERY SUCCESS (Domain Available) === \n \n " ;
$response .= " RDAP URL: { $fullRdapUrl } \n " ;
$response .= " HTTP Status: { $httpCode } \n " ;
$response .= " Domain Status: " . implode ( ', ' , $rdapData [ 'status' ]) . " \n \n " ;
$response .= " ✓ Domain is AVAILABLE (not registered) \n \n " ;
$parsedData [] = [ 'key' => 'Status' , 'value' => 'AVAILABLE' ];
$parsedData [] = [ 'key' => 'Registrar' , 'value' => 'Not Registered' ];
$isDomainAvailable = true ;
$response .= " --- RDAP JSON RESPONSE --- \n \n " ;
$response .= json_encode ( $rdapData , JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES );
break ;
}
}
2025-10-21 15:40:54 +03:00
} elseif ( $httpCode === 404 && $rdapData ) {
// Handle HTTP 404 with valid JSON response (like hosteroid.nl)
$rdapSucceeded = true ;
$response .= " \n === RDAP QUERY SUCCESS (HTTP 404 with JSON) === \n \n " ;
$response .= " RDAP URL: { $fullRdapUrl } \n " ;
$response .= " HTTP Status: { $httpCode } \n " ;
$response .= " Note: HTTP 404 but received valid JSON response \n \n " ;
// Check if it contains "free" status
if ( isset ( $rdapData [ 'status' ]) && is_array ( $rdapData [ 'status' ])) {
foreach ( $rdapData [ 'status' ] as $status ) {
if ( stripos ( $status , 'free' ) !== false ) {
$response .= " ✓ Domain is AVAILABLE (not registered) \n \n " ;
$parsedData [] = [ 'key' => 'Status' , 'value' => 'AVAILABLE' ];
$parsedData [] = [ 'key' => 'Registrar' , 'value' => 'Not Registered' ];
$isDomainAvailable = true ;
break ;
}
}
}
$response .= " --- RDAP JSON RESPONSE --- \n \n " ;
$response .= json_encode ( $rdapData , JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES );
2025-10-21 15:12:19 +03:00
}
if ( ! $isDomainAvailable ) {
2025-10-08 14:23:07 +03:00
$rdapSucceeded = true ;
$response .= " \n === RDAP QUERY SUCCESS === \n \n " ;
$response .= " RDAP URL: { $fullRdapUrl } \n " ;
$response .= " HTTP Status: { $httpCode } \n \n " ;
$response .= " --- RDAP JSON RESPONSE --- \n \n " ;
if ( $rdapData ) {
$response .= json_encode ( $rdapData , JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES );
// Parse some key fields for the table
if ( isset ( $rdapData [ 'entities' ])) {
foreach ( $rdapData [ 'entities' ] as $entity ) {
if ( isset ( $entity [ 'vcardArray' ][ 1 ])) {
foreach ( $entity [ 'vcardArray' ][ 1 ] as $field ) {
if ( is_array ( $field ) && count ( $field ) >= 4 ) {
$parsedData [] = [
'key' => $field [ 0 ],
'value' => is_array ( $field [ 3 ]) ? implode ( ', ' , $field [ 3 ]) : $field [ 3 ]
];
}
}
}
}
}
if ( isset ( $rdapData [ 'events' ])) {
foreach ( $rdapData [ 'events' ] as $event ) {
$parsedData [] = [
'key' => ucfirst ( $event [ 'eventAction' ] ? ? 'event' ),
'value' => $event [ 'eventDate' ] ? ? 'N/A'
];
}
}
} else {
$response .= $rdapResponse ;
}
}
} elseif ( $httpCode === 404 && $rdapResponse ) {
// Handle 404 responses as domain not found
$rdapData = json_decode ( $rdapResponse , true );
if ( $rdapData && isset ( $rdapData [ 'errorCode' ]) && $rdapData [ 'errorCode' ] == 404 ) {
$rdapSucceeded = true ; // Treat as successful domain not found
$response .= " \n === RDAP QUERY SUCCESS (Domain Not Found) === \n \n " ;
$response .= " RDAP URL: { $fullRdapUrl } \n " ;
$response .= " HTTP Status: { $httpCode } \n " ;
$response .= " RDAP Error Code: { $rdapData [ 'errorCode' ] } \n " ;
$response .= " Title: " . ( $rdapData [ 'title' ] ? ? 'N/A' ) . " \n " ;
$response .= " Description: " . ( isset ( $rdapData [ 'description' ]) ? implode ( ', ' , ( array ) $rdapData [ 'description' ]) : 'N/A' ) . " \n \n " ;
$response .= " ✓ Domain is AVAILABLE (not registered) \n \n " ;
$parsedData [] = [ 'key' => 'Status' , 'value' => 'AVAILABLE' ];
$parsedData [] = [ 'key' => 'Registrar' , 'value' => 'Not Registered' ];
$response .= " --- RDAP JSON RESPONSE --- \n \n " ;
$response .= json_encode ( $rdapData , JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES );
} else {
$response .= " \n === RDAP QUERY FAILED === \n \n " ;
$response .= " RDAP URL: { $fullRdapUrl } \n " ;
$response .= " HTTP Status: { $httpCode } \n " ;
$response .= " \n Error: Could not retrieve RDAP data \n \n " ;
}
} else {
$response .= " \n === RDAP QUERY FAILED === \n \n " ;
$response .= " RDAP URL: { $fullRdapUrl } \n " ;
$response .= " HTTP Status: { $httpCode } \n " ;
if ( $curlError ) {
$response .= " cURL Error: { $curlError } \n " ;
}
// Show detailed cURL info
$response .= " \n cURL Debug Info: \n " ;
$response .= " - Total Time: " . ( $curlInfo [ 'total_time' ] ? ? 'N/A' ) . " s \n " ;
$response .= " - Name Lookup Time: " . ( $curlInfo [ 'namelookup_time' ] ? ? 'N/A' ) . " s \n " ;
$response .= " - Connect Time: " . ( $curlInfo [ 'connect_time' ] ? ? 'N/A' ) . " s \n " ;
$response .= " - Primary IP: " . ( $curlInfo [ 'primary_ip' ] ? ? 'N/A' ) . " \n " ;
if ( $httpCode === 0 ) {
$response .= " \n Note: HTTP Status 0 usually means: \n " ;
$response .= " - SSL certificate verification failed \n " ;
$response .= " - Connection timeout \n " ;
$response .= " - DNS resolution failed \n " ;
$response .= " - URL is malformed \n " ;
}
$response .= " \n Error: Could not retrieve RDAP data \n \n " ;
}
}
// If RDAP failed or not available, query WHOIS
if ( ! $rdapSucceeded && $whoisServer ) {
if ( $rdapUrl ) {
$response .= " \n \n === WHOIS FALLBACK (RDAP Failed) === \n \n " ;
} else {
$response = " === WHOIS QUERY === \n \n " ;
$server = $whoisServer ;
}
$response .= " WHOIS Server: { $whoisServer } \n \n " ;
$response .= " --- WHOIS TEXT RESPONSE --- \n \n " ;
$fp = @ fsockopen ( $whoisServer , 43 , $errno , $errstr , 10 );
if ( $fp ) {
fputs ( $fp , $domain . " \r \n " );
$whoisResponse = '' ;
while ( ! feof ( $fp )) {
$whoisResponse .= fgets ( $fp , 128 );
}
fclose ( $fp );
$response .= $whoisResponse ;
// Check if domain is not found/available
$whoisResponseLower = strtolower ( $whoisResponse );
if ( preg_match ( '/not found|no match|no entries found|no data found|domain not found|no such domain|not registered|available for registration/i' , $whoisResponseLower )) {
$response .= " \n \n === DOMAIN STATUS DETECTED === \n " ;
$response .= " ✓ Domain is AVAILABLE (not registered) \n " ;
$parsedData [] = [ 'key' => 'Status' , 'value' => 'AVAILABLE' ];
$parsedData [] = [ 'key' => 'Registrar' , 'value' => 'Not Registered' ];
} else {
// Parse key-value pairs from WHOIS
$lines = explode ( " \n " , $whoisResponse );
foreach ( $lines as $line ) {
$line = trim ( $line );
if ( empty ( $line ) || $line [ 0 ] === '%' || $line [ 0 ] === '#' ) {
continue ;
}
if ( strpos ( $line , ':' ) !== false ) {
list ( $key , $value ) = explode ( ':' , $line , 2 );
$parsedData [] = [
'key' => trim ( $key ),
'value' => trim ( $value )
];
}
}
}
} else {
$response .= " Error: Could not connect to WHOIS server: $errstr ( $errno ) " ;
}
}
2025-10-21 15:40:54 +03:00
// Get parsed info using WhoisService (this will use the actual logic with WHOIS fallback)
2025-10-08 14:23:07 +03:00
$info = $whoisService -> getDomainInfo ( $domain );
2025-10-21 15:40:54 +03:00
// If we got info from WhoisService but debug tool didn't show RDAP success,
// it means WhoisService used WHOIS fallback for expiration date
$whoisFallbackUsed = false ;
if ( $info && ! $rdapSucceeded ) {
$whoisFallbackUsed = true ;
} elseif ( $info && $rdapSucceeded && empty ( $info [ 'expiration_date' ])) {
// RDAP succeeded but no expiration date, WhoisService should have tried WHOIS fallback
$whoisFallbackUsed = true ;
}
// Add note about WHOIS fallback if it was used
if ( $whoisFallbackUsed ) {
$response .= " \n \n === WHOIS FALLBACK USED BY WHOISSERVICE === \n \n " ;
$response .= " Note: The WhoisService automatically used WHOIS fallback to get missing data (like expiration dates). \n " ;
$response .= " This is the actual data that will be saved to the database. \n \n " ;
}
2025-10-21 15:16:56 +03:00
// Log debug results
$logger -> info ( 'WHOIS debug completed' , [
'domain' => $domain ,
'tld' => $tld ,
'server' => $server ,
'rdap_succeeded' => $rdapSucceeded ,
2025-10-21 15:40:54 +03:00
'whois_fallback_used' => $whoisFallbackUsed ,
2025-10-21 15:16:56 +03:00
'parsed_status' => $info [ 'status' ] ? ? 'unknown' ,
'parsed_registrar' => $info [ 'registrar' ] ? ? 'unknown' ,
'parsed_expiration' => $info [ 'expiration_date' ] ? ? 'unknown' ,
'user_id' => \Core\Auth :: id ()
]);
2025-10-08 14:23:07 +03:00
$this -> view ( 'debug/whois' , [
'domain' => $domain ,
'server' => $server ,
'tld' => $tld ,
'response' => $response ,
'parsedData' => $parsedData ,
'info' => $info ,
'title' => 'WHOIS Debug - ' . $domain
]);
}
}