161 lines
		
	
	
	
		
			4.4 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			161 lines
		
	
	
	
		
			4.4 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package net.minecraft.server.rcon.thread;
 | |
| 
 | |
| import com.mojang.logging.LogUtils;
 | |
| import java.io.BufferedInputStream;
 | |
| import java.io.ByteArrayOutputStream;
 | |
| import java.io.DataOutputStream;
 | |
| import java.io.IOException;
 | |
| import java.net.Socket;
 | |
| import java.nio.charset.StandardCharsets;
 | |
| import java.util.Locale;
 | |
| import net.minecraft.server.ServerInterface;
 | |
| import net.minecraft.server.rcon.PktUtils;
 | |
| import org.slf4j.Logger;
 | |
| 
 | |
| public class RconClient extends GenericThread {
 | |
| 	private static final Logger LOGGER = LogUtils.getLogger();
 | |
| 	private static final int SERVERDATA_AUTH = 3;
 | |
| 	private static final int SERVERDATA_EXECCOMMAND = 2;
 | |
| 	private static final int SERVERDATA_RESPONSE_VALUE = 0;
 | |
| 	private static final int SERVERDATA_AUTH_RESPONSE = 2;
 | |
| 	private static final int SERVERDATA_AUTH_FAILURE = -1;
 | |
| 	private boolean authed;
 | |
| 	private final Socket client;
 | |
| 	private final byte[] buf = new byte[1460];
 | |
| 	private final String rconPassword;
 | |
| 	private final ServerInterface serverInterface;
 | |
| 
 | |
| 	RconClient(ServerInterface serverInterface, String rconPassword, Socket client) {
 | |
| 		super("RCON Client " + client.getInetAddress());
 | |
| 		this.serverInterface = serverInterface;
 | |
| 		this.client = client;
 | |
| 
 | |
| 		try {
 | |
| 			this.client.setSoTimeout(0);
 | |
| 		} catch (Exception var5) {
 | |
| 			this.running = false;
 | |
| 		}
 | |
| 
 | |
| 		this.rconPassword = rconPassword;
 | |
| 	}
 | |
| 
 | |
| 	public void run() {
 | |
| 		try {
 | |
| 			try {
 | |
| 				while (this.running) {
 | |
| 					BufferedInputStream bufferedInputStream = new BufferedInputStream(this.client.getInputStream());
 | |
| 					int i = bufferedInputStream.read(this.buf, 0, 1460);
 | |
| 					if (10 > i) {
 | |
| 						return;
 | |
| 					}
 | |
| 
 | |
| 					int j = 0;
 | |
| 					int k = PktUtils.intFromByteArray(this.buf, 0, i);
 | |
| 					if (k != i - 4) {
 | |
| 						return;
 | |
| 					}
 | |
| 
 | |
| 					j += 4;
 | |
| 					int l = PktUtils.intFromByteArray(this.buf, j, i);
 | |
| 					j += 4;
 | |
| 					int m = PktUtils.intFromByteArray(this.buf, j);
 | |
| 					j += 4;
 | |
| 					switch (m) {
 | |
| 						case 2:
 | |
| 							if (this.authed) {
 | |
| 								String string2 = PktUtils.stringFromByteArray(this.buf, j, i);
 | |
| 
 | |
| 								try {
 | |
| 									this.sendCmdResponse(l, this.serverInterface.runCommand(string2));
 | |
| 								} catch (Exception var15) {
 | |
| 									this.sendCmdResponse(l, "Error executing: " + string2 + " (" + var15.getMessage() + ")");
 | |
| 								}
 | |
| 								break;
 | |
| 							}
 | |
| 
 | |
| 							this.sendAuthFailure();
 | |
| 							break;
 | |
| 						case 3:
 | |
| 							String string = PktUtils.stringFromByteArray(this.buf, j, i);
 | |
| 							j += string.length();
 | |
| 							if (!string.isEmpty() && string.equals(this.rconPassword)) {
 | |
| 								this.authed = true;
 | |
| 								this.send(l, 2, "");
 | |
| 								break;
 | |
| 							}
 | |
| 
 | |
| 							this.authed = false;
 | |
| 							this.sendAuthFailure();
 | |
| 							break;
 | |
| 						default:
 | |
| 							this.sendCmdResponse(l, String.format(Locale.ROOT, "Unknown request %s", Integer.toHexString(m)));
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				return;
 | |
| 			} catch (IOException var16) {
 | |
| 			} catch (Exception var17) {
 | |
| 				LOGGER.error("Exception whilst parsing RCON input", (Throwable)var17);
 | |
| 			}
 | |
| 		} finally {
 | |
| 			this.closeSocket();
 | |
| 			LOGGER.info("Thread {} shutting down", this.name);
 | |
| 			this.running = false;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Sends the given response message to the client
 | |
| 	 */
 | |
| 	private void send(int id, int type, String message) throws IOException {
 | |
| 		ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(1248);
 | |
| 		DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream);
 | |
| 		byte[] bs = message.getBytes(StandardCharsets.UTF_8);
 | |
| 		dataOutputStream.writeInt(Integer.reverseBytes(bs.length + 10));
 | |
| 		dataOutputStream.writeInt(Integer.reverseBytes(id));
 | |
| 		dataOutputStream.writeInt(Integer.reverseBytes(type));
 | |
| 		dataOutputStream.write(bs);
 | |
| 		dataOutputStream.write(0);
 | |
| 		dataOutputStream.write(0);
 | |
| 		this.client.getOutputStream().write(byteArrayOutputStream.toByteArray());
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Sends the standard RCon 'authorization failed' response packet
 | |
| 	 */
 | |
| 	private void sendAuthFailure() throws IOException {
 | |
| 		this.send(-1, 2, "");
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Splits the response message into individual packets and sends each one
 | |
| 	 */
 | |
| 	private void sendCmdResponse(int id, String message) throws IOException {
 | |
| 		int i = message.length();
 | |
| 
 | |
| 		do {
 | |
| 			int j = 4096 <= i ? 4096 : i;
 | |
| 			this.send(id, 0, message.substring(0, j));
 | |
| 			message = message.substring(j);
 | |
| 			i = message.length();
 | |
| 		} while (0 != i);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void stop() {
 | |
| 		this.running = false;
 | |
| 		this.closeSocket();
 | |
| 		super.stop();
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Closes the client socket
 | |
| 	 */
 | |
| 	private void closeSocket() {
 | |
| 		try {
 | |
| 			this.client.close();
 | |
| 		} catch (IOException var2) {
 | |
| 			LOGGER.warn("Failed to close socket", (Throwable)var2);
 | |
| 		}
 | |
| 	}
 | |
| }
 |