package net.glowstone.net.rcon; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import net.glowstone.EventFactory; import net.glowstone.GlowServer; import org.bukkit.ChatColor; import org.bukkit.command.CommandException; import org.bukkit.event.server.RemoteServerCommandEvent; import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; /** * Handler for Rcon messages. */ public class RconHandler extends SimpleChannelInboundHandler<ByteBuf> { private static final byte FAILURE = -1; private static final byte TYPE_RESPONSE = 0; private static final byte TYPE_COMMAND = 2; private static final byte TYPE_LOGIN = 3; private final String password; private boolean loggedIn = false; /** * The {@link RconServer} this handler belongs to. */ private RconServer rconServer; /** * The {@link RconCommandSender} for this connection. */ private RconCommandSender commandSender; public RconHandler(RconServer rconServer, String password) { this.rconServer = rconServer; this.password = password; this.commandSender = new RconCommandSender(rconServer.getServer()); } @Override protected void channelRead0(ChannelHandlerContext ctx, ByteBuf buf) throws Exception { buf = buf.order(ByteOrder.LITTLE_ENDIAN); if (buf.readableBytes() < 8) { return; } int requestId = buf.readInt(); int type = buf.readInt(); byte[] payloadData = new byte[buf.readableBytes() - 2]; buf.readBytes(payloadData); String payload = new String(payloadData, StandardCharsets.UTF_8); buf.readBytes(2); // two byte padding if (type == TYPE_LOGIN) { handleLogin(ctx, payload, requestId); } else if (type == TYPE_COMMAND) { handleCommand(ctx, payload, requestId); } else { sendLargeResponse(ctx, requestId, "Unknown request " + Integer.toHexString(type)); } } private void handleLogin(ChannelHandlerContext ctx, String payload, int requestId) { if (password.equals(payload)) { loggedIn = true; sendResponse(ctx, requestId, TYPE_COMMAND, ""); GlowServer.logger.info("Rcon connection from [" + ctx.channel().remoteAddress() + "]"); } else { loggedIn = false; sendResponse(ctx, FAILURE, TYPE_COMMAND, ""); } } private void handleCommand(ChannelHandlerContext ctx, String payload, int requestId) { if (!loggedIn) { sendResponse(ctx, FAILURE, TYPE_COMMAND, ""); return; } try { RemoteServerCommandEvent event = new RemoteServerCommandEvent(commandSender, payload); EventFactory.callEvent(event); rconServer.getServer().dispatchCommand(commandSender, event.getCommand()); String message = commandSender.flush(); if (!rconServer.getServer().useRconColors()) { message = ChatColor.stripColor(message); } sendLargeResponse(ctx, requestId, message); } catch (CommandException e) { sendLargeResponse(ctx, requestId, String.format("Error executing: %s (%s)", payload, e.getMessage())); } } private void sendResponse(ChannelHandlerContext ctx, int requestId, int type, String payload) { ByteBuf buf = ctx.alloc().buffer().order(ByteOrder.LITTLE_ENDIAN); buf.writeInt(requestId); buf.writeInt(type); buf.writeBytes(payload.getBytes(StandardCharsets.UTF_8)); buf.writeByte(0); buf.writeByte(0); ctx.write(buf); if (buf.refCnt() > 0) { buf.release(buf.refCnt()); } } private void sendLargeResponse(ChannelHandlerContext ctx, int requestId, String payload) { if (payload.length() == 0) { sendResponse(ctx, requestId, TYPE_RESPONSE, ""); return; } int start = 0; while (start < payload.length()) { int length = payload.length() - start; int truncated = length > 2048 ? 2048 : length; sendResponse(ctx, requestId, TYPE_RESPONSE, payload.substring(start, truncated)); start += truncated; } } }