package whois import ( "strings" "time" ) func parse(domain string, result string) (Result, error) { 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) } return data, err } func commonParser(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{"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:") { 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{}{} } 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:")) } } 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, rawData: data, } var r PersonalInfo if strings.HasPrefix("No matching record.", strings.TrimSpace(data)) { res.exists = false return res, nil } res.exists = true statusMap := make(map[string]struct{}) 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:") { 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{}{} } 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:")) } } for status := range statusMap { res.statusRaw = append(res.statusRaw, status) } 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.exists = true 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 = append(res.statusRaw, strings.TrimSpace(strings.TrimPrefix(line, "[状態]"))) } if strings.HasPrefix(line, "[ロック状態]") { res.statusRaw = append(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 = append(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 }