2021-06-11 14:02:28 +02:00
|
|
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
|
|
From: Minecrell <minecrell@minecrell.net>
|
|
|
|
Date: Wed, 11 Oct 2017 18:22:50 +0200
|
|
|
|
Subject: [PATCH] Make legacy ping handler more reliable
|
|
|
|
|
|
|
|
The Minecraft server often fails to respond to old ("legacy") pings
|
|
|
|
from old Minecraft versions using the protocol used before the switch
|
|
|
|
to Netty in Minecraft 1.7.
|
|
|
|
|
|
|
|
Due to packet fragmentation[1], we might not have all needed bytes
|
|
|
|
available when the LegacyPingHandler is called. In this case, it will
|
|
|
|
run into an error, remove the handler and continue using the modern
|
|
|
|
protocol.
|
|
|
|
|
|
|
|
This is unlikely to happen for the first two revisions of the legacy
|
|
|
|
ping protocol (used in Minecraft 1.5.x and older) since the request
|
|
|
|
consists of only one or two bytes, but happens frequently for the
|
|
|
|
last/third revision introduced in Minecraft 1.6.
|
|
|
|
|
|
|
|
It has much larger, variable packet sizes due to the inclusion of
|
|
|
|
the virtual host (the hostname/port used to connect to the server).
|
|
|
|
|
|
|
|
The solution[2] is simple: If we find more than two matching bytes,
|
|
|
|
we buffer the remaining bytes until we have enough to fully read and
|
|
|
|
respond to the request.
|
|
|
|
|
|
|
|
[1]: https://netty.io/wiki/user-guide-for-4.x.html#wiki-h3-11
|
|
|
|
[2]: https://netty.io/wiki/user-guide-for-4.x.html#wiki-h4-13
|
|
|
|
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/network/LegacyQueryHandler.java b/src/main/java/net/minecraft/server/network/LegacyQueryHandler.java
|
2021-06-12 21:30:37 +02:00
|
|
|
index a2b0237a27379d05e8ca15cb033ee3fd2a5bb29b..6a759cfd0c2df4daaf126d12d20ac8d701e41f9d 100644
|
2021-06-11 14:02:28 +02:00
|
|
|
--- a/src/main/java/net/minecraft/server/network/LegacyQueryHandler.java
|
|
|
|
+++ b/src/main/java/net/minecraft/server/network/LegacyQueryHandler.java
|
2021-06-12 21:30:37 +02:00
|
|
|
@@ -16,6 +16,7 @@ public class LegacyQueryHandler extends ChannelInboundHandlerAdapter {
|
2021-06-11 14:02:28 +02:00
|
|
|
private static final Logger LOGGER = LogManager.getLogger();
|
2021-06-12 21:30:37 +02:00
|
|
|
public static final int FAKE_PROTOCOL_VERSION = 127;
|
2021-06-11 14:02:28 +02:00
|
|
|
private final ServerConnectionListener serverConnectionListener;
|
|
|
|
+ private ByteBuf buf; // Paper
|
|
|
|
|
|
|
|
public LegacyQueryHandler(ServerConnectionListener networkIo) {
|
|
|
|
this.serverConnectionListener = networkIo;
|
2021-06-12 21:30:37 +02:00
|
|
|
@@ -24,6 +25,16 @@ public class LegacyQueryHandler extends ChannelInboundHandlerAdapter {
|
|
|
|
public void channelRead(ChannelHandlerContext channelhandlercontext, Object object) {
|
2021-06-11 14:02:28 +02:00
|
|
|
ByteBuf bytebuf = (ByteBuf) object;
|
|
|
|
|
|
|
|
+ // Paper start - Make legacy ping handler more reliable
|
|
|
|
+ if (this.buf != null) {
|
|
|
|
+ try {
|
|
|
|
+ readLegacy1_6(channelhandlercontext, bytebuf);
|
|
|
|
+ } finally {
|
|
|
|
+ bytebuf.release();
|
|
|
|
+ }
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ // Paper end
|
|
|
|
bytebuf.markReaderIndex();
|
|
|
|
boolean flag = true;
|
|
|
|
|
2021-06-12 21:30:37 +02:00
|
|
|
@@ -54,6 +65,10 @@ public class LegacyQueryHandler extends ChannelInboundHandlerAdapter {
|
2021-06-11 14:02:28 +02:00
|
|
|
this.sendFlushAndClose(channelhandlercontext, this.createReply(s));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
+ // Paper start - Replace with improved version below
|
|
|
|
+ if (bytebuf.readUnsignedByte() != 0x01 || bytebuf.readUnsignedByte() != 0xFA) return;
|
|
|
|
+ readLegacy1_6(channelhandlercontext, bytebuf);
|
|
|
|
+ /*
|
|
|
|
boolean flag1 = bytebuf.readUnsignedByte() == 1;
|
|
|
|
|
|
|
|
flag1 &= bytebuf.readUnsignedByte() == 250;
|
2021-06-12 21:30:37 +02:00
|
|
|
@@ -77,6 +92,7 @@ public class LegacyQueryHandler extends ChannelInboundHandlerAdapter {
|
2021-06-11 14:02:28 +02:00
|
|
|
} finally {
|
|
|
|
bytebuf1.release();
|
|
|
|
}
|
|
|
|
+ */ // Paper end - Replace with improved version below
|
|
|
|
}
|
|
|
|
|
|
|
|
bytebuf.release();
|
2021-06-12 21:30:37 +02:00
|
|
|
@@ -94,6 +110,90 @@ public class LegacyQueryHandler extends ChannelInboundHandlerAdapter {
|
2021-06-11 14:02:28 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
+ // Paper start
|
|
|
|
+ private static String readLegacyString(ByteBuf buf) {
|
|
|
|
+ int size = buf.readShort() * Character.BYTES;
|
|
|
|
+ if (!buf.isReadable(size)) {
|
|
|
|
+ return null;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ String result = buf.toString(buf.readerIndex(), size, StandardCharsets.UTF_16BE);
|
|
|
|
+ buf.skipBytes(size); // toString doesn't increase readerIndex automatically
|
|
|
|
+ return result;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void readLegacy1_6(ChannelHandlerContext ctx, ByteBuf part) {
|
|
|
|
+ ByteBuf buf = this.buf;
|
|
|
|
+
|
|
|
|
+ if (buf == null) {
|
|
|
|
+ this.buf = buf = ctx.alloc().buffer();
|
|
|
|
+ buf.markReaderIndex();
|
|
|
|
+ } else {
|
|
|
|
+ buf.resetReaderIndex();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ buf.writeBytes(part);
|
|
|
|
+
|
|
|
|
+ if (!buf.isReadable(Short.BYTES + Short.BYTES + Byte.BYTES + Short.BYTES + Integer.BYTES)) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ String s = readLegacyString(buf);
|
|
|
|
+ if (s == null) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!s.equals("MC|PingHost")) {
|
|
|
|
+ removeHandler(ctx);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!buf.isReadable(Short.BYTES) || !buf.isReadable(buf.readShort())) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ MinecraftServer server = this.serverConnectionListener.getServer();
|
|
|
|
+ int protocolVersion = buf.readByte();
|
|
|
|
+ String host = readLegacyString(buf);
|
|
|
|
+ if (host == null) {
|
|
|
|
+ removeHandler(ctx);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ int port = buf.readInt();
|
|
|
|
+
|
|
|
|
+ if (buf.isReadable()) {
|
|
|
|
+ removeHandler(ctx);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ buf.release();
|
|
|
|
+ this.buf = null;
|
|
|
|
+
|
|
|
|
+ LOGGER.debug("Ping: (1.6) from {}", ctx.channel().remoteAddress());
|
|
|
|
+
|
|
|
|
+ String response = String.format("\u00a71\u0000%d\u0000%s\u0000%s\u0000%d\u0000%d",
|
|
|
|
+ Byte.MAX_VALUE, server.getServerVersion(), server.getMotd(), server.getPlayerCount(), server.getMaxPlayers());
|
|
|
|
+ this.sendFlushAndClose(ctx, this.createReply(response));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void removeHandler(ChannelHandlerContext ctx) {
|
|
|
|
+ ByteBuf buf = this.buf;
|
|
|
|
+ this.buf = null;
|
|
|
|
+
|
|
|
|
+ buf.resetReaderIndex();
|
|
|
|
+ ctx.pipeline().remove(this);
|
|
|
|
+ ctx.fireChannelRead(buf);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void handlerRemoved(ChannelHandlerContext ctx) {
|
|
|
|
+ if (this.buf != null) {
|
|
|
|
+ this.buf.release();
|
|
|
|
+ this.buf = null;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ // Paper end
|
|
|
|
+
|
|
|
|
private void sendFlushAndClose(ChannelHandlerContext ctx, ByteBuf buf) {
|
|
|
|
ctx.pipeline().firstContext().writeAndFlush(buf).addListener(ChannelFutureListener.CLOSE);
|
|
|
|
}
|