update
This commit is contained in:
parent
83cfd8c5e7
commit
129514a159
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
|||||||
.idea
|
.idea
|
||||||
|
bin
|
||||||
2
go.mod
2
go.mod
@ -2,4 +2,4 @@ module b612.me/sdk/whois
|
|||||||
|
|
||||||
go 1.21.2
|
go 1.21.2
|
||||||
|
|
||||||
require golang.org/x/net v0.24.0
|
require golang.org/x/net v0.28.0
|
||||||
|
|||||||
4
go.sum
4
go.sum
@ -1,2 +1,2 @@
|
|||||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||||
|
|||||||
499
parse.go
499
parse.go
@ -16,13 +16,29 @@ func parse(domain string, result string) (Result, error) {
|
|||||||
data, err = dotJPParser(domain, result)
|
data, err = dotJPParser(domain, result)
|
||||||
case "tw":
|
case "tw":
|
||||||
data, err = dotTWParser(domain, result)
|
data, err = dotTWParser(domain, result)
|
||||||
|
case "name":
|
||||||
|
data, err = dotNameParser(domain, result)
|
||||||
default:
|
default:
|
||||||
data, err = commonParser(domain, result)
|
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
|
return data, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func commonParser(domain, data string) (Result, error) {
|
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{
|
var res = Result{
|
||||||
domain: domain,
|
domain: domain,
|
||||||
rawData: data,
|
rawData: data,
|
||||||
@ -34,8 +50,8 @@ func commonParser(domain, data string) (Result, error) {
|
|||||||
for _, line := range split {
|
for _, line := range split {
|
||||||
line = strings.TrimSpace(line)
|
line = strings.TrimSpace(line)
|
||||||
if !res.exists {
|
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"} {
|
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(token, line) {
|
if strings.HasPrefix(line, token) {
|
||||||
res.exists = false
|
res.exists = false
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
@ -43,6 +59,9 @@ func commonParser(domain, data string) (Result, error) {
|
|||||||
}
|
}
|
||||||
if strings.HasPrefix(line, "Domain Name:") {
|
if strings.HasPrefix(line, "Domain Name:") {
|
||||||
res.exists = true
|
res.exists = true
|
||||||
|
res.hasUpdateDate = hasUpdate
|
||||||
|
res.hasRegisterDate = hasCreate
|
||||||
|
res.hasExpireDate = hasExpire
|
||||||
res.nsServers = []string{}
|
res.nsServers = []string{}
|
||||||
res.domain = strings.TrimSpace(strings.TrimPrefix(line, "Domain Name:"))
|
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:"))
|
res.domainID = strings.TrimSpace(strings.TrimPrefix(line, "Registry Domain ID:"))
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(line, "Updated Date:") {
|
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:") {
|
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:") {
|
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:") {
|
if strings.HasPrefix(line, "Registrar:") {
|
||||||
res.registar = strings.TrimSpace(strings.TrimPrefix(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{}{}
|
statusMap[strings.Split(strings.TrimSpace(strings.TrimPrefix(line, "Status:")), " ")[0]] = struct{}{}
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(line, "Domain Status:") {
|
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{}{}
|
statusMap[strings.Split(strings.TrimSpace(strings.TrimPrefix(line, "Domain Status:")), " ")[0]] = struct{}{}
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(line, "Name Server:") {
|
if strings.HasPrefix(line, "Name Server:") {
|
||||||
@ -73,9 +111,6 @@ func commonParser(domain, data string) (Result, error) {
|
|||||||
if strings.HasPrefix(line, "DNSSEC:") {
|
if strings.HasPrefix(line, "DNSSEC:") {
|
||||||
res.dnssec = strings.TrimSpace(strings.TrimPrefix(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:") {
|
if strings.HasPrefix(line, "Registrant Name:") {
|
||||||
r.Name = strings.TrimSpace(strings.TrimPrefix(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
|
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) {
|
func dotCNParser(domain, data string) (Result, error) {
|
||||||
var res = Result{
|
var res = Result{
|
||||||
domain: domain,
|
domain: domain,
|
||||||
@ -204,6 +294,13 @@ func dotCNParser(domain, data string) (Result, error) {
|
|||||||
res.exists = false
|
res.exists = false
|
||||||
return res, nil
|
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
|
res.exists = true
|
||||||
statusMap := make(map[string]struct{})
|
statusMap := make(map[string]struct{})
|
||||||
split := strings.Split(data, "\n")
|
split := strings.Split(data, "\n")
|
||||||
@ -267,6 +364,9 @@ func dotJPParser(domain, data string) (Result, error) {
|
|||||||
line = strings.TrimSpace(line)
|
line = strings.TrimSpace(line)
|
||||||
if strings.HasPrefix(line, "[Domain Name]") {
|
if strings.HasPrefix(line, "[Domain Name]") {
|
||||||
res.exists = true
|
res.exists = true
|
||||||
|
res.hasUpdateDate = true
|
||||||
|
res.hasRegisterDate = true
|
||||||
|
res.hasExpireDate = true
|
||||||
res.nsServers = []string{}
|
res.nsServers = []string{}
|
||||||
res.domain = strings.TrimSpace(strings.TrimPrefix(line, "[Domain Name]"))
|
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:") {
|
if strings.HasPrefix(line, "Domain Name:") {
|
||||||
res.exists = true
|
res.exists = true
|
||||||
|
res.hasUpdateDate = true
|
||||||
|
res.hasRegisterDate = true
|
||||||
|
res.hasExpireDate = true
|
||||||
res.nsServers = []string{}
|
res.nsServers = []string{}
|
||||||
res.domain = strings.TrimSpace(strings.TrimPrefix(line, "Domain Name:"))
|
res.domain = strings.TrimSpace(strings.TrimPrefix(line, "Domain Name:"))
|
||||||
}
|
}
|
||||||
@ -398,8 +501,373 @@ func dotTWParser(domain, data string) (Result, error) {
|
|||||||
return res, nil
|
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 {
|
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)
|
t = t.In(time.Local)
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
@ -420,3 +888,16 @@ func parseJPDate(date string, onlyMD bool) time.Time {
|
|||||||
t = t.In(time.Local)
|
t = t.In(time.Local)
|
||||||
return t
|
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
|
||||||
|
}
|
||||||
|
|||||||
122
parse_test.go
122
parse_test.go
@ -2,10 +2,128 @@ package whois
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestWhois(t *testing.T) {
|
func TestWhoisInfo(t *testing.T) {
|
||||||
c := NewClient()
|
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())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
41
whois.go
41
whois.go
@ -7,6 +7,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/net/proxy"
|
"golang.org/x/net/proxy"
|
||||||
@ -76,8 +77,12 @@ var statusMap = map[string]string{
|
|||||||
// DefaultClient is default whois client
|
// DefaultClient is default whois client
|
||||||
var DefaultClient = NewClient()
|
var DefaultClient = NewClient()
|
||||||
|
|
||||||
|
var defaultWhoisMap = map[string]string{}
|
||||||
|
|
||||||
// Client is whois client
|
// Client is whois client
|
||||||
type Client struct {
|
type Client struct {
|
||||||
|
extCache map[string]string
|
||||||
|
mu sync.Mutex
|
||||||
dialer proxy.Dialer
|
dialer proxy.Dialer
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
elapsed time.Duration
|
elapsed time.Duration
|
||||||
@ -106,11 +111,15 @@ type Result struct {
|
|||||||
domainID string
|
domainID string
|
||||||
rawData string
|
rawData string
|
||||||
registar string
|
registar string
|
||||||
|
hasRegisterDate bool
|
||||||
registerDate time.Time
|
registerDate time.Time
|
||||||
|
hasUpdateDate bool
|
||||||
updateDate time.Time
|
updateDate time.Time
|
||||||
|
hasExpireDate bool
|
||||||
expireDate time.Time
|
expireDate time.Time
|
||||||
statusRaw []string
|
statusRaw []string
|
||||||
nsServers []string
|
nsServers []string
|
||||||
|
nsIps []string
|
||||||
dnssec string
|
dnssec string
|
||||||
whoisSer string
|
whoisSer string
|
||||||
ianaID string
|
ianaID string
|
||||||
@ -119,6 +128,22 @@ type Result struct {
|
|||||||
techInfo 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 {
|
func (r Result) Exists() bool {
|
||||||
return r.exists
|
return r.exists
|
||||||
}
|
}
|
||||||
@ -189,12 +214,18 @@ func NewClient() *Client {
|
|||||||
dialer: &net.Dialer{
|
dialer: &net.Dialer{
|
||||||
Timeout: defTimeout,
|
Timeout: defTimeout,
|
||||||
},
|
},
|
||||||
|
extCache: make(map[string]string),
|
||||||
timeout: defTimeout,
|
timeout: defTimeout,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Whois(domain string, servers ...string) (Result, error) {
|
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 {
|
if err != nil {
|
||||||
return Result{}, err
|
return Result{}, err
|
||||||
}
|
}
|
||||||
@ -234,6 +265,7 @@ func (c *Client) whois(domain string, servers ...string) (result string, err err
|
|||||||
port = defWhoisPort
|
port = defWhoisPort
|
||||||
} else {
|
} else {
|
||||||
ext := getExtension(domain)
|
ext := getExtension(domain)
|
||||||
|
if _, ok := c.extCache[ext]; !ok {
|
||||||
result, err := c.rawQuery(ext, defWhoisServer, defWhoisPort)
|
result, err := c.rawQuery(ext, defWhoisServer, defWhoisPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("whois: query for whois server failed: %w", err)
|
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 == "" {
|
if server == "" {
|
||||||
return "", fmt.Errorf("%w: %s", errors.New("whois server not found"), domain)
|
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)
|
result, err = c.rawQuery(domain, server, port)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user