ModernUO/Projects/UOContent/Accounting/Firewall.cs
Kamron Batman e869c105d0
fix(core): Fixes type caching & cleanup (#422)
- [X] Fixes some issues with the type caching
- [X] Changes type check default to ignore case
- [X] Splits out type caching of insensitive and sensitive lists. This means less stuff to iterate through when checking a type against the string.
- [X] Adds an ArrayEnumerator, mostly for reference purposes (copy/paste as needed)
2021-01-20 00:10:33 -08:00

233 lines
6.9 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Runtime.CompilerServices;
using Microsoft.Toolkit.HighPerformance.Extensions;
namespace Server
{
public static class Firewall
{
private const string firewallConfigPath = "firewall.cfg";
static Firewall()
{
Set = new HashSet<IFirewallEntry>();
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<IFirewallEntry> 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);
}
}
}