package whois import "strings" var notFoundTokens = []string{ "no object found", "domain not found", "no match for", "no match", "no data found", "no entries found", "no matching record", "no found", "query returned 0 objects", "does not have any data for", "domain unknown", "domain has not been registered", "the domain name is not available", "domain name is not available", "reserved name", "reserved domain name", "cannot be registered", "can not be registered", "the queried object does not exist", } var accessDeniedTokens = []string{ "this tld has no whois server", "requests of this client are not permitted", "restricted to specifically qualified registrants", "ip address used to perform the query is not authorised", "ip address used to perform the query is not authorised", } func isNotFoundLine(line string) bool { return isStrictNotFoundLine(line) || isAccessDeniedLine(line) } func isStrictNotFoundLine(line string) bool { l := strings.ToLower(strings.TrimSpace(line)) if l == "" { return false } for _, t := range notFoundTokens { if strings.Contains(l, t) { return true } } return false } func isAccessDeniedLine(line string) bool { l := strings.ToLower(strings.TrimSpace(line)) if l == "" { return false } for _, t := range accessDeniedTokens { if strings.Contains(l, t) { return true } } return false } func rawHasNotFound(raw string) bool { for _, line := range strings.Split(raw, "\n") { if isStrictNotFoundLine(line) { return true } } return false } func rawHasAccessDenied(raw string) bool { for _, line := range strings.Split(raw, "\n") { if isAccessDeniedLine(line) { return true } } return false } func splitKV(line string) (key, val string, ok bool) { if i := strings.Index(line, ":"); i > 0 { key = strings.TrimSpace(line[:i]) if key == "" || strings.Contains(key, "://") { return "", "", false } return key, strings.TrimSpace(line[i+1:]), true } return "", "", false } func normalizeKey(k string) string { k = strings.ToLower(strings.TrimSpace(k)) if k == "" { return "" } for strings.Contains(k, "..") { k = strings.ReplaceAll(k, "..", ".") } k = strings.Trim(k, ". ") replacer := strings.NewReplacer( ".", " ", "\t", " ", "_", " ", "-", " ", "/", " ", ) k = replacer.Replace(k) k = strings.Join(strings.Fields(k), " ") return k } func canonicalKey(k string) string { n := normalizeKey(k) switch n { case "domain", "domain name", "domainname", "nom de domaine": return "domain" case "registry domain id", "domain id", "roid": return "domain_id" case "updated date", "last modified", "domain record last updated", "changed", "record last updated on", "last updated": return "updated_at" case "creation date", "registration time", "registered", "domain record activated", "created", "record created", "domain created": return "created_at" case "registry expiry date", "registrar registration expiration date", "expiration time", "domain expires", "expire", "expires", "record expires on", "renewal date": return "expired_at" case "registrar", "sponsoring registrar", "registrar name", "registration service provider", "current registar", "registar created", "registrar handle": return "registrar" case "status", "domain status", "registration status", "domain state", "domaintype": return "status" case "name server", "name servers", "name server information", "nameserver", "nameservers", "nserver", "primary server", "secondary server": return "nameserver" case "dnssec", "ds records": return "dnssec" } if strings.Contains(n, "modification") && strings.Contains(n, "derni") { return "updated_at" } if strings.Contains(n, "date d expiration") || strings.Contains(n, "date de expiration") { return "expired_at" } if strings.Contains(n, "date de c") && strings.Contains(n, "ation") { return "created_at" } if strings.HasPrefix(n, "name server") || strings.HasPrefix(n, "nameserver") || strings.HasPrefix(n, "name servers in") { return "nameserver" } return n }