using System.Globalization; namespace PrivaPub.Federation.Crawler { public static class PeerHosts { static readonly IdnMapping Idn = new(); static readonly string[] Unreachable = { ".onion", ".i2p", ".local", ".localhost", ".internal" }; static readonly string[] Reserved = { ".test", ".invalid", ".example" }; //names from a peers list we could ever talk to: DNS names, ASCII (punycode), no addresses, ports or hidden services; //the reserved test names only on a test network public static IEnumerable Filter(IEnumerable names, Func allowed, bool testNetwork = false) => names .Select(Normalise) .Where(host => host != default && (testNetwork || !Reserved.Any(suffix => host.EndsWith(suffix, StringComparison.Ordinal))) && allowed(host)) .Distinct(StringComparer.Ordinal); public static string Normalise(string name) { var host = name?.Trim().TrimEnd('.').ToLowerInvariant(); if (string.IsNullOrEmpty(host) || host.Length > 253 || !host.Contains('.') || host.Contains(':') || host.Contains('/')) return default; try { host = Idn.GetAscii(host); } catch (ArgumentException) { return default; } if (Uri.CheckHostName(host) != UriHostNameType.Dns || Unreachable.Any(suffix => host.EndsWith(suffix, StringComparison.Ordinal))) return default; return host; } } }