From 143bf38489be75207a7e21cbafbda2ee9d9b9ddd Mon Sep 17 00:00:00 2001 From: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> Date: Wed, 3 Apr 2024 21:42:08 -0700 Subject: [PATCH 1/6] Support cookies Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> --- .../raknet/config/DefaultRakServerConfig.java | 16 +++++ .../raknet/config/RakChannelOption.java | 6 ++ .../raknet/config/RakServerChannelConfig.java | 4 ++ .../client/RakClientOfflineHandler.java | 36 ++++++++++-- .../server/RakServerOfflineHandler.java | 36 +++++++++++- .../org/cloudburstmc/netty/util/RakUtils.java | 2 +- .../java/org/cloudburstmc/netty/RakTests.java | 58 ++++++++++++++----- 7 files changed, 133 insertions(+), 25 deletions(-) diff --git a/transport-raknet/src/main/java/org/cloudburstmc/netty/channel/raknet/config/DefaultRakServerConfig.java b/transport-raknet/src/main/java/org/cloudburstmc/netty/channel/raknet/config/DefaultRakServerConfig.java index 11c2ed9f..1e75d67e 100644 --- a/transport-raknet/src/main/java/org/cloudburstmc/netty/channel/raknet/config/DefaultRakServerConfig.java +++ b/transport-raknet/src/main/java/org/cloudburstmc/netty/channel/raknet/config/DefaultRakServerConfig.java @@ -46,6 +46,7 @@ public class DefaultRakServerConfig extends DefaultChannelConfig implements RakS private volatile int packetLimit = RakConstants.DEFAULT_PACKET_LIMIT; private volatile int globalPacketLimit = RakConstants.DEFAULT_GLOBAL_PACKET_LIMIT; private volatile int unconnectedPacketLimit = RakConstants.DEFAULT_OFFLINE_PACKET_LIMIT; + private volatile boolean sendCookie; public DefaultRakServerConfig(RakServerChannel channel) { super(channel); @@ -98,6 +99,9 @@ public T getOption(ChannelOption option) { if (option == RakChannelOption.RAK_OFFLINE_PACKET_LIMIT) { return (T) Integer.valueOf(this.getUnconnectedPacketLimit()); } + if (option == RakChannelOption.RAK_SEND_COOKIE) { + return (T) Boolean.valueOf(this.sendCookie); + } return this.channel.parent().config().getOption(option); } @@ -129,6 +133,8 @@ public boolean setOption(ChannelOption option, T value) { this.setUnconnectedPacketLimit((Integer) value); } else if (option == RakChannelOption.RAK_GLOBAL_PACKET_LIMIT) { this.setGlobalPacketLimit((Integer) value); + } else if (option == RakChannelOption.RAK_SEND_COOKIE) { + this.sendCookie = (Boolean) value; } else { return this.channel.parent().config().setOption(option, value); } @@ -255,6 +261,11 @@ public int getPacketLimit() { return this.packetLimit; } + @Override + public boolean getSendCookie() { + return this.sendCookie; + } + @Override public int getUnconnectedPacketLimit() { return unconnectedPacketLimit; @@ -274,4 +285,9 @@ public int getGlobalPacketLimit() { public void setGlobalPacketLimit(int globalPacketLimit) { this.globalPacketLimit = globalPacketLimit; } + + @Override + public void setSendCookie(boolean sendCookie) { + this.sendCookie = sendCookie; + } } diff --git a/transport-raknet/src/main/java/org/cloudburstmc/netty/channel/raknet/config/RakChannelOption.java b/transport-raknet/src/main/java/org/cloudburstmc/netty/channel/raknet/config/RakChannelOption.java index 059cf37c..2d8efbf9 100644 --- a/transport-raknet/src/main/java/org/cloudburstmc/netty/channel/raknet/config/RakChannelOption.java +++ b/transport-raknet/src/main/java/org/cloudburstmc/netty/channel/raknet/config/RakChannelOption.java @@ -155,6 +155,12 @@ public class RakChannelOption extends ChannelOption { public static final ChannelOption RAK_GLOBAL_PACKET_LIMIT = valueOf(RakChannelOption.class, "RAK_GLOBAL_PACKET_LIMIT"); + /** + * Whether to send a cookie to the client during the connection process. + */ + public static final ChannelOption RAK_SEND_COOKIE = + valueOf(RakChannelOption.class, "RAK_SEND_COOKIE"); + @SuppressWarnings("deprecation") protected RakChannelOption() { super(null); diff --git a/transport-raknet/src/main/java/org/cloudburstmc/netty/channel/raknet/config/RakServerChannelConfig.java b/transport-raknet/src/main/java/org/cloudburstmc/netty/channel/raknet/config/RakServerChannelConfig.java index b2aa7958..00655937 100644 --- a/transport-raknet/src/main/java/org/cloudburstmc/netty/channel/raknet/config/RakServerChannelConfig.java +++ b/transport-raknet/src/main/java/org/cloudburstmc/netty/channel/raknet/config/RakServerChannelConfig.java @@ -68,4 +68,8 @@ public interface RakServerChannelConfig extends ChannelConfig { int getUnconnectedPacketLimit(); void setUnconnectedPacketLimit(int limit); + + boolean getSendCookie(); + + void setSendCookie(boolean sendCookie); } diff --git a/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/client/RakClientOfflineHandler.java b/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/client/RakClientOfflineHandler.java index 098d8c07..31b3c64b 100644 --- a/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/client/RakClientOfflineHandler.java +++ b/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/client/RakClientOfflineHandler.java @@ -19,7 +19,6 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; import io.netty.channel.*; -import io.netty.channel.socket.DatagramPacket; import io.netty.handler.codec.CorruptedFrameException; import io.netty.util.concurrent.ScheduledFuture; import org.cloudburstmc.netty.channel.raknet.RakChannel; @@ -45,6 +44,8 @@ public class RakClientOfflineHandler extends SimpleChannelInboundHandler future, Channel channel) { channel.eventLoop().execute(() -> { // Make sure this is not called at two places at the same time if (!future.isCancelled()) { diff --git a/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/server/RakServerOfflineHandler.java b/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/server/RakServerOfflineHandler.java index 406af851..4729eeac 100644 --- a/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/server/RakServerOfflineHandler.java +++ b/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/server/RakServerOfflineHandler.java @@ -36,6 +36,7 @@ import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; +import java.security.SecureRandom; import java.util.Arrays; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -47,6 +48,8 @@ public class RakServerOfflineHandler extends AdvancedChannelInboundHandler random = ThreadLocal.withInitial(SecureRandom::new); + private final ExpiringMap pendingConnections = ExpiringMap.builder() .expiration(10, TimeUnit.SECONDS) .expirationPolicy(ExpirationPolicy.CREATED) @@ -58,6 +61,12 @@ public class RakServerOfflineHandler extends AdvancedChannelInboundHandler cookies = ExpiringMap.builder() + .expiration(10, TimeUnit.SECONDS) + .expirationPolicy(ExpirationPolicy.CREATED) + .expirationListener((key, value) -> ReferenceCountUtil.release(value)) + .build(); + private final RakServerChannel channel; public RakServerOfflineHandler(RakServerChannel channel) { @@ -176,11 +185,19 @@ private void onOpenConnectionRequest1(ChannelHandlerContext ctx, DatagramPacket log.trace("Received duplicate open connection request 1 from {}", sender); } - ByteBuf replyBuffer = ctx.alloc().ioBuffer(28, 28); + boolean sendCookie = ctx.channel().config().getOption(RakChannelOption.RAK_SEND_COOKIE); + int bufferCapacity = sendCookie ? 32 : 28; // 4 byte cookie + + ByteBuf replyBuffer = ctx.alloc().ioBuffer(bufferCapacity, bufferCapacity); replyBuffer.writeByte(ID_OPEN_CONNECTION_REPLY_1); replyBuffer.writeBytes(magicBuf, magicBuf.readerIndex(), magicBuf.readableBytes()); replyBuffer.writeLong(guid); - replyBuffer.writeBoolean(false); // Security + replyBuffer.writeBoolean(sendCookie); // Security + if (sendCookie) { + int cookie = this.random.get().nextInt(); + this.cookies.put(sender, cookie); + replyBuffer.writeInt(cookie); + } replyBuffer.writeShort(RakUtils.clamp(mtu, ctx.channel().config().getOption(RakChannelOption.RAK_MIN_MTU), ctx.channel().config().getOption(RakChannelOption.RAK_MAX_MTU))); ctx.writeAndFlush(new DatagramPacket(replyBuffer, sender)); } @@ -191,6 +208,21 @@ private void onOpenConnectionRequest2(ChannelHandlerContext ctx, DatagramPacket // Skip already verified magic buffer.skipBytes(magicBuf.readableBytes()); + boolean sendCookie = ctx.channel().config().getOption(RakChannelOption.RAK_SEND_COOKIE); + if (sendCookie) { + int cookie = buffer.readInt(); + Integer expectedCookie = this.cookies.remove(sender); + if (expectedCookie == null || expectedCookie != cookie) { + if (log.isTraceEnabled()) { + log.trace("Received open connection request 2 from {} with invalid cookie (expected {}, but received {})", sender, expectedCookie, cookie); + } + // Incorrect cookie provided + // This is likely source IP spoofing so we will not reply + return; + } + buffer.readBoolean(); // Client wrote challenge + } + Integer version = this.pendingConnections.remove(sender); if (version == null) { // We can't determine the version without the previous request, so assume it's the wrong version. diff --git a/transport-raknet/src/main/java/org/cloudburstmc/netty/util/RakUtils.java b/transport-raknet/src/main/java/org/cloudburstmc/netty/util/RakUtils.java index 97f0f220..42344199 100644 --- a/transport-raknet/src/main/java/org/cloudburstmc/netty/util/RakUtils.java +++ b/transport-raknet/src/main/java/org/cloudburstmc/netty/util/RakUtils.java @@ -69,7 +69,7 @@ public static InetSocketAddress readAddress(ByteBuf buffer) { int scopeId = buffer.readInt(); address = Inet6Address.getByAddress(null, addressBytes, scopeId); } else { - throw new UnsupportedOperationException("Unknown Internet Protocol version."); + throw new UnsupportedOperationException("Unknown Internet Protocol version. Expected 4 or 6, got " + type); } } catch (UnknownHostException e) { throw new IllegalArgumentException(e); diff --git a/transport-raknet/src/test/java/org/cloudburstmc/netty/RakTests.java b/transport-raknet/src/test/java/org/cloudburstmc/netty/RakTests.java index 6e4b8d4a..36bbc0a6 100644 --- a/transport-raknet/src/test/java/org/cloudburstmc/netty/RakTests.java +++ b/transport-raknet/src/test/java/org/cloudburstmc/netty/RakTests.java @@ -100,7 +100,20 @@ private static ServerBootstrap serverBootstrap() { .option(RakChannelOption.RAK_MAX_CONNECTIONS, 1) .childOption(RakChannelOption.RAK_ORDERING_CHANNELS, 1) .option(RakChannelOption.RAK_GUID, ThreadLocalRandom.current().nextLong()) - .option(RakChannelOption.RAK_ADVERTISEMENT, Unpooled.wrappedBuffer(ADVERTISEMENT)); + .option(RakChannelOption.RAK_ADVERTISEMENT, Unpooled.wrappedBuffer(ADVERTISEMENT)) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(RakServerChannel ch) throws Exception { + System.out.println("Initialised server channel"); + } + }) + .childHandler(new ChannelInitializer() { + @Override + protected void initChannel(RakChildChannel ch) throws Exception { + System.out.println("Server child channel initialized " + ch.remoteAddress()); + ch.pipeline().addLast(RESEND_HANDLER()); + } + }); } private static Bootstrap clientBootstrap(int mtu) { @@ -117,32 +130,44 @@ private static IntStream validMtu() { .filter(i -> i % 12 == 0); } - @BeforeEach public void setupServer() { serverBootstrap() - .handler(new ChannelInitializer() { - @Override - protected void initChannel(RakServerChannel ch) throws Exception { - System.out.println("Initialised server channel"); - } - }) - .childHandler(new ChannelInitializer() { - @Override - protected void initChannel(RakChildChannel ch) throws Exception { - System.out.println("Server child channel initialized " + ch.remoteAddress()); - ch.pipeline().addLast(RESEND_HANDLER()); - } - }) + .bind(new InetSocketAddress("127.0.0.1", 19132)) + .awaitUninterruptibly(); + } + + public void setupCookieServer() { + serverBootstrap() + .option(RakChannelOption.RAK_SEND_COOKIE, true) .bind(new InetSocketAddress("127.0.0.1", 19132)) .awaitUninterruptibly(); } @Test public void testClientConnect() { + setupServer(); int mtu = RakConstants.MAXIMUM_MTU_SIZE; System.out.println("Testing client with MTU " + mtu); - Channel channel = clientBootstrap(mtu) + clientBootstrap(mtu) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(RakClientChannel ch) throws Exception { + System.out.println("Client channel initialized"); + } + }) + .connect(new InetSocketAddress("127.0.0.1", 19132)) + .awaitUninterruptibly() + .channel(); + } + + @Test + public void testClientConnectWithCookie() { + setupCookieServer(); + int mtu = RakConstants.MAXIMUM_MTU_SIZE; + System.out.println("Testing client with MTU " + mtu + " and cookie enabled"); + + clientBootstrap(mtu) .handler(new ChannelInitializer() { @Override protected void initChannel(RakClientChannel ch) throws Exception { @@ -158,6 +183,7 @@ protected void initChannel(RakClientChannel ch) throws Exception { @ParameterizedTest @MethodSource("validMtu") public void testClientResend(int mtu) { + setupServer(); System.out.println("Testing client with MTU " + mtu); SecureRandom random = new SecureRandom(); From df8c28b1660cf8f2b31d7cc64590c5f18c76a901 Mon Sep 17 00:00:00 2001 From: Kas-tle <26531652+Kas-tle@users.noreply.github.com> Date: Thu, 4 Apr 2024 20:02:07 +0000 Subject: [PATCH 2/6] Address existing GH reviews - Add RAK_SEND_COOKIE to DefaultRakServerConfig#getOptions() - Consolidate RakClientOfflineHandler#sendOpenConnectionRequest2 to single method - Combine pendingConnections ExpiringMap Signed-off-by: GitHub --- .../raknet/config/DefaultRakServerConfig.java | 3 +- .../client/RakClientOfflineHandler.java | 27 ++----- .../server/RakServerOfflineHandler.java | 72 +++++++++++-------- 3 files changed, 52 insertions(+), 50 deletions(-) diff --git a/transport-raknet/src/main/java/org/cloudburstmc/netty/channel/raknet/config/DefaultRakServerConfig.java b/transport-raknet/src/main/java/org/cloudburstmc/netty/channel/raknet/config/DefaultRakServerConfig.java index 1e75d67e..2d0dd9a9 100644 --- a/transport-raknet/src/main/java/org/cloudburstmc/netty/channel/raknet/config/DefaultRakServerConfig.java +++ b/transport-raknet/src/main/java/org/cloudburstmc/netty/channel/raknet/config/DefaultRakServerConfig.java @@ -57,7 +57,8 @@ public Map, Object> getOptions() { return getOptions( super.getOptions(), RakChannelOption.RAK_GUID, RakChannelOption.RAK_MAX_CHANNELS, RakChannelOption.RAK_MAX_CONNECTIONS, - RakChannelOption.RAK_SUPPORTED_PROTOCOLS, RakChannelOption.RAK_UNCONNECTED_MAGIC, RakChannelOption.RAK_ADVERTISEMENT); + RakChannelOption.RAK_SUPPORTED_PROTOCOLS, RakChannelOption.RAK_UNCONNECTED_MAGIC, RakChannelOption.RAK_ADVERTISEMENT, + RakChannelOption.RAK_SEND_COOKIE); } @SuppressWarnings("unchecked") diff --git a/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/client/RakClientOfflineHandler.java b/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/client/RakClientOfflineHandler.java index 31b3c64b..7f0a16e5 100644 --- a/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/client/RakClientOfflineHandler.java +++ b/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/client/RakClientOfflineHandler.java @@ -164,11 +164,7 @@ private void onOpenConnectionReply1(ChannelHandlerContext ctx, ByteBuf buffer) { this.rakChannel.config().setOption(RakChannelOption.RAK_REMOTE_GUID, serverGuid); this.state = RakOfflineState.HANDSHAKE_2; - if (this.security) { - this.sendOpenConnectionRequest2(ctx.channel(), this.cookie); - } else { - this.sendOpenConnectionRequest2(ctx.channel()); - } + this.sendOpenConnectionRequest2(ctx.channel()); } private void onOpenConnectionReply2(ChannelHandlerContext ctx, ByteBuf buffer) { @@ -209,24 +205,13 @@ private void sendOpenConnectionRequest2(Channel channel) { int mtuSize = this.rakChannel.config().getOption(RakChannelOption.RAK_MTU); ByteBuf magicBuf = this.rakChannel.config().getOption(RakChannelOption.RAK_UNCONNECTED_MAGIC); - ByteBuf request = channel.alloc().ioBuffer(34); + ByteBuf request = channel.alloc().ioBuffer(this.security ? 39 : 34); request.writeByte(ID_OPEN_CONNECTION_REQUEST_2); request.writeBytes(magicBuf, magicBuf.readerIndex(), magicBuf.readableBytes()); - RakUtils.writeAddress(request, (InetSocketAddress) channel.remoteAddress()); - request.writeShort(mtuSize); - request.writeLong(this.rakChannel.config().getOption(RakChannelOption.RAK_GUID)); - channel.writeAndFlush(request); - } - - private void sendOpenConnectionRequest2(Channel channel, int cookie) { - int mtuSize = this.rakChannel.config().getOption(RakChannelOption.RAK_MTU); - ByteBuf magicBuf = this.rakChannel.config().getOption(RakChannelOption.RAK_UNCONNECTED_MAGIC); - - ByteBuf request = channel.alloc().ioBuffer(39); - request.writeByte(ID_OPEN_CONNECTION_REQUEST_2); - request.writeBytes(magicBuf, magicBuf.readerIndex(), magicBuf.readableBytes()); - request.writeInt(cookie); - request.writeBoolean(false); // Client wrote challenge + if (this.security) { + request.writeInt(this.cookie); + request.writeBoolean(false); // Client wrote challenge + } RakUtils.writeAddress(request, (InetSocketAddress) channel.remoteAddress()); request.writeShort(mtuSize); request.writeLong(this.rakChannel.config().getOption(RakChannelOption.RAK_GUID)); diff --git a/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/server/RakServerOfflineHandler.java b/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/server/RakServerOfflineHandler.java index 4729eeac..90e14612 100644 --- a/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/server/RakServerOfflineHandler.java +++ b/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/server/RakServerOfflineHandler.java @@ -26,7 +26,6 @@ import net.jodah.expiringmap.ExpirationPolicy; import net.jodah.expiringmap.ExpiringMap; import org.cloudburstmc.netty.channel.raknet.RakChildChannel; -import org.cloudburstmc.netty.channel.raknet.RakConstants; import org.cloudburstmc.netty.channel.raknet.RakPing; import org.cloudburstmc.netty.channel.raknet.RakServerChannel; import org.cloudburstmc.netty.channel.raknet.config.RakChannelOption; @@ -50,7 +49,7 @@ public class RakServerOfflineHandler extends AdvancedChannelInboundHandler random = ThreadLocal.withInitial(SecureRandom::new); - private final ExpiringMap pendingConnections = ExpiringMap.builder() + private final ExpiringMap pendingConnections = ExpiringMap.builder() .expiration(10, TimeUnit.SECONDS) .expirationPolicy(ExpirationPolicy.CREATED) .expirationListener((key, value) -> ReferenceCountUtil.release(value)) @@ -61,12 +60,6 @@ public class RakServerOfflineHandler extends AdvancedChannelInboundHandler cookies = ExpiringMap.builder() - .expiration(10, TimeUnit.SECONDS) - .expirationPolicy(ExpirationPolicy.CREATED) - .expirationListener((key, value) -> ReferenceCountUtil.release(value)) - .build(); - private final RakServerChannel channel; public RakServerOfflineHandler(RakServerChannel channel) { @@ -180,12 +173,21 @@ private void onOpenConnectionRequest1(ChannelHandlerContext ctx, DatagramPacket // TODO: banned address check? // TODO: max connections check? - Integer version = this.pendingConnections.put(sender, protocolVersion); - if (version != null && log.isTraceEnabled()) { + + boolean sendCookie = ctx.channel().config().getOption(RakChannelOption.RAK_SEND_COOKIE); + int cookie; + + if (sendCookie) { + cookie = this.random.get().nextInt(); + } else { + cookie = 0; + } + + PendingConnection connection = this.pendingConnections.put(sender, new PendingConnection(protocolVersion, cookie)); + if (connection != null && log.isTraceEnabled()) { log.trace("Received duplicate open connection request 1 from {}", sender); } - boolean sendCookie = ctx.channel().config().getOption(RakChannelOption.RAK_SEND_COOKIE); int bufferCapacity = sendCookie ? 32 : 28; // 4 byte cookie ByteBuf replyBuffer = ctx.alloc().ioBuffer(bufferCapacity, bufferCapacity); @@ -194,8 +196,6 @@ private void onOpenConnectionRequest1(ChannelHandlerContext ctx, DatagramPacket replyBuffer.writeLong(guid); replyBuffer.writeBoolean(sendCookie); // Security if (sendCookie) { - int cookie = this.random.get().nextInt(); - this.cookies.put(sender, cookie); replyBuffer.writeInt(cookie); } replyBuffer.writeShort(RakUtils.clamp(mtu, ctx.channel().config().getOption(RakChannelOption.RAK_MIN_MTU), ctx.channel().config().getOption(RakChannelOption.RAK_MAX_MTU))); @@ -208,11 +208,21 @@ private void onOpenConnectionRequest2(ChannelHandlerContext ctx, DatagramPacket // Skip already verified magic buffer.skipBytes(magicBuf.readableBytes()); + + PendingConnection connection = this.pendingConnections.remove(sender); + if (connection == null) { + if (log.isTraceEnabled()) { + log.trace("Received open connection request 2 from {} without open connection request 1", sender); + } + // Don't respond yet as we cannot verify the connection source IP + return; + } + boolean sendCookie = ctx.channel().config().getOption(RakChannelOption.RAK_SEND_COOKIE); if (sendCookie) { int cookie = buffer.readInt(); - Integer expectedCookie = this.cookies.remove(sender); - if (expectedCookie == null || expectedCookie != cookie) { + int expectedCookie = connection.getCookie(); + if (expectedCookie != cookie) { if (log.isTraceEnabled()) { log.trace("Received open connection request 2 from {} with invalid cookie (expected {}, but received {})", sender, expectedCookie, cookie); } @@ -223,18 +233,6 @@ private void onOpenConnectionRequest2(ChannelHandlerContext ctx, DatagramPacket buffer.readBoolean(); // Client wrote challenge } - Integer version = this.pendingConnections.remove(sender); - if (version == null) { - // We can't determine the version without the previous request, so assume it's the wrong version. - if (log.isTraceEnabled()) { - log.trace("Received open connection request 2 from {} without open connection request 1", sender); - } - int[] supportedProtocols = ctx.channel().config().getOption(RakChannelOption.RAK_SUPPORTED_PROTOCOLS); - int latestVersion = supportedProtocols == null ? RakConstants.RAKNET_PROTOCOL_VERSION : supportedProtocols[supportedProtocols.length - 1]; - this.sendIncompatibleVersion(ctx, sender, latestVersion, magicBuf, guid); - return; - } - // TODO: Verify serverAddress matches? InetSocketAddress serverAddress = RakUtils.readAddress(buffer); int mtu = buffer.readUnsignedShort(); @@ -247,7 +245,7 @@ private void onOpenConnectionRequest2(ChannelHandlerContext ctx, DatagramPacket } RakServerChannel serverChannel = (RakServerChannel) ctx.channel(); - RakChildChannel channel = serverChannel.createChildChannel(sender, clientGuid, version, mtu); + RakChildChannel channel = serverChannel.createChildChannel(sender, clientGuid, connection.getProtocolVersion(), mtu); if (channel == null) { // Already connected this.sendAlreadyConnected(ctx, sender, magicBuf, guid); @@ -280,4 +278,22 @@ private void sendAlreadyConnected(ChannelHandlerContext ctx, InetSocketAddress s buffer.writeLong(guid); ctx.writeAndFlush(new DatagramPacket(buffer, sender)); } + + private class PendingConnection { + private final int protocolVersion; + private final int cookie; + + public PendingConnection(int protocolVersion, int cookie) { + this.protocolVersion = protocolVersion; + this.cookie = cookie; + } + + public int getProtocolVersion() { + return this.protocolVersion; + } + + public int getCookie() { + return this.cookie; + } + } } From fbaf5ac43f864038b681452276e7103958042848 Mon Sep 17 00:00:00 2001 From: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> Date: Thu, 4 Apr 2024 15:49:53 -0700 Subject: [PATCH 3/6] Remove unused packages Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> --- .../java/org/cloudburstmc/netty/channel/raknet/RakChannel.java | 1 - .../codec/raknet/client/RakClientOnlineInitialHandler.java | 2 -- 2 files changed, 3 deletions(-) diff --git a/transport-raknet/src/main/java/org/cloudburstmc/netty/channel/raknet/RakChannel.java b/transport-raknet/src/main/java/org/cloudburstmc/netty/channel/raknet/RakChannel.java index e379a6b3..35c67833 100644 --- a/transport-raknet/src/main/java/org/cloudburstmc/netty/channel/raknet/RakChannel.java +++ b/transport-raknet/src/main/java/org/cloudburstmc/netty/channel/raknet/RakChannel.java @@ -17,7 +17,6 @@ package org.cloudburstmc.netty.channel.raknet; import io.netty.channel.Channel; -import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelPipeline; import org.cloudburstmc.netty.channel.raknet.config.RakChannelConfig; diff --git a/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/client/RakClientOnlineInitialHandler.java b/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/client/RakClientOnlineInitialHandler.java index 287e694c..a78ad0ce 100644 --- a/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/client/RakClientOnlineInitialHandler.java +++ b/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/client/RakClientOnlineInitialHandler.java @@ -18,7 +18,6 @@ import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; @@ -32,7 +31,6 @@ import org.cloudburstmc.netty.util.RakUtils; import java.net.InetSocketAddress; -import java.util.concurrent.TimeUnit; import static org.cloudburstmc.netty.channel.raknet.RakConstants.*; From 59e67b4aa2da837c35a5b75efe299bd4054b8abe Mon Sep 17 00:00:00 2001 From: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> Date: Thu, 4 Apr 2024 17:16:59 -0700 Subject: [PATCH 4/6] Create provider for preferred SecureRandom algorithm Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> --- .../server/RakServerOfflineHandler.java | 10 ++++- .../netty/util/SecureAlgorithmProvider.java | 38 +++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 transport-raknet/src/main/java/org/cloudburstmc/netty/util/SecureAlgorithmProvider.java diff --git a/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/server/RakServerOfflineHandler.java b/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/server/RakServerOfflineHandler.java index 239b37a7..cbbd14fd 100644 --- a/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/server/RakServerOfflineHandler.java +++ b/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/server/RakServerOfflineHandler.java @@ -32,9 +32,11 @@ import org.cloudburstmc.netty.channel.raknet.config.RakServerMetrics; import org.cloudburstmc.netty.handler.codec.raknet.AdvancedChannelInboundHandler; import org.cloudburstmc.netty.util.RakUtils; +import org.cloudburstmc.netty.util.SecureAlgorithmProvider; import java.net.Inet6Address; import java.net.InetSocketAddress; +import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Arrays; import java.util.concurrent.TimeUnit; @@ -46,7 +48,13 @@ public class RakServerOfflineHandler extends AdvancedChannelInboundHandler random = ThreadLocal.withInitial(SecureRandom::new); + private final ThreadLocal random = ThreadLocal.withInitial(() -> { + try { + return SecureRandom.getInstance(SecureAlgorithmProvider.getSecurityAlgorithm()); + } catch (NoSuchAlgorithmException e) { + return new SecureRandom(); + } + }); private final ExpiringMap pendingConnections = ExpiringMap.builder() .expiration(10, TimeUnit.SECONDS) diff --git a/transport-raknet/src/main/java/org/cloudburstmc/netty/util/SecureAlgorithmProvider.java b/transport-raknet/src/main/java/org/cloudburstmc/netty/util/SecureAlgorithmProvider.java new file mode 100644 index 00000000..9f33b532 --- /dev/null +++ b/transport-raknet/src/main/java/org/cloudburstmc/netty/util/SecureAlgorithmProvider.java @@ -0,0 +1,38 @@ +package org.cloudburstmc.netty.util; + +import java.security.Provider; +import java.security.SecureRandom; +import java.security.Security; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class SecureAlgorithmProvider { + private static final String SECURITY_ALGORITHM; + // SecureRandom algorithms in order of most preferred to least preferred. + private static List preferredAlgorithms = Arrays.asList( + "SHA1PRNG", + "NativePRNGNonBlocking", + "Windows-PRNG", + "NativePRNG", + "PKCS11", + "DRBG", + "NativePRNGBlocking" + ); + + static { + SECURITY_ALGORITHM = Stream.of(Security.getProviders()) + .flatMap(provider -> provider.getServices().stream()) + .filter(service -> "SecureRandom".equals(service.getType())) + .map(Provider.Service::getAlgorithm) + .filter(preferredAlgorithms::contains) + .min((s1, s2) -> Integer.compare(preferredAlgorithms.indexOf(s1), preferredAlgorithms.indexOf(s2))) + .orElse(new SecureRandom().getAlgorithm()); + + preferredAlgorithms = null; + } + + public static String getSecurityAlgorithm() { + return SECURITY_ALGORITHM; + } +} From 7f08794b822635eb49bdc35c5d9a4d20ddc58235 Mon Sep 17 00:00:00 2001 From: Kas-tle <26531652+Kas-tle@users.noreply.github.com> Date: Thu, 4 Apr 2024 19:41:39 -0700 Subject: [PATCH 5/6] preferredAlgorithms in block --- .../netty/util/SecureAlgorithmProvider.java | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/transport-raknet/src/main/java/org/cloudburstmc/netty/util/SecureAlgorithmProvider.java b/transport-raknet/src/main/java/org/cloudburstmc/netty/util/SecureAlgorithmProvider.java index 9f33b532..e00dcc83 100644 --- a/transport-raknet/src/main/java/org/cloudburstmc/netty/util/SecureAlgorithmProvider.java +++ b/transport-raknet/src/main/java/org/cloudburstmc/netty/util/SecureAlgorithmProvider.java @@ -9,18 +9,19 @@ public class SecureAlgorithmProvider { private static final String SECURITY_ALGORITHM; - // SecureRandom algorithms in order of most preferred to least preferred. - private static List preferredAlgorithms = Arrays.asList( - "SHA1PRNG", - "NativePRNGNonBlocking", - "Windows-PRNG", - "NativePRNG", - "PKCS11", - "DRBG", - "NativePRNGBlocking" - ); static { + // SecureRandom algorithms in order of most preferred to least preferred. + private List preferredAlgorithms = Arrays.asList( + "SHA1PRNG", + "NativePRNGNonBlocking", + "Windows-PRNG", + "NativePRNG", + "PKCS11", + "DRBG", + "NativePRNGBlocking" + ); + SECURITY_ALGORITHM = Stream.of(Security.getProviders()) .flatMap(provider -> provider.getServices().stream()) .filter(service -> "SecureRandom".equals(service.getType())) @@ -28,8 +29,6 @@ public class SecureAlgorithmProvider { .filter(preferredAlgorithms::contains) .min((s1, s2) -> Integer.compare(preferredAlgorithms.indexOf(s1), preferredAlgorithms.indexOf(s2))) .orElse(new SecureRandom().getAlgorithm()); - - preferredAlgorithms = null; } public static String getSecurityAlgorithm() { From 66a52537f0aa50060f5c65a0f15df79befc7418c Mon Sep 17 00:00:00 2001 From: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> Date: Sat, 6 Apr 2024 00:43:57 -0700 Subject: [PATCH 6/6] Why we should not do web edits Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> --- .../org/cloudburstmc/netty/util/SecureAlgorithmProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transport-raknet/src/main/java/org/cloudburstmc/netty/util/SecureAlgorithmProvider.java b/transport-raknet/src/main/java/org/cloudburstmc/netty/util/SecureAlgorithmProvider.java index e00dcc83..9e634518 100644 --- a/transport-raknet/src/main/java/org/cloudburstmc/netty/util/SecureAlgorithmProvider.java +++ b/transport-raknet/src/main/java/org/cloudburstmc/netty/util/SecureAlgorithmProvider.java @@ -12,7 +12,7 @@ public class SecureAlgorithmProvider { static { // SecureRandom algorithms in order of most preferred to least preferred. - private List preferredAlgorithms = Arrays.asList( + final List preferredAlgorithms = Arrays.asList( "SHA1PRNG", "NativePRNGNonBlocking", "Windows-PRNG",