minecraft-src/net/minecraft/server/rcon/thread/RconClient.java
2025-07-04 01:41:11 +03:00

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