using System.Net; using System.Net.Sockets; using Supercell.Laser.Server.Networking.Session; using Supercell.Laser.Server.Settings; using System.IO; using System.Text; namespace Supercell.Laser.Server.Networking { public static class IPBlacklist { private static readonly string BlacklistFilePath = "ipblacklist.txt"; private static readonly object FileLock = new object(); private static HashSet BlockedIPs = new HashSet(); private static FileSystemWatcher FileWatcher; public static void Initialize() { LoadBlacklist(); SetupFileWatcher(); } public static void LoadBlacklist() { lock (FileLock) { if (!File.Exists(BlacklistFilePath)) { File.Create(BlacklistFilePath).Close(); Logger.Print("Created ipblacklist.txt file."); } BlockedIPs = File.ReadAllLines(BlacklistFilePath) .Where(ip => !string.IsNullOrWhiteSpace(ip)) .ToHashSet(); Logger.Print($"Loaded {BlockedIPs.Count} IPs from ipblacklist.txt."); } } public static void SetupFileWatcher() { FileWatcher = new FileSystemWatcher { Path = Directory.GetCurrentDirectory(), Filter = BlacklistFilePath, NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName }; FileWatcher.Changed += OnBlacklistFileChanged; FileWatcher.Created += OnBlacklistFileChanged; FileWatcher.Deleted += OnBlacklistFileChanged; FileWatcher.Renamed += OnBlacklistFileChanged; FileWatcher.EnableRaisingEvents = true; Logger.Print("File watcher for ipblacklist.txt is active."); } private static void OnBlacklistFileChanged(object sender, FileSystemEventArgs e) { Logger.Print($"ipblacklist.txt file changed. Reloading blacklist..."); LoadBlacklist(); } public static void BlockIP(string ip) { lock (FileLock) { if (BlockedIPs.Add(ip)) { File.AppendAllLines(BlacklistFilePath, new[] { ip }); Logger.Print($"IP {ip} added to blacklist and saved to ipblacklist.txt."); } } } public static bool IsIPBlocked(string ip) { return BlockedIPs.Contains(ip); } } public static class TCPGateway { private static List ActiveConnections = new List(); private static Dictionary PacketCounters = new Dictionary(); private static Dictionary ConnectionAttempts = new Dictionary(); private static Dictionary LastLogTimes = new Dictionary(); private static Dictionary IpConnectionCounts = new Dictionary(); // IP bağlantı sayısını tutmak için private static Socket Socket; private static Thread Thread; private static Timer CleanupTimer; private static ManualResetEvent AcceptEvent = new ManualResetEvent(false); private static readonly object ConnectionLock = new object(); public static void Init(string host, int port) { if (Configuration.Instance.antiddos) { IPBlacklist.Initialize(); } Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); Socket.Bind(new IPEndPoint(IPAddress.Parse(host), port)); Socket.Listen(99999999); Thread = new Thread(Update); Thread.Start(); CleanupTimer = new Timer(CleanupInactiveConnections, null, 10000, 10000); Logger.Print($"TCP server started on {host}:{port}"); WebhookHelper.SendNotification($"TCP sunucu başlatıldı: {host}:{port}"); } private static void Update() { while (true) { AcceptEvent.Reset(); Socket.BeginAccept(new AsyncCallback(OnAccept), null); AcceptEvent.WaitOne(); } } private static void OnAccept(IAsyncResult ar) { try { Socket client = Socket.EndAccept(ar); string clientIp = ((IPEndPoint)client.RemoteEndPoint).Address.ToString(); // Bağlantı sayısını kontrol et lock (ConnectionLock) { if (ActiveConnections.Count >= 10) { LogIfNeeded(clientIp, $"Rejected: Max connection limit reached (10)."); WebhookHelper.SendNotification($"Rejected: Max connection limit reached ( 10) from IP {clientIp}."); client.Close(); AcceptEvent.Set(); return; } } if (Configuration.Instance.antiddos && IPBlacklist.IsIPBlocked(clientIp)) { LogIfNeeded(clientIp, $"Rejected: IP {clientIp} is blocked."); WebhookHelper.SendNotification($"Rejected: IP {clientIp} is blocked."); client.Close(); AcceptEvent.Set(); return; } if (Configuration.Instance.antiddos) { HandleConnectionAttempt(clientIp); } // IP bağlantı sayısını güncelle if (!IpConnectionCounts.ContainsKey(clientIp)) { IpConnectionCounts[clientIp] = 0; } IpConnectionCounts[clientIp]++; File.AppendAllText("iplog.txt", $"{clientIp} - {IpConnectionCounts[clientIp]} bağlantı\n"); Connection connection = new Connection(client); lock (ConnectionLock) { ActiveConnections.Add(connection); } Logger.Print($"New connection from {clientIp} {IpConnectionCounts[clientIp]} bağlantı"); WebhookHelper.SendNotification($"Yeni bağlantı: {clientIp} {IpConnectionCounts[clientIp]} bağlantı"); Connections.AddConnection(connection); client.BeginReceive( connection.ReadBuffer, 0, 1024, SocketFlags.None, new AsyncCallback(OnReceive), connection ); } catch (Exception ex) { Logger.Print($"Error accepting connection: {ex.Message}"); } finally { AcceptEvent.Set(); } } private static void HandleConnectionAttempt(string clientIp) { if (!ConnectionAttempts.ContainsKey(clientIp)) { ConnectionAttempts[clientIp] = new ConnectionAttemptCounter(); } var attemptCounter = ConnectionAttempts[clientIp]; attemptCounter.AttemptCount++; if ((DateTime.Now - attemptCounter.FirstAttemptTime).TotalSeconds > 10) { attemptCounter.FirstAttemptTime = DateTime.Now; attemptCounter.AttemptCount = 1; } if (attemptCounter.AttemptCount > 4) { IPBlacklist.BlockIP(clientIp); Logger.Print($"IP {clientIp} banned for too many connection attempts."); WebhookHelper.SendNotification($"IP {clientIp} çok fazla bağlantı denemesinden dolayı yasaklandı."); } } private static void OnReceive(IAsyncResult ar) { Connection connection = (Connection)ar.AsyncState; if (connection == null || connection.Socket == null || !connection.Socket.Connected) return; string clientIp = ((IPEndPoint)connection.Socket.RemoteEndPoint).Address.ToString(); try { int bytesRead = connection.Socket.EndReceive(ar); if (bytesRead <= 0) { Logger.Print($"{clientIp} disconnected."); WebhookHelper.SendNotification($"{clientIp} çıktı"); RemoveConnection(connection); return; } if (Configuration.Instance.antiddos) { HandlePacketCounter(clientIp); } connection.Memory.Write(connection.ReadBuffer, 0, bytesRead); connection.UpdateLastActiveTime(); if (connection.Messaging.OnReceive() != 0) { RemoveConnection(connection); Logger.Print($"{clientIp} disconnected."); return; } connection.Socket.BeginReceive( connection.ReadBuffer, 0, 1024, SocketFlags.None, new AsyncCallback(OnReceive), connection ); } catch (ObjectDisposedException) { Logger.Print($"Client socket {clientIp} was already closed."); WebhookHelper.SendNotification($"İstemci soketi {clientIp} zaten kapalıydı."); } catch (SocketException) { RemoveConnection(connection); Logger.Print($"{clientIp} disconnected due to socket error."); } catch (Exception ex) { Logger.Print($"Unexpected error from {clientIp}: {ex.Message}"); RemoveConnection(connection); } } private static void HandlePacketCounter(string clientIp) { if (!PacketCounters.ContainsKey(clientIp)) { PacketCounters[clientIp] = new PacketCounter(); } var counter = PacketCounters[clientIp]; counter.PacketCount++; if ((DateTime.Now - counter.FirstPacketTime).TotalSeconds > 10) { counter.FirstPacketTime = DateTime.Now; counter.PacketCount = 1; } if (counter.PacketCount > 100) { IPBlacklist.BlockIP(clientIp); Logger.Print($"IP {clientIp} banned for exceeding packet limit."); } } private static void CleanupInactiveConnections(object state) { DateTime now = DateTime.Now; var connectionsToRemove = new List(); lock (ConnectionLock) { for (int i = ActiveConnections.Count - 1; i >= 0; i--) { var connection = ActiveConnections[i]; if (connection != null && connection.Socket != null) { if (connection.Socket.Connected) { if ((now - connection.LastActiveTime).TotalSeconds > 120) { Logger.Print( $"Closing inactive connection from {connection.Socket.RemoteEndPoint}." ); connectionsToRemove.Add(connection); } } else { Logger.Print($"Socket is already closed and will be removed."); connectionsToRemove.Add(connection); } } else { Logger.Print("Connection or its socket is null, removing."); connectionsToRemove.Add(connection); } } foreach (var conn in connectionsToRemove) { ActiveConnections.Remove(conn); conn.Close(); } } } private static void RemoveConnection(Connection connection) { lock (ConnectionLock) { if (ActiveConnections.Contains(connection)) { ActiveConnections.Remove(connection); } connection.Close(); if (connection.MessageManager.HomeMode != null) { Sessions.Remove(connection.Avatar.AccountId); } } } private static void LogIfNeeded(string clientIp, string message) { if ( !LastLogTimes.ContainsKey(clientIp) || (DateTime.Now - LastLogTimes[clientIp]).TotalSeconds >= 10 ) { Logger.Print(message); LastLogTimes[clientIp] = DateTime.Now; } } public static void OnSend(IAsyncResult ar) { try { Socket socket = (Socket)ar.AsyncState; socket.EndSend(ar); } catch (Exception ex) { Logger.Print($"Error sending data: {ex.Message}"); } } } public class PacketCounter { public DateTime FirstPacketTime { get; set; } public int PacketCount { get; set; } public PacketCounter() { FirstPacketTime = DateTime.Now; PacketCount = 0; } } public class ConnectionAttemptCounter { public DateTime FirstAttemptTime { get; set; } public int AttemptCount { get; set; } public ConnectionAttemptCounter() { FirstAttemptTime = DateTime.Now; AttemptCount = 0; } } }