diff --git a/Server/BanLists.cs b/Server/BanLists.cs index dbe0d12..3f16a61 100644 --- a/Server/BanLists.cs +++ b/Server/BanLists.cs @@ -148,30 +148,27 @@ public static class BanLists { public static void Crash( Client user, - bool permanent = false, - bool dispose_user = true, - int delay_ms = 0 + int delay_ms = 0 ) { user.Ignored = true; Task.Run(async () => { if (delay_ms > 0) { await Task.Delay(delay_ms); } + bool permanent = user.Banned; await user.Send(new ChangeStagePacket { Id = (permanent ? "$agogus/ban4lyfe" : "$among$us/cr4sh%"), Stage = (permanent ? "$ejected" : "$agogusStage"), Scenario = (sbyte) (permanent ? 69 : 21), SubScenarioType = (byte) (permanent ? 21 : 69), }); - if (dispose_user) { - user.Dispose(); - } }); } private static void CrashMultiple(string[] args, MUCH much) { foreach (Client user in much(args).toActUpon) { - Crash(user, true); + user.Banned = true; + Crash(user); } } @@ -245,8 +242,9 @@ public static class BanLists { } foreach (Client user in res.toActUpon) { + user.Banned = true; BanClient(user); - Crash(user, true); + Crash(user); } Save(); diff --git a/Server/Client.cs b/Server/Client.cs index 9c21a3f..21add6f 100644 --- a/Server/Client.cs +++ b/Server/Client.cs @@ -13,6 +13,7 @@ public class Client : IDisposable { public readonly ConcurrentDictionary Metadata = new ConcurrentDictionary(); // can be used to store any information about a player public bool Connected = false; public bool Ignored = false; + public bool Banned = false; public CostumePacket? CurrentCostume = null; // required for proper client sync public string Name { get => Logger.Name; @@ -52,6 +53,11 @@ public class Client : IDisposable { PacketAttribute packetAttribute = Constants.PacketMap[typeof(T)]; try { + // don't send most packets to ignored players + if (Ignored && packetAttribute.Type != PacketType.Init && packetAttribute.Type != PacketType.ChangeStage) { + memory.Dispose(); + return; + } Server.FillPacket(new PacketHeader { Id = sender?.Id ?? Id, Type = packetAttribute.Type, @@ -71,11 +77,16 @@ public class Client : IDisposable { PacketHeader header = new PacketHeader(); header.Deserialize(data.Span); - if (!Connected && header.Type is not PacketType.Connect) { + if (!Connected && !Ignored && header.Type != PacketType.Connect) { Server.Logger.Error($"Didn't send {header.Type} to {Id} because they weren't connected yet"); return; } + // don't send most packets to ignored players + if (Ignored && header.Type != PacketType.Init && header.Type != PacketType.ChangeStage) { + return; + } + await Socket!.SendAsync(data[..(Constants.HeaderSize + header.PacketSize)], SocketFlags.None); } diff --git a/Server/Program.cs b/Server/Program.cs index 6debd13..a0a7dd3 100644 --- a/Server/Program.cs +++ b/Server/Program.cs @@ -120,10 +120,17 @@ void logError(Task x) { server.PacketHandler = (c, p) => { switch (p) { case GamePacket gamePacket: { + // crash ignored player + if (c.Ignored) { + c.Logger.Info($"Crashing ignored player after entering stage {gamePacket.Stage}."); + BanLists.Crash(c, 500); + return false; + } + // crash player entering a banned stage if (BanLists.Enabled && BanLists.IsStageBanned(gamePacket.Stage)) { c.Logger.Warn($"Crashing player for entering banned stage {gamePacket.Stage}."); - BanLists.Crash(c, false, false, 500); + BanLists.Crash(c, 500); return false; } @@ -172,6 +179,11 @@ server.PacketHandler = (c, p) => { break; } + // ignore all other packets from ignored players + case IPacket pack when c.Ignored: { + return false; + } + case TagPacket tagPacket: { // c.Logger.Info($"Got tag packet: {tagPacket.IsIt}"); if ((tagPacket.UpdateType & TagPacket.TagUpdate.State) != 0) c.Metadata["seeking"] = tagPacket.IsIt; diff --git a/Server/Server.cs b/Server/Server.cs index d71bfd4..51d9ad0 100644 --- a/Server/Server.cs +++ b/Server/Server.cs @@ -29,12 +29,6 @@ public class Server { Socket socket = token.HasValue ? await serverSocket.AcceptAsync(token.Value) : await serverSocket.AcceptAsync(); socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); - // is the IPv4 address banned? - if (BanLists.Enabled && BanLists.IsIPv4Banned(((IPEndPoint) socket.RemoteEndPoint!).Address!)) { - Logger.Warn($"Ignoring banned IPv4 address {socket.RemoteEndPoint}"); - continue; - } - Logger.Warn($"Accepted connection for client {socket.RemoteEndPoint}"); // start sub thread to handle client @@ -80,7 +74,7 @@ public class Server { public delegate void PacketReplacer(Client from, Client to, T value); // replacer must send public void BroadcastReplace(T packet, Client sender, PacketReplacer packetReplacer) where T : struct, IPacket { - foreach (Client client in Clients.Where(client => client.Connected && sender.Id != client.Id)) { + foreach (Client client in Clients.Where(c => c.Connected && !c.Ignored && sender.Id != c.Id)) { packetReplacer(sender, client, packet); } } @@ -97,7 +91,7 @@ public class Server { } public Task Broadcast(T packet) where T : struct, IPacket { - return Task.WhenAll(Clients.Where(c => c.Connected).Select(async client => { + return Task.WhenAll(Clients.Where(c => c.Connected && !c.Ignored).Select(async client => { IMemoryOwner memory = MemoryPool.Shared.RentZero(Constants.HeaderSize + packet.Size); PacketHeader header = new PacketHeader { Id = client.Id, @@ -116,7 +110,7 @@ public class Server { /// Memory owner to dispose once done /// Optional sender to not broadcast data to public async Task Broadcast(IMemoryOwner data, Client? sender = null) { - await Task.WhenAll(Clients.Where(c => c.Connected && c != sender).Select(client => client.Send(data.Memory, sender))); + await Task.WhenAll(Clients.Where(c => c.Connected && !c.Ignored && c != sender).Select(client => client.Send(data.Memory, sender))); data.Dispose(); } @@ -126,7 +120,7 @@ public class Server { /// Memory to send to the clients /// Optional sender to not broadcast data to public async void Broadcast(Memory data, Client? sender = null) { - await Task.WhenAll(Clients.Where(c => c.Connected && c != sender).Select(client => client.Send(data, sender))); + await Task.WhenAll(Clients.Where(c => c.Connected && !c.Ignored && c != sender).Select(client => client.Send(data, sender))); } public Client? FindExistingClient(Guid id) { @@ -175,10 +169,6 @@ public class Server { break; } } - if (client.Ignored) { - memory.Dispose(); - continue; - } // connection initialization if (first) { @@ -195,19 +185,30 @@ public class Server { client.Id = header.Id; client.Name = connect.ClientName; - // is the profile ID banned? - if (BanLists.Enabled && BanLists.IsProfileBanned(client.Id)) { + // is the IPv4 address banned? + if (BanLists.Enabled && BanLists.IsIPv4Banned(((IPEndPoint) socket.RemoteEndPoint!).Address!)) { + Logger.Warn($"Ignoring banned IPv4 address for {client.Name} ({client.Id}/{remote})"); client.Ignored = true; - client.Logger.Warn($"Ignoring banned profile ID {client.Id}"); + client.Banned = true; + } + // is the profile ID banned? + else if (BanLists.Enabled && BanLists.IsProfileBanned(client.Id)) { + client.Logger.Warn($"Ignoring banned profile ID for {client.Name} ({client.Id}/{remote})"); + client.Ignored = true; + client.Banned = true; + } + + // send server init (required to crash ignored players later) + await client.Send(new InitPacket { + MaxPlayers = (client.Ignored ? (ushort) 1 : Settings.Instance.Server.MaxPlayers), + }); + + // don't init or announce an ignored client to other players any further + if (client.Ignored) { memory.Dispose(); continue; } - // send server init - await client.Send(new InitPacket { - MaxPlayers = Settings.Instance.Server.MaxPlayers, - }); - bool wasFirst = connect.ConnectionType == ConnectPacket.ConnectionTypes.FirstConnection; // add client to the set of connected players