From b1d81e2dc54596fff7efa31fd874134c3c2671a3 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Sun, 9 Feb 2025 01:23:05 +0100 Subject: [PATCH] Engine: strong optimizations for RCON system - All serializations now expect exact buffer lengths (which we knew for every call, but didn't make use of). - The prefix is now also constructed directly into the send buffer - The send buffer copies have been completely removed, the data that has been rendered into the only existing buffer will be used directly. --- src/common/igameserverdata.h | 1 - src/core/logger.cpp | 2 +- src/engine/client/cl_rcon.cpp | 25 ++++++++++++++------ src/engine/client/cl_rcon.h | 4 ++-- src/engine/server/sv_rcon.cpp | 39 ++++++++++++++++--------------- src/engine/server/sv_rcon.h | 10 ++++---- src/engine/shared/base_rcon.cpp | 21 +++++++---------- src/engine/shared/shared_rcon.cpp | 27 ++++++++++++++------- src/engine/shared/shared_rcon.h | 7 +++--- 9 files changed, 77 insertions(+), 59 deletions(-) diff --git a/src/common/igameserverdata.h b/src/common/igameserverdata.h index fea3e7bd..69811952 100644 --- a/src/common/igameserverdata.h +++ b/src/common/igameserverdata.h @@ -50,7 +50,6 @@ public: m_bValidated = false; m_bAuthorized = false; m_bInputOnly = true; - m_RecvBuffer.resize(sizeof(u_long)); // Reserve enough for length-prefix. } }; diff --git a/src/core/logger.cpp b/src/core/logger.cpp index c4c286e6..5bc2b3af 100644 --- a/src/core/logger.cpp +++ b/src/core/logger.cpp @@ -340,7 +340,7 @@ void EngineLoggerSink(LogType_t logType, LogLevel_t logLevel, eDLL_T context, #ifndef CLIENT_DLL if (!LoggedFromClient(context) && RCONServer()->ShouldSend(netcon::response_e::SERVERDATA_RESPONSE_CONSOLE_LOG)) { - RCONServer()->SendEncoded(formatted.c_str(), pszUpTime, netcon::response_e::SERVERDATA_RESPONSE_CONSOLE_LOG, + RCONServer()->SendEncoded(formatted.c_str(), formatted.length(), pszUpTime, contextTextStartIndex, netcon::response_e::SERVERDATA_RESPONSE_CONSOLE_LOG, int(context), int(logType)); } #endif // !CLIENT_DLL diff --git a/src/engine/client/cl_rcon.cpp b/src/engine/client/cl_rcon.cpp index 1cbf4be0..76985e72 100644 --- a/src/engine/client/cl_rcon.cpp +++ b/src/engine/client/cl_rcon.cpp @@ -162,11 +162,11 @@ void CRConClient::RequestConsoleLog(const bool bWantLog) // sending logs will cause the print func to get called recursively forever. Assert(!(bWantLog && IsRemoteLocal())); - const char* szEnable = bWantLog ? "1" : "0"; + const char* const szEnable = bWantLog ? "1" : "0"; const SocketHandle_t hSocket = GetSocket(); vector vecMsg; - bool ret = Serialize(vecMsg, "", szEnable, netcon::request_e::SERVERDATA_REQUEST_SEND_CONSOLE_LOG); + const bool ret = Serialize(vecMsg, "", 0, szEnable, 1, netcon::request_e::SERVERDATA_REQUEST_SEND_CONSOLE_LOG); if (ret && !Send(hSocket, vecMsg.data(), int(vecMsg.size()))) { @@ -177,14 +177,16 @@ void CRConClient::RequestConsoleLog(const bool bWantLog) //----------------------------------------------------------------------------- // Purpose: serializes input // Input : *svReqBuf - +// nReqMsgLen - // *svReqVal - +// nReqValLen - // request_t - // Output : serialized results as string //----------------------------------------------------------------------------- -bool CRConClient::Serialize(vector& vecBuf, const char* szReqBuf, - const char* szReqVal, const netcon::request_e requestType) const +bool CRConClient::Serialize(vector& vecBuf, const char* szReqBuf, const size_t nReqMsgLen, + const char* szReqVal, const size_t nReqValLen, const netcon::request_e requestType) const { - return NetconClient_Serialize(this, vecBuf, szReqBuf, szReqVal, requestType, + return NetconClient_Serialize(this, vecBuf, szReqBuf, nReqMsgLen, szReqVal, nReqValLen, requestType, rcon_encryptframes.GetBool(), rcon_debug.GetBool()); } @@ -329,7 +331,10 @@ static void RCON_CmdQuery_f(const CCommand& args) { if (argCount > 2) { - bSuccess = RCONClient()->Serialize(vecMsg, args.Arg(2), "", netcon::request_e::SERVERDATA_REQUEST_AUTH); + const char* const pass = args.Arg(2); + const size_t passLen = strlen(pass); + + bSuccess = RCONClient()->Serialize(vecMsg, pass, passLen, "", 0, netcon::request_e::SERVERDATA_REQUEST_AUTH); } else // Need at least 3 arguments for a password in PASS command (rcon PASS ) { @@ -350,7 +355,13 @@ static void RCON_CmdQuery_f(const CCommand& args) return; } - bSuccess = RCONClient()->Serialize(vecMsg, args.Arg(1), args.ArgS(), netcon::request_e::SERVERDATA_REQUEST_EXECCOMMAND); + const char* const request = args.Arg(1); + const size_t requestLen = strlen(request); + + const char* const value = args.ArgS(); + const size_t valueLen = strlen(value); + + bSuccess = RCONClient()->Serialize(vecMsg, request, requestLen, value, valueLen, netcon::request_e::SERVERDATA_REQUEST_EXECCOMMAND); if (bSuccess) { RCONClient()->Send(hSocket, vecMsg.data(), int(vecMsg.size())); diff --git a/src/engine/client/cl_rcon.h b/src/engine/client/cl_rcon.h index 0dd4ed4a..996118a6 100644 --- a/src/engine/client/cl_rcon.h +++ b/src/engine/client/cl_rcon.h @@ -17,8 +17,8 @@ public: virtual void Disconnect(const char* szReason = nullptr) override; virtual bool ProcessMessage(const char* pMsgBuf, const int nMsgLen) override; - bool Serialize(vector& vecBuf, const char* szReqBuf, - const char* szReqVal, const netcon::request_e requestType) const; + bool Serialize(vector& vecBuf, const char* szReqBuf, const size_t nReqMsgLen, + const char* szReqVal, const size_t nReqValLen, const netcon::request_e requestType) const; void RequestConsoleLog(const bool bWantLog); bool ShouldReceive(void); diff --git a/src/engine/server/sv_rcon.cpp b/src/engine/server/sv_rcon.cpp index 01e00976..fb4cdbed 100644 --- a/src/engine/server/sv_rcon.cpp +++ b/src/engine/server/sv_rcon.cpp @@ -252,7 +252,7 @@ void CRConServer::RunFrame(void) if (CheckForBan(data)) { - SendEncoded(data.m_hSocket, s_BannedMessage, "", + SendEncoded(data.m_hSocket, s_BannedMessage, sizeof(s_BannedMessage)-1, "", 0, netcon::response_e::SERVERDATA_RESPONSE_AUTH, int(eDLL_T::NETCON)); Disconnect("banned"); @@ -272,23 +272,16 @@ void CRConServer::RunFrame(void) //----------------------------------------------------------------------------- bool CRConServer::SendToAll(const char* pMsgBuf, const int nMsgLen) const { - ostringstream sendbuf; - const u_long nLen = htonl(u_long(nMsgLen)); - + const int nCount = m_Socket.GetAcceptedSocketCount(); bool bSuccess = true; - sendbuf.write(reinterpret_cast(&nLen), sizeof(u_long)); - sendbuf.write(pMsgBuf, nMsgLen); - - const int nCount = m_Socket.GetAcceptedSocketCount(); for (int i = nCount - 1; i >= 0; i--) { const CConnectedNetConsoleData& data = m_Socket.GetAcceptedSocketData(i); if (data.m_bAuthorized && !data.m_bInputOnly) { - int ret = ::send(data.m_hSocket, sendbuf.str().data(), - int(sendbuf.str().size()), MSG_NOSIGNAL); + const int ret = ::send(data.m_hSocket, pMsgBuf, nMsgLen, MSG_NOSIGNAL); if (ret == SOCKET_ERROR) { @@ -306,17 +299,19 @@ bool CRConServer::SendToAll(const char* pMsgBuf, const int nMsgLen) const //----------------------------------------------------------------------------- // Purpose: encode and send message to all connected sockets // Input : *pResponseMsg - +// nResponseMsgLen - // *pResponseVal - +// nResponseValLen - // responseType - // nMessageId - // nMessageType - // Output: true on success, false otherwise //----------------------------------------------------------------------------- -bool CRConServer::SendEncoded(const char* pResponseMsg, const char* pResponseVal, +bool CRConServer::SendEncoded(const char* pResponseMsg, const size_t nResponseMsgLen, const char* pResponseVal, const size_t nResponseValLen, const netcon::response_e responseType, const int nMessageId, const int nMessageType) const { vector vecMsg; - if (!Serialize(vecMsg, pResponseMsg, pResponseVal, + if (!Serialize(vecMsg, pResponseMsg, nResponseMsgLen, pResponseVal, nResponseValLen, responseType, nMessageId, nMessageType)) { return false; @@ -334,17 +329,20 @@ bool CRConServer::SendEncoded(const char* pResponseMsg, const char* pResponseVal // Purpose: encode and send message to specific socket // Input : hSocket - // *pResponseMsg - +// nResponseMsgLen - // *pResponseVal - +// nResponseValLen - // responseType - // nMessageId - // nMessageType - // Output: true on success, false otherwise //----------------------------------------------------------------------------- -bool CRConServer::SendEncoded(const SocketHandle_t hSocket, const char* pResponseMsg, const char* pResponseVal, +bool CRConServer::SendEncoded(const SocketHandle_t hSocket, + const char* pResponseMsg, const size_t nResponseMsgLen, const char* pResponseVal, const size_t nResponseValLen, const netcon::response_e responseType, const int nMessageId, const int nMessageType) const { vector vecMsg; - if (!Serialize(vecMsg, pResponseMsg, pResponseVal, + if (!Serialize(vecMsg, pResponseMsg, nResponseMsgLen, pResponseVal, nResponseValLen, responseType, nMessageId, nMessageType)) { return false; @@ -362,16 +360,19 @@ bool CRConServer::SendEncoded(const SocketHandle_t hSocket, const char* pRespons // Purpose: serializes input // Input : &vecBuf - // *responseMsg - +// nResponseMsgLen - // *responseVal - +// nResponseValLen - // responseType - // nMessageId - // nMessageType - // Output : serialized results as string //----------------------------------------------------------------------------- -bool CRConServer::Serialize(vector& vecBuf, const char* pResponseMsg, const char* pResponseVal, +bool CRConServer::Serialize(vector& vecBuf, + const char* pResponseMsg, const size_t nResponseMsgLen, const char* pResponseVal, const size_t nResponseValLen, const netcon::response_e responseType, const int nMessageId, const int nMessageType) const { - return NetconServer_Serialize(this, vecBuf, pResponseMsg, pResponseVal, responseType, nMessageId, nMessageType, + return NetconServer_Serialize(this, vecBuf, pResponseMsg, nResponseMsgLen, pResponseVal, nResponseValLen, responseType, nMessageId, nMessageType, rcon_encryptframes.GetBool(), rcon_debug.GetBool()); } @@ -399,7 +400,7 @@ void CRConServer::Authenticate(const netcon::request& request, CConnectedNetCons const char* pSendLogs = (!sv_rcon_sendlogs.GetBool() || data.m_bInputOnly) ? "0" : "1"; - SendEncoded(data.m_hSocket, s_AuthMessage, pSendLogs, + SendEncoded(data.m_hSocket, s_AuthMessage, sizeof(s_AuthMessage)-1, pSendLogs, 1, netcon::response_e::SERVERDATA_RESPONSE_AUTH, static_cast(eDLL_T::NETCON)); } else // Bad password. @@ -410,7 +411,7 @@ void CRConServer::Authenticate(const netcon::request& request, CConnectedNetCons Msg(eDLL_T::SERVER, "Bad RCON password attempt from '%s'\n", netAdr.ToString()); } - SendEncoded(data.m_hSocket, s_WrongPwMessage, "", + SendEncoded(data.m_hSocket, s_WrongPwMessage, sizeof(s_WrongPwMessage)-1, "", 0, netcon::response_e::SERVERDATA_RESPONSE_AUTH, static_cast(eDLL_T::NETCON)); data.m_bAuthorized = false; @@ -460,7 +461,7 @@ bool CRConServer::ProcessMessage(const char* pMsgBuf, const int nMsgLen) request.requesttype() != netcon::request_e::SERVERDATA_REQUEST_AUTH) { // Notify netconsole that authentication is required. - SendEncoded(data.m_hSocket, s_NoAuthMessage, "", + SendEncoded(data.m_hSocket, s_NoAuthMessage, sizeof(s_NoAuthMessage)-1, "", 0, netcon::response_e::SERVERDATA_RESPONSE_AUTH, static_cast(eDLL_T::NETCON)); data.m_bValidated = false; diff --git a/src/engine/server/sv_rcon.h b/src/engine/server/sv_rcon.h index 821c7589..a3b97e55 100644 --- a/src/engine/server/sv_rcon.h +++ b/src/engine/server/sv_rcon.h @@ -25,19 +25,19 @@ public: void Think(void); void RunFrame(void); - bool SendEncoded(const char* pResponseMsg, const char* pResponseVal, + bool SendEncoded(const char* pResponseMsg, const size_t nResponseMsgLen, const char* pResponseVal, const size_t nResponseValLen, const netcon::response_e responseType, const int nMessageId = static_cast(eDLL_T::NETCON), const int nMessageType = static_cast(LogType_t::LOG_NET)) const; - bool SendEncoded(const SocketHandle_t hSocket, const char* pResponseMsg, - const char* pResponseVal, const netcon::response_e responseType, + bool SendEncoded(const SocketHandle_t hSocket, const char* pResponseMsg, const size_t nResponseMsgLen, + const char* pResponseVal, const size_t nResponseValLen, const netcon::response_e responseType, const int nMessageId = static_cast(eDLL_T::NETCON), const int nMessageType = static_cast(LogType_t::LOG_NET)) const; bool SendToAll(const char* pMsgBuf, const int nMsgLen) const; - bool Serialize(vector& vecBuf, const char* pResponseMsg, const char* pResponseVal, const netcon::response_e responseType, - const int nMessageId = static_cast(eDLL_T::NETCON), const int nMessageType = static_cast(LogType_t::LOG_NET)) const; + bool Serialize(vector& vecBuf, const char* pResponseMsg, const size_t nResponseMsgLen, const char* pResponseVal, const size_t nResponseValLen, + const netcon::response_e responseType, const int nMessageId = static_cast(eDLL_T::NETCON), const int nMessageType = static_cast(LogType_t::LOG_NET)) const; void Authenticate(const netcon::request& request, CConnectedNetConsoleData& data); bool Comparator(const string& svPassword) const; diff --git a/src/engine/shared/base_rcon.cpp b/src/engine/shared/base_rcon.cpp index 8fde116a..5878e15d 100644 --- a/src/engine/shared/base_rcon.cpp +++ b/src/engine/shared/base_rcon.cpp @@ -148,6 +148,7 @@ bool CNetConBase::ProcessBuffer(CConnectedNetConsoleData& data, const char* pRecvBuf, int nRecvLen, const int nMaxLen) { bool bSuccess = true; + byte prefix[sizeof(u_long)] = {}; while (nRecvLen > 0) { @@ -173,18 +174,22 @@ bool CNetConBase::ProcessBuffer(CConnectedNetConsoleData& data, data.m_nPayloadRead = 0; } } - else if (data.m_nPayloadRead < sizeof(int)) // Read size field. + else if (data.m_nPayloadRead < sizeof(u_long)) // Read size field. { - data.m_RecvBuffer[data.m_nPayloadRead++] = *pRecvBuf; + prefix[data.m_nPayloadRead++] = *pRecvBuf; pRecvBuf++; nRecvLen--; } else // Build prefix. { - data.m_nPayloadLen = int(ntohl(*reinterpret_cast(&data.m_RecvBuffer[0]))); + u_long* const pPrefix = reinterpret_cast(&prefix[0]); + + data.m_nPayloadLen = int(ntohl(*pPrefix)); data.m_nPayloadRead = 0; + *pPrefix = 0; + if (!data.m_bAuthorized && nMaxLen > -1) { if (data.m_nPayloadLen > nMaxLen) @@ -281,15 +286,7 @@ bool CNetConBase::Decode(google::protobuf::MessageLite* pMsg, bool CNetConBase::Send(const SocketHandle_t hSocket, const char* pMsgBuf, const int nMsgLen) const { - std::ostringstream sendbuf; - const u_long nLen = htonl(u_long(nMsgLen)); - - sendbuf.write(reinterpret_cast(&nLen), sizeof(u_long)); - sendbuf.write(pMsgBuf, nMsgLen); - - int ret = ::send(hSocket, sendbuf.str().data(), int(sendbuf.str().size()), - MSG_NOSIGNAL); - + const int ret = ::send(hSocket, pMsgBuf, nMsgLen, MSG_NOSIGNAL); return (ret != SOCKET_ERROR); } diff --git a/src/engine/shared/shared_rcon.cpp b/src/engine/shared/shared_rcon.cpp index aca8c94c..d8008c7e 100644 --- a/src/engine/shared/shared_rcon.cpp +++ b/src/engine/shared/shared_rcon.cpp @@ -13,7 +13,9 @@ // Input : *pBase - // &vecBuf - // *pResponseMsg - +// nResponseMsgLen - // *pResponseVal - +// nResponseValLen - // responseType - // nMessageId - // nMessageType - @@ -21,7 +23,8 @@ // bDebug - // Output : true on success, false otherwise //----------------------------------------------------------------------------- -bool NetconServer_Serialize(const CNetConBase* pBase, vector& vecBuf, const char* pResponseMsg, const char* pResponseVal, +bool NetconServer_Serialize(const CNetConBase* pBase, vector& vecBuf, + const char* pResponseMsg, const size_t nResponseMsgLen, const char* pResponseVal, const size_t nResponseValLen, const netcon::response_e responseType, const int nMessageId, const int nMessageType, const bool bEncrypt, const bool bDebug) { netcon::response response; @@ -29,8 +32,8 @@ bool NetconServer_Serialize(const CNetConBase* pBase, vector& vecBuf, cons response.set_messageid(nMessageId); response.set_messagetype(nMessageType); response.set_responsetype(responseType); - response.set_responsemsg(pResponseMsg); - response.set_responseval(pResponseVal); + response.set_responsemsg(pResponseMsg, nResponseMsgLen); + response.set_responseval(pResponseVal, nResponseValLen); if (!NetconShared_PackEnvelope(pBase, vecBuf, response.ByteSizeLong(), &response, bEncrypt, bDebug)) { @@ -45,21 +48,23 @@ bool NetconServer_Serialize(const CNetConBase* pBase, vector& vecBuf, cons // Input : *pBase - // &vecBuf - // *szReqBuf - +// nReqMsgLen - // *szReqVal - +// nReqValLen - // *requestType - // bEncrypt - // bDebug - // Output : true on success, false otherwise //----------------------------------------------------------------------------- -bool NetconClient_Serialize(const CNetConBase* pBase, vector& vecBuf, const char* szReqBuf, - const char* szReqVal, const netcon::request_e requestType, const bool bEncrypt, const bool bDebug) +bool NetconClient_Serialize(const CNetConBase* pBase, vector& vecBuf, const char* szReqBuf, const size_t nReqMsgLen, + const char* szReqVal, const size_t nReqValLen, const netcon::request_e requestType, const bool bEncrypt, const bool bDebug) { netcon::request request; request.set_messageid(-1); request.set_requesttype(requestType); - request.set_requestmsg(szReqBuf); - request.set_requestval(szReqVal); + request.set_requestmsg(szReqBuf, nReqMsgLen); + request.set_requestval(szReqVal, nReqValLen); if (!NetconShared_PackEnvelope(pBase, vecBuf, request.ByteSizeLong(), &request, bEncrypt, bDebug)) { @@ -169,9 +174,13 @@ bool NetconShared_PackEnvelope(const CNetConBase* pBase, vector& outMsgBuf envelope.set_data(dataBuf, nMsgLen); const size_t envelopeSize = envelope.ByteSizeLong(); - outMsgBuf.resize(envelopeSize); + outMsgBuf.resize(envelopeSize + sizeof(u_long)); + char* const scratch = outMsgBuf.data(); - if (!pBase->Encode(&envelope, &outMsgBuf[0], envelopeSize)) + // Write out frame size in network byte order. + *reinterpret_cast(scratch) = htonl(u_long(envelopeSize)); + + if (!pBase->Encode(&envelope, &scratch[sizeof(u_long)], envelopeSize)) { if (bDebug) { diff --git a/src/engine/shared/shared_rcon.h b/src/engine/shared/shared_rcon.h index af2a3819..20212536 100644 --- a/src/engine/shared/shared_rcon.h +++ b/src/engine/shared/shared_rcon.h @@ -16,11 +16,12 @@ extern void RCON_InitClientAndTrySyncKeys(); #endif // !DEDICATED #endif // _TOOLS -bool NetconServer_Serialize(const CNetConBase* pBase, vector& vecBuf, const char* pResponseMsg, const char* pResponseVal, +bool NetconServer_Serialize(const CNetConBase* pBase, vector& vecBuf, + const char* pResponseMsg, const size_t nResponseMsgLen, const char* pResponseVal, const size_t nResponseValLen, const netcon::response_e responseType, const int nMessageId, const int nMessageType, const bool bEncrypt, const bool bDebug); -bool NetconClient_Serialize(const CNetConBase* pBase, vector& vecBuf, const char* szReqBuf, - const char* szReqVal, const netcon::request_e requestType, const bool bEncrypt, const bool bDebug); +bool NetconClient_Serialize(const CNetConBase* pBase, vector& vecBuf, const char* szReqBuf, const size_t nReqMsgLen, + const char* szReqVal, const size_t nReqValLen, const netcon::request_e requestType, const bool bEncrypt, const bool bDebug); bool NetconClient_Connect(CNetConBase* pBase, const char* pHostAdr, const int nHostPort); bool NetconShared_PackEnvelope(const CNetConBase* pBase, vector& outMsgBuf, const size_t nMsgLen, google::protobuf::MessageLite* inMsg, const bool bEncrypt, const bool bDebug);