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;
}
}
}