首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP Whois 查詢實作:fsockopen 連接 43 埠解析域名註冊資訊

     編輯:PHP 相關知識

Whois 協定簡介

Whois (RFC 3912) 為簡單 TCP 文字協定,客戶端連接伺服器 43 埠,發送查詢字串 (域名/IP/AS 號) + CRLF,伺服器回傳註冊資訊後斷線。無認證、無加密、無複雜指令。

核心實作

<?php
/**
 * Whois 查詢函式
 * @param string $server Whois 伺服器 (如 whois.verisign-grs.com, whois.cnnic.cn)
 * @param string $query  查詢字串 (域名、IP、AS號)
 * @param int    $timeout 連線逾時秒數
 * @return string 原始回應文字
 */
function whois_query($server, $query, $timeout = 10) {
    $fp = @fsockopen($server, 43, $errno, $errstr, $timeout);
    if (!$fp) {
        return "連線失敗 [$errno]: $errstr";
    }
    
    // 發送查詢 (必須以 \r\n 結尾)
    fwrite($fp, $query . "\r\n");
    
    // 讀取完整回應
    $response = '';
    stream_set_timeout($fp, $timeout);
    while (!feof($fp)) {
        $chunk = fread($fp, 4096);
        if ($chunk === false) break;
        $response .= $chunk;
        // 檢查逾時
        $info = stream_get_meta_data($fp);
        if ($info['timed_out']) {
            $response .= "\n[讀取逾時]";
            break;
        }
    }
    fclose($fp);
    
    return $response;
}

/**
 * 依域名後綴自動選擇 Whois 伺服器
 */
function get_whois_server($domain) {
    $tld = strtolower(substr(strrchr($domain, '.'), 1));
    $servers = [
        'com' => 'whois.verisign-grs.com',
        'net' => 'whois.verisign-grs.com',
        'org' => 'whois.pir.org',
        'info' => 'whois.afilias.net',
        'biz' => 'whois.biz',
        'cn' => 'whois.cnnic.cn',
        'tw' => 'whois.twnic.net.tw',
        'jp' => 'whois.jprs.jp',
        'de' => 'whois.denic.de',
        'eu' => 'whois.eu',
        'io' => 'whois.nic.io',
        'ai' => 'whois.nic.ai',
        'app' => 'whois.nic.google',
        'dev' => 'whois.nic.google',
        // 更多參考: https://www.iana.org/whois
    ];
    return $servers[$tld] ?? 'whois.iana.org'; // 根伺服器會轉介
}

// ===== 使用範例 =====
$domain = 'example.com';
$server = get_whois_server($domain);
echo "查詢 $domain @ $server ...\n\n";
$result = whois_query($server, $domain);
echo $result;

Web 介面版 (含表單與結果顯示)

<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title>Whois 查詢</title>
<style>
body {font-family: monospace; max-width: 900px; margin: 2rem auto; padding: 0 1rem;}
form {margin-bottom: 1rem;} pre {background:#f5f5f5; padding:1rem; overflow:auto; white-space:pre-wrap;}
.server-select {margin-right: 1rem;}
</style></head><body>
<h1>🔍 Whois 域名查詢</h1>
<form method="GET">
    <label class="server-select">
        伺服器: 
        <select name="server">
            <option value="auto" selected>自動判斷</option>
            <option value="whois.verisign-grs.com">.com/.net (Verisign)</option>
            <option value="whois.pir.org">.org (PIR)</option>
            <option value="whois.cnnic.cn">.cn (CNNIC)</option>
            <option value="whois.twnic.net.tw">.tw (TWNIC)</option>
            <option value="whois.iana.org">IANA 根伺服器</option>
        </select>
    </label>
    <label>
        域名/IP: <input type="text" name="query" placeholder="example.com" required style="width:300px;">
    </label>
    <button type="submit">查詢</button>
</form>

<?php
if (isset($_GET['query']) && $_GET['query'] !== '') {
    $query = trim($_GET['query']);
    $server = $_GET['server'] === 'auto' ? get_whois_server($query) : $_GET['server'];
    
    echo "<h2>結果: $query @ $server</h2>";
    echo "<pre>";
    echo htmlspecialchars(whois_query($server, $query), ENT_QUOTES, 'UTF-8');
    echo "</pre>";
}
?>
</body></html>

進階:解析結構化欄位

<?php
function parse_whois($raw_text) {
    $data = [];
    $lines = explode("\n", $raw_text);
    foreach ($lines as $line) {
        $line = trim($line);
        if ($line === '' || $line[0] === '%' || $line[0] === '#') continue; // 註解/空行
        if (preg_match('/^([^:]+):\s*(.+)$/', $line, $m)) {
            $key = strtolower(trim($m[1]));
            $val = trim($m[2]);
            // 合併多行同欄位
            if (isset($data[$key])) {
                $data[$key] = is_array($data[$key]) ? $data[$key] : [$data[$key]];
                $data[$key][] = $val;
            } else {
                $data[$key] = $val;
            }
        }
    }
    return $data;
}

$parsed = parse_whois($result);
echo "註冊商: " . ($parsed['registrar'] ?? 'N/A') . "\n";
echo "建立日期: " . ($parsed['creation date'] ?? $parsed['created'] ?? 'N/A') . "\n";
echo "到期日期: " . ($parsed['expiration date'] ?? $parsed['expires'] ?? 'N/A') . "\n";
echo "名稱伺服器: " . implode(', ', (array)($parsed['name server'] ?? [])) . "\n";

常見問題與對策

問題 原因 解決方案
連線逾時/拒絕伺服器限制頻率、IP 被封、防火牆阻擋 43 埠加入延遲、使用 Proxy、輪換多個 Whois 伺服器、快取結果
回應編碼亂碼部分國別伺服器回傳非 UTF-8 (如 Big5、EUC-KR)mb_detect_encoding() + mb_convert_encoding()
資料不完整/格式異常各註冊商/註冊局格式差異極大,無統一標準針對主要 TLD 寫專用解析器,或使用成熟庫
隱私保護/代理註冊GDPR、隱私保護服務遮蔽持有者資訊僅能取得註冊商、NS、建立/到期日等公開欄位

生產環境建議:使用成熟庫

# Composer 安裝
composer require phpwhois/phpwhois

# 使用
use PhpWhois\Whois;

$whois = new Whois();
$result = $whois->lookup('example.com', false); // false = 不快取
print_r($result); // 結構化陣列,含 parsed 欄位
  • phpwhois/phpwhois:支援 300+ TLD、自動選擇伺服器、結構化解析、快取。
  • webmozart/whois:現代 PSR 標準、型別安全、支援 RDAP (JSON 格式新標準)。

總結

Whois 協定極簡單:TCP 43 埠 → 送查詢 + CRLF → 讀回應 → 關閉。PHP 用 fsockopen() 幾行即可實作。但實務上伺服器分散、格式混亂、頻率限制、隱私遮蔽使得自行維護解析器成本極高。建議:簡單需求用現成庫 (phpwhois/webmozart/whois),複雜需求接入註冊商 API 或 RDAP 服務。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved