diff --git a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java index f43706db0..673cf4a42 100644 --- a/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java +++ b/core/src/main/java/org/geysermc/geyser/network/GameProtocol.java @@ -27,17 +27,15 @@ package org.geysermc.geyser.network; import com.github.steveice10.mc.protocol.codec.MinecraftCodec; import com.github.steveice10.mc.protocol.codec.PacketCodec; -import io.netty.buffer.ByteBuf; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec; -import org.cloudburstmc.protocol.bedrock.codec.BedrockCodecHelper; -import org.cloudburstmc.protocol.bedrock.codec.v582.serializer.TrimDataSerializer_v582; import org.cloudburstmc.protocol.bedrock.codec.v622.Bedrock_v622; import org.cloudburstmc.protocol.bedrock.codec.v630.Bedrock_v630; import org.cloudburstmc.protocol.bedrock.codec.v649.Bedrock_v649; import org.cloudburstmc.protocol.bedrock.codec.v662.Bedrock_v662; import org.cloudburstmc.protocol.bedrock.netty.codec.packet.BedrockPacketCodec; -import org.cloudburstmc.protocol.bedrock.packet.TrimDataPacket; +import org.cloudburstmc.protocol.bedrock.packet.LabTablePacket; +import org.cloudburstmc.protocol.bedrock.packet.PhotoTransferPacket; import org.geysermc.geyser.session.GeyserSession; import java.util.ArrayList; @@ -170,11 +168,9 @@ public final class GameProtocol { private static BedrockCodec processCodec(BedrockCodec codec) { return codec.toBuilder() - .updateSerializer(TrimDataPacket.class, new TrimDataSerializer_v582() { - @Override - public void deserialize(ByteBuf buffer, BedrockCodecHelper helper, TrimDataPacket packet) { - } - }) + // De-register unused serverbound EDU packets + .deregisterPacket(PhotoTransferPacket.class) + .deregisterPacket(LabTablePacket.class) .build(); } diff --git a/core/src/main/java/org/geysermc/geyser/network/GeyserServerInitializer.java b/core/src/main/java/org/geysermc/geyser/network/GeyserServerInitializer.java index bb8e87440..d869a96bd 100644 --- a/core/src/main/java/org/geysermc/geyser/network/GeyserServerInitializer.java +++ b/core/src/main/java/org/geysermc/geyser/network/GeyserServerInitializer.java @@ -25,12 +25,16 @@ package org.geysermc.geyser.network; +import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; import io.netty.channel.DefaultEventLoopGroup; +import io.netty.channel.SimpleChannelInboundHandler; import io.netty.util.concurrent.DefaultThreadFactory; import org.checkerframework.checker.nullness.qual.NonNull; import org.cloudburstmc.protocol.bedrock.BedrockPeer; import org.cloudburstmc.protocol.bedrock.BedrockServerSession; +import org.cloudburstmc.protocol.bedrock.netty.codec.packet.BedrockPacketCodec; import org.cloudburstmc.protocol.bedrock.netty.initializer.BedrockServerInitializer; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.event.bedrock.SessionInitializeEvent; @@ -63,6 +67,10 @@ public class GeyserServerInitializer extends BedrockServerInitializer { bedrockServerSession.setLogging(true); GeyserSession session = new GeyserSession(this.geyser, bedrockServerSession, this.eventLoopGroup.next()); + + Channel channel = bedrockServerSession.getPeer().getChannel(); + channel.pipeline().addAfter(BedrockPacketCodec.NAME, InvalidPacketHandler.NAME, new InvalidPacketHandler(session)); + bedrockServerSession.setPacketHandler(new UpstreamPacketHandler(this.geyser, session)); this.geyser.eventBus().fire(new SessionInitializeEvent(session)); } catch (Throwable e) { @@ -72,6 +80,38 @@ public class GeyserServerInitializer extends BedrockServerInitializer { } } + @Override + protected void postInitChannel(Channel channel) throws Exception { + super.postInitChannel(channel); + + channel.pipeline().addLast(new SimpleChannelInboundHandler() { + private static final int allowedExceptions = 5; + private static final long delayUntilExceptionsReset = 60; + + private long lastException; + private int exceptionCounts; + + @Override + protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { + ctx.fireChannelRead(msg); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + final long currentTime = System.currentTimeMillis(); + if (currentTime >= lastException + (delayUntilExceptionsReset * 1000)) { + exceptionCounts = 0; + } + lastException = currentTime; + if (exceptionCounts++ > allowedExceptions) { + ctx.pipeline().get(GeyserBedrockPeer.class).close("Too many exceptions created."); + return; + } + super.exceptionCaught(ctx, cause); + } + }); + } + @Override protected BedrockPeer createPeer(Channel channel) { return new GeyserBedrockPeer(channel, this::createSession); diff --git a/core/src/main/java/org/geysermc/geyser/network/InvalidPacketHandler.java b/core/src/main/java/org/geysermc/geyser/network/InvalidPacketHandler.java new file mode 100644 index 000000000..81a9c7ce7 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/network/InvalidPacketHandler.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2019-2024 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.network; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.handler.codec.DecoderException; +import lombok.RequiredArgsConstructor; +import org.geysermc.geyser.session.GeyserSession; + +@RequiredArgsConstructor +public class InvalidPacketHandler extends ChannelInboundHandlerAdapter { + public static final String NAME = "rak-error-handler"; + + private final GeyserSession session; + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + if (!(cause instanceof DecoderException)) { + super.exceptionCaught(ctx, cause); + return; + } + + Throwable actualCause = cause.getCause(); + + if (!(actualCause instanceof IllegalArgumentException)) { + super.exceptionCaught(ctx, actualCause); + return; + } + + // Kick users that try to send Clientbound packets + session.disconnect("Invalid packet received!"); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/network/netty/GeyserServer.java b/core/src/main/java/org/geysermc/geyser/network/netty/GeyserServer.java index db103d10e..c23bd40d1 100644 --- a/core/src/main/java/org/geysermc/geyser/network/netty/GeyserServer.java +++ b/core/src/main/java/org/geysermc/geyser/network/netty/GeyserServer.java @@ -74,7 +74,6 @@ import java.util.function.IntFunction; import java.util.function.Supplier; import static org.cloudburstmc.netty.channel.raknet.RakConstants.DEFAULT_GLOBAL_PACKET_LIMIT; -import static org.cloudburstmc.netty.channel.raknet.RakConstants.DEFAULT_OFFLINE_PACKET_LIMIT; import static org.cloudburstmc.netty.channel.raknet.RakConstants.DEFAULT_PACKET_LIMIT; public final class GeyserServer { @@ -217,11 +216,6 @@ public final class GeyserServer { int rakPacketLimit = positivePropOrDefault("Geyser.RakPacketLimit", DEFAULT_PACKET_LIMIT); this.geyser.getLogger().debug("Setting RakNet packet limit to " + rakPacketLimit); - boolean isWhitelistedProxyProtocol = this.geyser.getConfig().getBedrock().isEnableProxyProtocol() - && !this.geyser.getConfig().getBedrock().getProxyProtocolWhitelistedIPs().isEmpty(); - int rakOfflinePacketLimit = positivePropOrDefault("Geyser.RakOfflinePacketLimit", isWhitelistedProxyProtocol ? Integer.MAX_VALUE : DEFAULT_OFFLINE_PACKET_LIMIT); - this.geyser.getLogger().debug("Setting RakNet offline packet limit to " + rakOfflinePacketLimit); - int rakGlobalPacketLimit = positivePropOrDefault("Geyser.RakGlobalPacketLimit", DEFAULT_GLOBAL_PACKET_LIMIT); this.geyser.getLogger().debug("Setting RakNet global packet limit to " + rakGlobalPacketLimit); @@ -231,7 +225,6 @@ public final class GeyserServer { .option(RakChannelOption.RAK_HANDLE_PING, true) .option(RakChannelOption.RAK_MAX_MTU, this.geyser.getConfig().getMtu()) .option(RakChannelOption.RAK_PACKET_LIMIT, rakPacketLimit) - .option(RakChannelOption.RAK_OFFLINE_PACKET_LIMIT, rakOfflinePacketLimit) .option(RakChannelOption.RAK_GLOBAL_PACKET_LIMIT, rakGlobalPacketLimit) .childHandler(serverInitializer); } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b194c5a4b..21b88a686 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -113,9 +113,12 @@ viaproxy = { group = "net.raphimc", name = "ViaProxy", version.ref = "viaproxy" viaversion = { group = "com.viaversion", name = "viaversion", version.ref = "viaversion" } websocket = { group = "org.java-websocket", name = "Java-WebSocket", version.ref = "websocket" } -protocol-common = { group = "org.cloudburstmc.protocol", name = "common", version.ref = "protocol-connection" } -protocol-codec = { group = "org.cloudburstmc.protocol", name = "bedrock-codec", version.ref = "protocol" } -protocol-connection = { group = "org.cloudburstmc.protocol", name = "bedrock-connection", version.ref = "protocol-connection" } +#protocol-common = { group = "org.cloudburstmc.protocol", name = "common", version.ref = "protocol-connection" } +#protocol-codec = { group = "org.cloudburstmc.protocol", name = "bedrock-codec", version.ref = "protocol" } +#protocol-connection = { group = "org.cloudburstmc.protocol", name = "bedrock-connection", version.ref = "protocol-connection" } +protocol-common = { group = "com.github.CloudburstMC.Protocol", name = "common", version = "packet-flow-SNAPSHOT" } +protocol-codec = { group = "com.github.CloudburstMC.Protocol", name = "bedrock-codec", version = "packet-flow-SNAPSHOT" } +protocol-connection = { group = "com.github.CloudburstMC.Protocol", name = "bedrock-connection", version = "packet-flow-SNAPSHOT" } math = { group = "org.cloudburstmc.math", name = "immutable", version = "2.0" }