using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Runtime.CompilerServices; using Microsoft.Toolkit.HighPerformance; namespace Server { public static class Firewall { private const string firewallConfigPath = "firewall.cfg"; static Firewall() { Set = new HashSet(); if (File.Exists(firewallConfigPath)) { using var ip = new StreamReader(firewallConfigPath); string line; while ((line = ip.ReadLine()) != null) { line = line.Trim(); if (line.Length == 0) { continue; } Set.Add(ToFirewallEntry(line)); } } } public static HashSet Set { get; } public static IFirewallEntry ToFirewallEntry(object entry) { return entry switch { IFirewallEntry firewallEntry => firewallEntry, IPAddress address => new IPFirewallEntry(address), string s => ToFirewallEntry(s), _ => null }; } public static IFirewallEntry ToFirewallEntry(string entry) { if (entry == null) { return null; } if (IPAddress.TryParse(entry, out var addr)) { return new IPFirewallEntry(addr); } // Try CIDR parse var tokenizer = entry.Tokenize('/'); var ip = tokenizer.MoveNext() ? tokenizer.Current : null; var length = tokenizer.MoveNext() ? tokenizer.Current : null; if ( length != null && IPAddress.TryParse(ip, out var cidrPrefix) && int.TryParse(length, out var cidrLength) ) { return new CIDRFirewallEntry(cidrPrefix, cidrLength); } return new WildcardIPFirewallEntry(entry); } public static void Remove(object obj) { var entry = ToFirewallEntry(obj); if (entry != null) { Set.Remove(entry); Save(); } } public static void Add(object obj) { Add(ToFirewallEntry(obj)); } public static void Add(IFirewallEntry entry) { Set.Add(entry); Save(); } public static void Add(string pattern) { Add(ToFirewallEntry(pattern)); } public static void Add(IPAddress ip) { Add(ToFirewallEntry(ip)); } public static void Save() { using var op = new StreamWriter(firewallConfigPath); foreach (var entry in Set) { op.WriteLine(entry); } } public static bool IsBlocked(IPAddress ip) { foreach (var entry in Set) { if (entry.IsBlocked(ip)) { return true; } } return false; } public interface IFirewallEntry { bool IsBlocked(IPAddress address); } public class IPFirewallEntry : IFirewallEntry { private readonly IPAddress m_Address; public IPFirewallEntry(IPAddress address) => m_Address = address; public bool IsBlocked(IPAddress address) => m_Address.Equals(address); public override string ToString() => m_Address.ToString(); public override bool Equals(object obj) { return obj switch { IPAddress => obj.Equals(m_Address), string s when IPAddress.TryParse(s, out var otherAddress) => otherAddress.Equals(m_Address), IPFirewallEntry entry => m_Address.Equals(entry.m_Address), _ => false }; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => m_Address.GetHashCode(); } public class CIDRFirewallEntry : IFirewallEntry { private readonly int m_CIDRLength; private readonly IPAddress m_CIDRPrefix; public CIDRFirewallEntry(IPAddress cidrPrefix, int cidrLength) { m_CIDRPrefix = cidrPrefix; m_CIDRLength = cidrLength; } public bool IsBlocked(IPAddress address) => Utility.IPMatchCIDR(m_CIDRPrefix, address, m_CIDRLength); public override string ToString() => $"{m_CIDRPrefix}/{m_CIDRLength}"; public override bool Equals(object obj) { if (obj is string entry) { var str = entry.Split('/'); if (str.Length == 2) { if (IPAddress.TryParse(str[0], out var cidrPrefix)) { if (int.TryParse(str[1], out var cidrLength)) { return m_CIDRPrefix.Equals(cidrPrefix) && m_CIDRLength.Equals(cidrLength); } } } } else if (obj is CIDRFirewallEntry cidrEntry) { return m_CIDRPrefix.Equals(cidrEntry.m_CIDRPrefix) && m_CIDRLength.Equals(cidrEntry.m_CIDRLength); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => HashCode.Combine(m_CIDRPrefix, m_CIDRLength); } public class WildcardIPFirewallEntry : IFirewallEntry { private readonly string m_Entry; private bool m_Valid = true; public WildcardIPFirewallEntry(string entry) => m_Entry = entry; public bool IsBlocked(IPAddress address) => m_Valid && Utility.IPMatch(m_Entry, address, out m_Valid); public override string ToString() => m_Entry; public override bool Equals(object obj) { if (obj is string) { return obj.Equals(m_Entry); } return obj is WildcardIPFirewallEntry entry && m_Entry == entry.m_Entry; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => m_Entry.GetHashCode(StringComparison.Ordinal); } } }