init
This commit is contained in:
commit
6a8915039e
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
.idea
|
||||
5
go.mod
Normal file
5
go.mod
Normal file
@ -0,0 +1,5 @@
|
||||
module b612.me/sdk/whois
|
||||
|
||||
go 1.21.2
|
||||
|
||||
require golang.org/x/net v0.24.0
|
||||
2
go.sum
Normal file
2
go.sum
Normal file
@ -0,0 +1,2 @@
|
||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
415
parse.go
Normal file
415
parse.go
Normal file
@ -0,0 +1,415 @@
|
||||
package whois
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func parse(domain string, result string) {
|
||||
os.WriteFile(domain+".txt", []byte(result), 0644)
|
||||
ext := getExtension(domain)
|
||||
secExt := getExtension(strings.TrimSuffix(domain, "."+ext))
|
||||
_ = secExt
|
||||
var data Result
|
||||
var err error
|
||||
switch ext {
|
||||
case "cn":
|
||||
data, err = dotCNParser(domain, result)
|
||||
case "jp":
|
||||
data, err = dotJPParser(domain, result)
|
||||
case "tw":
|
||||
data, err = dotTWParser(domain, result)
|
||||
default:
|
||||
data, err = commonParser(domain, result)
|
||||
}
|
||||
fmt.Println(err)
|
||||
fmt.Println(data.domain, data.registerDate, data.expireDate, data.nsServers)
|
||||
fmt.Println(data.exists, data.statusRaw, data.nsServers)
|
||||
fmt.Printf("%+v\n%+v\n", data.adminInfo, data.registerInfo)
|
||||
}
|
||||
|
||||
func commonParser(domain, data string) (Result, error) {
|
||||
var res = Result{
|
||||
domain: domain,
|
||||
rawData: data,
|
||||
}
|
||||
var r, a, t PersonalInfo
|
||||
|
||||
split := strings.Split(data, "\n")
|
||||
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) {
|
||||
res.exists = false
|
||||
return res, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(line, "Domain Name:") {
|
||||
res.exists = true
|
||||
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, "Updated Date:") {
|
||||
res.updateDate = parseDate(strings.TrimSpace(strings.TrimPrefix(line, "Updated Date:")))
|
||||
}
|
||||
if strings.HasPrefix(line, "Creation Date:") {
|
||||
res.registerDate = parseDate(strings.TrimSpace(strings.TrimPrefix(line, "Creation Date:")))
|
||||
}
|
||||
if strings.HasPrefix(line, "Registry Expiry Date:") {
|
||||
res.expireDate = parseDate(strings.TrimSpace(strings.TrimPrefix(line, "Registry Expiry Date:")))
|
||||
}
|
||||
if strings.HasPrefix(line, "Registrar:") {
|
||||
res.registar = strings.TrimSpace(strings.TrimPrefix(line, "Registrar:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Status:") {
|
||||
res.statusRaw = strings.TrimSpace(strings.TrimPrefix(line, "Status:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Name Server:") {
|
||||
res.nsServers = append(res.nsServers, strings.TrimSpace(strings.TrimPrefix(line, "Name Server:")))
|
||||
}
|
||||
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:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Registrant Organization:") {
|
||||
r.Org = strings.TrimSpace(strings.TrimPrefix(line, "Registrant Organization:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Registrant Street:") {
|
||||
r.Addr = strings.TrimSpace(strings.TrimPrefix(line, "Registrant Street:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Registrant City:") {
|
||||
r.City = strings.TrimSpace(strings.TrimPrefix(line, "Registrant City:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Registrant State/Province:") {
|
||||
r.State = strings.TrimSpace(strings.TrimPrefix(line, "Registrant State/Province:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Registrant Postal Code:") {
|
||||
r.Zip = strings.TrimSpace(strings.TrimPrefix(line, "Registrant Postal Code:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Registrant Country:") {
|
||||
r.Country = strings.TrimSpace(strings.TrimPrefix(line, "Registrant Country:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Registrant Phone:") {
|
||||
r.Phone = strings.TrimSpace(strings.TrimPrefix(line, "Registrant Phone:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Registrant Phone Ext:") {
|
||||
r.PhoneExt = strings.TrimSpace(strings.TrimPrefix(line, "Registrant Phone Ext:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Registrant Fax:") {
|
||||
r.Fax = strings.TrimSpace(strings.TrimPrefix(line, "Registrant Fax:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Registrant Fax Ext:") {
|
||||
r.FaxExt = strings.TrimSpace(strings.TrimPrefix(line, "Registrant Fax Ext:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Registrant Email:") {
|
||||
r.Email = strings.TrimSpace(strings.TrimPrefix(line, "Registrant Email:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Admin Name:") {
|
||||
a.Name = strings.TrimSpace(strings.TrimPrefix(line, "Admin Name:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Admin Organization:") {
|
||||
a.Org = strings.TrimSpace(strings.TrimPrefix(line, "Admin Organization:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Admin Street:") {
|
||||
a.Addr = strings.TrimSpace(strings.TrimPrefix(line, "Admin Street:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Admin City:") {
|
||||
a.City = strings.TrimSpace(strings.TrimPrefix(line, "Admin City:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Admin State/Province:") {
|
||||
a.State = strings.TrimSpace(strings.TrimPrefix(line, "Admin State/Province:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Admin Postal Code:") {
|
||||
a.Zip = strings.TrimSpace(strings.TrimPrefix(line, "Admin Postal Code:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Admin Country:") {
|
||||
a.Country = strings.TrimSpace(strings.TrimPrefix(line, "Admin Country:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Admin Phone:") {
|
||||
a.Phone = strings.TrimSpace(strings.TrimPrefix(line, "Admin Phone:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Admin Phone Ext:") {
|
||||
a.PhoneExt = strings.TrimSpace(strings.TrimPrefix(line, "Admin Phone Ext:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Admin Fax:") {
|
||||
a.Fax = strings.TrimSpace(strings.TrimPrefix(line, "Admin Fax:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Admin Fax Ext:") {
|
||||
a.FaxExt = strings.TrimSpace(strings.TrimPrefix(line, "Admin Fax Ext:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Admin Email:") {
|
||||
a.Email = strings.TrimSpace(strings.TrimPrefix(line, "Admin Email:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Tech Name:") {
|
||||
t.Name = strings.TrimSpace(strings.TrimPrefix(line, "Tech Name:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Tech Organization:") {
|
||||
t.Org = strings.TrimSpace(strings.TrimPrefix(line, "Tech Organization:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Tech Street:") {
|
||||
t.Addr = strings.TrimSpace(strings.TrimPrefix(line, "Tech Street:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Tech City:") {
|
||||
t.City = strings.TrimSpace(strings.TrimPrefix(line, "Tech City:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Tech State/Province:") {
|
||||
t.State = strings.TrimSpace(strings.TrimPrefix(line, "Tech State/Province:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Tech Postal Code:") {
|
||||
t.Zip = strings.TrimSpace(strings.TrimPrefix(line, "Tech Postal Code:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Tech Country:") {
|
||||
t.Country = strings.TrimSpace(strings.TrimPrefix(line, "Tech Country:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Tech Phone:") {
|
||||
t.Phone = strings.TrimSpace(strings.TrimPrefix(line, "Tech Phone:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Tech Phone Ext:") {
|
||||
t.PhoneExt = strings.TrimSpace(strings.TrimPrefix(line, "Tech Phone Ext:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Tech Fax:") {
|
||||
t.Fax = strings.TrimSpace(strings.TrimPrefix(line, "Tech Fax:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Tech Fax Ext:") {
|
||||
t.FaxExt = strings.TrimSpace(strings.TrimPrefix(line, "Tech Fax Ext:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Tech Email:") {
|
||||
t.Email = strings.TrimSpace(strings.TrimPrefix(line, "Tech Email:"))
|
||||
}
|
||||
}
|
||||
res.registerInfo = r
|
||||
res.adminInfo = a
|
||||
res.techInfo = t
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func dotCNParser(domain, data string) (Result, error) {
|
||||
var res = Result{
|
||||
domain: domain,
|
||||
rawData: data,
|
||||
}
|
||||
var r PersonalInfo
|
||||
if strings.HasPrefix("No matching record.", strings.TrimSpace(data)) {
|
||||
res.exists = false
|
||||
return res, nil
|
||||
}
|
||||
res.exists = true
|
||||
split := strings.Split(data, "\n")
|
||||
for _, line := range split {
|
||||
line = strings.TrimSpace(line)
|
||||
if strings.HasPrefix(line, "Domain Name:") {
|
||||
res.nsServers = []string{}
|
||||
res.domain = strings.TrimSpace(strings.TrimPrefix(line, "Domain Name:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "ROID:") {
|
||||
res.domainID = strings.TrimSpace(strings.TrimPrefix(line, "ROID:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Registration Time:") {
|
||||
res.registerDate = parseCNDate(strings.TrimSpace(strings.TrimPrefix(line, "Registration Time:")))
|
||||
}
|
||||
if strings.HasPrefix(line, "Expiration Time:") {
|
||||
res.expireDate = parseCNDate(strings.TrimSpace(strings.TrimPrefix(line, "Expiration Time:")))
|
||||
}
|
||||
if strings.HasPrefix(line, "Sponsoring Registrar:") {
|
||||
res.registar = strings.TrimSpace(strings.TrimPrefix(line, "Sponsoring Registrar:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Status:") {
|
||||
res.statusRaw = strings.TrimSpace(strings.TrimPrefix(line, "Status:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Name Server:") {
|
||||
res.nsServers = append(res.nsServers, strings.TrimSpace(strings.TrimPrefix(line, "Name Server:")))
|
||||
}
|
||||
if strings.HasPrefix(line, "DNSSEC:") {
|
||||
res.dnssec = strings.TrimSpace(strings.TrimPrefix(line, "DNSSEC:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Registrant:") {
|
||||
r.Name = strings.TrimSpace(strings.TrimPrefix(line, "Registrant:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Registrant Contact Email:") {
|
||||
r.Email = strings.TrimSpace(strings.TrimPrefix(line, "Registrant Contact Email:"))
|
||||
}
|
||||
}
|
||||
res.registerInfo = r
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func dotJPParser(domain, data string) (Result, error) {
|
||||
var res = Result{
|
||||
domain: domain,
|
||||
rawData: data,
|
||||
}
|
||||
var r PersonalInfo
|
||||
split := strings.Split(data, "\n")
|
||||
startAddress := false
|
||||
for _, line := range split {
|
||||
if strings.HasPrefix(line, "No match!!") {
|
||||
res.exists = false
|
||||
return res, nil
|
||||
}
|
||||
line = strings.TrimSpace(line)
|
||||
if strings.HasPrefix(line, "[Domain Name]") {
|
||||
res.nsServers = []string{}
|
||||
res.domain = strings.TrimSpace(strings.TrimPrefix(line, "[Domain Name]"))
|
||||
}
|
||||
if strings.HasPrefix(line, "[登録者名]") {
|
||||
r.Name = strings.TrimSpace(strings.TrimPrefix(line, "[登録者名]"))
|
||||
}
|
||||
if strings.HasPrefix(line, "[Name Server]") {
|
||||
res.nsServers = append(res.nsServers, strings.TrimSpace(strings.TrimPrefix(line, "[Name Server]")))
|
||||
}
|
||||
if strings.HasPrefix(line, "[状態]") {
|
||||
res.statusRaw += strings.TrimSpace(strings.TrimPrefix(line, "[状態]"))
|
||||
}
|
||||
if strings.HasPrefix(line, "[ロック状態]") {
|
||||
res.statusRaw += strings.TrimSpace(strings.TrimPrefix(line, "[ロック状態]"))
|
||||
}
|
||||
if strings.HasPrefix(line, "[登録年月日]") {
|
||||
res.registerDate = parseJPDate(strings.TrimSpace(strings.TrimPrefix(line, "[登録年月日]")), true)
|
||||
}
|
||||
if strings.HasPrefix(line, "[最終更新]") {
|
||||
res.updateDate = parseJPDate(strings.TrimSpace(strings.TrimPrefix(line, "[最終更新]")), false)
|
||||
}
|
||||
if strings.HasPrefix(line, "[有効期限]") {
|
||||
res.expireDate = parseJPDate(strings.TrimSpace(strings.TrimPrefix(line, "[有効期限]")), true)
|
||||
}
|
||||
if strings.HasPrefix(line, "[名前]") {
|
||||
r.Name = strings.TrimSpace(strings.TrimPrefix(line, "[名前]"))
|
||||
}
|
||||
if strings.HasPrefix(line, "[組織名]") {
|
||||
r.Org = strings.TrimSpace(strings.TrimPrefix(line, "[組織名]"))
|
||||
}
|
||||
if strings.HasPrefix(line, "[Email]") {
|
||||
r.Email = strings.TrimSpace(strings.TrimPrefix(line, "[Email]"))
|
||||
}
|
||||
if strings.HasPrefix(line, "[住所]") {
|
||||
r.Addr = strings.TrimSpace(strings.TrimPrefix(line, "[住所]"))
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(line, "[Postal Address]") {
|
||||
startAddress = false
|
||||
continue
|
||||
}
|
||||
if startAddress {
|
||||
r.Addr += "\n" + line
|
||||
}
|
||||
if strings.HasPrefix(line, "[電話番号]") {
|
||||
r.Phone = strings.TrimSpace(strings.TrimPrefix(line, "[電話番号]"))
|
||||
}
|
||||
if strings.HasPrefix(line, "[FAX番号]") {
|
||||
r.Fax = strings.TrimSpace(strings.TrimPrefix(line, "[FAX番号]"))
|
||||
break
|
||||
}
|
||||
}
|
||||
res.registerInfo = r
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func dotTWParser(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 strings.HasPrefix(line, "No Found") || strings.HasPrefix(line, "網域名稱不合規定") {
|
||||
res.exists = false
|
||||
return res, nil
|
||||
}
|
||||
if strings.HasPrefix(line, "Domain Name:") {
|
||||
res.exists = true
|
||||
res.nsServers = []string{}
|
||||
res.domain = strings.TrimSpace(strings.TrimPrefix(line, "Domain Name:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Registration Service Provider:") {
|
||||
startNs = false
|
||||
res.registar = strings.TrimSpace(strings.TrimPrefix(line, "Registration Service Provider:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Record created on") {
|
||||
res.registerDate = parseCNDate(strings.TrimSpace(strings.TrimSuffix(strings.TrimPrefix(line, "Record created on"), "(UTC+8)")))
|
||||
}
|
||||
if strings.HasPrefix(line, "Record expires on") {
|
||||
res.expireDate = parseCNDate(strings.TrimSpace(strings.TrimSuffix(strings.TrimPrefix(line, "Record expires on"), "(UTC+8)")))
|
||||
}
|
||||
if strings.HasPrefix(line, "Domain Status:") {
|
||||
res.statusRaw = strings.TrimSpace(strings.TrimPrefix(line, "Domain Status:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Domain servers in listed order:") {
|
||||
startNs = true
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(line, "Registrant:") {
|
||||
currentStatus = 1
|
||||
startIdx = idx
|
||||
p = PersonalInfo{}
|
||||
continue
|
||||
}
|
||||
if currentStatus > 0 {
|
||||
if line == "(Redacted for privacy)" {
|
||||
switch currentStatus {
|
||||
case 1:
|
||||
r = p
|
||||
case 2:
|
||||
a = p
|
||||
case 3:
|
||||
t = p
|
||||
}
|
||||
continue
|
||||
}
|
||||
switch idx - startIdx {
|
||||
case 1:
|
||||
p.Name = line
|
||||
case 3:
|
||||
p.Email = line
|
||||
case 4:
|
||||
p.Country = line
|
||||
}
|
||||
}
|
||||
if startNs {
|
||||
res.nsServers = append(res.nsServers, line)
|
||||
}
|
||||
}
|
||||
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 = t.In(time.Local)
|
||||
return t
|
||||
}
|
||||
|
||||
func parseCNDate(date string) time.Time {
|
||||
t, _ := time.ParseInLocation("2006-01-02 15:04:05", date, time.FixedZone("CST", 8*3600))
|
||||
t = t.In(time.Local)
|
||||
return t
|
||||
}
|
||||
|
||||
func parseJPDate(date string, onlyMD bool) time.Time {
|
||||
if onlyMD {
|
||||
t, _ := time.ParseInLocation("2006/01/02", date, time.FixedZone("JST", 9*3600))
|
||||
t = t.In(time.Local)
|
||||
return t
|
||||
}
|
||||
t, _ := time.ParseInLocation("2006/01/02 15:04:05 (JST)", date, time.FixedZone("JST", 9*3600))
|
||||
t = t.In(time.Local)
|
||||
return t
|
||||
}
|
||||
11
parse_test.go
Normal file
11
parse_test.go
Normal file
@ -0,0 +1,11 @@
|
||||
package whois
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestWhois(t *testing.T) {
|
||||
c := NewClient()
|
||||
fmt.Println(c.Whois("b612.com"))
|
||||
}
|
||||
258
whois.go
Normal file
258
whois.go
Normal file
@ -0,0 +1,258 @@
|
||||
package whois
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/proxy"
|
||||
)
|
||||
|
||||
const (
|
||||
defWhoisServer = "whois.iana.org"
|
||||
defWhoisPort = "43"
|
||||
defTimeout = 30 * time.Second
|
||||
asnPrefix = "AS"
|
||||
)
|
||||
|
||||
// DefaultClient is default whois client
|
||||
var DefaultClient = NewClient()
|
||||
|
||||
// Client is whois client
|
||||
type Client struct {
|
||||
dialer proxy.Dialer
|
||||
timeout time.Duration
|
||||
elapsed time.Duration
|
||||
disableReferral bool
|
||||
}
|
||||
type PersonalInfo struct {
|
||||
FirstName string
|
||||
LastName string
|
||||
Name string
|
||||
Org string
|
||||
Fax string
|
||||
FaxExt string
|
||||
Addr string
|
||||
City string
|
||||
State string
|
||||
Country string
|
||||
Zip string
|
||||
Phone string
|
||||
PhoneExt string
|
||||
Email string
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// NewClient returns new whois client
|
||||
func NewClient() *Client {
|
||||
return &Client{
|
||||
dialer: &net.Dialer{
|
||||
Timeout: defTimeout,
|
||||
},
|
||||
timeout: defTimeout,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Whois(domain string, servers ...string) (string, error) {
|
||||
data, err := c.whois(domain)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
parse(domain, data)
|
||||
return "", err
|
||||
}
|
||||
|
||||
func (c *Client) whois(domain string, servers ...string) (result string, err error) {
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
result = strings.TrimSpace(result)
|
||||
if result != "" {
|
||||
result = fmt.Sprintf("%s\n\n%% Query time: %d msec\n%% WHEN: %s\n",
|
||||
result, time.Since(start).Milliseconds(), start.Format("Mon Jan 02 15:04:05 MST 2006"),
|
||||
)
|
||||
}
|
||||
}()
|
||||
|
||||
domain = strings.Trim(strings.TrimSpace(domain), ".")
|
||||
if domain == "" {
|
||||
return "", errors.New("whois: domain is empty")
|
||||
}
|
||||
|
||||
isASN := IsASN(domain)
|
||||
if isASN {
|
||||
if !strings.HasPrefix(strings.ToUpper(domain), asnPrefix) {
|
||||
domain = asnPrefix + domain
|
||||
}
|
||||
}
|
||||
|
||||
if !strings.Contains(domain, ".") && !strings.Contains(domain, ":") && !isASN {
|
||||
return c.rawQuery(domain, defWhoisServer, defWhoisPort)
|
||||
}
|
||||
|
||||
var server, port string
|
||||
if len(servers) > 0 && servers[0] != "" {
|
||||
server = strings.ToLower(servers[0])
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
result, err = c.rawQuery(domain, server, port)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if c.disableReferral {
|
||||
return
|
||||
}
|
||||
|
||||
refServer, refPort := getServer(result)
|
||||
if refServer == "" || refServer == server {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := c.rawQuery(domain, refServer, refPort)
|
||||
if err == nil {
|
||||
result += data
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SetDialer set query net dialer
|
||||
func (c *Client) SetDialer(dialer proxy.Dialer) *Client {
|
||||
c.dialer = dialer
|
||||
return c
|
||||
}
|
||||
|
||||
// SetTimeout set query timeout
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
c.timeout = timeout
|
||||
return c
|
||||
}
|
||||
|
||||
// rawQuery do raw query to the server
|
||||
func (c *Client) rawQuery(domain, server, port string) (string, error) {
|
||||
c.elapsed = 0
|
||||
start := time.Now()
|
||||
|
||||
if server == "whois.arin.net" {
|
||||
if IsASN(domain) {
|
||||
domain = "a + " + domain
|
||||
} else {
|
||||
domain = "n + " + domain
|
||||
}
|
||||
}
|
||||
|
||||
conn, err := c.dialer.Dial("tcp", net.JoinHostPort(server, port))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("whois: connect to whois server failed: %w", err)
|
||||
}
|
||||
|
||||
defer conn.Close()
|
||||
c.elapsed = time.Since(start)
|
||||
|
||||
_ = conn.SetWriteDeadline(time.Now().Add(c.timeout - c.elapsed))
|
||||
_, err = conn.Write([]byte(domain + "\r\n"))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("whois: send to whois server failed: %w", err)
|
||||
}
|
||||
|
||||
c.elapsed = time.Since(start)
|
||||
|
||||
_ = conn.SetReadDeadline(time.Now().Add(c.timeout - c.elapsed))
|
||||
buffer, err := io.ReadAll(conn)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("whois: read from whois server failed: %w", err)
|
||||
}
|
||||
|
||||
c.elapsed = time.Since(start)
|
||||
|
||||
return string(buffer), nil
|
||||
}
|
||||
|
||||
// getExtension returns extension of domain
|
||||
func getExtension(domain string) string {
|
||||
ext := domain
|
||||
|
||||
if net.ParseIP(domain) == nil {
|
||||
domains := strings.Split(domain, ".")
|
||||
ext = domains[len(domains)-1]
|
||||
}
|
||||
|
||||
if strings.Contains(ext, "/") {
|
||||
ext = strings.Split(ext, "/")[0]
|
||||
}
|
||||
|
||||
return ext
|
||||
}
|
||||
|
||||
// getServer returns server from whois data
|
||||
func getServer(data string) (string, string) {
|
||||
tokens := []string{
|
||||
"Registrar WHOIS Server: ",
|
||||
"whois: ",
|
||||
"ReferralServer: ",
|
||||
"refer: ",
|
||||
}
|
||||
for _, token := range tokens {
|
||||
start := strings.Index(data, token)
|
||||
if start != -1 {
|
||||
start += len(token)
|
||||
end := strings.Index(data[start:], "\n")
|
||||
server := strings.TrimSpace(data[start : start+end])
|
||||
server = strings.TrimPrefix(server, "http:")
|
||||
server = strings.TrimPrefix(server, "https:")
|
||||
server = strings.TrimPrefix(server, "whois:")
|
||||
server = strings.TrimPrefix(server, "rwhois:")
|
||||
server = strings.Trim(server, "/")
|
||||
port := defWhoisPort
|
||||
if strings.Contains(server, ":") {
|
||||
v := strings.Split(server, ":")
|
||||
server, port = v[0], v[1]
|
||||
}
|
||||
return server, port
|
||||
}
|
||||
}
|
||||
return "", ""
|
||||
}
|
||||
|
||||
// IsASN returns if s is ASN
|
||||
func IsASN(s string) bool {
|
||||
s = strings.ToUpper(s)
|
||||
|
||||
s = strings.TrimPrefix(s, asnPrefix)
|
||||
_, err := strconv.Atoi(s)
|
||||
|
||||
return err == nil
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user