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

3
.gitignore vendored
View File

@ -1 +1,2 @@
.idea
.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
@ -101,22 +106,42 @@ type PersonalInfo struct {
}
type Result struct {
exists bool
domain string
domainID string
rawData string
registar string
registerDate time.Time
updateDate time.Time
expireDate time.Time
statusRaw []string
nsServers []string
dnssec string
whoisSer string
ianaID string
registerInfo PersonalInfo
adminInfo PersonalInfo
techInfo PersonalInfo
exists bool
domain string
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
registerInfo PersonalInfo
adminInfo PersonalInfo
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 {
@ -189,12 +214,18 @@ func NewClient() *Client {
dialer: &net.Dialer{
Timeout: defTimeout,
},
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,13 +265,21 @@ func (c *Client) whois(domain string, servers ...string) (result string, err err
port = defWhoisPort
} else {
ext := getExtension(domain)
result, err := c.rawQuery(ext, defWhoisServer, defWhoisPort)
if err != nil {
return "", fmt.Errorf("whois: query for whois server failed: %w", err)
}
server, port = getServer(result)
if server == "" {
return "", fmt.Errorf("%w: %s", errors.New("whois server not found"), 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)
}
server, port = getServer(result)
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]
}
}