--- a/net/minecraft/server/network/LegacyQueryHandler.java +++ b/net/minecraft/server/network/LegacyQueryHandler.java @@ -15,6 +15,7 @@ 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 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,11 +45,23 @@ 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 // Paper + com.destroystokyo.paper.event.server.PaperServerListPingEvent event; // Paper if (i == 0) { LegacyQueryHandler.LOGGER.debug("Ping: (<1.3.x) from {}", socketaddress); - s = LegacyQueryHandler.createVersion0Response(this.server); + + // Paper start - Call PaperServerListPingEvent and use results + event = com.destroystokyo.paper.network.PaperLegacyStatusClient.processRequest(net.minecraft.server.MinecraftServer.getServer(), (java.net.InetSocketAddress) socketaddress, 39, null); + if (event == null) { + channelhandlercontext.close(); + bytebuf.release(); + flag = false; + return; + } + s = String.format(Locale.ROOT, "%s\u00a7%d\u00a7%d", com.destroystokyo.paper.network.PaperLegacyStatusClient.getUnformattedMotd(event), event.getNumPlayers(), event.getMaxPlayers()); + // Paper end LegacyQueryHandler.sendFlushAndClose(channelhandlercontext, LegacyQueryHandler.createLegacyDisconnectPacket(channelhandlercontext.alloc(), s)); } else { if (bytebuf.readUnsignedByte() != 1) { @@ -46,16 +69,35 @@ } 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); + if (s == null) { + // Paper start - Call PaperServerListPingEvent and use results + event = com.destroystokyo.paper.network.PaperLegacyStatusClient.processRequest(net.minecraft.server.MinecraftServer.getServer(), (java.net.InetSocketAddress) socketaddress, 127, null); // Paper + if (event == null) { + channelhandlercontext.close(); + bytebuf.release(); + flag = false; + return; + } + s = String.format(Locale.ROOT, "\u00a71\u0000%d\u0000%s\u0000%s\u0000%d\u0000%d", new Object[] { event.getProtocolVersion(), this.server.getServerVersion(), event.getMotd(), event.getNumPlayers(), event.getMaxPlayers()}); // CraftBukkit + // Paper end + } LegacyQueryHandler.sendFlushAndClose(channelhandlercontext, LegacyQueryHandler.createLegacyDisconnectPacket(channelhandlercontext.alloc(), s)); } @@ -106,14 +148,110 @@ } } - private static String createVersion0Response(ServerInfo server) { - return String.format(Locale.ROOT, "%s\u00a7%d\u00a7%d", server.getMotd(), server.getPlayerCount(), server.getMaxPlayers()); + // 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 static String createVersion1Response(ServerInfo server) { - return String.format(Locale.ROOT, "\u00a71\u0000%d\u0000%s\u0000%s\u0000%d\u0000%d", 127, server.getServerVersion(), server.getMotd(), server.getPlayerCount(), server.getMaxPlayers()); + 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()); + + java.net.InetSocketAddress virtualHost = com.destroystokyo.paper.network.PaperNetworkClient.prepareVirtualHost(host, port); + com.destroystokyo.paper.event.server.PaperServerListPingEvent event = com.destroystokyo.paper.network.PaperLegacyStatusClient.processRequest( + server, (java.net.InetSocketAddress) ctx.channel().remoteAddress(), protocolVersion, virtualHost); + if (event == null) { + ctx.close(); + return null; + } + + String response = String.format("\u00a71\u0000%d\u0000%s\u0000%s\u0000%d\u0000%d", event.getProtocolVersion(), event.getVersion(), + com.destroystokyo.paper.network.PaperLegacyStatusClient.getMotd(event), event.getNumPlayers(), event.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()); + // CraftBukkit end + } + + // CraftBukkit start + private static String createVersion1Response(ServerInfo serverinfo, org.bukkit.event.server.ServerListPingEvent event) { + return String.format(Locale.ROOT, "\u00a71\u0000%d\u0000%s\u0000%s\u0000%d\u0000%d", 127, serverinfo.getServerVersion(), event.getMotd(), event.getNumPlayers(), event.getMaxPlayers()); + // CraftBukkit end + } + private static void sendFlushAndClose(ChannelHandlerContext context, ByteBuf buf) { context.pipeline().firstContext().writeAndFlush(buf).addListener(ChannelFutureListener.CLOSE); }