gary answer, mozilla, . , -, domainname-parser .
namespace DomainName.Parser
{
using System.Collections.Generic;
using System.Linq;
using System;
public class DomainName
{
public DomainName(string rawDomainName, string publicSuffix, string registerableDomainName)
{
this.RawDomainName = rawDomainName;
this.PublicSuffix = publicSuffix;
this.RegisterableDomainName = registerableDomainName;
}
public string RawDomainName { get; private set; }
public string PublicSuffix { get; private set; }
public string RegisterableDomainName { get; private set; }
public static bool TryParse(string rawDomainName, PublicSuffixRuleCache ruleCache, out DomainName domainName)
{
if (string.IsNullOrEmpty(rawDomainName) || !rawDomainName.Contains('.') || rawDomainName.StartsWith("."))
{
domainName = new DomainName(rawDomainName, null, null);
return true;
}
try
{
rawDomainName = rawDomainName.ToLower();
var domainLabels = rawDomainName.Trim().Split('.').ToList();
domainLabels.Reverse();
var prevailingRule = FindMatchingRule(domainLabels, ruleCache) ?? new PublicSuffixRule("*");
if (prevailingRule.Type == PublicSuffixRule.RuleType.Exception)
{
var labels = prevailingRule.Labels;
labels.Reverse();
labels.RemoveAt(0);
prevailingRule = new PublicSuffixRule(string.Join(".", labels));
}
var publicSuffix = Enumerable.Range(0, prevailingRule.Labels.Count).Aggregate(string.Empty, (current, i) => string.Format("{0}.{1}", domainLabels[i], current).Trim('.'));
var registrableDomain = string.Format("{0}.{1}", domainLabels[prevailingRule.Labels.Count], publicSuffix);
domainName = new DomainName(rawDomainName, publicSuffix, registrableDomain);
return true;
}
catch
{
domainName = null;
return false;
}
}
private static PublicSuffixRule FindMatchingRule(List<string> domainLabels, PublicSuffixRuleCache ruleCache)
{
var ruleMatches = ruleCache.PublicSuffixRules.Where(r => r.AppliesTo(domainLabels)).ToList();
if (ruleMatches.Count() == 1)
{
return ruleMatches[0];
}
var exceptionRules = ruleMatches.Where(r => r.Type == PublicSuffixRule.RuleType.Exception).ToList();
if (exceptionRules.Count() == 1)
{
return exceptionRules[0];
}
if (exceptionRules.Count() > 1)
{
throw new ApplicationException("Unexpectedly found multiple matching exception rules.");
}
var prevailingRule = ruleMatches.OrderByDescending(r => r.Labels.Count).Take(1).SingleOrDefault();
return prevailingRule;
}
}
public class PublicSuffixRule
{
public PublicSuffixRule(string ruleLine)
{
if (string.IsNullOrEmpty(ruleLine) || string.IsNullOrWhiteSpace(ruleLine))
{
throw new ArgumentNullException("ruleLine");
}
string ruleName;
if (ruleLine.StartsWith("*", StringComparison.InvariantCultureIgnoreCase))
{
this.Type = RuleType.Wildcard;
ruleName = ruleLine;
}
else if (ruleLine.StartsWith("!", StringComparison.InvariantCultureIgnoreCase))
{
this.Type = RuleType.Exception;
ruleName = ruleLine.Substring(1);
}
else
{
this.Type = RuleType.Normal;
ruleName = ruleLine;
}
this.Name = ruleName.Split(' ')[0];
var labels = this.Name.Split('.').ToList();
labels.Reverse();
this.Labels = labels;
}
public string Name { get; private set; }
public RuleType Type { get; private set; }
public List<string> Labels { get; private set; }
public bool AppliesTo(List<string> domainLabels)
{
if (this.Labels.Count > domainLabels.Count)
{
return false;
}
foreach (var position in Enumerable.Range(0, this.Labels.Count))
{
if (this.Labels[position] == "*")
{
return true;
}
if (this.Labels[position] != domainLabels[position])
{
return false;
}
}
return true;
}
public enum RuleType
{
Normal,
Wildcard,
Exception
}
}
public class PublicSuffixRuleCache
{
public PublicSuffixRuleCache(string publicSuffixRulesFileLocation)
{
if (string.IsNullOrEmpty(publicSuffixRulesFileLocation))
{
throw new ArgumentNullException("publicSuffixRulesFileLocation");
}
this.PublicSuffixRules = GetRules(publicSuffixRulesFileLocation);
}
public PublicSuffixRuleCache(IEnumerable<string> publicSuffixRules)
{
this.PublicSuffixRules = GetRules(publicSuffixRules);
}
public List<PublicSuffixRule> PublicSuffixRules { get; private set; }
private static List<PublicSuffixRule> GetRules(string publicSuffixRulesFileLocation)
{
var results = new List<PublicSuffixRule>();
if (File.Exists(publicSuffixRulesFileLocation))
{
var ruleStrings = File.ReadAllLines(publicSuffixRulesFileLocation, Encoding.UTF8).ToList();
results = GetRules(ruleStrings);
}
return results;
}
private static List<PublicSuffixRule> GetRules(IEnumerable<string> publicSuffixRules)
{
return
publicSuffixRules.Where(
ruleString =>
ruleString.Trim().Length != 0
&& !ruleString.StartsWith("//", StringComparison.InvariantCultureIgnoreCase)).Select(ruleString => new PublicSuffixRule(ruleString)).ToList();
}
}
}
:
namespace DomainName.Library.Tests
{
using FluentAssertions;
using Xunit;
using DomainName.Parser;
public class CheckPublicSuffixTest2
{
public bool CheckRegisterableDomain(string domainName, string registerableDomain)
{
var ruleCache = new PublicSuffixRuleCache(@"C:\publicsuffix.txt");
DomainName outDomain;
if (DomainName.TryParse(domainName, ruleCache, out outDomain))
{
return outDomain.RegisterableDomainName == registerableDomain;
}
return registerableDomain == null;
}
[Fact]
public void TestCheckPublicSuffix()
{
this.CheckRegisterableDomain(null, null).Should().BeTrue();
this.CheckRegisterableDomain("COM", null).Should().BeTrue();
this.CheckRegisterableDomain("example.COM", "example.com").Should().BeTrue();
this.CheckRegisterableDomain("WwW.example.COM", "example.com").Should().BeTrue();
this.CheckRegisterableDomain(".com", null).Should().BeTrue();
this.CheckRegisterableDomain(".example", null).Should().BeTrue();
this.CheckRegisterableDomain(".example.com", null).Should().BeTrue();
this.CheckRegisterableDomain(".example.example", null).Should().BeTrue();
this.CheckRegisterableDomain("example", null).Should().BeTrue();
this.CheckRegisterableDomain("example.example", "example.example").Should().BeTrue();
this.CheckRegisterableDomain("b.example.example", "example.example").Should().BeTrue();
this.CheckRegisterableDomain("a.b.example.example", "example.example").Should().BeTrue();
this.CheckRegisterableDomain("biz", null).Should().BeTrue();
this.CheckRegisterableDomain("domain.biz", "domain.biz").Should().BeTrue();
this.CheckRegisterableDomain("b.domain.biz", "domain.biz").Should().BeTrue();
this.CheckRegisterableDomain("a.b.domain.biz", "domain.biz").Should().BeTrue();
this.CheckRegisterableDomain("com", null).Should().BeTrue();
this.CheckRegisterableDomain("example.com", "example.com").Should().BeTrue();
this.CheckRegisterableDomain("b.example.com", "example.com").Should().BeTrue();
this.CheckRegisterableDomain("a.b.example.com", "example.com").Should().BeTrue();
this.CheckRegisterableDomain("uk.com", null).Should().BeTrue();
this.CheckRegisterableDomain("example.uk.com", "example.uk.com").Should().BeTrue();
this.CheckRegisterableDomain("b.example.uk.com", "example.uk.com").Should().BeTrue();
this.CheckRegisterableDomain("a.b.example.uk.com", "example.uk.com").Should().BeTrue();
this.CheckRegisterableDomain("test.ac", "test.ac").Should().BeTrue();
this.CheckRegisterableDomain("cy", null).Should().BeTrue();
this.CheckRegisterableDomain("c.cy", null).Should().BeTrue();
this.CheckRegisterableDomain("b.c.cy", "b.c.cy").Should().BeTrue();
this.CheckRegisterableDomain("a.b.c.cy", "b.c.cy").Should().BeTrue();
this.CheckRegisterableDomain("jp", null).Should().BeTrue();
this.CheckRegisterableDomain("test.jp", "test.jp").Should().BeTrue();
this.CheckRegisterableDomain("www.test.jp", "test.jp").Should().BeTrue();
this.CheckRegisterableDomain("ac.jp", null).Should().BeTrue();
this.CheckRegisterableDomain("test.ac.jp", "test.ac.jp").Should().BeTrue();
this.CheckRegisterableDomain("www.test.ac.jp", "test.ac.jp").Should().BeTrue();
this.CheckRegisterableDomain("kyoto.jp", null).Should().BeTrue();
this.CheckRegisterableDomain("test.kyoto.jp", "test.kyoto.jp").Should().BeTrue();
this.CheckRegisterableDomain("ide.kyoto.jp", null).Should().BeTrue();
this.CheckRegisterableDomain("b.ide.kyoto.jp", "b.ide.kyoto.jp").Should().BeTrue();
this.CheckRegisterableDomain("a.b.ide.kyoto.jp", "b.ide.kyoto.jp").Should().BeTrue();
this.CheckRegisterableDomain("c.kobe.jp", null).Should().BeTrue();
this.CheckRegisterableDomain("b.c.kobe.jp", "b.c.kobe.jp").Should().BeTrue();
this.CheckRegisterableDomain("a.b.c.kobe.jp", "b.c.kobe.jp").Should().BeTrue();
this.CheckRegisterableDomain("city.kobe.jp", "city.kobe.jp").Should().BeTrue();
this.CheckRegisterableDomain("www.city.kobe.jp", "city.kobe.jp").Should().BeTrue();
this.CheckRegisterableDomain("om", null).Should().BeTrue();
this.CheckRegisterableDomain("test.om", null).Should().BeTrue();
this.CheckRegisterableDomain("b.test.om", "b.test.om").Should().BeTrue();
this.CheckRegisterableDomain("a.b.test.om", "b.test.om").Should().BeTrue();
this.CheckRegisterableDomain("songfest.om", "songfest.om").Should().BeTrue();
this.CheckRegisterableDomain("www.songfest.om", "songfest.om").Should().BeTrue();
this.CheckRegisterableDomain("us", null).Should().BeTrue();
this.CheckRegisterableDomain("test.us", "test.us").Should().BeTrue();
this.CheckRegisterableDomain("www.test.us", "test.us").Should().BeTrue();
this.CheckRegisterableDomain("ak.us", null).Should().BeTrue();
this.CheckRegisterableDomain("test.ak.us", "test.ak.us").Should().BeTrue();
this.CheckRegisterableDomain("www.test.ak.us", "test.ak.us").Should().BeTrue();
this.CheckRegisterableDomain("k12.ak.us", null).Should().BeTrue();
this.CheckRegisterableDomain("test.k12.ak.us", "test.k12.ak.us").Should().BeTrue();
this.CheckRegisterableDomain("www.test.k12.ak.us", "test.k12.ak.us").Should().BeTrue();
}
}
}