using System.Collections.Generic;
using System.Net.Sockets;
private static readonly DomainNameTrie _excludedDomainNames = new();
private static readonly IPAddressTrie _excludedIPAddresses = new();
public static void Main()
InlineData("0.0.0.0/0", "0.0.0.0", "127.127.127.127", "255.255.255.255");
public static void InlineData(string ipNetworkString, params string[] ipAddressesStrings)
var splitIpNetwork = ipNetworkString.Split('/');
var ipTrie = new IPAddressTrie();
var ipNetwork = new IPNetwork(IPAddress.Parse(splitIpNetwork[0]), Convert.ToUInt16(splitIpNetwork[1]));
ipTrie.Add(ipNetwork, new(true));
foreach (var addressString in ipAddressesStrings)
var address = IPAddress.Parse(addressString);
ushort cidrSuffix = address.AddressFamily switch
AddressFamily.InterNetwork => 32,
AddressFamily.InterNetworkV6 => 128,
_ => throw new NotSupportedException()
var specificIPAddressNetwork = new IPNetwork(address, cidrSuffix);
if (!IsIPAddressExcluded(specificIPAddressNetwork))
Console.WriteLine("{0} was not contained within {1}.", addressString, ipNetworkString);
public static bool IsDomainNameExcluded(string domainName)
if (_excludedDomainNames.ContainsKey(domainName))
var predecessor = _excludedDomainNames.FindPredecessor(domainName);
if (predecessor is null || predecessor.IsRoot)
return predecessor.Value.AllowSubdomains;
public static bool IsIPAddressExcluded(IPNetwork network)
if (_excludedIPAddresses.ContainsKey(network))
Console.WriteLine("{0}/{1} was not explicitly added as an exclusion.", network.Address, network.CidrSuffix);
var predecessor = _excludedIPAddresses.FindPredecessor(network);
Console.WriteLine("{0}/{1} predecessor {2}", network.Address, network.CidrSuffix, predecessor is null ? "is null" : "is not null");
return predecessor.Value.AllowIpAddressesInCidrRange;
public record DomainNameTrieNode(bool AllowSubdomains);
internal class DomainNameTrie : AbstractTrie<string, char, DomainNameTrieNode>, IComparer<char>
public DomainNameTrie() : base(DomainNameTrieKeyMapper, new TrieNode<char, DomainNameTrieNode>(null, '.'))
static IEnumerable<char> DomainNameTrieKeyMapper(string key)
foreach (var value in key.Reverse())
yield return char.ToLowerInvariant(value);
protected override ITrieNode<char, DomainNameTrieNode> CreateRoot(char key)
=> new TrieNode<char, DomainNameTrieNode>(null, key);
public int Compare(char x, char y)
=> char.ToLowerInvariant(x).CompareTo(char.ToLowerInvariant(y));
public record IPAddressTrieNode(bool AllowIpAddressesInCidrRange);
public readonly record struct IPNetwork(IPAddress Address, ushort CidrSuffix);
internal class IPAddressTrie : AbstractTrie<IPNetwork, char, IPAddressTrieNode>, IComparer<char>
public IPAddressTrie() : base(IPAddressTrieKeyMapper, new TrieNode<char, IPAddressTrieNode>(null, '.'))
private static IEnumerable<char> IPAddressTrieKeyMapper(IPNetwork network)
var bitString = ".".Concat(network.Address.GetAddressBytes().SelectMany(x => Convert.ToString(x, 2).PadLeft(8, '0')).Take(network.CidrSuffix));
protected override ITrieNode<char, IPAddressTrieNode> CreateRoot(char key)
=> new TrieNode<char, IPAddressTrieNode>(null, key);
public int Compare(char x, char y)
=> char.ToLowerInvariant(x).CompareTo(char.ToLowerInvariant(y));