package net.glowstone.net.handler.legacyping; import java.io.UnsupportedEncodingException; import java.net.InetSocketAddress; import org.bukkit.event.server.ServerListPingEvent; import com.google.common.base.Charsets; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import net.glowstone.EventFactory; import net.glowstone.GlowServer; import net.glowstone.net.GameServer; public class LegacyPingHandler extends ChannelInboundHandlerAdapter { GameServer networkServer; public LegacyPingHandler(GameServer networkServer) { this.networkServer = networkServer; } @Override public void channelRead(ChannelHandlerContext channelHandlerContext, Object object) throws Exception { ByteBuf bytebuf = (ByteBuf) object; bytebuf.markReaderIndex(); boolean legacyPingProtocol = false; try { if (bytebuf.readByte() == (byte) 0xFE) { GlowServer server = networkServer.getServer(); int readableBytes = bytebuf.readableBytes(); InetSocketAddress inetsocketaddress = (InetSocketAddress) channelHandlerContext.channel().remoteAddress(); ServerListPingEvent legacyPingEvent = new ServerListPingEvent(inetsocketaddress.getAddress(), server.getMotd(), server.getOnlinePlayers().size(), server.getMaxPlayers()); EventFactory.callEvent(legacyPingEvent); switch (readableBytes) { case 0: sendByteBuf(channelHandlerContext, responseToByteBuf(channelHandlerContext, String.format("%s\u00a7%d\u00a7%d", legacyPingEvent.getMotd(), legacyPingEvent.getNumPlayers(), legacyPingEvent.getMaxPlayers()))); legacyPingProtocol = true; break; case 1: if (bytebuf.readByte() == (byte) 0x01) { sendByteBuf(channelHandlerContext, responseToByteBuf(channelHandlerContext, String.format("\u00a71\u0000%d\u0000%s\u0000%s\u0000%d\u0000%d", GlowServer.PROTOCOL_VERSION, GlowServer.GAME_VERSION, legacyPingEvent.getMotd(), legacyPingEvent.getNumPlayers(), legacyPingEvent.getMaxPlayers()))); legacyPingProtocol = true; } break; default: if (bytebuf.readByte() == (byte) 0x01 && bytebuf.readByte() == (byte) 0xFA && "MC|PingHost".equals(new String(bytebuf.readBytes(bytebuf.readShort() * 2).array(), Charsets.UTF_16BE))) { int dataLength = bytebuf.readUnsignedShort(); short clientVersion = bytebuf.readUnsignedByte(); String hostname = bytebuf.readBytes(bytebuf.readShort() * 2).toString(Charsets.UTF_16BE); @SuppressWarnings("unused") int port = bytebuf.readInt(); if (clientVersion >= 73 && 7 + (hostname.length() * 2) == dataLength && bytebuf.readableBytes() == 0) { sendByteBuf(channelHandlerContext, responseToByteBuf(channelHandlerContext, String.format("\u00a71\u0000%d\u0000%s\u0000%s\u0000%d\u0000%d", GlowServer.PROTOCOL_VERSION, GlowServer.GAME_VERSION, legacyPingEvent.getMotd(), legacyPingEvent.getNumPlayers(), legacyPingEvent.getMaxPlayers()))); legacyPingProtocol = true; } } break; } } } catch (RuntimeException e) { // Silently catch the exception ; } finally { // check if not successful, otherwise the connection has already been closed if (!legacyPingProtocol) { bytebuf.resetReaderIndex(); channelHandlerContext.pipeline().remove("legacy_ping"); channelHandlerContext.fireChannelRead(object); } } } private void sendByteBuf(ChannelHandlerContext channelhandlercontext, ByteBuf bytebuf) { channelhandlercontext.writeAndFlush(bytebuf).addListener(ChannelFutureListener.CLOSE); } private ByteBuf responseToByteBuf(ChannelHandlerContext ctx, String string) { ByteBuf bytebuf = ctx.alloc().buffer(3 + string.length()); bytebuf.writeByte(0xFF); bytebuf.writeShort(string.length()); try { bytebuf.writeBytes(string.getBytes("UTF-16BE")); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return bytebuf; } }