aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0181-Make-legacy-ping-handler-more-reliable.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patches/server/0181-Make-legacy-ping-handler-more-reliable.patch')
-rw-r--r--patches/server/0181-Make-legacy-ping-handler-more-reliable.patch188
1 files changed, 188 insertions, 0 deletions
diff --git a/patches/server/0181-Make-legacy-ping-handler-more-reliable.patch b/patches/server/0181-Make-legacy-ping-handler-more-reliable.patch
new file mode 100644
index 0000000000..e9431bd1eb
--- /dev/null
+++ b/patches/server/0181-Make-legacy-ping-handler-more-reliable.patch
@@ -0,0 +1,188 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Minecrell <[email protected]>
+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
+index 33e3a81f8ec18f0ba7170d885c4717640bf40eab..738da83f82bf01bde94c956ac22525a638db3906 100644
+--- a/src/main/java/net/minecraft/server/network/LegacyQueryHandler.java
++++ b/src/main/java/net/minecraft/server/network/LegacyQueryHandler.java
+@@ -15,6 +15,7 @@ public class LegacyQueryHandler extends ChannelInboundHandlerAdapter {
+
+ private static final Logger LOGGER = LogUtils.getLogger();
+ private final ServerInfo server;
++ private ByteBuf buf; // Paper
+
+ public LegacyQueryHandler(ServerInfo server) {
+ this.server = server;
+@@ -23,6 +24,16 @@ public class LegacyQueryHandler extends ChannelInboundHandlerAdapter {
+ public void channelRead(ChannelHandlerContext channelhandlercontext, Object object) {
+ 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;
+
+@@ -34,7 +45,7 @@ public class LegacyQueryHandler extends ChannelInboundHandlerAdapter {
+
+ SocketAddress socketaddress = channelhandlercontext.channel().remoteAddress();
+ int i = bytebuf.readableBytes();
+- String s;
++ String s = null; // Paper
+ org.bukkit.event.server.ServerListPingEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callServerListPingEvent(socketaddress, this.server.getMotd(), this.server.getPlayerCount(), this.server.getMaxPlayers()); // CraftBukkit
+
+ if (i == 0) {
+@@ -47,16 +58,24 @@ public class LegacyQueryHandler extends ChannelInboundHandlerAdapter {
+ }
+
+ if (bytebuf.isReadable()) {
+- if (!LegacyQueryHandler.readCustomPayloadPacket(bytebuf)) {
+- return;
++ // Paper start - Replace with improved version below
++ if (bytebuf.readUnsignedByte() != 250) {
++ s = this.readLegacy1_6(channelhandlercontext, bytebuf);
++ if (s == null) {
++ return;
++ }
+ }
+-
+- LegacyQueryHandler.LOGGER.debug("Ping: (1.6) from {}", socketaddress);
++ // if (!LegacyQueryHandler.readCustomPayloadPacket(bytebuf)) {
++ // return;
++ // }
++ //
++ // LegacyQueryHandler.LOGGER.debug("Ping: (1.6) from {}", socketaddress);
++ // Paper end
+ } else {
+ LegacyQueryHandler.LOGGER.debug("Ping: (1.4-1.5.x) from {}", socketaddress);
+ }
+
+- s = LegacyQueryHandler.createVersion1Response(this.server, event); // CraftBukkit
++ if (s == null) s = LegacyQueryHandler.createVersion1Response(this.server, event); // CraftBukkit // Paper
+ LegacyQueryHandler.sendFlushAndClose(channelhandlercontext, LegacyQueryHandler.createLegacyDisconnectPacket(channelhandlercontext.alloc(), s));
+ }
+
+@@ -107,6 +126,90 @@ public class LegacyQueryHandler extends ChannelInboundHandlerAdapter {
+ }
+ }
+
++ // 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, java.nio.charset.StandardCharsets.UTF_16BE);
++ buf.skipBytes(size); // toString doesn't increase readerIndex automatically
++ return result;
++ }
++
++ private String 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 null;
++ }
++
++ String s = readLegacyString(buf);
++ if (s == null) {
++ return null;
++ }
++
++ if (!s.equals("MC|PingHost")) {
++ removeHandler(ctx);
++ return null;
++ }
++
++ if (!buf.isReadable(Short.BYTES) || !buf.isReadable(buf.readShort())) {
++ return null;
++ }
++
++ net.minecraft.server.MinecraftServer server = net.minecraft.server.MinecraftServer.getServer();
++ int protocolVersion = buf.readByte();
++ String host = readLegacyString(buf);
++ if (host == null) {
++ removeHandler(ctx);
++ return null;
++ }
++ int port = buf.readInt();
++
++ if (buf.isReadable()) {
++ removeHandler(ctx);
++ return null;
++ }
++
++ 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());
++ return 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
++
+ // CraftBukkit start
+ private static String createVersion0Response(ServerInfo serverinfo, org.bukkit.event.server.ServerListPingEvent event) {
+ return String.format(Locale.ROOT, "%s\u00a7%d\u00a7%d", event.getMotd(), event.getNumPlayers(), event.getMaxPlayers());