package whois import ( "strings" "time" ) // ===== Specialized parsers kept from old parse.go ===== func dotCNParser(domain, data string) (Result, error) { var res = Result{ domain: domain, rawData: data, } var r PersonalInfo trim := strings.TrimSpace(data) if strings.HasPrefix(trim, "No matching record.") { res.exists = false return res, nil } if strings.HasPrefix(trim, "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") 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.registrar = 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.hasUpdateDate = true res.hasRegisterDate = true res.hasExpireDate = 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, "[住所]")) startAddress = true 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, "網域名稱不合規定") || strings.Contains(strings.ToLower(line), "reserved name") { 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, "Registration Service Provider:") { startNs = false res.registrar = 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 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 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 && line != "" { res.nsServers = append(res.nsServers, line) } } res.registerInfo = r res.adminInfo = a res.techInfo = t 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 && line != "" { 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 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.registrar = strings.TrimSpace(strings.TrimPrefix(line, "Registrar:")) } if strings.HasPrefix(line, "Registrant:") { currentStatus = 1 p = PersonalInfo{} tmpSlice = []string{} continue } if strings.HasPrefix(line, "Administrative contact:") { currentStatus = 2 p = PersonalInfo{} tmpSlice = []string{} continue } if strings.HasPrefix(line, "Technical contact:") { currentStatus = 3 p = PersonalInfo{} tmpSlice = []string{} 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 ns == "" { continue } if idx == 0 { res.nsServers = append(res.nsServers, ns) } else { 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.TrimSpace(strings.Join(tmpSlice, "\n")) r = p case 2: if len(tmpSlice) > 2 { p.Addr = strings.TrimSpace(strings.Join(tmpSlice[:len(tmpSlice)-2], "\n")) p.Email = strings.TrimSpace(tmpSlice[len(tmpSlice)-2]) p.Phone = strings.TrimSpace(tmpSlice[len(tmpSlice)-1]) } else { p.Addr = strings.TrimSpace(strings.Join(tmpSlice, "\n")) } a = p case 3: if len(tmpSlice) > 2 { p.Addr = strings.TrimSpace(strings.Join(tmpSlice[:len(tmpSlice)-2], "\n")) p.Email = strings.TrimSpace(tmpSlice[len(tmpSlice)-2]) p.Phone = strings.TrimSpace(tmpSlice[len(tmpSlice)-1]) } else { p.Addr = strings.TrimSpace(strings.Join(tmpSlice, "\n")) } t = p } tmpSlice = []string{} 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 dotMkParser(domain, data string) (Result, error) { var res = Result{ domain: domain, rawData: data, } var r, a, t PersonalInfo split := strings.Split(data, "\n") cid := 0 for _, line := range split { line = strings.TrimSpace(line) if !res.exists { for _, token := range []string{"%ERROR:101: no entries found"} { if strings.HasPrefix(line, token) { res.exists = false return res, nil } } } if strings.HasPrefix(line, "domain:") { res.exists = true res.hasRegisterDate = true res.hasExpireDate = true res.nsServers = []string{} res.domain = strings.TrimSpace(strings.TrimPrefix(line, "domain:")) } if strings.HasPrefix(line, "registered:") { res.registerDate = parseMkDate(strings.TrimSpace(strings.TrimPrefix(line, "registered:")), 2) } if strings.HasPrefix(line, "expire:") { res.expireDate = parseMkDate(strings.TrimSpace(strings.TrimPrefix(line, "expire:")), 2) } if strings.HasPrefix(line, "changed:") { if res.updateDate.IsZero() { res.updateDate = parseMkDate(strings.TrimSpace(strings.TrimPrefix(line, "changed:")), 2) res.hasUpdateDate = true } } if strings.HasPrefix(line, "registrar:") { res.registrar = strings.TrimSpace(strings.TrimPrefix(line, "registrar:")) } if strings.HasPrefix(line, "contact:") { cid++ continue } if strings.HasPrefix(line, "org:") { switch cid { case 1: r.Org = strings.TrimSpace(strings.TrimPrefix(line, "org:")) case 2: a.Org = strings.TrimSpace(strings.TrimPrefix(line, "org:")) } continue } if strings.HasPrefix(line, "name:") { switch cid { case 1: r.Name = strings.TrimSpace(strings.TrimPrefix(line, "name:")) case 2: a.Name = strings.TrimSpace(strings.TrimPrefix(line, "name:")) } continue } if strings.HasPrefix(line, "address:") { switch cid { case 1: r.Addr += strings.TrimSpace(strings.TrimPrefix(line, "address:")) + "\n" case 2: a.Addr += strings.TrimSpace(strings.TrimPrefix(line, "address:")) + "\n" } continue } if strings.HasPrefix(line, "phone:") { switch cid { case 1: r.Phone = strings.TrimSpace(strings.TrimPrefix(line, "phone:")) case 2: a.Phone = strings.TrimSpace(strings.TrimPrefix(line, "phone:")) } continue } if strings.HasPrefix(line, "e-mail:") { switch cid { case 1: r.Email = strings.TrimSpace(strings.TrimPrefix(line, "e-mail:")) case 2: a.Email = strings.TrimSpace(strings.TrimPrefix(line, "e-mail:")) } continue } if strings.HasPrefix(line, "nserver:") { res.nsServers = append(res.nsServers, strings.TrimSpace(strings.TrimPrefix(line, "nserver:"))) } } r.Addr = strings.TrimSpace(r.Addr) a.Addr = strings.TrimSpace(a.Addr) res.registerInfo = r res.adminInfo = a res.techInfo = t return res, nil } func dotArParser(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{"El dominio no se encuentra registrado en NIC Argentina"} { if strings.HasPrefix(line, token) { res.exists = false return res, nil } } } if strings.HasPrefix(line, "domain:") { res.exists = true res.hasRegisterDate = true res.hasExpireDate = true res.nsServers = []string{} res.domain = strings.TrimSpace(strings.TrimPrefix(line, "domain:")) } if strings.HasPrefix(line, "registered:") { res.registerDate = parseYMDHMSDate(strings.TrimSpace(strings.TrimPrefix(line, "registered:")), -3) } if strings.HasPrefix(line, "expire:") { res.expireDate = parseYMDHMSDate(strings.TrimSpace(strings.TrimPrefix(line, "expire:")), -3) } if strings.HasPrefix(line, "changed:") { if res.updateDate.IsZero() { res.updateDate = parseYMDHMSDate(strings.TrimSpace(strings.TrimPrefix(line, "changed:")), -3) res.hasUpdateDate = true } } if strings.HasPrefix(line, "registrar:") { res.registrar = strings.TrimSpace(strings.TrimPrefix(line, "registrar:")) } if strings.HasPrefix(line, "name:") { r.Name = strings.TrimSpace(strings.TrimPrefix(line, "name:")) } if strings.HasPrefix(line, "nserver:") { tmp := strings.Split(strings.TrimSpace(strings.TrimPrefix(line, "nserver:")), " ") for idx, ns := range tmp { if idx == 0 { res.nsServers = append(res.nsServers, ns) continue } clean := strings.TrimSpace(strings.ReplaceAll(strings.ReplaceAll(ns, "(", ""), ")", "")) if clean != "" { res.nsIps = append(res.nsIps, clean) } } } } r.Addr = strings.TrimSpace(r.Addr) a.Addr = strings.TrimSpace(a.Addr) res.registerInfo = r res.adminInfo = a res.techInfo = t return res, nil } // ===== Date helpers kept for specialized parsers ===== func parseCNDate(date string) time.Time { t, _ := time.ParseInLocation("2006-01-02 15:04:05", date, time.FixedZone("CST", 8*3600)) return t.In(time.Local) } func parseJPDate(date string, onlyMD bool) time.Time { if onlyMD { t, _ := time.ParseInLocation("2006/01/02", date, time.FixedZone("JST", 9*3600)) return t.In(time.Local) } t, _ := time.ParseInLocation("2006/01/02 15:04:05 (JST)", date, time.FixedZone("JST", 9*3600)) return t.In(time.Local) } func parseEduDate(date string) time.Time { t, _ := time.Parse("02-Jan-2006", date) return t.In(time.Local) } func parseYMDDate(date string) time.Time { t, _ := time.Parse("2006-01-02", date) return t.In(time.Local) } func parseYMDHMSDate(date string, tz int) time.Time { t, _ := time.ParseInLocation("2006-01-02 15:04:05", date, time.FixedZone("myzone", tz*3600)) return t.In(time.Local) } func parseMkDate(date string, tz int) time.Time { t, err := time.ParseInLocation("02.01.2006 15:04:05", date, time.FixedZone("myzone", tz*3600)) if err == nil { return t.In(time.Local) } t, _ = time.Parse("02.01.2006", date) return t.In(time.Local) }