/************************************************************************* * ModernUO * * Copyright 2019-2024 - ModernUO Development Team * * Email: hi@modernuo.com * * File: IPLimiter.cs * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * *************************************************************************/ using System; using System.Collections.Generic; using System.Net; using System.Runtime.InteropServices; namespace Server.Misc; public static class IPLimiter { private static readonly Dictionary _connectionAttempts = new(128); private static readonly HashSet _throttledAddresses = new(); private static long _lastClearedThrottles; private static long _lastClearedAttempts; public static readonly IPAddress[] Exemptions = { IPAddress.Parse( "127.0.0.1" ) }; public static TimeSpan ClearConnectionAttemptsDuration { get; private set; } public static TimeSpan ClearThrottledDuration { get; private set; } public static bool Enabled { get; private set; } public static int MaxAddresses { get; private set; } public static void Configure() { Enabled = ServerConfiguration.GetOrUpdateSetting("ipLimiter.enable", true); MaxAddresses = ServerConfiguration.GetOrUpdateSetting("ipLimiter.maxConnectionsPerIP", 10); ClearConnectionAttemptsDuration = ServerConfiguration.GetOrUpdateSetting("ipLimiter.clearConnectionAttemptsDuration", TimeSpan.FromSeconds(10)); ClearThrottledDuration = ServerConfiguration.GetOrUpdateSetting("ipLimiter.clearThrottledDuration", TimeSpan.FromMinutes(2)); } public static bool IsExempt(IPAddress ip) { for (int i = 0; i < Exemptions.Length; i++) { if (ip.Equals(Exemptions[i])) { return true; } } return false; } public static bool Verify(IPAddress ourAddress) { if (!Enabled || IsExempt(ourAddress)) { return true; } var now = Core.TickCount; if (_throttledAddresses.Count > 0) { if (now - _lastClearedThrottles > ClearThrottledDuration.TotalMilliseconds) { _lastClearedThrottles = now; ClearThrottledAddresses(); } else if (_throttledAddresses.Contains(ourAddress)) { return false; } } if (_connectionAttempts.Count > 0 && now - _lastClearedAttempts > ClearConnectionAttemptsDuration.TotalMilliseconds) { _lastClearedAttempts = now; ClearConnectionAttempts(); } ref var count = ref CollectionsMarshal.GetValueRefOrAddDefault(_connectionAttempts, ourAddress, out _); count++; if (count > MaxAddresses) { _connectionAttempts.Remove(ourAddress); _throttledAddresses.Add(ourAddress); return false; } return true; } private static void ClearThrottledAddresses() { _throttledAddresses.Clear(); } private static void ClearConnectionAttempts() { _connectionAttempts.Clear(); _connectionAttempts.TrimExcess(128); } }