From 556a15a6f8820467a0161b34654b3cdb35c7c89c Mon Sep 17 00:00:00 2001 From: Outfluencer Date: Sat, 15 Feb 2025 17:26:15 +1100 Subject: [PATCH] #3733: Add incoming packet configuration options --- .../net/md_5/bungee/conf/Configuration.java | 4 ++ .../net/md_5/bungee/netty/HandlerBoss.java | 50 +++++++++++++------ .../net/md_5/bungee/netty/PipelineUtils.java | 11 +++- .../net/md_5/bungee/util/PacketLimiter.java | 45 +++++++++++++++++ proxy/src/main/resources/messages.properties | 1 + 5 files changed, 96 insertions(+), 15 deletions(-) create mode 100644 proxy/src/main/java/net/md_5/bungee/util/PacketLimiter.java diff --git a/proxy/src/main/java/net/md_5/bungee/conf/Configuration.java b/proxy/src/main/java/net/md_5/bungee/conf/Configuration.java index 6967d4fc..95320050 100644 --- a/proxy/src/main/java/net/md_5/bungee/conf/Configuration.java +++ b/proxy/src/main/java/net/md_5/bungee/conf/Configuration.java @@ -69,6 +69,8 @@ public class Configuration implements ProxyConfig private boolean preventProxyConnections; private boolean forgeSupport; private boolean rejectTransfers; + private int maxPacketsPerSecond = 1 << 12; + private int maxPacketDataPerSecond = 1 << 25; public void load() { @@ -105,6 +107,8 @@ public class Configuration implements ProxyConfig preventProxyConnections = adapter.getBoolean( "prevent_proxy_connections", preventProxyConnections ); forgeSupport = adapter.getBoolean( "forge_support", forgeSupport ); rejectTransfers = adapter.getBoolean( "reject_transfers", rejectTransfers ); + maxPacketsPerSecond = adapter.getInt( "max_packets_per_second", maxPacketsPerSecond ); + maxPacketDataPerSecond = adapter.getInt( "max_packets_data_per_second", maxPacketDataPerSecond ); disabledCommands = new CaseInsensitiveSet( (Collection) adapter.getList( "disabled_commands", Arrays.asList( "disabledcommandhere" ) ) ); diff --git a/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java b/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java index d82173b1..a73a29c7 100644 --- a/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java +++ b/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java @@ -10,14 +10,19 @@ import io.netty.handler.timeout.ReadTimeoutException; import java.io.IOException; import java.net.InetSocketAddress; import java.util.logging.Level; +import lombok.Setter; import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.connection.CancelSendSignal; import net.md_5.bungee.connection.InitialHandler; import net.md_5.bungee.connection.PingHandler; +import net.md_5.bungee.connection.UpstreamBridge; import net.md_5.bungee.protocol.BadPacketException; import net.md_5.bungee.protocol.OverflowPacketException; import net.md_5.bungee.protocol.PacketWrapper; import net.md_5.bungee.protocol.Protocol; +import net.md_5.bungee.protocol.packet.Kick; +import net.md_5.bungee.util.PacketLimiter; import net.md_5.bungee.util.QuietException; /** @@ -28,6 +33,8 @@ import net.md_5.bungee.util.QuietException; public class HandlerBoss extends ChannelInboundHandlerAdapter { + @Setter + private PacketLimiter limiter; private ChannelWrapper channel; private PacketHandler handler; private boolean healthCheck; @@ -107,20 +114,35 @@ public class HandlerBoss extends ChannelInboundHandlerAdapter } PacketWrapper packet = (PacketWrapper) msg; - if ( packet.packet != null ) - { - Protocol nextProtocol = packet.packet.nextProtocol(); - if ( nextProtocol != null ) - { - channel.setDecodeProtocol( nextProtocol ); - } - } - if ( handler != null ) + try { - boolean sendPacket = handler.shouldHandle( packet ); - try + // check if the player exceeds packet limits, put inside try final, so we always release. + if ( limiter != null && !limiter.incrementAndCheck( packet.buf.readableBytes() ) ) { + // we shouldn't tell the player what limits he exceeds by default + // but if someone applies custom message we should allow them to display counter and bytes + channel.close( handler instanceof UpstreamBridge ? new Kick( TextComponent.fromLegacy( ProxyServer.getInstance().getTranslation( "packet_limit_kick", limiter.getCounter(), limiter.getDataCounter() ) ) ) : null ); + // but the server admin should know + ProxyServer.getInstance().getLogger().log( Level.WARNING, "{0} exceeded packet limit ({1} packets and {2} bytes per second)", new Object[] + { + handler, limiter.getCounter(), limiter.getDataCounter() + } ); + return; + } + + if ( packet.packet != null ) + { + Protocol nextProtocol = packet.packet.nextProtocol(); + if ( nextProtocol != null ) + { + channel.setDecodeProtocol( nextProtocol ); + } + } + + if ( handler != null ) + { + boolean sendPacket = handler.shouldHandle( packet ); if ( sendPacket && packet.packet != null ) { try @@ -135,10 +157,10 @@ public class HandlerBoss extends ChannelInboundHandlerAdapter { handler.handle( packet ); } - } finally - { - packet.trySingleRelease(); } + } finally + { + packet.trySingleRelease(); } } diff --git a/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java b/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java index f242bdf8..2934cdef 100644 --- a/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java +++ b/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java @@ -50,6 +50,7 @@ import net.md_5.bungee.protocol.Protocol; import net.md_5.bungee.protocol.Varint21FrameDecoder; import net.md_5.bungee.protocol.channel.BungeeChannelInitializer; import net.md_5.bungee.protocol.channel.ChannelAcceptor; +import net.md_5.bungee.util.PacketLimiter; public class PipelineUtils { @@ -79,8 +80,16 @@ public class PipelineUtils ch.pipeline().addAfter( FRAME_DECODER, PACKET_DECODER, new MinecraftDecoder( Protocol.HANDSHAKE, true, ProxyServer.getInstance().getProtocolVersion() ) ); ch.pipeline().addAfter( FRAME_PREPENDER_AND_COMPRESS, PACKET_ENCODER, new MinecraftEncoder( Protocol.HANDSHAKE, true, ProxyServer.getInstance().getProtocolVersion() ) ); ch.pipeline().addBefore( FRAME_PREPENDER_AND_COMPRESS, LEGACY_KICKER, legacyKicker ); - ch.pipeline().get( HandlerBoss.class ).setHandler( new InitialHandler( BungeeCord.getInstance(), listener ) ); + HandlerBoss handlerBoss = ch.pipeline().get( HandlerBoss.class ); + handlerBoss.setHandler( new InitialHandler( BungeeCord.getInstance(), listener ) ); + + int packetLimit = BungeeCord.getInstance().getConfig().getMaxPacketsPerSecond(); + int packetDataLimit = BungeeCord.getInstance().getConfig().getMaxPacketDataPerSecond(); + if ( packetLimit > 0 || packetDataLimit > 0 ) + { + handlerBoss.setLimiter( new PacketLimiter( packetLimit, packetDataLimit ) ); + } if ( listener.isProxyProtocol() ) { ch.pipeline().addFirst( new HAProxyMessageDecoder() ); diff --git a/proxy/src/main/java/net/md_5/bungee/util/PacketLimiter.java b/proxy/src/main/java/net/md_5/bungee/util/PacketLimiter.java new file mode 100644 index 00000000..ea89d240 --- /dev/null +++ b/proxy/src/main/java/net/md_5/bungee/util/PacketLimiter.java @@ -0,0 +1,45 @@ +package net.md_5.bungee.util; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class PacketLimiter +{ + + // max amount of packets allowed per second + private final int limit; + // max amount of data allowed per second + private final int dataLimit; + + @Getter + private int counter; + @Getter + private int dataCounter; + private long nextSecond; + + /** + * Counts the received packet amount and size. + * + * @param size size of the packet + * @return return false if the player should be kicked + */ + public boolean incrementAndCheck(int size) + { + counter++; + dataCounter += size; + + if ( ( limit > 0 && counter > limit ) || ( dataLimit > 0 && dataCounter > dataLimit ) ) + { + long now = System.currentTimeMillis(); + if ( nextSecond > now ) + { + return false; + } + nextSecond = now + 1000; + counter = 0; + dataCounter = 0; + } + return true; + } +} diff --git a/proxy/src/main/resources/messages.properties b/proxy/src/main/resources/messages.properties index 149c742f..c0a41fb0 100644 --- a/proxy/src/main/resources/messages.properties +++ b/proxy/src/main/resources/messages.properties @@ -41,3 +41,4 @@ command_ip=\u00a79IP of {0} is {1} illegal_chat_characters=\u00a7cIllegal characters in chat ({0}) kick_message=\u00a7cYou have been kicked off the proxy. reject_transfer=\u00a7cYour transfer was rejected. +packet_limit_kick=\u00a7cYou have sent too many packets