deploy: Produktions-Update 2026-01-18 - Performance Improvements and Bug Fixes
This commit is contained in:
161
api/dns-lookup.php
Normal file
161
api/dns-lookup.php
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* HexaDNS - DNS Lookup API
|
||||||
|
*
|
||||||
|
* Führt echte DNS-Abfragen durch und gibt die Ergebnisse als JSON zurück.
|
||||||
|
*
|
||||||
|
* Verwendung: GET /api/dns-lookup.php?domain=example.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
// CORS Headers für Frontend-Zugriff
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
header('Access-Control-Allow-Origin: *');
|
||||||
|
header('Access-Control-Allow-Methods: GET, OPTIONS');
|
||||||
|
header('Access-Control-Allow-Headers: Content-Type');
|
||||||
|
|
||||||
|
// Preflight request handling
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||||
|
http_response_code(200);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nur GET-Anfragen erlauben
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
||||||
|
http_response_code(405);
|
||||||
|
echo json_encode(['error' => 'Nur GET-Anfragen erlaubt']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Domain-Parameter prüfen
|
||||||
|
$domain = isset($_GET['domain']) ? trim($_GET['domain']) : '';
|
||||||
|
|
||||||
|
if (empty($domain)) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'Domain-Parameter fehlt']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Domain validieren (einfache Prüfung)
|
||||||
|
if (!preg_match('/^[a-zA-Z0-9][a-zA-Z0-9\-\.]*\.[a-zA-Z]{2,}$/', $domain)) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'Ungültiges Domain-Format']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNS-Abfrage durchführen
|
||||||
|
$startTime = microtime(true);
|
||||||
|
$result = performDnsLookup($domain);
|
||||||
|
$queryTime = round((microtime(true) - $startTime) * 1000, 2);
|
||||||
|
|
||||||
|
// Ergebnis zurückgeben
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'domain' => $domain,
|
||||||
|
'query_time_ms' => $queryTime,
|
||||||
|
'timestamp' => date('Y-m-d H:i:s'),
|
||||||
|
'records' => $result
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Führt DNS-Lookup für verschiedene Record-Typen durch
|
||||||
|
*/
|
||||||
|
function performDnsLookup(string $domain): array {
|
||||||
|
$records = [
|
||||||
|
'A' => [],
|
||||||
|
'AAAA' => [],
|
||||||
|
'MX' => [],
|
||||||
|
'NS' => [],
|
||||||
|
'TXT' => [],
|
||||||
|
'CNAME' => [],
|
||||||
|
'SOA' => []
|
||||||
|
];
|
||||||
|
|
||||||
|
// A-Records (IPv4)
|
||||||
|
$aRecords = @dns_get_record($domain, DNS_A);
|
||||||
|
if ($aRecords) {
|
||||||
|
foreach ($aRecords as $record) {
|
||||||
|
$records['A'][] = [
|
||||||
|
'ip' => $record['ip'],
|
||||||
|
'ttl' => $record['ttl']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AAAA-Records (IPv6)
|
||||||
|
$aaaaRecords = @dns_get_record($domain, DNS_AAAA);
|
||||||
|
if ($aaaaRecords) {
|
||||||
|
foreach ($aaaaRecords as $record) {
|
||||||
|
$records['AAAA'][] = [
|
||||||
|
'ipv6' => $record['ipv6'],
|
||||||
|
'ttl' => $record['ttl']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MX-Records (Mail)
|
||||||
|
$mxRecords = @dns_get_record($domain, DNS_MX);
|
||||||
|
if ($mxRecords) {
|
||||||
|
foreach ($mxRecords as $record) {
|
||||||
|
$records['MX'][] = [
|
||||||
|
'target' => $record['target'],
|
||||||
|
'priority' => $record['pri'],
|
||||||
|
'ttl' => $record['ttl']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
// Nach Priorität sortieren
|
||||||
|
usort($records['MX'], fn($a, $b) => $a['priority'] <=> $b['priority']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NS-Records (Nameserver)
|
||||||
|
$nsRecords = @dns_get_record($domain, DNS_NS);
|
||||||
|
if ($nsRecords) {
|
||||||
|
foreach ($nsRecords as $record) {
|
||||||
|
$records['NS'][] = [
|
||||||
|
'target' => $record['target'],
|
||||||
|
'ttl' => $record['ttl']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TXT-Records
|
||||||
|
$txtRecords = @dns_get_record($domain, DNS_TXT);
|
||||||
|
if ($txtRecords) {
|
||||||
|
foreach ($txtRecords as $record) {
|
||||||
|
$records['TXT'][] = [
|
||||||
|
'txt' => $record['txt'],
|
||||||
|
'ttl' => $record['ttl']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CNAME-Records
|
||||||
|
$cnameRecords = @dns_get_record($domain, DNS_CNAME);
|
||||||
|
if ($cnameRecords) {
|
||||||
|
foreach ($cnameRecords as $record) {
|
||||||
|
$records['CNAME'][] = [
|
||||||
|
'target' => $record['target'],
|
||||||
|
'ttl' => $record['ttl']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SOA-Record (Start of Authority)
|
||||||
|
$soaRecords = @dns_get_record($domain, DNS_SOA);
|
||||||
|
if ($soaRecords) {
|
||||||
|
foreach ($soaRecords as $record) {
|
||||||
|
$records['SOA'][] = [
|
||||||
|
'mname' => $record['mname'] ?? '',
|
||||||
|
'rname' => $record['rname'] ?? '',
|
||||||
|
'serial' => $record['serial'] ?? 0,
|
||||||
|
'refresh' => $record['refresh'] ?? 0,
|
||||||
|
'retry' => $record['retry'] ?? 0,
|
||||||
|
'expire' => $record['expire'] ?? 0,
|
||||||
|
'minimum_ttl' => $record['minimum-ttl'] ?? 0,
|
||||||
|
'ttl' => $record['ttl']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Leere Arrays entfernen
|
||||||
|
return array_filter($records, fn($arr) => !empty($arr));
|
||||||
|
}
|
||||||
175
api/dns-propagation.php
Normal file
175
api/dns-propagation.php
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* HexaDNS - DNS Propagation Check API
|
||||||
|
*
|
||||||
|
* Prüft DNS-Records bei verschiedenen öffentlichen DNS-Servern
|
||||||
|
*
|
||||||
|
* Verwendung: GET /api/dns-propagation.php?domain=example.com&type=A
|
||||||
|
*/
|
||||||
|
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
header('Access-Control-Allow-Origin: *');
|
||||||
|
header('Access-Control-Allow-Methods: GET, OPTIONS');
|
||||||
|
header('Access-Control-Allow-Headers: Content-Type');
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||||
|
http_response_code(200);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Öffentliche DNS-Server für Propagation-Check
|
||||||
|
$dnsServers = [
|
||||||
|
['name' => 'Google', 'ip' => '8.8.8.8', 'location' => 'Global'],
|
||||||
|
['name' => 'Google Secondary', 'ip' => '8.8.4.4', 'location' => 'Global'],
|
||||||
|
['name' => 'Cloudflare', 'ip' => '1.1.1.1', 'location' => 'Global'],
|
||||||
|
['name' => 'Cloudflare Secondary', 'ip' => '1.0.0.1', 'location' => 'Global'],
|
||||||
|
['name' => 'Quad9', 'ip' => '9.9.9.9', 'location' => 'Global'],
|
||||||
|
['name' => 'OpenDNS', 'ip' => '208.67.222.222', 'location' => 'USA'],
|
||||||
|
['name' => 'Comodo', 'ip' => '8.26.56.26', 'location' => 'USA'],
|
||||||
|
['name' => 'Level3', 'ip' => '4.2.2.1', 'location' => 'USA'],
|
||||||
|
];
|
||||||
|
|
||||||
|
$domain = isset($_GET['domain']) ? trim($_GET['domain']) : '';
|
||||||
|
$type = isset($_GET['type']) ? strtoupper(trim($_GET['type'])) : 'A';
|
||||||
|
|
||||||
|
if (empty($domain)) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'Domain-Parameter fehlt']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!preg_match('/^[a-zA-Z0-9][a-zA-Z0-9\-\.]*\.[a-zA-Z]{2,}$/', $domain)) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'Ungültiges Domain-Format']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erlaubte Record-Typen
|
||||||
|
$allowedTypes = ['A', 'AAAA', 'MX', 'NS', 'TXT', 'CNAME'];
|
||||||
|
if (!in_array($type, $allowedTypes)) {
|
||||||
|
$type = 'A';
|
||||||
|
}
|
||||||
|
|
||||||
|
$results = [];
|
||||||
|
$startTime = microtime(true);
|
||||||
|
|
||||||
|
foreach ($dnsServers as $server) {
|
||||||
|
$serverResult = [
|
||||||
|
'server' => $server['name'],
|
||||||
|
'ip' => $server['ip'],
|
||||||
|
'location' => $server['location'],
|
||||||
|
'records' => [],
|
||||||
|
'status' => 'success',
|
||||||
|
'response_time' => 0
|
||||||
|
];
|
||||||
|
|
||||||
|
$queryStart = microtime(true);
|
||||||
|
|
||||||
|
// DNS-Abfrage mit spezifischem Server via dig (falls verfügbar) oder dns_get_record
|
||||||
|
$records = queryDnsServer($domain, $type, $server['ip']);
|
||||||
|
|
||||||
|
$serverResult['response_time'] = round((microtime(true) - $queryStart) * 1000, 2);
|
||||||
|
$serverResult['records'] = $records;
|
||||||
|
|
||||||
|
if (empty($records)) {
|
||||||
|
$serverResult['status'] = 'no_records';
|
||||||
|
}
|
||||||
|
|
||||||
|
$results[] = $serverResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
$totalTime = round((microtime(true) - $startTime) * 1000, 2);
|
||||||
|
|
||||||
|
// Propagation-Status berechnen
|
||||||
|
$propagationStatus = calculatePropagationStatus($results);
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'domain' => $domain,
|
||||||
|
'record_type' => $type,
|
||||||
|
'propagation_status' => $propagationStatus,
|
||||||
|
'total_time_ms' => $totalTime,
|
||||||
|
'timestamp' => date('Y-m-d H:i:s'),
|
||||||
|
'servers' => $results
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DNS-Abfrage bei spezifischem Server
|
||||||
|
*/
|
||||||
|
function queryDnsServer(string $domain, string $type, string $server): array {
|
||||||
|
$records = [];
|
||||||
|
|
||||||
|
// Versuche zuerst dig zu verwenden (genauer)
|
||||||
|
$digResult = @shell_exec("dig @{$server} {$domain} {$type} +short +time=2 +tries=1 2>/dev/null");
|
||||||
|
|
||||||
|
if ($digResult !== null && trim($digResult) !== '') {
|
||||||
|
$lines = array_filter(explode("\n", trim($digResult)));
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
$records[] = trim($line);
|
||||||
|
}
|
||||||
|
return $records;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback auf PHP dns_get_record (verwendet System-DNS)
|
||||||
|
$dnsType = constant('DNS_' . $type);
|
||||||
|
$result = @dns_get_record($domain, $dnsType);
|
||||||
|
|
||||||
|
if ($result) {
|
||||||
|
foreach ($result as $record) {
|
||||||
|
switch ($type) {
|
||||||
|
case 'A':
|
||||||
|
$records[] = $record['ip'] ?? '';
|
||||||
|
break;
|
||||||
|
case 'AAAA':
|
||||||
|
$records[] = $record['ipv6'] ?? '';
|
||||||
|
break;
|
||||||
|
case 'MX':
|
||||||
|
$records[] = ($record['pri'] ?? '') . ' ' . ($record['target'] ?? '');
|
||||||
|
break;
|
||||||
|
case 'NS':
|
||||||
|
case 'CNAME':
|
||||||
|
$records[] = $record['target'] ?? '';
|
||||||
|
break;
|
||||||
|
case 'TXT':
|
||||||
|
$records[] = $record['txt'] ?? '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_filter($records);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Berechnet den Propagation-Status
|
||||||
|
*/
|
||||||
|
function calculatePropagationStatus(array $results): array {
|
||||||
|
$totalServers = count($results);
|
||||||
|
$serversWithRecords = 0;
|
||||||
|
$allRecords = [];
|
||||||
|
|
||||||
|
foreach ($results as $result) {
|
||||||
|
if (!empty($result['records'])) {
|
||||||
|
$serversWithRecords++;
|
||||||
|
foreach ($result['records'] as $record) {
|
||||||
|
$allRecords[] = $record;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Einzigartige Records
|
||||||
|
$uniqueRecords = array_unique($allRecords);
|
||||||
|
|
||||||
|
// Konsistenz prüfen (haben alle Server die gleichen Records?)
|
||||||
|
$isConsistent = count($uniqueRecords) <= 1 || $serversWithRecords === 0;
|
||||||
|
|
||||||
|
$percentage = $totalServers > 0 ? round(($serversWithRecords / $totalServers) * 100) : 0;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'percentage' => $percentage,
|
||||||
|
'servers_responding' => $serversWithRecords,
|
||||||
|
'total_servers' => $totalServers,
|
||||||
|
'is_consistent' => $isConsistent,
|
||||||
|
'unique_values' => array_values($uniqueRecords)
|
||||||
|
];
|
||||||
|
}
|
||||||
179
api/ping-check.php
Normal file
179
api/ping-check.php
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* HexaDNS - Ping/Verfügbarkeitstest API
|
||||||
|
*
|
||||||
|
* Prüft die Erreichbarkeit einer Domain
|
||||||
|
*
|
||||||
|
* Verwendung: GET /api/ping-check.php?domain=example.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
header('Access-Control-Allow-Origin: *');
|
||||||
|
header('Access-Control-Allow-Methods: GET, OPTIONS');
|
||||||
|
header('Access-Control-Allow-Headers: Content-Type');
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||||
|
http_response_code(200);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$domain = isset($_GET['domain']) ? trim($_GET['domain']) : '';
|
||||||
|
|
||||||
|
if (empty($domain)) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'Domain-Parameter fehlt']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protokoll und Pfad entfernen
|
||||||
|
$domain = preg_replace('/^(https?:\/\/)?/', '', $domain);
|
||||||
|
$domain = explode('/', $domain)[0];
|
||||||
|
|
||||||
|
if (!preg_match('/^[a-zA-Z0-9][a-zA-Z0-9\-\.]*\.[a-zA-Z]{2,}$/', $domain)) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'Ungültiges Domain-Format']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$startTime = microtime(true);
|
||||||
|
$results = performConnectivityCheck($domain);
|
||||||
|
$totalTime = round((microtime(true) - $startTime) * 1000, 2);
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'domain' => $domain,
|
||||||
|
'total_time_ms' => $totalTime,
|
||||||
|
'timestamp' => date('Y-m-d H:i:s'),
|
||||||
|
'results' => $results
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Führt verschiedene Erreichbarkeitstests durch
|
||||||
|
*/
|
||||||
|
function performConnectivityCheck(string $domain): array {
|
||||||
|
$results = [
|
||||||
|
'dns_resolution' => checkDnsResolution($domain),
|
||||||
|
'icmp_ping' => checkIcmpPing($domain),
|
||||||
|
'http' => checkHttpConnection($domain, false),
|
||||||
|
'https' => checkHttpConnection($domain, true),
|
||||||
|
'overall_status' => 'offline'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Overall-Status bestimmen
|
||||||
|
if ($results['https']['reachable'] || $results['http']['reachable']) {
|
||||||
|
$results['overall_status'] = 'online';
|
||||||
|
} elseif ($results['icmp_ping']['reachable']) {
|
||||||
|
$results['overall_status'] = 'partial';
|
||||||
|
} elseif ($results['dns_resolution']['resolved']) {
|
||||||
|
$results['overall_status'] = 'dns_only';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DNS-Auflösung prüfen
|
||||||
|
*/
|
||||||
|
function checkDnsResolution(string $domain): array {
|
||||||
|
$start = microtime(true);
|
||||||
|
$ip = gethostbyname($domain);
|
||||||
|
$time = round((microtime(true) - $start) * 1000, 2);
|
||||||
|
|
||||||
|
$resolved = $ip !== $domain;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'resolved' => $resolved,
|
||||||
|
'ip' => $resolved ? $ip : null,
|
||||||
|
'response_time_ms' => $time
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ICMP Ping (falls verfügbar)
|
||||||
|
*/
|
||||||
|
function checkIcmpPing(string $domain): array {
|
||||||
|
$result = [
|
||||||
|
'reachable' => false,
|
||||||
|
'response_time_ms' => null,
|
||||||
|
'packet_loss' => null
|
||||||
|
];
|
||||||
|
|
||||||
|
// Versuche ping-Kommando
|
||||||
|
$pingResult = @shell_exec("ping -c 3 -W 2 {$domain} 2>/dev/null");
|
||||||
|
|
||||||
|
if ($pingResult) {
|
||||||
|
// Prüfe auf erfolgreiche Antworten
|
||||||
|
if (preg_match('/(\d+)% packet loss/', $pingResult, $lossMatch)) {
|
||||||
|
$result['packet_loss'] = (int)$lossMatch[1];
|
||||||
|
$result['reachable'] = $result['packet_loss'] < 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Durchschnittliche Zeit extrahieren
|
||||||
|
if (preg_match('/avg.*?=.*?[\d.]+\/([\d.]+)\//', $pingResult, $timeMatch)) {
|
||||||
|
$result['response_time_ms'] = (float)$timeMatch[1];
|
||||||
|
} elseif (preg_match('/time[=<]([\d.]+)\s*ms/', $pingResult, $timeMatch)) {
|
||||||
|
$result['response_time_ms'] = (float)$timeMatch[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP(S)-Verbindung prüfen
|
||||||
|
*/
|
||||||
|
function checkHttpConnection(string $domain, bool $https = false): array {
|
||||||
|
$protocol = $https ? 'https' : 'http';
|
||||||
|
$port = $https ? 443 : 80;
|
||||||
|
$url = "{$protocol}://{$domain}";
|
||||||
|
|
||||||
|
$result = [
|
||||||
|
'reachable' => false,
|
||||||
|
'status_code' => null,
|
||||||
|
'response_time_ms' => null,
|
||||||
|
'redirect_url' => null,
|
||||||
|
'server' => null
|
||||||
|
];
|
||||||
|
|
||||||
|
$start = microtime(true);
|
||||||
|
|
||||||
|
// cURL verwenden
|
||||||
|
$ch = curl_init();
|
||||||
|
curl_setopt_array($ch, [
|
||||||
|
CURLOPT_URL => $url,
|
||||||
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
|
CURLOPT_HEADER => true,
|
||||||
|
CURLOPT_NOBODY => true,
|
||||||
|
CURLOPT_TIMEOUT => 10,
|
||||||
|
CURLOPT_CONNECTTIMEOUT => 5,
|
||||||
|
CURLOPT_FOLLOWLOCATION => false,
|
||||||
|
CURLOPT_SSL_VERIFYPEER => false,
|
||||||
|
CURLOPT_SSL_VERIFYHOST => 0,
|
||||||
|
CURLOPT_USERAGENT => 'HexaDNS Ping Check/1.0'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
$result['response_time_ms'] = round((microtime(true) - $start) * 1000, 2);
|
||||||
|
|
||||||
|
if ($response !== false) {
|
||||||
|
$result['status_code'] = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
$result['reachable'] = $result['status_code'] > 0;
|
||||||
|
|
||||||
|
// Redirect-URL
|
||||||
|
$redirectUrl = curl_getinfo($ch, CURLINFO_REDIRECT_URL);
|
||||||
|
if (!empty($redirectUrl)) {
|
||||||
|
$result['redirect_url'] = $redirectUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server-Header
|
||||||
|
if (preg_match('/Server:\s*([^\r\n]+)/i', $response, $serverMatch)) {
|
||||||
|
$result['server'] = trim($serverMatch[1]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$result['error'] = curl_error($ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
209
api/reverse-dns.php
Normal file
209
api/reverse-dns.php
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* HexaDNS - Reverse DNS Lookup API
|
||||||
|
*
|
||||||
|
* Löst eine IP-Adresse zu einem Hostnamen auf
|
||||||
|
*
|
||||||
|
* Verwendung: GET /api/reverse-dns.php?ip=8.8.8.8
|
||||||
|
*/
|
||||||
|
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
header('Access-Control-Allow-Origin: *');
|
||||||
|
header('Access-Control-Allow-Methods: GET, OPTIONS');
|
||||||
|
header('Access-Control-Allow-Headers: Content-Type');
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||||
|
http_response_code(200);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ip = isset($_GET['ip']) ? trim($_GET['ip']) : '';
|
||||||
|
|
||||||
|
if (empty($ip)) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'IP-Parameter fehlt']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPv4 oder IPv6 validieren
|
||||||
|
$isIPv4 = filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
|
||||||
|
$isIPv6 = filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
|
||||||
|
|
||||||
|
if (!$isIPv4 && !$isIPv6) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'Ungültiges IP-Format']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$startTime = microtime(true);
|
||||||
|
$result = performReverseLookup($ip, $isIPv6 ? 'IPv6' : 'IPv4');
|
||||||
|
$queryTime = round((microtime(true) - $startTime) * 1000, 2);
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'ip' => $ip,
|
||||||
|
'ip_version' => $isIPv6 ? 'IPv6' : 'IPv4',
|
||||||
|
'query_time_ms' => $queryTime,
|
||||||
|
'timestamp' => date('Y-m-d H:i:s'),
|
||||||
|
'result' => $result
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Führt Reverse DNS Lookup durch
|
||||||
|
*/
|
||||||
|
function performReverseLookup(string $ip, string $version): array {
|
||||||
|
$result = [
|
||||||
|
'hostname' => null,
|
||||||
|
'ptr_record' => null,
|
||||||
|
'additional_info' => []
|
||||||
|
];
|
||||||
|
|
||||||
|
// PHP gethostbyaddr
|
||||||
|
$hostname = @gethostbyaddr($ip);
|
||||||
|
|
||||||
|
if ($hostname && $hostname !== $ip) {
|
||||||
|
$result['hostname'] = $hostname;
|
||||||
|
|
||||||
|
// Verifizieren durch Forward-Lookup
|
||||||
|
$forwardIp = gethostbyname($hostname);
|
||||||
|
$result['forward_verified'] = ($forwardIp === $ip);
|
||||||
|
|
||||||
|
// Zusätzliche Infos über den Host sammeln
|
||||||
|
$result['additional_info'] = getHostInfo($hostname);
|
||||||
|
} else {
|
||||||
|
$result['error'] = 'Kein PTR-Record gefunden';
|
||||||
|
}
|
||||||
|
|
||||||
|
// PTR-Record direkt abfragen
|
||||||
|
$ptrRecord = getPtrRecord($ip, $version);
|
||||||
|
if ($ptrRecord) {
|
||||||
|
$result['ptr_record'] = $ptrRecord;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IP-Info (GeoIP wenn verfügbar, sonst Basic-Infos)
|
||||||
|
$result['ip_info'] = getIpInfo($ip);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PTR-Record direkt abfragen
|
||||||
|
*/
|
||||||
|
function getPtrRecord(string $ip, string $version): ?string {
|
||||||
|
if ($version === 'IPv4') {
|
||||||
|
// IPv4: Reverse die Oktette
|
||||||
|
$parts = array_reverse(explode('.', $ip));
|
||||||
|
$ptrDomain = implode('.', $parts) . '.in-addr.arpa';
|
||||||
|
} else {
|
||||||
|
// IPv6: Komplexer - jedes Nibble umkehren
|
||||||
|
$expanded = expandIPv6($ip);
|
||||||
|
$nibbles = str_replace(':', '', $expanded);
|
||||||
|
$reversed = implode('.', array_reverse(str_split($nibbles)));
|
||||||
|
$ptrDomain = $reversed . '.ip6.arpa';
|
||||||
|
}
|
||||||
|
|
||||||
|
$records = @dns_get_record($ptrDomain, DNS_PTR);
|
||||||
|
|
||||||
|
if ($records && !empty($records[0]['target'])) {
|
||||||
|
return $records[0]['target'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expandiert eine IPv6-Adresse
|
||||||
|
*/
|
||||||
|
function expandIPv6(string $ip): string {
|
||||||
|
// Ersetze :: mit der richtigen Anzahl von 0000
|
||||||
|
if (strpos($ip, '::') !== false) {
|
||||||
|
$parts = explode('::', $ip);
|
||||||
|
$left = $parts[0] ? explode(':', $parts[0]) : [];
|
||||||
|
$right = isset($parts[1]) && $parts[1] ? explode(':', $parts[1]) : [];
|
||||||
|
$missing = 8 - count($left) - count($right);
|
||||||
|
$middle = array_fill(0, $missing, '0000');
|
||||||
|
$all = array_merge($left, $middle, $right);
|
||||||
|
} else {
|
||||||
|
$all = explode(':', $ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jedes Segment auf 4 Zeichen auffüllen
|
||||||
|
$all = array_map(function($segment) {
|
||||||
|
return str_pad($segment, 4, '0', STR_PAD_LEFT);
|
||||||
|
}, $all);
|
||||||
|
|
||||||
|
return implode(':', $all);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sammelt Infos über einen Hostnamen
|
||||||
|
*/
|
||||||
|
function getHostInfo(string $hostname): array {
|
||||||
|
$info = [];
|
||||||
|
|
||||||
|
// Domain-Teile analysieren
|
||||||
|
$parts = explode('.', $hostname);
|
||||||
|
$tld = end($parts);
|
||||||
|
|
||||||
|
$info['tld'] = $tld;
|
||||||
|
|
||||||
|
// Bekannte Hosting-Provider erkennen
|
||||||
|
$providerPatterns = [
|
||||||
|
'amazonaws.com' => 'Amazon AWS',
|
||||||
|
'googleusercontent.com' => 'Google Cloud',
|
||||||
|
'cloudfront.net' => 'Amazon CloudFront',
|
||||||
|
'azure' => 'Microsoft Azure',
|
||||||
|
'hetzner' => 'Hetzner',
|
||||||
|
'ovh' => 'OVH',
|
||||||
|
'digitalocean' => 'DigitalOcean',
|
||||||
|
'linode' => 'Linode',
|
||||||
|
'vultr' => 'Vultr',
|
||||||
|
'contabo' => 'Contabo',
|
||||||
|
'netcup' => 'Netcup',
|
||||||
|
'strato' => 'Strato',
|
||||||
|
'ionos' => 'IONOS',
|
||||||
|
'1und1' => '1&1',
|
||||||
|
'telekom' => 'Deutsche Telekom',
|
||||||
|
'vodafone' => 'Vodafone',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($providerPatterns as $pattern => $provider) {
|
||||||
|
if (stripos($hostname, $pattern) !== false) {
|
||||||
|
$info['provider'] = $provider;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basis-Infos zur IP
|
||||||
|
*/
|
||||||
|
function getIpInfo(string $ip): array {
|
||||||
|
$info = [
|
||||||
|
'type' => 'unknown'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Private IP-Bereiche prüfen
|
||||||
|
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
|
||||||
|
if (preg_match('/^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.)/', $ip)) {
|
||||||
|
$info['type'] = 'private';
|
||||||
|
} elseif (preg_match('/^(127\.)/', $ip)) {
|
||||||
|
$info['type'] = 'loopback';
|
||||||
|
} else {
|
||||||
|
$info['type'] = 'public';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// IPv6
|
||||||
|
if (preg_match('/^(fc|fd)/i', $ip)) {
|
||||||
|
$info['type'] = 'private';
|
||||||
|
} elseif (preg_match('/^::1$/', $ip) || preg_match('/^fe80:/i', $ip)) {
|
||||||
|
$info['type'] = 'loopback/link-local';
|
||||||
|
} else {
|
||||||
|
$info['type'] = 'public';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $info;
|
||||||
|
}
|
||||||
164
api/ssl-check.php
Normal file
164
api/ssl-check.php
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* HexaDNS - SSL Certificate Check API
|
||||||
|
*
|
||||||
|
* Prüft SSL-Zertifikat-Informationen einer Domain
|
||||||
|
*
|
||||||
|
* Verwendung: GET /api/ssl-check.php?domain=example.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
header('Access-Control-Allow-Origin: *');
|
||||||
|
header('Access-Control-Allow-Methods: GET, OPTIONS');
|
||||||
|
header('Access-Control-Allow-Headers: Content-Type');
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||||
|
http_response_code(200);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$domain = isset($_GET['domain']) ? trim($_GET['domain']) : '';
|
||||||
|
|
||||||
|
if (empty($domain)) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'Domain-Parameter fehlt']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protokoll und Pfad entfernen
|
||||||
|
$domain = preg_replace('/^(https?:\/\/)?/', '', $domain);
|
||||||
|
$domain = explode('/', $domain)[0];
|
||||||
|
$domain = explode(':', $domain)[0]; // Port entfernen
|
||||||
|
|
||||||
|
if (!preg_match('/^[a-zA-Z0-9][a-zA-Z0-9\-\.]*\.[a-zA-Z]{2,}$/', $domain)) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'Ungültiges Domain-Format']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$startTime = microtime(true);
|
||||||
|
$sslData = checkSslCertificate($domain);
|
||||||
|
$queryTime = round((microtime(true) - $startTime) * 1000, 2);
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => $sslData['success'],
|
||||||
|
'domain' => $domain,
|
||||||
|
'query_time_ms' => $queryTime,
|
||||||
|
'timestamp' => date('Y-m-d H:i:s'),
|
||||||
|
'ssl' => $sslData
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prüft SSL-Zertifikat einer Domain
|
||||||
|
*/
|
||||||
|
function checkSslCertificate(string $domain): array {
|
||||||
|
$result = [
|
||||||
|
'success' => false,
|
||||||
|
'has_ssl' => false,
|
||||||
|
'is_valid' => false,
|
||||||
|
'error' => null,
|
||||||
|
'certificate' => null
|
||||||
|
];
|
||||||
|
|
||||||
|
// Stream Context für SSL
|
||||||
|
$context = stream_context_create([
|
||||||
|
'ssl' => [
|
||||||
|
'capture_peer_cert' => true,
|
||||||
|
'verify_peer' => false,
|
||||||
|
'verify_peer_name' => false,
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Verbindung herstellen
|
||||||
|
$socket = @stream_socket_client(
|
||||||
|
"ssl://{$domain}:443",
|
||||||
|
$errno,
|
||||||
|
$errstr,
|
||||||
|
10, // Timeout
|
||||||
|
STREAM_CLIENT_CONNECT,
|
||||||
|
$context
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!$socket) {
|
||||||
|
// Versuche ohne SSL (um zu prüfen ob Server erreichbar)
|
||||||
|
$httpSocket = @fsockopen($domain, 80, $errno, $errstr, 5);
|
||||||
|
if ($httpSocket) {
|
||||||
|
fclose($httpSocket);
|
||||||
|
$result['error'] = 'Kein SSL-Zertifikat auf Port 443 gefunden';
|
||||||
|
} else {
|
||||||
|
$result['error'] = "Verbindung fehlgeschlagen: {$errstr}";
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result['has_ssl'] = true;
|
||||||
|
$result['success'] = true;
|
||||||
|
|
||||||
|
// Zertifikat extrahieren
|
||||||
|
$params = stream_context_get_params($socket);
|
||||||
|
$cert = $params['options']['ssl']['peer_certificate'] ?? null;
|
||||||
|
|
||||||
|
if ($cert) {
|
||||||
|
$certInfo = openssl_x509_parse($cert);
|
||||||
|
|
||||||
|
if ($certInfo) {
|
||||||
|
$validFrom = $certInfo['validFrom_time_t'] ?? 0;
|
||||||
|
$validTo = $certInfo['validTo_time_t'] ?? 0;
|
||||||
|
$now = time();
|
||||||
|
|
||||||
|
$isExpired = $now > $validTo;
|
||||||
|
$isNotYetValid = $now < $validFrom;
|
||||||
|
$result['is_valid'] = !$isExpired && !$isNotYetValid;
|
||||||
|
|
||||||
|
// Tage bis Ablauf
|
||||||
|
$daysUntilExpiry = floor(($validTo - $now) / 86400);
|
||||||
|
|
||||||
|
// Subject Alternative Names (SANs)
|
||||||
|
$sans = [];
|
||||||
|
if (isset($certInfo['extensions']['subjectAltName'])) {
|
||||||
|
$sanStr = $certInfo['extensions']['subjectAltName'];
|
||||||
|
preg_match_all('/DNS:([^,\s]+)/', $sanStr, $matches);
|
||||||
|
$sans = $matches[1] ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Issuer aufbereiten
|
||||||
|
$issuer = [];
|
||||||
|
if (isset($certInfo['issuer'])) {
|
||||||
|
if (isset($certInfo['issuer']['O'])) $issuer['organization'] = $certInfo['issuer']['O'];
|
||||||
|
if (isset($certInfo['issuer']['CN'])) $issuer['common_name'] = $certInfo['issuer']['CN'];
|
||||||
|
if (isset($certInfo['issuer']['C'])) $issuer['country'] = $certInfo['issuer']['C'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subject aufbereiten
|
||||||
|
$subject = [];
|
||||||
|
if (isset($certInfo['subject'])) {
|
||||||
|
if (isset($certInfo['subject']['CN'])) $subject['common_name'] = $certInfo['subject']['CN'];
|
||||||
|
if (isset($certInfo['subject']['O'])) $subject['organization'] = $certInfo['subject']['O'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$result['certificate'] = [
|
||||||
|
'subject' => $subject,
|
||||||
|
'issuer' => $issuer,
|
||||||
|
'valid_from' => date('Y-m-d H:i:s', $validFrom),
|
||||||
|
'valid_to' => date('Y-m-d H:i:s', $validTo),
|
||||||
|
'days_until_expiry' => $daysUntilExpiry,
|
||||||
|
'is_expired' => $isExpired,
|
||||||
|
'serial_number' => $certInfo['serialNumberHex'] ?? null,
|
||||||
|
'signature_algorithm' => $certInfo['signatureTypeSN'] ?? null,
|
||||||
|
'san_domains' => $sans,
|
||||||
|
'version' => $certInfo['version'] ?? null,
|
||||||
|
];
|
||||||
|
|
||||||
|
// Warnung wenn bald ablaufend
|
||||||
|
if ($daysUntilExpiry <= 30 && $daysUntilExpiry > 0) {
|
||||||
|
$result['warning'] = "Zertifikat läuft in {$daysUntilExpiry} Tagen ab!";
|
||||||
|
} elseif ($isExpired) {
|
||||||
|
$result['error'] = 'Zertifikat ist abgelaufen!';
|
||||||
|
$result['is_valid'] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose($socket);
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
365
api/whois-lookup.php
Normal file
365
api/whois-lookup.php
Normal file
@@ -0,0 +1,365 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* HexaDNS - WHOIS Lookup API
|
||||||
|
*
|
||||||
|
* Ruft WHOIS-Informationen für eine Domain ab
|
||||||
|
*
|
||||||
|
* Verwendung: GET /api/whois-lookup.php?domain=example.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
header('Access-Control-Allow-Origin: *');
|
||||||
|
header('Access-Control-Allow-Methods: GET, OPTIONS');
|
||||||
|
header('Access-Control-Allow-Headers: Content-Type');
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||||
|
http_response_code(200);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$domain = isset($_GET['domain']) ? trim($_GET['domain']) : '';
|
||||||
|
|
||||||
|
if (empty($domain)) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'Domain-Parameter fehlt']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nur Root-Domain extrahieren
|
||||||
|
$domain = extractRootDomain($domain);
|
||||||
|
|
||||||
|
if (!preg_match('/^[a-zA-Z0-9][a-zA-Z0-9\-]*\.[a-zA-Z]{2,}$/', $domain)) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'Ungültiges Domain-Format']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$startTime = microtime(true);
|
||||||
|
$whoisData = performWhoisLookup($domain);
|
||||||
|
$queryTime = round((microtime(true) - $startTime) * 1000, 2);
|
||||||
|
|
||||||
|
if ($whoisData === null) {
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['error' => 'WHOIS-Abfrage fehlgeschlagen']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'domain' => $domain,
|
||||||
|
'query_time_ms' => $queryTime,
|
||||||
|
'timestamp' => date('Y-m-d H:i:s'),
|
||||||
|
'whois' => $whoisData
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extrahiert die Root-Domain (ohne Subdomain)
|
||||||
|
*/
|
||||||
|
function extractRootDomain(string $domain): string {
|
||||||
|
$domain = strtolower($domain);
|
||||||
|
$domain = preg_replace('/^(https?:\/\/)?(www\.)?/', '', $domain);
|
||||||
|
$domain = explode('/', $domain)[0];
|
||||||
|
|
||||||
|
$parts = explode('.', $domain);
|
||||||
|
if (count($parts) > 2) {
|
||||||
|
// Einfache Logik: nimm die letzten 2 Teile
|
||||||
|
// (funktioniert nicht perfekt für .co.uk etc., aber gut genug)
|
||||||
|
return implode('.', array_slice($parts, -2));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Führt WHOIS-Lookup durch
|
||||||
|
*/
|
||||||
|
function performWhoisLookup(string $domain): ?array {
|
||||||
|
// Primär: Socket-basierte Abfrage (funktioniert ohne shell_exec)
|
||||||
|
$whoisRaw = whoisViaSocket($domain);
|
||||||
|
|
||||||
|
// Fallback: Shell-Kommando (sicher escaped)
|
||||||
|
if (empty($whoisRaw) && function_exists('shell_exec')) {
|
||||||
|
$escapedDomain = escapeshellarg($domain);
|
||||||
|
$whoisRaw = @shell_exec("whois {$escapedDomain} 2>/dev/null");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($whoisRaw)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse WHOIS-Daten
|
||||||
|
return parseWhoisData($whoisRaw, $domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WHOIS-Abfrage über Socket (unabhängig von shell_exec)
|
||||||
|
*/
|
||||||
|
function whoisViaSocket(string $domain): ?string {
|
||||||
|
$whoisServer = getWhoisServer($domain);
|
||||||
|
|
||||||
|
if (!$whoisServer) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = queryWhoisServer($whoisServer, $domain);
|
||||||
|
|
||||||
|
// Prüfe auf Weiterleitungen zu anderen WHOIS-Servern
|
||||||
|
if ($result && preg_match('/Registrar WHOIS Server:\s*(\S+)/i', $result, $matches)) {
|
||||||
|
$referralServer = trim($matches[1]);
|
||||||
|
if ($referralServer && $referralServer !== $whoisServer) {
|
||||||
|
$referralResult = queryWhoisServer($referralServer, $domain);
|
||||||
|
if ($referralResult) {
|
||||||
|
$result = $referralResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abfrage an einen spezifischen WHOIS-Server
|
||||||
|
*/
|
||||||
|
function queryWhoisServer(string $server, string $domain): ?string {
|
||||||
|
$port = 43;
|
||||||
|
$timeout = 10;
|
||||||
|
|
||||||
|
$socket = @fsockopen($server, $port, $errno, $errstr, $timeout);
|
||||||
|
|
||||||
|
if (!$socket) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setze Stream-Timeout
|
||||||
|
stream_set_timeout($socket, $timeout);
|
||||||
|
|
||||||
|
// Sende Anfrage
|
||||||
|
fwrite($socket, $domain . "\r\n");
|
||||||
|
|
||||||
|
// Lese Antwort
|
||||||
|
$response = '';
|
||||||
|
while (!feof($socket)) {
|
||||||
|
$response .= fread($socket, 8192);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose($socket);
|
||||||
|
|
||||||
|
return !empty($response) ? $response : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ermittelt den zuständigen WHOIS-Server für eine TLD
|
||||||
|
*/
|
||||||
|
function getWhoisServer(string $domain): ?string {
|
||||||
|
$parts = explode('.', $domain);
|
||||||
|
$tld = strtolower(end($parts));
|
||||||
|
|
||||||
|
// Bekannte WHOIS-Server nach TLD
|
||||||
|
$whoisServers = [
|
||||||
|
// Generische TLDs
|
||||||
|
'com' => 'whois.verisign-grs.com',
|
||||||
|
'net' => 'whois.verisign-grs.com',
|
||||||
|
'org' => 'whois.pir.org',
|
||||||
|
'info' => 'whois.afilias.net',
|
||||||
|
'biz' => 'whois.biz',
|
||||||
|
'name' => 'whois.nic.name',
|
||||||
|
'mobi' => 'whois.dotmobiregistry.net',
|
||||||
|
'pro' => 'whois.registrypro.pro',
|
||||||
|
'aero' => 'whois.aero',
|
||||||
|
'asia' => 'whois.nic.asia',
|
||||||
|
'cat' => 'whois.nic.cat',
|
||||||
|
'coop' => 'whois.nic.coop',
|
||||||
|
'edu' => 'whois.educause.edu',
|
||||||
|
'gov' => 'whois.dotgov.gov',
|
||||||
|
'int' => 'whois.iana.org',
|
||||||
|
'jobs' => 'whois.nic.jobs',
|
||||||
|
'mil' => 'whois.nic.mil',
|
||||||
|
'museum' => 'whois.museum',
|
||||||
|
'post' => 'whois.dotpostregistry.net',
|
||||||
|
'tel' => 'whois.nic.tel',
|
||||||
|
'travel' => 'whois.nic.travel',
|
||||||
|
'xxx' => 'whois.nic.xxx',
|
||||||
|
|
||||||
|
// Neue gTLDs
|
||||||
|
'app' => 'whois.nic.google',
|
||||||
|
'dev' => 'whois.nic.google',
|
||||||
|
'page' => 'whois.nic.google',
|
||||||
|
'blog' => 'whois.nic.blog',
|
||||||
|
'cloud' => 'whois.nic.cloud',
|
||||||
|
'shop' => 'whois.nic.shop',
|
||||||
|
'store' => 'whois.nic.store',
|
||||||
|
'online' => 'whois.nic.online',
|
||||||
|
'site' => 'whois.nic.site',
|
||||||
|
'website' => 'whois.nic.website',
|
||||||
|
'tech' => 'whois.nic.tech',
|
||||||
|
'io' => 'whois.nic.io',
|
||||||
|
'co' => 'whois.nic.co',
|
||||||
|
'me' => 'whois.nic.me',
|
||||||
|
'tv' => 'whois.nic.tv',
|
||||||
|
'cc' => 'ccwhois.verisign-grs.com',
|
||||||
|
'ws' => 'whois.website.ws',
|
||||||
|
|
||||||
|
// Europäische ccTLDs
|
||||||
|
'de' => 'whois.denic.de',
|
||||||
|
'at' => 'whois.nic.at',
|
||||||
|
'ch' => 'whois.nic.ch',
|
||||||
|
'li' => 'whois.nic.li',
|
||||||
|
'uk' => 'whois.nic.uk',
|
||||||
|
'fr' => 'whois.nic.fr',
|
||||||
|
'it' => 'whois.nic.it',
|
||||||
|
'es' => 'whois.nic.es',
|
||||||
|
'pt' => 'whois.dns.pt',
|
||||||
|
'nl' => 'whois.domain-registry.nl',
|
||||||
|
'be' => 'whois.dns.be',
|
||||||
|
'pl' => 'whois.dns.pl',
|
||||||
|
'cz' => 'whois.nic.cz',
|
||||||
|
'sk' => 'whois.sk-nic.sk',
|
||||||
|
'hu' => 'whois.nic.hu',
|
||||||
|
'ro' => 'whois.rotld.ro',
|
||||||
|
'bg' => 'whois.register.bg',
|
||||||
|
'hr' => 'whois.dns.hr',
|
||||||
|
'si' => 'whois.register.si',
|
||||||
|
'rs' => 'whois.rnids.rs',
|
||||||
|
'gr' => 'grwhois.ics.forth.gr',
|
||||||
|
'dk' => 'whois.punktum.dk',
|
||||||
|
'se' => 'whois.iis.se',
|
||||||
|
'no' => 'whois.norid.no',
|
||||||
|
'fi' => 'whois.fi',
|
||||||
|
'ie' => 'whois.iedr.ie',
|
||||||
|
'eu' => 'whois.eu',
|
||||||
|
'lu' => 'whois.dns.lu',
|
||||||
|
|
||||||
|
// Andere ccTLDs
|
||||||
|
'ru' => 'whois.tcinet.ru',
|
||||||
|
'ua' => 'whois.ua',
|
||||||
|
'us' => 'whois.nic.us',
|
||||||
|
'ca' => 'whois.cira.ca',
|
||||||
|
'mx' => 'whois.mx',
|
||||||
|
'br' => 'whois.registro.br',
|
||||||
|
'ar' => 'whois.nic.ar',
|
||||||
|
'au' => 'whois.auda.org.au',
|
||||||
|
'nz' => 'whois.srs.net.nz',
|
||||||
|
'jp' => 'whois.jprs.jp',
|
||||||
|
'kr' => 'whois.kr',
|
||||||
|
'cn' => 'whois.cnnic.cn',
|
||||||
|
'in' => 'whois.registry.in',
|
||||||
|
'sg' => 'whois.sgnic.sg',
|
||||||
|
'hk' => 'whois.hkirc.hk',
|
||||||
|
'tw' => 'whois.twnic.net.tw',
|
||||||
|
'za' => 'whois.registry.net.za',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Spezielle Behandlung für .co.uk, .com.au etc.
|
||||||
|
if (count($parts) >= 2) {
|
||||||
|
$sld = $parts[count($parts) - 2];
|
||||||
|
$combinedTld = $sld . '.' . $tld;
|
||||||
|
|
||||||
|
$secondLevelTlds = [
|
||||||
|
'co.uk' => 'whois.nic.uk',
|
||||||
|
'org.uk' => 'whois.nic.uk',
|
||||||
|
'me.uk' => 'whois.nic.uk',
|
||||||
|
'com.au' => 'whois.auda.org.au',
|
||||||
|
'net.au' => 'whois.auda.org.au',
|
||||||
|
'org.au' => 'whois.auda.org.au',
|
||||||
|
'co.nz' => 'whois.srs.net.nz',
|
||||||
|
'com.br' => 'whois.registro.br',
|
||||||
|
];
|
||||||
|
|
||||||
|
if (isset($secondLevelTlds[$combinedTld])) {
|
||||||
|
return $secondLevelTlds[$combinedTld];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $whoisServers[$tld] ?? 'whois.iana.org';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parsed WHOIS-Rohdaten in strukturiertes Format
|
||||||
|
*/
|
||||||
|
function parseWhoisData(string $raw, string $domain): array {
|
||||||
|
$data = [
|
||||||
|
'raw' => $raw,
|
||||||
|
'parsed' => [
|
||||||
|
'domain_name' => $domain,
|
||||||
|
'registrar' => null,
|
||||||
|
'registrar_url' => null,
|
||||||
|
'creation_date' => null,
|
||||||
|
'expiration_date' => null,
|
||||||
|
'updated_date' => null,
|
||||||
|
'status' => [],
|
||||||
|
'nameservers' => [],
|
||||||
|
'dnssec' => null,
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$lines = explode("\n", $raw);
|
||||||
|
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
$line = trim($line);
|
||||||
|
if (empty($line) || strpos($line, '%') === 0 || strpos($line, '#') === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key: Value Format
|
||||||
|
if (strpos($line, ':') !== false) {
|
||||||
|
list($key, $value) = array_map('trim', explode(':', $line, 2));
|
||||||
|
$keyLower = strtolower($key);
|
||||||
|
|
||||||
|
// Registrar
|
||||||
|
if (strpos($keyLower, 'registrar') !== false && strpos($keyLower, 'abuse') === false && strpos($keyLower, 'url') === false) {
|
||||||
|
if (empty($data['parsed']['registrar'])) {
|
||||||
|
$data['parsed']['registrar'] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registrar URL
|
||||||
|
if (strpos($keyLower, 'registrar') !== false && strpos($keyLower, 'url') !== false) {
|
||||||
|
$data['parsed']['registrar_url'] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erstellungsdatum
|
||||||
|
if (preg_match('/(creation|created|registered)/i', $keyLower) && strpos($keyLower, 'registrar') === false) {
|
||||||
|
if (empty($data['parsed']['creation_date'])) {
|
||||||
|
$data['parsed']['creation_date'] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ablaufdatum
|
||||||
|
if (preg_match('/(expir|paid-till)/i', $keyLower)) {
|
||||||
|
if (empty($data['parsed']['expiration_date'])) {
|
||||||
|
$data['parsed']['expiration_date'] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aktualisierungsdatum
|
||||||
|
if (preg_match('/(updated|modified|changed)/i', $keyLower) && strpos($keyLower, 'registrar') === false) {
|
||||||
|
if (empty($data['parsed']['updated_date'])) {
|
||||||
|
$data['parsed']['updated_date'] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status
|
||||||
|
if (preg_match('/(status|state)/i', $keyLower) && !empty($value)) {
|
||||||
|
$data['parsed']['status'][] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nameserver
|
||||||
|
if (preg_match('/^(name.?server|nserver)/i', $keyLower) && !empty($value)) {
|
||||||
|
$ns = strtolower(explode(' ', $value)[0]);
|
||||||
|
if (!in_array($ns, $data['parsed']['nameservers'])) {
|
||||||
|
$data['parsed']['nameservers'][] = $ns;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNSSEC
|
||||||
|
if (strpos($keyLower, 'dnssec') !== false) {
|
||||||
|
$data['parsed']['dnssec'] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status einzigartig machen
|
||||||
|
$data['parsed']['status'] = array_unique($data['parsed']['status']);
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user