2024-08-15 08:38:28 +08:00
package whois
import (
"strings"
"time"
)
2024-08-15 15:10:18 +08:00
func parse ( domain string , result string ) ( Result , error ) {
2024-08-15 08:38:28 +08:00
ext := getExtension ( domain )
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 )
}
2024-08-15 15:10:18 +08:00
return data , err
2024-08-15 08:38:28 +08:00
}
func commonParser ( domain , data string ) ( Result , error ) {
var res = Result {
domain : domain ,
rawData : data ,
}
var r , a , t PersonalInfo
split := strings . Split ( data , "\n" )
2024-08-15 15:10:18 +08:00
statusMap := make ( map [ string ] struct { } )
2024-08-15 08:38:28 +08:00
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:" ) {
2024-08-15 15:10:18 +08:00
statusMap [ strings . Split ( strings . TrimSpace ( strings . TrimPrefix ( line , "Status:" ) ) , " " ) [ 0 ] ] = struct { } { }
}
if strings . HasPrefix ( line , "Domain Status:" ) {
statusMap [ strings . Split ( strings . TrimSpace ( strings . TrimPrefix ( line , "Domain Status:" ) ) , " " ) [ 0 ] ] = struct { } { }
2024-08-15 08:38:28 +08:00
}
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:" ) )
}
}
2024-08-15 15:10:18 +08:00
for status := range statusMap {
res . statusRaw = append ( res . statusRaw , status )
}
2024-08-15 08:38:28 +08:00
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
2024-08-15 15:10:18 +08:00
statusMap := make ( map [ string ] struct { } )
2024-08-15 08:38:28 +08:00
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:" ) {
2024-08-15 15:10:18 +08:00
statusMap [ strings . Split ( strings . TrimSpace ( strings . TrimPrefix ( line , "Status:" ) ) , " " ) [ 0 ] ] = struct { } { }
}
if strings . HasPrefix ( line , "Domain Status:" ) {
statusMap [ strings . Split ( strings . TrimSpace ( strings . TrimPrefix ( line , "Domain Status:" ) ) , " " ) [ 0 ] ] = struct { } { }
2024-08-15 08:38:28 +08:00
}
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:" ) )
}
}
2024-08-15 15:10:18 +08:00
for status := range statusMap {
res . statusRaw = append ( res . statusRaw , status )
}
2024-08-15 08:38:28 +08:00
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]" ) {
2024-08-15 15:10:18 +08:00
res . exists = true
2024-08-15 08:38:28 +08:00
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 , "[状態]" ) {
2024-08-15 15:10:18 +08:00
res . statusRaw = append ( res . statusRaw , strings . TrimSpace ( strings . TrimPrefix ( line , "[状態]" ) ) )
2024-08-15 08:38:28 +08:00
}
if strings . HasPrefix ( line , "[ロック状態]" ) {
2024-08-15 15:10:18 +08:00
res . statusRaw = append ( res . statusRaw , strings . TrimSpace ( strings . TrimPrefix ( line , "[ロック状態]" ) ) )
2024-08-15 08:38:28 +08:00
}
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:" ) {
2024-08-15 15:10:18 +08:00
res . statusRaw = append ( res . statusRaw , strings . TrimSpace ( strings . TrimPrefix ( line , "Domain Status:" ) ) )
2024-08-15 08:38:28 +08:00
}
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
}