2023-06-20 04:33:09 +00:00
|
|
|
#include "StarServerRconThread.hpp"
|
|
|
|
#include "StarServerRconClient.hpp"
|
|
|
|
#include "StarLogging.hpp"
|
|
|
|
#include "StarRoot.hpp"
|
|
|
|
#include "StarConfiguration.hpp"
|
|
|
|
#include "StarUniverseServer.hpp"
|
|
|
|
#include "StarLexicalCast.hpp"
|
|
|
|
|
|
|
|
namespace Star {
|
|
|
|
|
|
|
|
ServerRconClient::ServerRconClient(UniverseServer* universe, TcpSocketPtr socket)
|
|
|
|
: Thread("RconClient"),
|
|
|
|
m_universe(universe),
|
|
|
|
m_socket(socket),
|
|
|
|
m_packetBuffer(MaxPacketSize),
|
|
|
|
m_stop(true),
|
|
|
|
m_authed(false) {
|
|
|
|
auto& root = Root::singleton();
|
|
|
|
auto cfg = root.configuration();
|
|
|
|
|
|
|
|
m_packetBuffer.setByteOrder(ByteOrder::LittleEndian);
|
|
|
|
m_packetBuffer.setNullTerminatedStrings(true);
|
|
|
|
|
|
|
|
m_rconPassword = cfg->get("rconServerPassword").toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
ServerRconClient::~ServerRconClient() {
|
|
|
|
stop();
|
|
|
|
join();
|
|
|
|
}
|
|
|
|
|
|
|
|
String ServerRconClient::handleCommand(String commandLine) {
|
|
|
|
String command = commandLine.extract();
|
|
|
|
|
|
|
|
if (command == "echo") {
|
|
|
|
return commandLine;
|
|
|
|
} else if (command == "broadcast" || command == "say") {
|
|
|
|
m_universe->adminBroadcast(commandLine);
|
2023-06-27 10:23:44 +00:00
|
|
|
return strf("OK: said {}", commandLine);
|
2023-06-20 04:33:09 +00:00
|
|
|
} else if (command == "stop") {
|
|
|
|
m_universe->stop();
|
|
|
|
return "OK: shutting down";
|
|
|
|
} else {
|
2023-06-27 10:23:44 +00:00
|
|
|
return m_universe->adminCommand(strf("{} {}", command, commandLine));
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ServerRconClient::receive(size_t size) {
|
|
|
|
m_packetBuffer.reset(size);
|
|
|
|
auto ptr = m_packetBuffer.ptr();
|
|
|
|
while (size > 0) {
|
|
|
|
size_t r = m_socket->receive(ptr, size);
|
|
|
|
if (r == 0)
|
|
|
|
throw NoMoreRequests();
|
|
|
|
size -= r;
|
|
|
|
ptr += r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ServerRconClient::send(uint32_t requestId, uint32_t cmd, String str) {
|
|
|
|
m_packetBuffer.clear();
|
|
|
|
m_packetBuffer << (uint32_t)(str.utf8Size() + 10) << requestId << cmd << str << (uint8_t)0x00;
|
|
|
|
m_socket->send(m_packetBuffer.ptr(), m_packetBuffer.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
void ServerRconClient::sendAuthFailure() {
|
|
|
|
send(SERVERDATA_AUTH_FAILURE, SERVERDATA_AUTH_RESPONSE, "");
|
|
|
|
}
|
|
|
|
|
|
|
|
void ServerRconClient::sendCmdResponse(uint32_t requestId, String response) {
|
|
|
|
size_t len = response.length();
|
|
|
|
// Always send at least one packet even if the response was blank
|
|
|
|
do {
|
|
|
|
auto dataLen = (len >= MaxPacketSize) ? MaxPacketSize : len;
|
|
|
|
send(requestId, SERVERDATA_RESPONSE_VALUE, response.substr(0, dataLen));
|
|
|
|
response = response.substr(dataLen);
|
|
|
|
len = response.length();
|
|
|
|
} while (len > 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ServerRconClient::start() {
|
|
|
|
m_stop = false;
|
|
|
|
Thread::start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ServerRconClient::stop() {
|
|
|
|
m_stop = true;
|
|
|
|
m_socket->close();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ServerRconClient::processRequest() {
|
|
|
|
receive(4);
|
|
|
|
uint32_t size = m_packetBuffer.read<uint32_t>();
|
|
|
|
|
|
|
|
receive(size);
|
|
|
|
uint32_t requestId;
|
|
|
|
m_packetBuffer >> requestId;
|
|
|
|
|
|
|
|
uint32_t cmd;
|
|
|
|
m_packetBuffer >> cmd;
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case SERVERDATA_AUTH: {
|
|
|
|
String password;
|
|
|
|
m_packetBuffer >> password;
|
|
|
|
if (!m_rconPassword.empty() && m_rconPassword.equals(password)) {
|
|
|
|
m_authed = true;
|
|
|
|
send(requestId, SERVERDATA_RESPONSE_VALUE);
|
|
|
|
send(requestId, SERVERDATA_AUTH_RESPONSE);
|
|
|
|
} else {
|
|
|
|
m_authed = false;
|
|
|
|
sendAuthFailure();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SERVERDATA_EXECCOMMAND:
|
|
|
|
if (m_authed) {
|
|
|
|
String command;
|
|
|
|
m_packetBuffer >> command;
|
|
|
|
try {
|
2023-06-27 10:23:44 +00:00
|
|
|
Logger::info("RCON {}: {}", m_socket->remoteAddress(), command);
|
2023-06-20 04:33:09 +00:00
|
|
|
sendCmdResponse(requestId, handleCommand(command));
|
|
|
|
} catch (std::exception const& e) {
|
2023-06-27 10:23:44 +00:00
|
|
|
sendCmdResponse(requestId, strf("RCON: Error executing: {}: {}", command, outputException(e, true)));
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
sendAuthFailure();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2023-06-27 10:23:44 +00:00
|
|
|
sendCmdResponse(requestId, strf("Unknown request {:06x}", cmd));
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ServerRconClient::run() {
|
|
|
|
try {
|
|
|
|
while (!m_stop)
|
|
|
|
processRequest();
|
|
|
|
} catch (NoMoreRequests const&) {
|
|
|
|
} catch (std::exception const& e) {
|
2023-06-27 10:23:44 +00:00
|
|
|
Logger::error("ServerRconClient exception caught: {}", outputException(e, false));
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|