如何使用Golang编写简单的域名Whois查询器_Socket通信初探

1次阅读

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

如何使用Golang编写简单的域名Whois查询器_Socket通信初探

Whois协议本质就是发字符串、收响应,别被“查询器”吓住

Whois 查询不依赖任何 SDK 或 http API,它只是向特定端口(通常是 whois 端口,即 TCP 43)发送纯文本域名,然后读取服务器返回的原始响应。gonet.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 验证目标服务器是否通
  • 必须在查询字符串末尾加 rnwindows 风格换行),很多服务器只认这个,仅 n 会无响应
  • 响应可能很长,且无固定长度结尾,得一直读到 conn.Read 返回 io.EOF0 字节数才安全结束

用 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.confwhois --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,只默默返回空或错乱结果。

text=ZqhQzanResources