public DomainInfo(string domain, TldRule tldRule) { if (string.IsNullOrEmpty(domain)) { return; } if (tldRule == null) { return; } var domainParts = domain.Split('.').Reverse().ToList(); var ruleParts = tldRule.Name.Split('.').Skip(tldRule.Type == TldRuleType.WildcardException ? 1 : 0).Reverse().ToList(); var tld = string.Join(".", domainParts.Take(ruleParts.Count).Reverse()); var registrableDomain = string.Join(".", domainParts.Take(ruleParts.Count + 1).Reverse()); if (domain.Equals(tld)) { return; } this.TldRule = tldRule; this.Hostname = domain; this.Tld = tld; this.RegistrableDomain = registrableDomain; this.Domain = domainParts.Skip(ruleParts.Count).FirstOrDefault(); var subDomain = string.Join(".", domainParts.Skip(ruleParts.Count + 1).Reverse()); this.SubDomain = string.IsNullOrEmpty(subDomain) ? null : subDomain; }
private void AddRule(TldRule tldRule) { var structure = this._domainDataStructure; var domainPart = string.Empty; var parts = tldRule.Name.Split('.').Reverse().ToList(); for (var i = 0; i < parts.Count; i++) { domainPart = parts[i]; if (parts.Count - 1 > i) { //Check if domain exists if (!structure.Nested.ContainsKey(domainPart)) { structure.Nested.Add(domainPart, new DomainDataStructure(domainPart)); } structure = structure.Nested[domainPart]; continue; } //Check if domain exists if (structure.Nested.ContainsKey(domainPart)) { structure.Nested[domainPart].TldRule = tldRule; continue; } structure.Nested.Add(domainPart, new DomainDataStructure(domainPart, tldRule)); } }
public IEnumerable <TldRule> ParseRules(IEnumerable <string> lines) { var items = new List <TldRule>(); var division = TldRuleDivision.Unknown; foreach (var line in lines.Select(x => x.Trim())) { //Ignore empty lines if (string.IsNullOrEmpty(line)) { continue; } //Ignore comments (and set Division) if (line.StartsWith("//")) { //Detect Division if (line.StartsWith("// ===BEGIN ICANN DOMAINS===")) { division = TldRuleDivision.ICANN; } else if (line.StartsWith("// ===END ICANN DOMAINS===")) { division = TldRuleDivision.Unknown; } else if (line.StartsWith("// ===BEGIN PRIVATE DOMAINS===")) { division = TldRuleDivision.Private; } else if (line.StartsWith("// ===END PRIVATE DOMAINS===")) { division = TldRuleDivision.Unknown; } continue; } //TODO: Handle rules with white-space (Allowed by spec, but not encountered in wild yet) var tldRule = new TldRule(line, division); items.Add(tldRule); } return(items); }
public DomainDataStructure(string domain, TldRule tldRule) { this.Domain = domain; this.TldRule = tldRule; this.Nested = new Dictionary <string, DomainDataStructure>(); }
public async Task <DomainInfo> ParseAsync(string domain) { if (string.IsNullOrEmpty(domain)) { return(null); } var isRulesLoaded = await _rulesLoaded.Value.ConfigureAwait(false); if (!isRulesLoaded) { throw new InvalidOperationException("Rules not loaded yet"); } //We use Uri methods to normalize host (So Punycode is converted to UTF-8 if (!domain.Contains("://")) { domain = string.Concat("https://", domain); } Uri uri; if (!Uri.TryCreate(domain, UriKind.RelativeOrAbsolute, out uri)) { return(null); } string normalizedDomain = uri.Host; string normalizedHost = uri.GetComponents(UriComponents.NormalizedHost, UriFormat.UriEscaped); //Normalize Punycode var parts = normalizedHost .Split('.') .Reverse() .ToList(); if (parts.Count == 0 || parts.Any(x => x.Equals(""))) { return(null); } var structure = this._domainDataStructure; var matches = new List <TldRule>(); FindMatches(parts, structure, matches); //Sort so exceptions are first, then by biggest label count (with wildcards at bottom) var sortedMatches = matches.OrderByDescending(x => x.Type == TldRuleType.WildcardException ? 1 : 0) .ThenByDescending(x => x.LabelCount) .ThenByDescending(x => x.Name); var winningRule = sortedMatches.FirstOrDefault(); if (winningRule == null) { winningRule = new TldRule("*"); } //Domain is TLD if (parts.Count == winningRule.LabelCount) { return(null); } var domainName = new DomainInfo(normalizedDomain, winningRule); return(domainName); }