whois协议本质是向tcp 43端口发送带rn的纯文本域名并读取原始响应;需正确选择服务器、punycode编码idn域名、分块读取避免截断或卡死。

Whois协议本质就是发字符串、收响应,别被“查询器”吓住
Whois 查询不依赖任何 SDK 或 http API,它只是向特定端口(通常是 whois 端口,即 TCP 43)发送纯文本域名,然后读取服务器返回的原始响应。go 的 net.Dial 足够胜任——不需要第三方库,也不需要解析 HTML 或 json。
常见错误现象:dial tcp: lookup whois.example.com: no such host(误把域名当 whois 服务器)、read: connection reset by peer(没加换行符导致服务器直接断连)、响应截断(没读完就关闭连接)。
- Whois 服务器不是统一的:比如
google.com要查whois.markmonitor.com,而github.com往往走whois.verisign-grs.com;先用系统命令whois -h whois.verisign-grs.com github.com验证目标服务器是否通 - 必须在查询字符串末尾加
rn(windows 风格换行),很多服务器只认这个,仅n会无响应 - 响应可能很长,且无固定长度结尾,得一直读到
conn.Read返回io.EOF或0字节数才安全结束
用 net.Dial + bufio.Reader 实现稳定读写
别用 fmt.Fprint 直接写,也别用 io.ReadAll 无脑读——前者不保证换行生效,后者可能卡死(有些 whois 服务不主动断连)。稳妥做法是控制写入内容 + 分块读取。
使用场景:需要支持不同 TLD(如 .cn、.jp)对应不同 whois 服务器,或集成进 CLI 工具中做批量查询。
立即学习“go语言免费学习笔记(深入)”;
- 写入前显式构造完整请求:
fmt.Sprintf("%srn", domain),然后用conn.Write发送 - 读响应时用
bufio.NewReader(conn),循环调用reader.ReadString('n')或reader.ReadBytes('n'),直到返回io.EOF - 注意超时:
net.DialTimeout("tcp", serverAddr, 10*time.Second),否则遇到无响应服务器会 hang 住整个 goroutine
conn, err := net.DialTimeout("tcp", "whois.verisign-grs.com:43", 10*time.Second) if err != nil { log.Fatal(err) } defer conn.Close() conn.Write([]byte("github.comrn")) reader := bufio.NewReader(conn) for { line, err := reader.ReadString('n') fmt.Print(line) if err == io.EOF { break } if err != nil { log.Fatal(err) } }
如何自动发现权威 whois 服务器?别硬编码
顶级域(TLD)决定 whois 服务器,但 Go 标准库不提供内置映射。硬编码 whois.verisign-grs.com 只能查 .com/.net,查 example.cn 就得换 whois.cnnic.cn——否则返回 “No match for example.cn” 这类提示,不是失败,而是查到了“没注册”,容易误判。
参数差异:IANA 官方 TLD 列表(https://data.iana.org/TLD/tlds-alpha-by-domain.txt)本身不含 whois 服务器信息;实际可用的是 WHOIS Server 字段,藏在各 TLD 的 IANA 页面 HTML 中,或更靠谱地,复用 linux whois 命令的配置逻辑(见 /etc/whois.conf 或 whois --verbose 输出)。
- 最简方案:查域名后缀,查表匹配。例如提取
domainName := strings.SplitN(domain, ".", 2)[1],再查 map[string]string{“.com”: “whois.verisign-grs.com”, “.org”: “whois.pir.org”, “.cn”: “whois.cnnic.cn”} - 更健壮的做法:先向根 whois(如
whois.iana.org)查一次,它会在响应里返回真正负责该域名的 whois 服务器地址,格式通常是whois: xxx.xxx行 - 注意大小写和点号:TLD 是大小写不敏感的,但拼接服务器地址时别漏掉
:43,写成"whois.cnnic.cn"会默认走 0 端口,连接失败
中文域名、国际化域名(IDN)要先 Punycode 编码
直接传 例子.中国 给 whois 服务器,99% 会返回“无效域名”。因为 whois 协议本身不理解 UTF-8,所有非 ASCII 域名必须先转成 ASCII 兼容编码(ACE),也就是 Punycode 格式,形如 xn--fsq.xn--0zwm56d。
性能影响几乎为零,但漏掉这步会导致所有中文/日文/阿拉伯语域名查询全部失败,且错误不明显——服务器通常静默忽略或返回空响应。
- 用
golang.org/x/net/idna包,别自己实现:它处理了 IDNA2008 标准细节(比如某些字符的映射规则变更) - 调用
idna.ToASCII("例子.中国"),得到xn--fsq.xn--0zwm56d,再把这个结果作为查询字符串发给 whois 服务器 - 注意:只编码域名部分,不要对 whois 服务器地址(如
whois.cnnic.cn)做 ToASCII
域名 whois 查询看着简单,真正卡住人的往往不是连接或读写,而是服务器选择不准、换行符写错、IDN 没转码这三处——它们都不报 panic,只默默返回空或错乱结果。