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 p_11587_, String p_11588_, Socket p_11589_) { super("RCON Client " + p_11589_.getInetAddress()); this.serverInterface = p_11587_; this.client = p_11589_; try { this.client.setSoTimeout(0); } catch (Exception exception) { this.running = false; } this.rconPassword = p_11588_; } @Override 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 i1 = PktUtils.intFromByteArray(this.buf, j); j += 4; switch (i1) { case 2: if (this.authed) { String s1 = PktUtils.stringFromByteArray(this.buf, j, i); try { this.sendCmdResponse(l, this.serverInterface.runCommand(s1)); } catch (Exception exception) { this.sendCmdResponse(l, "Error executing: " + s1 + " (" + exception.getMessage() + ")"); } break; } this.sendAuthFailure(); break; case 3: String s = PktUtils.stringFromByteArray(this.buf, j, i); j += s.length(); if (!s.isEmpty() && s.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(i1))); } } return; } catch (IOException ioexception) { } catch (Exception exception1) { LOGGER.error("Exception whilst parsing RCON input", (Throwable)exception1); } } finally { this.closeSocket(); LOGGER.info("Thread {} shutting down", this.name); this.running = false; } } private void send(int p_11591_, int p_11592_, String p_11593_) throws IOException { ByteArrayOutputStream bytearrayoutputstream = new ByteArrayOutputStream(1248); DataOutputStream dataoutputstream = new DataOutputStream(bytearrayoutputstream); byte[] abyte = p_11593_.getBytes(StandardCharsets.UTF_8); dataoutputstream.writeInt(Integer.reverseBytes(abyte.length + 10)); dataoutputstream.writeInt(Integer.reverseBytes(p_11591_)); dataoutputstream.writeInt(Integer.reverseBytes(p_11592_)); dataoutputstream.write(abyte); dataoutputstream.write(0); dataoutputstream.write(0); this.client.getOutputStream().write(bytearrayoutputstream.toByteArray()); } private void sendAuthFailure() throws IOException { this.send(-1, 2, ""); } private void sendCmdResponse(int p_11595_, String p_11596_) throws IOException { int i = p_11596_.length(); do { int j = 4096 <= i ? 4096 : i; this.send(p_11595_, 0, p_11596_.substring(0, j)); p_11596_ = p_11596_.substring(j); i = p_11596_.length(); } while (0 != i); } @Override public void stop() { this.running = false; this.closeSocket(); super.stop(); } private void closeSocket() { try { this.client.close(); } catch (IOException ioexception) { LOGGER.warn("Failed to close socket", (Throwable)ioexception); } } }