224 lines
5.4 KiB
Go
224 lines
5.4 KiB
Go
package whois
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"errors"
|
|
"net"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"golang.org/x/text/encoding/simplifiedchinese"
|
|
)
|
|
|
|
func TestGetServerReferralVariants(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
raw string
|
|
host string
|
|
port string
|
|
}{
|
|
{
|
|
name: "registrar whois server",
|
|
raw: "Registrar WHOIS Server: whois.markmonitor.com",
|
|
host: "whois.markmonitor.com",
|
|
port: "43",
|
|
},
|
|
{
|
|
name: "referral server with scheme and port",
|
|
raw: "ReferralServer: whois://whois.example.net:4343",
|
|
host: "whois.example.net",
|
|
port: "4343",
|
|
},
|
|
{
|
|
name: "refer short key",
|
|
raw: "refer: whois.nic.io",
|
|
host: "whois.nic.io",
|
|
port: "43",
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
host, port := getServer(tc.raw + "\n")
|
|
if host != tc.host || port != tc.port {
|
|
t.Fatalf("unexpected server, got=%s:%s want=%s:%s", host, port, tc.host, tc.port)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNormalizeServerListDedupAndTrim(t *testing.T) {
|
|
got := normalizeServerList([]string{" whois.a.com ", "", "WHOIS.A.COM", "whois.b.com"})
|
|
if len(got) != 2 {
|
|
t.Fatalf("unexpected size: %d", len(got))
|
|
}
|
|
if got[0] != "whois.a.com" || got[1] != "whois.b.com" {
|
|
t.Fatalf("unexpected values: %#v", got)
|
|
}
|
|
}
|
|
|
|
func TestWhoisWithOptionsContextCanceled(t *testing.T) {
|
|
addr, shutdown := startMockWhoisServerWithDelay(t, strings.Join([]string{
|
|
"Domain Name: EXAMPLE.COM",
|
|
"Registrar: TEST-REG",
|
|
"",
|
|
}, "\n"), 500*time.Millisecond)
|
|
defer shutdown()
|
|
|
|
c := NewClient().SetTimeout(2 * time.Second)
|
|
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Millisecond)
|
|
defer cancel()
|
|
|
|
_, err := c.WhoisWithOptionsContext(ctx, "example.com", QueryOptions{
|
|
Level: QueryAuto,
|
|
OverrideServer: addr,
|
|
})
|
|
if err == nil {
|
|
t.Fatal("expected context timeout error")
|
|
}
|
|
if !errors.Is(err, context.DeadlineExceeded) {
|
|
t.Fatalf("expected context deadline exceeded, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestWhoisEmptyResponseTypedError(t *testing.T) {
|
|
addr, shutdown := startMockWhoisServer(t, "")
|
|
defer shutdown()
|
|
|
|
c := NewClient()
|
|
_, err := c.WhoisWithOptions("example.com", QueryOptions{
|
|
Level: QueryAuto,
|
|
OverrideServer: addr,
|
|
})
|
|
if err == nil {
|
|
t.Fatal("expected empty response error")
|
|
}
|
|
if !errors.Is(err, ErrEmptyResponse) {
|
|
t.Fatalf("expected ErrEmptyResponse, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestResultMetaAndTypedError(t *testing.T) {
|
|
addr, shutdown := startMockWhoisServer(t, strings.Join([]string{
|
|
"Domain Name: EXAMPLE.COM",
|
|
"",
|
|
}, "\n"))
|
|
defer shutdown()
|
|
|
|
c := NewClient()
|
|
r, err := c.WhoisWithOptions("example.com", QueryOptions{
|
|
Level: QueryAuto,
|
|
OverrideServer: addr,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("WhoisWithOptions() error: %v", err)
|
|
}
|
|
meta := r.Meta()
|
|
if meta.Source != "whois" {
|
|
t.Fatalf("unexpected source: %q", meta.Source)
|
|
}
|
|
if meta.Server == "" || !strings.Contains(meta.Server, ":") {
|
|
t.Fatalf("unexpected server: %q", meta.Server)
|
|
}
|
|
if meta.RawLen <= 0 {
|
|
t.Fatalf("unexpected raw length: %d", meta.RawLen)
|
|
}
|
|
if meta.ReasonCode != ErrorCodeParseWeak {
|
|
t.Fatalf("unexpected reason code: %s", meta.ReasonCode)
|
|
}
|
|
if r.TypedError() == nil || !errors.Is(r.TypedError(), ErrParseWeak) {
|
|
t.Fatalf("expected typed parse-weak error, got: %v", r.TypedError())
|
|
}
|
|
}
|
|
|
|
func TestAccessDeniedResultReason(t *testing.T) {
|
|
addr, shutdown := startMockWhoisServer(t, strings.Join([]string{
|
|
"Requests of this client are not permitted. Please use https://www.nic.ch/whois/ for queries.",
|
|
"",
|
|
}, "\n"))
|
|
defer shutdown()
|
|
|
|
c := NewClient()
|
|
r, err := c.WhoisWithOptions("nic.ch", QueryOptions{
|
|
Level: QueryAuto,
|
|
OverrideServer: addr,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("WhoisWithOptions() error: %v", err)
|
|
}
|
|
if r.Meta().ReasonCode != ErrorCodeAccessDenied {
|
|
t.Fatalf("unexpected reason code: %s", r.Meta().ReasonCode)
|
|
}
|
|
if !errors.Is(r.TypedError(), ErrAccessDenied) {
|
|
t.Fatalf("expected ErrAccessDenied, got: %v", r.TypedError())
|
|
}
|
|
}
|
|
|
|
func TestWhoisDetectLegacyCharsetGBK(t *testing.T) {
|
|
raw := strings.Join([]string{
|
|
"Domain Name: test.com",
|
|
"Registrar: 测试注册商",
|
|
"Name Server: ns1.test.com",
|
|
"",
|
|
}, "\n")
|
|
gbkBytes, err := simplifiedchinese.GBK.NewEncoder().Bytes([]byte(raw))
|
|
if err != nil {
|
|
t.Fatalf("encode GBK failed: %v", err)
|
|
}
|
|
|
|
addr, shutdown := startRawWhoisServer(t, gbkBytes)
|
|
defer shutdown()
|
|
|
|
c := NewClient()
|
|
r, err := c.WhoisWithOptions("test.com", QueryOptions{
|
|
Level: QueryAuto,
|
|
OverrideServer: addr,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("WhoisWithOptions() error: %v", err)
|
|
}
|
|
|
|
if r.Meta().Charset != "gbk" && r.Meta().Charset != "gb18030" {
|
|
t.Fatalf("unexpected charset: %q", r.Meta().Charset)
|
|
}
|
|
if !strings.Contains(r.RawData(), "测试注册商") {
|
|
t.Fatalf("decoded raw data does not contain expected chinese text: %q", r.RawData())
|
|
}
|
|
}
|
|
|
|
func startRawWhoisServer(t *testing.T, payload []byte) (addr string, shutdown func()) {
|
|
t.Helper()
|
|
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
t.Fatalf("listen raw whois failed: %v", err)
|
|
}
|
|
done := make(chan struct{})
|
|
go func() {
|
|
for {
|
|
conn, err := ln.Accept()
|
|
if err != nil {
|
|
select {
|
|
case <-done:
|
|
return
|
|
default:
|
|
return
|
|
}
|
|
}
|
|
go func(c net.Conn) {
|
|
defer c.Close()
|
|
_ = c.SetDeadline(time.Now().Add(5 * time.Second))
|
|
_, _ = bufio.NewReader(c).ReadString('\n')
|
|
if len(payload) > 0 {
|
|
_, _ = c.Write(payload)
|
|
}
|
|
}(conn)
|
|
}
|
|
}()
|
|
return ln.Addr().String(), func() {
|
|
close(done)
|
|
_ = ln.Close()
|
|
}
|
|
}
|