This commit is contained in:
兔子 2024-08-16 21:30:27 +08:00
parent 83cfd8c5e7
commit 129514a159
6 changed files with 679 additions and 40 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
.idea
bin

2
go.mod
View File

@ -2,4 +2,4 @@ module b612.me/sdk/whois
go 1.21.2
require golang.org/x/net v0.24.0
require golang.org/x/net v0.28.0

4
go.sum
View File

@ -1,2 +1,2 @@
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=

499
parse.go
View File

@ -16,13 +16,29 @@ func parse(domain string, result string) (Result, error) {
data, err = dotJPParser(domain, result)
case "tw":
data, err = dotTWParser(domain, result)
case "name":
data, err = dotNameParser(domain, result)
default:
data, err = commonParser(domain, result)
case "edu":
data, err = dotEduParser(domain, result)
case "int":
data, err = dotIntParser(domain, result)
case "ae":
data, err = dotAeParser(domain, result)
case "ai":
data, err = commonParserWithDate(domain, result, false, false, false)
case "am":
data, err = dotAmParser(domain, result)
}
return data, err
}
func commonParser(domain, data string) (Result, error) {
return commonParserWithDate(domain, data, true, true, true)
}
func commonParserWithDate(domain, data string, hasCreate bool, hasExpire bool, hasUpdate bool) (Result, error) {
var res = Result{
domain: domain,
rawData: data,
@ -34,8 +50,8 @@ func commonParser(domain, data string) (Result, error) {
for _, line := range split {
line = strings.TrimSpace(line)
if !res.exists {
for _, token := range []string{"Domain not found.", "No match for", "No match", "No Data Found", "No entries found", "No match for domain", "No matching record", "No Found", "No Object"} {
if strings.HasPrefix(token, line) {
for _, token := range []string{"No Object Found", "Domain not found.", "No match for", "No match", "No Data Found", "No entries found", "No match for domain", "No matching record", "No Found", "No Object"} {
if strings.HasPrefix(line, token) {
res.exists = false
return res, nil
}
@ -43,6 +59,9 @@ func commonParser(domain, data string) (Result, error) {
}
if strings.HasPrefix(line, "Domain Name:") {
res.exists = true
res.hasUpdateDate = hasUpdate
res.hasRegisterDate = hasCreate
res.hasExpireDate = hasExpire
res.nsServers = []string{}
res.domain = strings.TrimSpace(strings.TrimPrefix(line, "Domain Name:"))
}
@ -50,13 +69,28 @@ func commonParser(domain, data string) (Result, error) {
res.domainID = strings.TrimSpace(strings.TrimPrefix(line, "Registry Domain ID:"))
}
if strings.HasPrefix(line, "Updated Date:") {
res.updateDate = parseDate(strings.TrimSpace(strings.TrimPrefix(line, "Updated Date:")))
tmpDate := parseDate(strings.TrimSpace(strings.TrimPrefix(line, "Updated Date:")))
if !tmpDate.IsZero() {
res.updateDate = tmpDate
}
}
if strings.HasPrefix(line, "Creation Date:") {
res.registerDate = parseDate(strings.TrimSpace(strings.TrimPrefix(line, "Creation Date:")))
tmpDate := parseDate(strings.TrimSpace(strings.TrimPrefix(line, "Creation Date:")))
if !tmpDate.IsZero() {
res.registerDate = tmpDate
}
}
if strings.HasPrefix(line, "Registry Expiry Date:") {
res.expireDate = parseDate(strings.TrimSpace(strings.TrimPrefix(line, "Registry Expiry Date:")))
tmpDate := parseDate(strings.TrimSpace(strings.TrimPrefix(line, "Registry Expiry Date:")))
if !tmpDate.IsZero() {
res.expireDate = tmpDate
}
}
if strings.HasPrefix(line, "Registrar Registration Expiration Date:") {
tmpDate := parseDate(strings.TrimSpace(strings.TrimPrefix(line, "Registrar Registration Expiration Date:")))
if !tmpDate.IsZero() {
res.expireDate = tmpDate
}
}
if strings.HasPrefix(line, "Registrar:") {
res.registar = strings.TrimSpace(strings.TrimPrefix(line, "Registrar:"))
@ -65,6 +99,10 @@ func commonParser(domain, data string) (Result, error) {
statusMap[strings.Split(strings.TrimSpace(strings.TrimPrefix(line, "Status:")), " ")[0]] = struct{}{}
}
if strings.HasPrefix(line, "Domain Status:") {
if strings.Contains(line, "No Object Found") {
res.exists = false
return res, nil
}
statusMap[strings.Split(strings.TrimSpace(strings.TrimPrefix(line, "Domain Status:")), " ")[0]] = struct{}{}
}
if strings.HasPrefix(line, "Name Server:") {
@ -73,9 +111,6 @@ func commonParser(domain, data string) (Result, error) {
if strings.HasPrefix(line, "DNSSEC:") {
res.dnssec = strings.TrimSpace(strings.TrimPrefix(line, "DNSSEC:"))
}
if strings.HasPrefix(line, "Registrar Registration Expiration Date:") {
res.expireDate = parseDate(strings.TrimSpace(strings.TrimPrefix(line, "Registrar Registration Expiration Date:")))
}
if strings.HasPrefix(line, "Registrant Name:") {
r.Name = strings.TrimSpace(strings.TrimPrefix(line, "Registrant Name:"))
}
@ -194,6 +229,61 @@ func commonParser(domain, data string) (Result, error) {
return res, nil
}
func dotNameParser(domain, data string) (Result, error) {
var res = Result{
domain: domain,
rawData: data,
}
var r, a, t PersonalInfo
split := strings.Split(data, "\n")
statusMap := make(map[string]struct{})
for _, line := range split {
line = strings.TrimSpace(line)
if !res.exists {
for _, token := range []string{"No match for"} {
if strings.HasPrefix(line, token) {
res.exists = false
return res, nil
}
}
}
if strings.HasPrefix(line, "Domain Name:") {
res.exists = true
res.hasUpdateDate = false
res.hasRegisterDate = false
res.hasExpireDate = false
res.nsServers = []string{}
res.domain = strings.TrimSpace(strings.TrimPrefix(line, "Domain Name:"))
}
if strings.HasPrefix(line, "Registry Domain ID:") {
res.domainID = strings.TrimSpace(strings.TrimPrefix(line, "Registry Domain ID:"))
}
if strings.HasPrefix(line, "Registrar:") {
res.registar = strings.TrimSpace(strings.TrimPrefix(line, "Registrar:"))
}
if strings.HasPrefix(line, "Status:") {
statusMap[strings.Split(strings.TrimSpace(strings.TrimPrefix(line, "Status:")), " ")[0]] = struct{}{}
}
if strings.HasPrefix(line, "Domain Status:") {
if strings.Contains(line, "No Object Found") {
res.exists = false
return res, nil
}
statusMap[strings.Split(strings.TrimSpace(strings.TrimPrefix(line, "Domain Status:")), " ")[0]] = struct{}{}
}
}
for status := range statusMap {
res.statusRaw = append(res.statusRaw, status)
}
res.registerInfo = r
res.adminInfo = a
res.techInfo = t
return res, nil
}
func dotCNParser(domain, data string) (Result, error) {
var res = Result{
domain: domain,
@ -204,6 +294,13 @@ func dotCNParser(domain, data string) (Result, error) {
res.exists = false
return res, nil
}
if strings.HasPrefix(strings.TrimSpace(data), "the Domain Name you apply can not be registered online") {
res.exists = false
return res, nil
}
res.hasUpdateDate = false
res.hasRegisterDate = true
res.hasExpireDate = true
res.exists = true
statusMap := make(map[string]struct{})
split := strings.Split(data, "\n")
@ -267,6 +364,9 @@ func dotJPParser(domain, data string) (Result, error) {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "[Domain Name]") {
res.exists = true
res.hasUpdateDate = true
res.hasRegisterDate = true
res.hasExpireDate = true
res.nsServers = []string{}
res.domain = strings.TrimSpace(strings.TrimPrefix(line, "[Domain Name]"))
}
@ -341,6 +441,9 @@ func dotTWParser(domain, data string) (Result, error) {
}
if strings.HasPrefix(line, "Domain Name:") {
res.exists = true
res.hasUpdateDate = true
res.hasRegisterDate = true
res.hasExpireDate = true
res.nsServers = []string{}
res.domain = strings.TrimSpace(strings.TrimPrefix(line, "Domain Name:"))
}
@ -398,8 +501,373 @@ func dotTWParser(domain, data string) (Result, error) {
return res, nil
}
func dotEduParser(domain, data string) (Result, error) {
var res = Result{
domain: domain,
rawData: data,
}
split := strings.Split(data, "\n")
startNs := false
var currentStatus uint8 = 0
var startIdx int
var r, a, t, p PersonalInfo
for idx, line := range split {
line = strings.TrimSpace(line)
if startNs && len(line) == 0 {
startNs = false
}
if strings.HasPrefix(line, "NO MATCH:") {
res.exists = false
return res, nil
}
if strings.HasPrefix(line, "Domain Name:") {
res.exists = true
res.hasUpdateDate = true
res.hasRegisterDate = true
res.hasExpireDate = true
res.nsServers = []string{}
res.domain = strings.TrimSpace(strings.TrimPrefix(line, "Domain Name:"))
}
if strings.HasPrefix(line, "Domain record activated:") {
res.registerDate = parseEduDate(strings.TrimSpace(strings.TrimPrefix(line, "Domain record activated:")))
}
if strings.HasPrefix(line, "Domain record last updated:") {
res.updateDate = parseEduDate(strings.TrimSpace(strings.TrimPrefix(line, "Domain record last updated:")))
}
if strings.HasPrefix(line, "Domain expires:") {
res.expireDate = parseEduDate(strings.TrimSpace(strings.TrimPrefix(line, "Domain expires:")))
}
if strings.HasPrefix(line, "Registrant:") {
currentStatus = 1
startIdx = idx
p = PersonalInfo{}
continue
}
if strings.HasPrefix(line, "Administrative Contact:") {
currentStatus = 2
startIdx = idx
p = PersonalInfo{}
continue
}
if strings.HasPrefix(line, "Technical Contact:") {
currentStatus = 3
startIdx = idx
p = PersonalInfo{}
continue
}
if strings.HasPrefix(line, "Name Servers:") {
startNs = true
continue
}
if startNs {
res.nsServers = append(res.nsServers, line)
}
if currentStatus > 0 {
switch idx - startIdx {
case 1:
p.Name = line
case 2, 3, 4, 5:
p.Addr += line
case 6:
p.Phone = line
case 7:
p.Email = line
}
if len(line) == 0 {
switch currentStatus {
case 1:
r = p
case 2:
a = p
case 3:
t = p
}
currentStatus = 0
}
}
}
res.registerInfo = r
res.adminInfo = a
res.techInfo = t
return res, nil
}
func dotIntParser(domain, data string) (Result, error) {
var res = Result{
domain: domain,
rawData: data,
}
split := strings.Split(data, "\n")
var currentStatus uint8 = 0
var r, a, t, p PersonalInfo
for _, line := range split {
line = strings.TrimSpace(line)
if strings.Contains(line, "but this server does not have") {
res.exists = false
return res, nil
}
if strings.HasPrefix(line, "domain:") {
res.exists = true
res.hasUpdateDate = true
res.hasRegisterDate = true
res.hasExpireDate = false
res.nsServers = []string{}
res.domain = strings.TrimSpace(strings.TrimPrefix(line, "domain:"))
}
if strings.HasPrefix(line, "created:") {
res.registerDate = parseYMDDate(strings.TrimSpace(strings.TrimPrefix(line, "created:")))
}
if strings.HasPrefix(line, "changed:") {
res.updateDate = parseYMDDate(strings.TrimSpace(strings.TrimPrefix(line, "changed:")))
}
if strings.HasPrefix(line, "organisation:") {
currentStatus = 1
p = PersonalInfo{
Org: strings.TrimSpace(strings.TrimPrefix(line, "organisation:")),
}
continue
}
if strings.HasPrefix(line, "contact:") && strings.Contains(line, "administrative") {
currentStatus = 2
p = PersonalInfo{}
continue
}
if strings.HasPrefix(line, "contact:") && strings.Contains(line, "technical") {
currentStatus = 3
p = PersonalInfo{}
continue
}
if strings.HasPrefix(line, "nserver:") {
nsList := strings.Split(strings.TrimSpace(strings.TrimPrefix(line, "nserver:")), " ")
for idx, ns := range nsList {
if idx == 0 {
res.nsServers = append(res.nsServers, ns)
continue
}
res.nsIps = append(res.nsIps, ns)
}
}
if currentStatus > 0 {
if strings.HasPrefix(line, "address:") {
p.Addr += strings.TrimSpace(strings.TrimPrefix(line, "address:")) + "\n"
continue
}
if strings.HasPrefix(line, "phone:") {
p.Phone = strings.TrimSpace(strings.TrimPrefix(line, "phone:"))
continue
}
if strings.HasPrefix(line, "e-mail:") {
p.Email = strings.TrimSpace(strings.TrimPrefix(line, "e-mail:"))
continue
}
if strings.HasPrefix(line, "fax-no:") {
p.Fax = strings.TrimSpace(strings.TrimPrefix(line, "fax-no:"))
continue
}
if len(line) == 0 {
p.Addr = strings.TrimSpace(p.Addr)
switch currentStatus {
case 1:
r = p
case 2:
a = p
case 3:
t = p
}
}
}
}
res.registerInfo = r
res.adminInfo = a
res.techInfo = t
return res, nil
}
func dotAeParser(domain, data string) (Result, error) {
var res = Result{
domain: domain,
rawData: data,
}
var r, a, t PersonalInfo
split := strings.Split(data, "\n")
statusMap := make(map[string]struct{})
for _, line := range split {
line = strings.TrimSpace(line)
if !res.exists {
for _, token := range []string{"No Data Found"} {
if strings.HasPrefix(line, token) {
res.exists = false
return res, nil
}
}
}
if strings.HasPrefix(line, "Domain Name:") {
res.exists = true
res.hasUpdateDate = false
res.hasRegisterDate = false
res.hasExpireDate = false
res.nsServers = []string{}
res.domain = strings.TrimSpace(strings.TrimPrefix(line, "Domain Name:"))
}
if strings.HasPrefix(line, "Registrar Name:") {
res.registar = strings.TrimSpace(strings.TrimPrefix(line, "Registrar Name:"))
}
if strings.HasPrefix(line, "Status:") {
statusMap[strings.Split(strings.TrimSpace(strings.TrimPrefix(line, "Status:")), " ")[0]] = struct{}{}
}
if strings.HasPrefix(line, "Name Server:") {
res.nsServers = append(res.nsServers, strings.TrimSpace(strings.TrimPrefix(line, "Name Server:")))
}
if strings.HasPrefix(line, "Registrant Contact Name:") {
r.Name = strings.TrimSpace(strings.TrimPrefix(line, "Registrant Contact Name:"))
}
if strings.HasPrefix(line, "Registrant Contact Organisation::") {
r.Org = strings.TrimSpace(strings.TrimPrefix(line, "Registrant Contact Organisation:"))
}
if strings.HasPrefix(line, "Registrant Contact Email:") {
r.Email = strings.TrimSpace(strings.TrimPrefix(line, "Registrant Contact Email:"))
}
if strings.HasPrefix(line, "Tech Contact Name:") {
t.Name = strings.TrimSpace(strings.TrimPrefix(line, "Tech Contact Name:"))
}
if strings.HasPrefix(line, "Tech Contact Organisation::") {
t.Org = strings.TrimSpace(strings.TrimPrefix(line, "Tech Contact Organisation:"))
}
if strings.HasPrefix(line, "Tech Contact Email:") {
t.Email = strings.TrimSpace(strings.TrimPrefix(line, "Tech Contact Email:"))
}
}
for status := range statusMap {
res.statusRaw = append(res.statusRaw, status)
}
res.registerInfo = r
res.adminInfo = a
res.techInfo = t
return res, nil
}
func dotAmParser(domain, data string) (Result, error) {
var res = Result{
domain: domain,
rawData: data,
}
split := strings.Split(data, "\n")
var currentStatus uint8 = 0
statusMap := make(map[string]struct{})
var r, a, t, p PersonalInfo
startNs := false
var tmpSlice []string
for _, line := range split {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "Status:") {
statusMap[strings.Split(strings.TrimSpace(strings.TrimPrefix(line, "Status:")), " ")[0]] = struct{}{}
}
if strings.Contains(line, "No match") {
res.exists = false
return res, nil
}
if strings.HasPrefix(line, "Domain name:") {
res.exists = true
res.hasUpdateDate = true
res.hasRegisterDate = true
res.hasExpireDate = false
res.nsServers = []string{}
res.domain = strings.TrimSpace(strings.TrimPrefix(line, "Domain name:"))
}
if strings.HasPrefix(line, "Registered:") {
res.registerDate = parseYMDDate(strings.TrimSpace(strings.TrimPrefix(line, "Registered:")))
}
if strings.HasPrefix(line, "Last modified:") {
res.updateDate = parseYMDDate(strings.TrimSpace(strings.TrimPrefix(line, "Last modified:")))
}
if strings.HasPrefix(line, "Registrar:") {
res.registar = strings.TrimSpace(strings.TrimPrefix(line, "Registrar:"))
}
if strings.HasPrefix(line, "Registrant:") {
currentStatus = 1
p = PersonalInfo{}
continue
}
if strings.HasPrefix(line, "Administrative contact:") {
currentStatus = 2
p = PersonalInfo{}
continue
}
if strings.HasPrefix(line, "Technical contact:") {
currentStatus = 3
p = PersonalInfo{}
continue
}
if strings.HasPrefix(line, "DNS servers (") {
startNs = true
continue
}
if startNs {
tmp := strings.Split(line, "-")
for idx, ns := range tmp {
ns = strings.TrimSpace(ns)
if idx == 0 {
res.nsServers = append(res.nsServers, ns)
continue
}
res.nsIps = append(res.nsIps, ns)
}
}
if len(line) == 0 {
startNs = false
}
if currentStatus > 0 {
tmpSlice = append(tmpSlice, line)
if len(line) == 0 {
switch currentStatus {
case 1:
p.Addr = strings.Join(tmpSlice, "\n")
tmpSlice = []string{}
r = p
case 2:
if len(tmpSlice) > 2 {
p.Addr = strings.Join(tmpSlice[:len(tmpSlice)-2], "\n")
p.Phone = tmpSlice[len(tmpSlice)-1]
p.Email = tmpSlice[len(tmpSlice)-2]
} else {
p.Addr = strings.Join(tmpSlice, "\n")
}
tmpSlice = []string{}
a = p
case 3:
if len(tmpSlice) > 2 {
p.Addr = strings.Join(tmpSlice[:len(tmpSlice)-2], "\n")
p.Phone = tmpSlice[len(tmpSlice)-1]
p.Email = tmpSlice[len(tmpSlice)-2]
} else {
p.Addr = strings.Join(tmpSlice, "\n")
}
tmpSlice = []string{}
t = p
}
currentStatus = 0
}
}
}
for status := range statusMap {
res.statusRaw = append(res.statusRaw, status)
}
res.registerInfo = r
res.adminInfo = a
res.techInfo = t
return res, nil
}
func parseDate(date string) time.Time {
t, _ := time.Parse("2006-01-02T15:04:05Z", date)
t, err := time.Parse("2006-01-02T15:04:05Z", date)
if err == nil {
t = t.In(time.Local)
return t
}
t, err = time.Parse("2006-01-02T15:04:05-0700", date)
t = t.In(time.Local)
return t
}
@ -420,3 +888,16 @@ func parseJPDate(date string, onlyMD bool) time.Time {
t = t.In(time.Local)
return t
}
func parseEduDate(date string) time.Time {
//example 20-Dec-1996
t, _ := time.Parse("02-Jan-2006", date)
t = t.In(time.Local)
return t
}
func parseYMDDate(date string) time.Time {
t, _ := time.Parse("2006-01-02", date)
t = t.In(time.Local)
return t
}

View File

@ -2,10 +2,128 @@ package whois
import (
"fmt"
"os"
"testing"
)
func TestWhois(t *testing.T) {
func TestWhoisInfo(t *testing.T) {
c := NewClient()
fmt.Println(c.Whois("b612.in"))
domain := "who.int"
h, err := c.Whois(domain)
if err != nil {
t.Fatal(err)
}
os.WriteFile("./bin/"+domain+".txt", []byte(h.RawData()), 0644)
fmt.Println("Domain:", h.Domain())
fmt.Println("Exists:", h.Exists())
fmt.Println("RegistrarDate:", h.RegisterDate())
fmt.Println("ExpireDate:", h.ExpireDate())
fmt.Println("UpdateDate:", h.UpdateDate())
fmt.Println("Status:", h.Status())
fmt.Println("NsServers:", h.NsServers())
fmt.Println("NsIp:", h.NsIps())
fmt.Println("Dnssec:", h.Dnssec())
fmt.Println("WhoisSer:", h.WhoisSer())
fmt.Println("IanaID:", h.IanaID())
fmt.Println("======RegisterInfo=======")
fmt.Println("Name:", h.RegisterInfo().Name)
fmt.Println("Organization:", h.RegisterInfo().Org)
fmt.Println("Addr:", h.RegisterInfo().Addr)
fmt.Println("State:", h.RegisterInfo().State)
fmt.Println("Country:", h.RegisterInfo().Country)
fmt.Println("Email:", h.RegisterInfo().Email)
fmt.Println("Phone:", h.RegisterInfo().Phone)
fmt.Println("Fax:", h.RegisterInfo().Fax)
fmt.Println("======AdminInfo=======")
fmt.Println("Name:", h.AdminInfo().Name)
fmt.Println("Organization:", h.AdminInfo().Org)
fmt.Println("Addr:", h.AdminInfo().Addr)
fmt.Println("State:", h.AdminInfo().State)
fmt.Println("Country:", h.AdminInfo().Country)
fmt.Println("Email:", h.AdminInfo().Email)
fmt.Println("Phone:", h.AdminInfo().Phone)
fmt.Println("Fax:", h.AdminInfo().Fax)
fmt.Println("======TechInfo=======")
fmt.Println("Name:", h.TechInfo().Name)
fmt.Println("Organization:", h.TechInfo().Org)
fmt.Println("Addr:", h.TechInfo().Addr)
fmt.Println("State:", h.TechInfo().State)
fmt.Println("Country:", h.TechInfo().Country)
fmt.Println("Email:", h.TechInfo().Email)
fmt.Println("Phone:", h.TechInfo().Phone)
fmt.Println("Fax:", h.TechInfo().Fax)
fmt.Println("====================")
fmt.Println("\nRaw:", h.RawData())
}
func TestWhois(t *testing.T) {
os.MkdirAll("./bin", 0755)
domainSuffix := []string{"com", "net", "org", "cn", "io", "me", "cc", "top", "xyz", "vip", "club", "site", "win", "bid",
"loan", "ek", "kim", "ren", "ltd", "link", "red", "pro", "info", "mobi", "name", "tv", "ws", "asia",
"biz", "gov", "edu", "mil", "int", "aero", "coop", "museum", "jobs", "travel", "xxx",
"ac", "ad", "ae", "af", "ag", "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au", "aw", "az",
"ba", "bb", "bd", "be", "bf", "bg", "bh", "bi", "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by", "bz",
"ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl", "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz",
"de", "dj", "dk", "dm", "do", "dz",
"ec", "ee", "eg", "eh", "er", "es", "et", "eu",
"fi", "fj", "fk", "fm", "fo", "fr",
"ga", "gb", "gd", "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq", "gr", "gs", "gt", "gu", "gw", "gy",
"hk", "hm", "hn", "hr", "ht", "hu",
"id", "ie", "il", "im", "in", "io", "iq", "ir", "is", "it",
"je", "jm", "jo", "jp",
"ke", "kg", "kh", "ki", "km", "kn", "kp", "kr", "kw", "ky", "kz",
"la", "lb", "lc", "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly",
"ma", "mc", "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq", "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz",
"na", "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu", "nz",
"om",
"pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm", "pn", "pr", "ps", "pt", "pw", "py",
"qa",
"re", "ro", "ru", "rw",
"sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj", "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
"tc", "td", "tf", "tg", "th", "tj", "tk", "tl", "tm", "tn", "to", "tp", "tr", "tt", "tv", "tw", "tz",
"ua", "ug", "uk", "us", "uy", "uz",
"va", "vc", "ve", "vg", "vi", "vn", "vu",
"wf", "ws",
"ye", "yt", "yu",
"za", "zm", "zw"}
prefix := "nic."
c := NewClient()
for idx, suffix := range domainSuffix {
if idx < 50 {
continue
}
domain := prefix + suffix
if suffix == "cn" {
domain = "cnnic.cn"
}
if suffix == "int" {
domain = "who.int"
}
h, err := c.Whois(domain)
if err != nil {
fmt.Println(idx, domain, "net fail", err)
continue
}
os.WriteFile("./bin/"+domain+".txt", []byte(h.RawData()), 0644)
if !h.exists {
fmt.Println(idx, h.Domain(), "fail")
t.Fatal(domain, "not exists")
}
fmt.Println(idx, h.Domain(), "ok")
fmt.Println(h.ExpireDate(), h.RegisterDate())
if h.HasExpireDate() && h.ExpireDate().IsZero() {
t.Fatal("ExpireDate is zero")
}
if h.Domain() == "" {
t.Fatal("Domain is empty")
}
if h.HasRegisterDate() && h.RegisterDate().IsZero() {
t.Fatal("RegisterDate is zero")
}
if len(h.NsServers()) == 0 {
fmt.Println("NsServers is empty")
}
fmt.Println(h.Status())
}
}

View File

@ -7,6 +7,7 @@ import (
"net"
"strconv"
"strings"
"sync"
"time"
"golang.org/x/net/proxy"
@ -76,8 +77,12 @@ var statusMap = map[string]string{
// DefaultClient is default whois client
var DefaultClient = NewClient()
var defaultWhoisMap = map[string]string{}
// Client is whois client
type Client struct {
extCache map[string]string
mu sync.Mutex
dialer proxy.Dialer
timeout time.Duration
elapsed time.Duration
@ -106,11 +111,15 @@ type Result struct {
domainID string
rawData string
registar string
hasRegisterDate bool
registerDate time.Time
hasUpdateDate bool
updateDate time.Time
hasExpireDate bool
expireDate time.Time
statusRaw []string
nsServers []string
nsIps []string
dnssec string
whoisSer string
ianaID string
@ -119,6 +128,22 @@ type Result struct {
techInfo PersonalInfo
}
func (r Result) NsIps() []string {
return r.nsIps
}
func (r Result) HasRegisterDate() bool {
return r.hasRegisterDate
}
func (r Result) HasUpdateDate() bool {
return r.hasUpdateDate
}
func (r Result) HasExpireDate() bool {
return r.hasExpireDate
}
func (r Result) Exists() bool {
return r.exists
}
@ -189,12 +214,18 @@ func NewClient() *Client {
dialer: &net.Dialer{
Timeout: defTimeout,
},
extCache: make(map[string]string),
timeout: defTimeout,
}
}
func (c *Client) Whois(domain string, servers ...string) (Result, error) {
data, err := c.whois(domain)
if len(servers) == 0 {
if v, ok := defaultWhoisMap[getExtension(domain)]; ok {
servers = append(servers, v)
}
}
data, err := c.whois(domain, servers...)
if err != nil {
return Result{}, err
}
@ -234,6 +265,7 @@ func (c *Client) whois(domain string, servers ...string) (result string, err err
port = defWhoisPort
} else {
ext := getExtension(domain)
if _, ok := c.extCache[ext]; !ok {
result, err := c.rawQuery(ext, defWhoisServer, defWhoisPort)
if err != nil {
return "", fmt.Errorf("whois: query for whois server failed: %w", err)
@ -242,6 +274,13 @@ func (c *Client) whois(domain string, servers ...string) (result string, err err
if server == "" {
return "", fmt.Errorf("%w: %s", errors.New("whois server not found"), domain)
}
c.mu.Lock()
c.extCache[ext] = fmt.Sprintf("%s:%s", server, port)
c.mu.Unlock()
} else {
v := strings.Split(c.extCache[ext], ":")
server, port = v[0], v[1]
}
}
result, err = c.rawQuery(domain, server, port)