直接调用系统 whois 命令在 go 中易失败,因主流域名注册局已停用公开 whois 端口(43),转而强制使用 rdap 或对 whois 施加限速、封禁及混淆响应。

为什么直接调用系统 whois 命令在 Go 里容易失败
Go 本身不内置 WHOIS 协议实现,很多人第一反应是 exec.Command("whois", "example.com"),但实际跑起来常卡住、超时或返回空——根本原因是多数域名注册局(如 .com/.net)已关闭公开 WHOIS 端口(43),转而要求使用 RDAP 协议,或对传统 WHOIS 加了速率限制、IP 封禁、甚至返回混淆文本。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 别依赖本地
whois命令,它查的是过时的 WHOIS 服务器,对新 gTLD(如 .app、.dev)基本无效 - 优先走 RDAP:标准协议,JSON 返回,结构清晰,
https://rdap.verisign.com/com/rdap/domain/example.com这类地址可直接 GET - 若必须用 WHOIS,得手动连对应注册局的 WHOIS 服务器(比如
whois.verisign-grs.comfor .com),且要处理响应乱码、分页、字段不一致等问题 - 注意 DNS 解析和连接超时:很多 WHOIS 服务器响应慢,
net.DialTimeout至少设为 10 秒,否则容易误判为“域名未注册”
用 net.Conn 手动连 WHOIS 服务器的最小可行代码
真要查 .com/.org 这类主流后缀,最稳的方式还是直连注册局 WHOIS 服务端(非 HTTP),自己发请求、收响应。关键不是“能不能”,而是“怎么连不丢数据”。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- WHOIS 查询无认证、无状态,就是纯 TCP 文本协议:发域名 +
\r\n,读到 EOF 或超时为止 - 必须用
net.DialTimeout而非net.Dial,否则卡死没提示;推荐10 * time.Second连接 +15 * time.Second读取 - 响应里可能含 UTF-8、ISO-8859-1 混合编码,Go 默认不自动转码,遇到乱码先试
strings.ToValidUTF8或用golang.org/x/text/encoding转 ISO-8859-1 - 示例片段:
conn, _ := net.DialTimeout("tcp", "whois.verisign-grs.com:43", 10*time.Second) conn.Write([]byte("example.com\r\n")) buf := make([]byte, 4096) n, _ := conn.Read(buf) fmt.Println(string(buf[:n]))
解析 WHOIS 响应时最容易漏掉的三个字段
WHOIS 响应没有固定 schema,不同注册局字段名、缩进、换行全靠约定俗成。光靠 strings.Contains 找 “Expiry Date” 很容易漏——因为有的写 Registry Expiry Date,有的是 expires:,还有的藏在 Status: 后面的注释里。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 别只匹配单个关键词,用正则覆盖常见变体:
regexp.MustCompile(`(?i)(?:registry\s+)?(?:expires?|expiry)\s*[:\s]+(\S+\s+\S+\s+\S+)`) - 注意时区:响应里时间常带
UTC、GMT或无时区,Go 的time.Parse必须显式指定 layout,推荐统一转成time.RFC3339再比较 - 警惕“fake”过期日:部分注册局(如某些国家 ccTLD)会在域名未续费后仍返回原 expiry,实际应结合
Status:字段判断是否含clientHold或serverHold
RDAP 查询比 WHOIS 更可靠,但要注意这几点
ICANN 强制新注册局提供 RDAP 接口,返回标准 JSON,字段语义明确(如 eventAction: "expiration"),不用猜字段名。但它不是“开箱即用”——路径构造、HTTP 头、重定向都得手动处理。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- Rdap 查询 URL 不是固定一个,得先查 IANA 的 RDAP 服务根:比如
https://rdap.icann.org/查example.com的注册局信息,再拼出真实 endpoint - 必须加
User-Agent头,不少 RDAP 服务(如 ARIN)会 403 拦截无 UA 的请求 - 响应里
events是数组,过期事件不一定是第一个,要遍历找eventAction == "expiration"对应的eventDate - 示例 endpoint:
https://rdap.verisign.com/com/rdap/domain/example.com(.com)、https://rdap.publicinterestregistry.net/rdap/org/domain/example.org(.org)
真正麻烦的从来不是连上服务器,而是 WHOIS 响应里那几行不起眼的缩写字段,还有 RDAP 返回里嵌套三层的 events 数组——这些地方不写死逻辑、不加日志,上线后就只能靠用户报错才发现问题。










