From 435d6eb4d1dc85e2ee7bbb44f1403456e8133612 Mon Sep 17 00:00:00 2001 From: Jessica James Date: Sun, 7 Nov 2021 20:50:59 -0600 Subject: [PATCH] Added LogTraffic and RconUsername options Fixed some bugs discovered via LogTraffic --- baseline/Configs/RenX.Relay.ini | 4 +- src/Plugins/RenX/RenX.Relay/RenX_Relay.cpp | 82 +++++++++++++++++----- src/Plugins/RenX/RenX.Relay/RenX_Relay.h | 8 ++- 3 files changed, 76 insertions(+), 18 deletions(-) diff --git a/baseline/Configs/RenX.Relay.ini b/baseline/Configs/RenX.Relay.ini index f9751c8..d4987e5 100644 --- a/baseline/Configs/RenX.Relay.ini +++ b/baseline/Configs/RenX.Relay.ini @@ -20,9 +20,10 @@ ; Upstream Settings: ; UpstreamHost=String (Default: devbot.ren-x.com) ; UpstreamPort=Integer (Default: 21337) +; RconUsername=String (Default: blank; leave blank to forward using real connection username) +; LogTraffic=Bool (Default: false; logs ALL RCON traffic going to and from each upstream to a file, useful for debugging) ; FakePings=Bool (Default: true; respond to pings directly rather than looping through game server) ; FakeSuppressedCommands=Bool (Default: true; reply acknowledging the command was received, without forwarding) - ; SanitizeNames=Bool (Default: true; sanitizes all player names from messages) ; SanitizeIPs=Bool (Default: true; sanitizes all player IPs from all messages) ; SanitizeHWIDs=Bool (Default: true; sanitizes all player HWIDs from all messages) @@ -37,3 +38,4 @@ Upstreams=DevBot [DevBot] UpstreamHost=devbot.ren-x.com +RconUsername=DevBot diff --git a/src/Plugins/RenX/RenX.Relay/RenX_Relay.cpp b/src/Plugins/RenX/RenX.Relay/RenX_Relay.cpp index 9393378..2f90b9e 100644 --- a/src/Plugins/RenX/RenX.Relay/RenX_Relay.cpp +++ b/src/Plugins/RenX/RenX.Relay/RenX_Relay.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "Jupiter/IRC.h" #include "RenX_Functions.h" #include "RenX_Server.h" @@ -294,7 +295,6 @@ void RenX_RelayPlugin::RenX_OnRaw(RenX::Server &server, const Jupiter::ReadableS // Suppress unassociated command execution logs from going upstream if (tokens.token_count >= 5 - && !m_command_tracker.empty() && tokens.tokens[0] == "lRCON" && tokens.tokens[1] == "Command;" && tokens.tokens[3] == "executed:" @@ -305,10 +305,14 @@ void RenX_RelayPlugin::RenX_OnRaw(RenX::Server &server, const Jupiter::ReadableS return; } } + else if (m_command_tracker.empty()) { + // This command response wasn't requested by any current upstream connections; suppress it + return; + } else { - // if m_processing_command is already true, there's an unhandled protocol error here, and something is likely to eventually go wrong upstream_server_info* front_server_info = m_command_tracker.front(); if (front_server_info == nullptr + || front_server_info->m_processing_command || tokens.tokens[4] != front_server_info->m_response_queue.front().m_command) { // This command response wasn't requested by any current upstream connections; suppress it return; @@ -316,6 +320,10 @@ void RenX_RelayPlugin::RenX_OnRaw(RenX::Server &server, const Jupiter::ReadableS // This is the next command we're been waiting on; mark processing command and let this go through front_server_info->m_processing_command = true; + + // This is a command response from upstream; this is only relevant to one server: that server + process_renx_message(server, *front_server_info, line, tokens); + return; } } @@ -329,8 +337,7 @@ void RenX_RelayPlugin::RenX_OnRaw(RenX::Server &server, const Jupiter::ReadableS void RenX_RelayPlugin::process_renx_message(RenX::Server& server, upstream_server_info& server_info, const Jupiter::ReadableString& line, Jupiter::ReadableString::TokenizeResult tokens) { bool required_sanitization = false; - Jupiter::TCPSocket* socket = server_info.m_socket.get(); - if (!socket) { + if (!server_info.m_socket) { // early out: no upstream RCON session return; } @@ -343,7 +350,7 @@ void RenX_RelayPlugin::process_renx_message(RenX::Server& server, upstream_serve // Suppress unassociated command responses from going upstream if (tokens.tokens[0].isNotEmpty() - && tokens.tokens[0] == 'r' + && (tokens.tokens[0][0] == 'r' || tokens.tokens[0][0] == 'c') && !server_info.m_processing_command) { // This command response wasn't requested upstream; suppress it return; @@ -494,7 +501,7 @@ void RenX_RelayPlugin::process_renx_message(RenX::Server& server, upstream_serve } line_sanitized += '\n'; - socket->send(line_sanitized); + send_upstream(server_info, line_sanitized, server); if (line_sanitized[0] == 'c' && server_info.m_processing_command) { @@ -514,8 +521,8 @@ void RenX_RelayPlugin::process_renx_message(RenX::Server& server, upstream_serve std::string response; while (!queue.empty() && queue.front().m_is_fake) { - response = queue.front().to_rcon(server.getRCONUsername()); - server_info.m_socket->send(response.c_str(), response.size()); + response = queue.front().to_rcon(get_upstream_rcon_username(server_info, server)); + send_upstream(server_info, response, server); queue.pop_front(); } } @@ -527,6 +534,8 @@ RenX_RelayPlugin::upstream_settings RenX_RelayPlugin::get_settings(const Jupiter // Read in settings result.m_upstream_hostname = in_config.get("UpstreamHost"_jrs, m_default_settings.m_upstream_hostname); result.m_upstream_port = in_config.get("UpstreamPort"_jrs, m_default_settings.m_upstream_port); + result.m_rcon_username = in_config.get("RconUsername"_jrs, m_default_settings.m_rcon_username); + result.m_log_traffic = in_config.get("LogTraffic"_jrs, m_default_settings.m_log_traffic); result.m_fake_pings = in_config.get("FakePings"_jrs, m_default_settings.m_fake_pings); result.m_fake_ignored_commands = in_config.get("FakeSuppressedCommands"_jrs, m_default_settings.m_fake_ignored_commands); result.m_sanitize_names = in_config.get("SanitizeNames"_jrs, m_default_settings.m_sanitize_names); @@ -553,10 +562,51 @@ RenX_RelayPlugin::upstream_settings RenX_RelayPlugin::get_settings(const Jupiter return result; } +std::string RenX_RelayPlugin::get_log_filename(RenX::Server& in_server, const upstream_server_info& in_server_info) { + return { "log__"s + + in_server_info.m_settings->m_label + + "__" + in_server.getSocketHostname() + + "__" + std::to_string(in_server.getPort()) + + ".txt" }; +} + std::string_view RenX_RelayPlugin::get_upstream_name(const upstream_server_info& in_server_info) { return in_server_info.m_settings->m_upstream_hostname; // Will point to stream-specific name later } +std::string_view RenX_RelayPlugin::get_upstream_rcon_username(const upstream_server_info& in_server_info, RenX::Server& in_server) { + const auto& rcon_username = in_server_info.m_settings->m_rcon_username; + if (rcon_username.empty()) { + const auto& real_rcon_username = in_server.getRCONUsername(); + return real_rcon_username; + } + + return rcon_username; +} + +int RenX_RelayPlugin::send_upstream(upstream_server_info& in_server_info, std::string_view in_message, RenX::Server& in_server) { + if (in_server_info.m_settings->m_log_traffic) { + std::ofstream log_file{ get_log_filename(in_server, in_server_info), std::ios::out | std::ios::app | std::ios::binary }; + if (log_file) { + log_file << '[' << getTimeFormat("%H:%M:%S") << "] (Jupiter -> " << in_server_info.m_settings->m_label << "): " << in_message << std::endl; + } + } + + return in_server_info.m_socket->send(in_message.data(), in_message.size()); +} + +int RenX_RelayPlugin::send_downstream(RenX::Server& in_server, std::string_view in_message, upstream_server_info& in_server_info) { + if (in_server_info.m_settings->m_log_traffic) { + std::ofstream log_file{ get_log_filename(in_server, in_server_info), std::ios::out | std::ios::app | std::ios::binary }; + if (log_file) { + log_file << '[' << getTimeFormat("%H:%M:%S") << "] (" << in_server_info.m_settings->m_label << " -> RenX::Server): " << in_message << std::endl; + } + } + + // TODO: add a string_view constructor for ReferenceString, also add sendData(string_view) variant + return in_server.sendData(Jupiter::ReferenceString{ in_message.data(), in_message.size() }); +} + void RenX_RelayPlugin::upstream_connected(RenX::Server& in_server, upstream_server_info& in_server_info) { in_server_info.m_connected = true; in_server_info.m_socket->setBlocking(false); @@ -573,12 +623,12 @@ void RenX_RelayPlugin::upstream_connected(RenX::Server& in_server, upstream_serv version_message += '\n'; // Tack on username auth - auto& username = in_server.getRCONUsername(); + const auto& rcon_username = get_upstream_rcon_username(in_server_info, in_server); version_message += 'a'; - version_message.append(username.ptr(), username.size()); + version_message += rcon_username; version_message += '\n'; - in_server_info.m_socket->send(version_message.c_str(), version_message.size()); + send_upstream(in_server_info, version_message, in_server); } void RenX_RelayPlugin::upstream_disconnected(RenX::Server&, upstream_server_info& in_server_info) { @@ -642,8 +692,8 @@ void RenX_RelayPlugin::process_upstream_message(RenX::Server* in_server, const J // Push upstream or to queue if (in_server_info.m_response_queue.empty()) { // No commands are in the queue; go ahead and shove back the response - auto response = command.to_rcon(in_server->getRCONUsername()); - in_server_info.m_socket->send(response.c_str(), response.size()); + auto response = command.to_rcon(get_upstream_rcon_username(in_server_info, *in_server)); + send_upstream(in_server_info, response, *in_server); return; } @@ -678,8 +728,8 @@ void RenX_RelayPlugin::process_upstream_message(RenX::Server* in_server, const J if (command.m_is_fake) { if (in_server_info.m_response_queue.empty()) { // No commands are in the queue; go ahead and shove back the response - auto response = command.to_rcon(in_server->getRCONUsername()); - in_server_info.m_socket->send(response.c_str(), response.size()); + auto response = command.to_rcon(get_upstream_rcon_username(in_server_info, *in_server)); + send_upstream(in_server_info, response, *in_server); return; } @@ -696,7 +746,7 @@ void RenX_RelayPlugin::process_upstream_message(RenX::Server* in_server, const J // Send line to game server Jupiter::StringS sanitized_message = in_line; sanitized_message += '\n'; - in_server->sendData(sanitized_message); + send_downstream(*in_server, sanitized_message, in_server_info); } std::string RenX_RelayPlugin::UpstreamCommand::to_rcon(const std::string_view& rcon_username) const { diff --git a/src/Plugins/RenX/RenX.Relay/RenX_Relay.h b/src/Plugins/RenX/RenX.Relay/RenX_Relay.h index 3e35755..4067656 100644 --- a/src/Plugins/RenX/RenX.Relay/RenX_Relay.h +++ b/src/Plugins/RenX/RenX.Relay/RenX_Relay.h @@ -40,8 +40,10 @@ private: struct upstream_settings { std::string m_label{}; // config section name - std::string m_upstream_hostname{ "devbot.ren-x.com" }; + std::string m_upstream_hostname{}; uint16_t m_upstream_port{ 21337u }; + std::string m_rcon_username{}; + bool m_log_traffic{ false }; bool m_fake_pings{ true }; bool m_fake_ignored_commands{ true }; bool m_sanitize_names{ true }; @@ -70,7 +72,11 @@ private: }; upstream_settings get_settings(const Jupiter::Config& in_config); + std::string get_log_filename(RenX::Server& in_server, const upstream_server_info& in_server_info); std::string_view get_upstream_name(const upstream_server_info& in_server_info); + std::string_view get_upstream_rcon_username(const upstream_server_info& in_server_info, RenX::Server& in_server); + int send_upstream(upstream_server_info& in_server_info, std::string_view in_message, RenX::Server& in_server); + int send_downstream(RenX::Server& in_server, std::string_view in_message, upstream_server_info& in_server_info); void upstream_connected(RenX::Server& in_server, upstream_server_info& in_server_info); void upstream_disconnected(RenX::Server& in_server, upstream_server_info& in_server_info);