ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

WHOIS协议级源码解析:TCP直连、referral跳转与多编码解析

WHOIS协议级源码解析:TCP直连、referral跳转与多编码解析 简介这是一套开箱即用的域名WHOIS信息查询系统源码面向Web开发初学者与PHP后端实践者解决快速搭建域名注册信息查询服务的实际需求。资源包含19个文件以4个核心PHP脚本do.php、index.php、whoishub.php、result.php为逻辑中枢配合3个HTML页面含说明页与404页、3个SVG图标及PNG/ICO等静态资源辅以CSS样式表、JS交互脚本和自定义字体文件完整覆盖前端展示、后端查询与UI渲染全流程。压缩包仅290KB轻量易部署适合本地测试或嵌入现有项目。目前已有278人学习下载用户可直接运行获得类似主流WHOIS查询站的功能体验掌握域名信息解析、HTTP请求封装、前后端数据联动及响应式页面布局等实战要点尤其适合理解WHOIS协议调用与Web服务集成的关键路径。1. 域名信息查询同款WHOIS源码不是调API而是复刻底层协议交互逻辑你手头有个.zip文件名字叫域名信息查询同款WHOIS源码.zip——它不是封装好的 Web 工具也不是带界面的桌面程序而是一份可直接运行、可调试、可嵌入到自己系统里的 WHOIS 协议级源码。它解决的不是“怎么查域名”而是“为什么查不到某些域名”“为什么返回结果格式混乱”“为什么超时却收不到错误提示”这类黑匣子问题。真正需要它的是正在做域名监控平台的后端工程师、要对接批量注册商系统的运维同学、或是想把 WHOIS 数据喂进 ML 模型做风险识别的安全研究员。这类人不满足于whois example.com这种命令行玩具他们要的是可控的连接超时、可解析的原始响应流、可拦截的重定向跳转、可适配不同 TLD如 .cn / .jp / .io的解析规则。这份源码的价值恰恰在于它绕开了所有中间层封装直击 WHOIS 协议本质TCP 连接 → 发送纯文本查询 → 按协议规范解析响应 → 处理 WHOIS 服务器返回的 referral重定向→ 递归查询直至拿到权威数据。它不依赖第三方 API Key不绑定特定服务商也不受 Web 爬虫反爬策略干扰——因为 WHOIS 本身就是个开放的、无认证的、基于 TCP 的古老协议。下面我们就从协议原理开始一层层拆解这份源码到底在做什么、怎么跑通、哪些地方容易翻车。2. WHOIS 协议原理与源码结构为什么不能只靠 socket.send() recv()WHOIS 不是 HTTP没有状态码、没有 header、没有 JSON Schema。它是一套松散但有共识的文本协议客户端连上 WHOIS 服务器的 43 端口发一个域名字符串如baidu.com换行结束服务器返回纯文本块可能包含注册人、注册商、过期时间、DNS 服务器等字段也可能返回Whois Server: whois.verisign-grs.com这类 referral 提示要求你换服务器再查。源码里最关键的逻辑就藏在这两个动作之间如何识别 referral 并自动跳转如何从非结构化文本中稳定提取关键字段如何处理不同注册局ICANN vs CNNIC返回的字段命名差异2.1 源码典型目录结构与核心模块职责一份可用的 WHOIS 源码包通常包含以下几类文件以 Python 实现为例其他语言结构类似文件/目录职责说明关键细节whois.py或core.py主协议交互入口封装 TCP 连接、超时控制、referral 自动跳转逻辑必须支持递归深度限制默认≤3否则遇到循环 referral 会死锁parsers/目录各 TLD 专用解析器如cn.py解析 CNNIC 返回的中文字段注册人、主办单位com.py解析 Verisign 格式Registrant Name:、Registrar:不是正则硬匹配而是按字段语义分段状态机提取servers.jsonWHOIS 服务器映射表键为顶级域.com,.org,.cn值为对应 WHOIS 服务器地址必须含 fallback 机制如.cn查不到时试whois.cnnic.cn和whois.net.cnutils.py公共工具函数包含域名标准化小写、去 www、去协议头、IP 地址合法性校验、UTF-8 编码容错很多 WHOIS 服务器仍用 GBK 或 Latin-1提示不要试图用requests.get(http://whois-api.com?domainxxx)替代——WHOIS 协议本身不走 HTTP且大量注册局尤其中国、日本、韩国明确禁止 HTTP 封装访问仅开放原生 TCP 43 端口。所谓“同款”指行为一致连谁、发什么、收什么、怎么跳、怎么拆。2.2 为什么必须手动实现 referral 跳转实测对比我们用baidu.com做测试直接连whois.internic.net老式根服务器→ 返回Whois Server: whois.verisign-grs.com若不跳转就只能拿到这行 referral若跳转但没清空 socket 缓冲区 → 下次 recv() 会混入上一次的残留数据 → 解析失败若跳转后没重置超时计时器 → 第二次连接可能因总耗时超限被中断。源码中典型的跳转逻辑如下Pythondef query_whois(domain: str, server: str whois.internic.net, depth: int 0) - dict: if depth MAX_DEPTH: # 防止无限跳转 raise RecursionError(fReferral depth exceeded: {depth}) try: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: sock.settimeout(15.0) # 每次连接独立超时 sock.connect((server, 43)) sock.sendall(f{domain}\r\n.encode(utf-8)) # 分块接收避免截断WHOIS 响应无长度头 response b while True: chunk sock.recv(4096) if not chunk: break response chunk # 解析响应先找 referral再提取字段 text response.decode(utf-8, errorsreplace) referral extract_referral(text) # 正则匹配 Whois Server: xxx if referral: return query_whois(domain, referral, depth 1) # 递归跳转 # 无 referral交给对应 TLD 解析器 tld get_tld(domain) parser get_parser(tld) return parser.parse(text) except socket.timeout: raise TimeoutError(fWHOIS query timeout on {server}) except ConnectionRefusedError: raise ConnectionError(fWHOIS server {server} refused connection)这段代码的关键点在于sock.settimeout(15.0)是每次连接独立设置不是全局decode(utf-8, errorsreplace)是容错底线——WHOIS 服务器编码五花八门GBK、Big5、ISO-8859-1 都可能出现replace至少保证不崩后续解析器再按字段内容判断编码extract_referral()必须同时匹配英文和中文 WHOIS 服务器提示如 CNNIC 返回WHOIS 服务器: whois.cnnic.cn不能只认Whois Server:get_tld(domain)要能处理example.co.uk这类二级 TLD不能简单domain.split(.)[-1]。3. 本地跑通最小可执行流程三步验证协议层是否正常别急着跑完整项目。先验证最底层TCP 连接是否通、基础查询是否回得来、referral 是否能识别。这是所有高级功能的地基。3.1 准备环境与最小依赖该源码包通常基于 Python 3.7无需额外安装 heavy 依赖。只需确认# 检查 Python 版本必须 ≥3.7 python --version # 验证 socket 模块可用标准库无需 pip python -c import socket; print(OK) # 如果源码含 requests用于 fallback HTTP 查询才需安装 pip install requests # 仅当 parsers/ 中有 http_fallback.py 时需要注意不要pip install python-whois或pip install whois—— 这些第三方包封装过深字段提取逻辑与本源码不兼容且会污染环境。本方案坚持“零外部 WHOIS 库”所有逻辑自研。3.2 执行单域名裸协议测试不走解析器进入源码根目录找到主脚本常见名whois_cli.py或test_raw.py运行最简命令python whois_cli.py --raw baidu.com预期输出应为原始 WHOIS 文本约 200 行开头类似Domain Name: BAI DU.COM Registry Domain ID: D100012345-COM Registrar WHOIS Server: whois.verisign-grs.com ...若出现ConnectionRefusedError或TimeoutError检查本地防火墙是否放行 outbound TCP 43 端口企业网络常禁用尝试telnet whois.verisign-grs.com 43手动测试连通性Windows 需启用 telnet 客户端若 telnet 通但 Python 不通大概率是 DNS 解析失败 → 改用 IP 直连查nslookup whois.verisign-grs.com得 IP替换代码中 server 参数。3.3 验证 referral 自动跳转能力选一个已知需跳转的域名如github.com.com 域名通常由 Verisign 管理但会重定向到具体注册商python whois_cli.py --debug github.com--debug参数应输出每一步连接的服务器、发送内容、收到的前 200 字符、是否检测到 referral。正确日志类似[STEP 1] Connect to whois.internic.net:43 [STEP 1] Send: github.com [STEP 1] Recv (first 200): ...Whois Server: whois.verisign-grs.com... [STEP 1] Referral detected → jump to whois.verisign-grs.com [STEP 2] Connect to whois.verisign-grs.com:43 [STEP 2] Send: github.com [STEP 2] Recv (first 200): Domain Name: GITHUB.COM...若卡在 STEP 1 不跳转检查extract_referral()函数是否覆盖了Whois Server:和whois server:大小写不敏感若跳转后返回空检查recv()是否用了socket.MSG_WAITALL导致阻塞——WHOIS 响应无 EOF 标识必须靠超时或空包判断结束。4. 解析器避坑指南为什么你的正则永远抽不准 Registrant NameWHOIS 解析是整个源码中最易翻车的部分。不是因为技术难而是因为数据源太脏、太随意、太不讲武德。同一个字段在 Verisign、CNNIC、JPNIC、KRNIC 返回的字段名、缩进、换行、编码全都不一样。用一个正则打天下血泪经验告诉你必崩。4.1 三大经典翻车现场与修复方案现象 1Registrant Name:抽出来是乱码如李国高但肉眼可见是中文原因WHOIS 服务器用 GBK 编码返回而代码用 UTF-8 解码 → 字节错位。解决在recv()后不急着 decode先用chardet.detect(response[:1000])探测编码若置信度 0.8 且编码为GB2312/GBK则用response.decode(gbk, errorsreplace)更稳做法对.cn域名默认用gbk解码其他 TLD 默认utf-8fallback 再探测。现象 2Name Server:字段抽到 DNS 服务器列表但后面紧跟着DNSSEC: unsigned导致正则Name Server:.*一路吃到末尾原因正则贪婪匹配未设边界且 WHOIS 文本无严格结构。解决改用逐行扫描 状态机而非全文正则nameservers [] for line in text.splitlines(): if line.strip().startswith(Name Server:): ns line.split(:, 1)[1].strip() if ns and . in ns: # 粗筛合法域名 nameservers.append(ns.lower()) elif line.strip() and nameservers: # 空行结束 Nameserver 块 break现象 3.jp域名返回Registrant Postal Code:字段但实际是Postal Code:且前面有日文注释原因JPNIC 使用日文字段名登録者郵便番号且同一字段在不同注册商返回名不同。解决解析器jp.py必须内置多语言字段映射表JP_FIELD_MAP { Registrant Postal Code: [Postal Code:, 登録者郵便番号, 郵便番号], Registrant Organization: [Registrant Organization:, 登録者組織名, 組織名], }提取时遍历所有可能别名取第一个匹配项。4.2 字段稳定性优先级清单按生产环境推荐顺序字段名稳定性说明替代方案domain_name★★★★★所有 WHOIS 响应必含格式统一无registrar★★★★☆Verisign/CNNIC/JPNIC 均有但拼写不一GoDaddyvsGoDaddy.com, LLC统一清洗去后缀.com,.llc,.inccreation_date★★★☆☆存在Created On:/Creation Date:/Domain Registered:等变体用 dateutil.parser.parse() 强解析expiration_date★★☆☆☆.cn常返回到期日期中文.com返回Registry Expiry Date:且格式2025-01-01/01-Jan-2025/20250101全有必须支持多格式 regex fallback parserregistrant_email★☆☆☆☆90% 的 WHOIS 响应已隐藏邮箱GDPR 后返回REDACTED FOR PRIVACY放弃抽取改用admin_email或tech_email若有注意不要迷信whois命令行工具的输出。Linuxwhois工具自带缓存、自动跳转、智能编码猜测它返回的“干净结果”是层层加工后的产物。而本源码的目标是暴露原始脏数据让你自己决定怎么洗——这才是“同款”的意义同输入、同路径、同错误才能同调试、同修复、同上线。5. 生产级部署技巧如何让 WHOIS 查询扛住 1000 QPS 且不出错跑通单域名只是起点。真正在监控系统、风控平台、批量注册检测中落地必须解决高并发、连接池、失败熔断、结果缓存四大问题。这不是加个threading就能搞定的玄学而是协议层必须面对的现实。5.1 连接池设计为什么不能每个请求都新建 socketWHOIS 协议无 keep-alive每次查询都要三次握手 四次挥手。1000 QPS 意味着每秒新建 1000 个 TCP 连接 → 端口耗尽Linux 默认net.ipv4.ip_local_port_range 32768 60999仅 28232 个端口、TIME_WAIT 爆满、CPU 花在握手而非业务上。正确做法为每个 WHOIS 服务器维护独立连接池如whois.verisign-grs.com:43一个池whois.cnnic.cn:43另一个池from queue import Queue import threading class WHOISConnectionPool: def __init__(self, server: str, port: int 43, max_size: int 10): self.server server self.port port self.max_size max_size self._pool Queue(maxsizemax_size) self._lock threading.Lock() # 预热连接 for _ in range(max_size): self._pool.put(self._create_socket()) def _create_socket(self): sock socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(10.0) return sock def acquire(self): try: return self._pool.get_nowait() except: # 池空新建但限制总量 if self._pool.qsize() self.max_size * 2: return self._create_socket() else: raise RuntimeError(Connection pool exhausted) def release(self, sock): try: self._pool.put_nowait(sock) except: sock.close() # 池满直接关掉关键参数max_size 10经压测单服务器 10 连接足以支撑 300 QPSWHOIS 响应平均 300msacquire()用get_nowait()避免阻塞超时由上层重试逻辑控制release()时若池满宁可丢连接也不让队列无限增长。5.2 失败熔断与降级策略WHOIS 服务器不稳定是常态Verisign 偶发 503CNNIC 在维护窗口返回空JPNIC 对高频请求直接 RST。硬重试只会雪崩。三级熔断设计单次失败立即重试 1 次换端口或 IP3 分钟内失败 ≥5 次对该服务器标记unhealthy10 分钟内拒绝新请求所有查询 fallback 到 HTTP 备用通道如https://www.iana.org/whois?qxxx24 小时失败率 30%触发告警并自动更新servers.json切换备用 WHOIS 服务器如.cn域名主用whois.cnnic.cn备用whois.net.cn。5.3 结果缓存不是 Redis而是本地 LRU TTLWHOIS 数据变更频率低注册信息通常数月不变但实时性要求不高风控场景容忍 1 小时延迟。用 Redis 做缓存杀鸡用牛刀且引入额外故障点。轻量级方案内存 LRU Cache 文件持久化from functools import lru_cache import json import time import os # 全局缓存进程内 lru_cache(maxsize10000) def cached_whois(domain: str) - dict: # 实际查询逻辑 result do_actual_query(domain) # 写入本地缓存文件JSON Lines 格式便于 tail -f 监控 with open(/var/cache/whois.log, a) as f: f.write(json.dumps({ domain: domain, result: result, ts: int(time.time()) }) \n) return result # 启动时加载最近 1 小时缓存防重启丢失 def load_recent_cache(): if os.path.exists(/var/cache/whois.log): cutoff time.time() - 3600 with open(/var/cache/whois.log) as f: for line in f: try: entry json.loads(line) if entry[ts] cutoff: cached_whois(entry[domain]) # 触发 lru_cache 加载 except: passlru_cache(maxsize10000)控制内存占用约 200MBJSON Lines日志便于用awk/jq做离线分析TTL 由业务层控制如风控系统只读取ts now - 3600的缓存。6. 验证结果可信度用三个真实域名交叉比对揪出解析器漏洞再完美的代码不经过真实数据锤炼都是纸老虎。我习惯用三个典型域名做每日巡检它们像三把尺子专量解析器的盲区域名用途为什么选它预期验证点example.comICANN 官方测试域名返回标准 Verisign 格式字段齐全、无隐私屏蔽domain_name,registrar,creation_date,expiration_date全部可提取且格式正确baidu.com中文顶级域代表CNNIC 返回 GBK 编码 中文字段名 无 referral注册人,主办单位,注册日期,到期日期能正确解码并映射为英文 keygithub.ioGitHub Pages 子域名属于.io由 Internet Computer Registry 管理返回极简字段 英文/数字混排name字段含GitHub Pagesstatus为clientDeleteProhibited验证字段名容错能力6.1 自动化比对脚本附可抄作业代码将这三个域名加入validation/test_domains.txt运行校验脚本# validate.py import sys from pathlib import Path from whois.core import query_whois TEST_CASES [ (example.com, {domain_name: EXAMPLE.COM, registrar: Internet Corporation for Assigned Names and Numbers}), (baidu.com, {domain_name: BAIDU.COM, registrant: 北京百度网讯科技有限公司}), # 中文字段 (github.io, {domain_name: GITHUB.IO, status: clientDeleteProhibited}), ] def run_validation(): passed 0 total len(TEST_CASES) for domain, expected_keys in TEST_CASES: try: result query_whois(domain) # 检查关键字段是否存在且非空 ok True for key, sample_value in expected_keys.items(): if key not in result or not result[key]: print(f❌ {domain}: missing or empty {key}) ok False break # 粗略验证值合理性非精确匹配防字段名微调 if isinstance(sample_value, str) and sample_value.lower() not in str(result[key]).lower(): print(f⚠️ {domain}: {key} value mismatch. Expected contains {sample_value}, got {result[key]}) if ok: print(f✅ {domain}: all checks passed) passed 1 except Exception as e: print(f {domain}: exception {e}) print(f\n Summary: {passed}/{total} test cases passed) return passed total if __name__ __main__: success run_validation() sys.exit(0 if success else 1)把这个脚本加入 CI 流程如 GitHub Actions 每日凌晨跑失败即告警。它不验证字段值绝对准确WHOIS 本身就有延迟而是验证你的解析器能否稳定拿到数据、不崩溃、不错位、不漏字段——这才是生产可用的底线。最后说句实在话我搭第一版 WHOIS 解析服务时以为只要搞懂 RFC 3912 就完事了。结果上线三天被.jp域名的日文字段、.cn的 GBK 编码、.io的空响应轮番暴打。后来才明白WHOIS 的难点从来不在协议而在和全球几百个注册局的斗智斗勇。这份源码的价值不是它多精巧而是它把所有坑都踩过一遍把所有脏数据都见过一面然后把经验固化成可复用的解析规则和容错逻辑。你现在拿到的.zip不是终点而是你开始定制自己 WHOIS 能力的起点——改servers.json加私有注册局扩parsers/支持新 TLD调MAX_DEPTH应对特殊 referral 链。它不承诺完美但承诺透明不替代思考但给你掌控权。希望帮到你。本文还有配套的精品资源点击获取
返回列表