diff --git a/r5dev/common/igameserverdata.h b/r5dev/common/igameserverdata.h index 66cf24d8..c7c4ce75 100644 --- a/r5dev/common/igameserverdata.h +++ b/r5dev/common/igameserverdata.h @@ -30,21 +30,27 @@ enum class ServerDataResponseType_t : int class CConnectedNetConsoleData { public: - SocketHandle_t m_hSocket {}; - int m_nCharsInCommandBuffer {}; - char m_pszInputCommandBuffer[MAX_NETCONSOLE_INPUT_LEN] {}; - bool m_bValidated {}; // Revalidates netconsole if false. - bool m_bAuthorized {}; // Set to true after netconsole successfully authed. - bool m_bInputOnly {}; // If set, don't send spew to this net console. - int m_nFailedAttempts {}; // Num failed authentication attempts. - int m_nIgnoredMessage {}; // Count how many times client ignored the no-auth message. + SocketHandle_t m_hSocket; + int m_nPayloadLen; // Num bytes for this message. + int m_nPayloadRead; // Num read bytes from input buffer. + int m_nFailedAttempts; // Num failed authentication attempts. + int m_nIgnoredMessage; // Count how many times client ignored the no-auth message. + bool m_bValidated; // Revalidates netconsole if false. + bool m_bAuthorized; // Set to true after successfull netconsole auth. + bool m_bInputOnly; // If set, don't send spew to this net console. + std::vector m_RecvBuffer; CConnectedNetConsoleData(SocketHandle_t hSocket = -1) { - m_nCharsInCommandBuffer = 0; - m_bAuthorized = false; - m_hSocket = hSocket; - m_bInputOnly = false; + m_hSocket = hSocket; + m_nPayloadLen = 0; + m_nPayloadRead = 0; + m_nFailedAttempts = 0; + m_nIgnoredMessage = 0; + m_bValidated = false; + m_bAuthorized = false; + m_bInputOnly = false; + m_RecvBuffer.reserve(sizeof(int)); // Reserve enough for length-prefix. } }; diff --git a/r5dev/engine/client/cl_rcon.cpp b/r5dev/engine/client/cl_rcon.cpp index b3e62305..4588cdc9 100644 --- a/r5dev/engine/client/cl_rcon.cpp +++ b/r5dev/engine/client/cl_rcon.cpp @@ -11,9 +11,31 @@ #include "protoc/sv_rcon.pb.h" #include "protoc/cl_rcon.pb.h" #include "engine/client/cl_rcon.h" +#include "engine/net.h" #include "squirrel/sqvm.h" #include "common/igameserverdata.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CRConClient::CRConClient() + : m_bInitialized(false) + , m_bConnEstablished(false) +{ + m_pNetAdr2 = new CNetAdr2("localhost", "37015"); + m_pSocket = new CSocketCreator(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CRConClient::~CRConClient(void) +{ + delete m_pNetAdr2; + delete m_pSocket; +} + //----------------------------------------------------------------------------- // Purpose: NETCON systems init //----------------------------------------------------------------------------- @@ -119,7 +141,7 @@ bool CRConClient::Connect(const std::string& svInAdr, const std::string& svInPor //----------------------------------------------------------------------------- void CRConClient::Disconnect(void) { - ::closesocket(m_pSocket->GetAcceptedSocketHandle(0)); + m_pSocket->CloseAcceptedSocket(0); m_bConnEstablished = false; } @@ -129,7 +151,16 @@ void CRConClient::Disconnect(void) //----------------------------------------------------------------------------- void CRConClient::Send(const std::string& svMessage) const { - int nSendResult = ::send(m_pSocket->GetAcceptedSocketData(0)->m_hSocket, svMessage.c_str(), svMessage.size(), MSG_NOSIGNAL); + std::ostringstream ssSendBuf; + + ssSendBuf << static_cast(static_cast(svMessage.size()) >> 24); + ssSendBuf << static_cast(static_cast(svMessage.size()) >> 16); + ssSendBuf << static_cast(static_cast(svMessage.size()) >> 8 ); + ssSendBuf << static_cast(static_cast(svMessage.size())); + ssSendBuf << svMessage; + + int nSendResult = ::send(m_pSocket->GetAcceptedSocketData(0)->m_hSocket, + ssSendBuf.str().data(), static_cast(ssSendBuf.str().size()), MSG_NOSIGNAL); if (nSendResult == SOCKET_ERROR) { Warning(eDLL_T::CLIENT, "Failed to send RCON message: (SOCKET_ERROR)\n"); @@ -141,10 +172,11 @@ void CRConClient::Send(const std::string& svMessage) const //----------------------------------------------------------------------------- void CRConClient::Recv(void) { - static char szRecvBuf[MAX_NETCONSOLE_INPUT_LEN]{}; + static char szRecvBuf[1024]; + CConnectedNetConsoleData* pData = m_pSocket->GetAcceptedSocketData(0); {////////////////////////////////////////////// - int nPendingLen = ::recv(m_pSocket->GetAcceptedSocketData(0)->m_hSocket, szRecvBuf, sizeof(szRecvBuf), MSG_PEEK); + int nPendingLen = ::recv(pData->m_hSocket, szRecvBuf, sizeof(char), MSG_PEEK); if (nPendingLen == SOCKET_ERROR && m_pSocket->IsSocketBlocking()) { return; @@ -158,13 +190,11 @@ void CRConClient::Recv(void) }////////////////////////////////////////////// u_long nReadLen; // Find out how much we have to read. - ::ioctlsocket(m_pSocket->GetAcceptedSocketData(0)->m_hSocket, FIONREAD, &nReadLen); + ::ioctlsocket(pData->m_hSocket, FIONREAD, &nReadLen); while (nReadLen > 0) { - memset(szRecvBuf, '\0', sizeof(szRecvBuf)); - int nRecvLen = ::recv(m_pSocket->GetAcceptedSocketData(0)->m_hSocket, szRecvBuf, MIN(sizeof(szRecvBuf), nReadLen), MSG_NOSIGNAL); - + int nRecvLen = ::recv(pData->m_hSocket, szRecvBuf, MIN(sizeof(szRecvBuf), nReadLen), MSG_NOSIGNAL); if (nRecvLen == 0 && m_bConnEstablished) // Socket was closed. { this->Disconnect(); @@ -173,50 +203,71 @@ void CRConClient::Recv(void) } if (nRecvLen < 0 && !m_pSocket->IsSocketBlocking()) { + Error(eDLL_T::CLIENT, "RCON Cmd: recv error (%s)\n", NET_ErrorString(WSAGetLastError())); break; } nReadLen -= nRecvLen; // Process what we've got. - this->ProcessBuffer(szRecvBuf, nRecvLen); + this->ProcessBuffer(szRecvBuf, nRecvLen, pData); } } //----------------------------------------------------------------------------- -// Purpose: handles input response buffer +// Purpose: parses input response buffer using length-prefix framing // Input : *pszIn - // nRecvLen - +// *pData - //----------------------------------------------------------------------------- -void CRConClient::ProcessBuffer(const char* pszIn, int nRecvLen) const +void CRConClient::ProcessBuffer(const char* pRecvBuf, int nRecvLen, CConnectedNetConsoleData* pData) { - int nCharsInRespondBuffer = 0; - char szInputRespondBuffer[MAX_NETCONSOLE_INPUT_LEN]{}; - - while (nRecvLen) + while (nRecvLen > 0) { - switch (*pszIn) + if (pData->m_nPayloadLen) { - case '\r': - { - if (nCharsInRespondBuffer) + if (pData->m_nPayloadRead < pData->m_nPayloadLen) { - sv_rcon::response sv_response = this->Deserialize(szInputRespondBuffer); - this->ProcessMessage(sv_response); - } - nCharsInRespondBuffer = 0; - break; - } + pData->m_RecvBuffer[pData->m_nPayloadRead++] = *pRecvBuf; - default: - { - if (nCharsInRespondBuffer < MAX_NETCONSOLE_INPUT_LEN - 1) - { - szInputRespondBuffer[nCharsInRespondBuffer++] = *pszIn; + pRecvBuf++; + nRecvLen--; + } + if (pData->m_nPayloadRead == pData->m_nPayloadLen) + { + this->ProcessMessage(this->Deserialize(std::string( + reinterpret_cast(pData->m_RecvBuffer.data()), pData->m_nPayloadLen))); + + pData->m_nPayloadLen = 0; + pData->m_nPayloadRead = 0; } - break; } + else if (pData->m_nPayloadRead < sizeof(int)) // Read size field. + { + pData->m_RecvBuffer[pData->m_nPayloadRead++] = *pRecvBuf; + + pRecvBuf++; + nRecvLen--; + } + else // Build prefix. + { + pData->m_nPayloadLen = static_cast( + pData->m_RecvBuffer[0] << 24 | + pData->m_RecvBuffer[1] << 16 | + pData->m_RecvBuffer[2] << 8 | + pData->m_RecvBuffer[3]); + pData->m_nPayloadRead = 0; + + if (pData->m_nPayloadLen < 0) + { + Error(eDLL_T::CLIENT, "RCON Cmd: sync error (%d)\n", pData->m_nPayloadLen); + this->Disconnect(); // Out of sync (irrecoverable). + + break; + } + else + { + pData->m_RecvBuffer.resize(pData->m_nPayloadLen); + } } - pszIn++; - nRecvLen--; } } @@ -319,7 +370,7 @@ std::string CRConClient::Serialize(const std::string& svReqBuf, const std::strin break; } } - return cl_request.SerializeAsString().append("\r"); + return cl_request.SerializeAsString(); } //----------------------------------------------------------------------------- @@ -330,7 +381,7 @@ std::string CRConClient::Serialize(const std::string& svReqBuf, const std::strin sv_rcon::response CRConClient::Deserialize(const std::string& svBuf) const { sv_rcon::response sv_response; - sv_response.ParseFromArray(svBuf.c_str(), static_cast(svBuf.size())); + sv_response.ParseFromArray(svBuf.data(), static_cast(svBuf.size())); return sv_response; } diff --git a/r5dev/engine/client/cl_rcon.h b/r5dev/engine/client/cl_rcon.h index 9430a3d3..529cd630 100644 --- a/r5dev/engine/client/cl_rcon.h +++ b/r5dev/engine/client/cl_rcon.h @@ -7,8 +7,8 @@ class CRConClient { public: - CRConClient(void){}; - ~CRConClient(void) { delete m_pNetAdr2; delete m_pSocket; }; + CRConClient(void); + ~CRConClient(void); void Init(void); void Shutdown(void); @@ -23,7 +23,7 @@ public: void Send(const std::string& svMessage) const; void Recv(void); - void ProcessBuffer(const char* pszIn, int nRecvLen) const; + void ProcessBuffer(const char* pRecvBuf, int nRecvLen, CConnectedNetConsoleData* pData); void ProcessMessage(const sv_rcon::response& sv_response) const; std::string Serialize(const std::string& svReqBuf, const std::string& svReqVal, cl_rcon::request_t request_t) const; @@ -33,8 +33,8 @@ public: bool IsConnected(void) const; private: - CNetAdr2* m_pNetAdr2 = new CNetAdr2("localhost", "37015"); - CSocketCreator* m_pSocket = new CSocketCreator(); + CNetAdr2* m_pNetAdr2; + CSocketCreator* m_pSocket; bool m_bInitialized = false; bool m_bConnEstablished = false; diff --git a/r5dev/engine/server/sv_rcon.cpp b/r5dev/engine/server/sv_rcon.cpp index 2f528a7a..8846dd65 100644 --- a/r5dev/engine/server/sv_rcon.cpp +++ b/r5dev/engine/server/sv_rcon.cpp @@ -10,12 +10,25 @@ #include "tier1/IConVar.h" #include "tier1/NetAdr2.h" #include "tier2/socketcreator.h" +#include "engine/net.h" #include "engine/server/sv_rcon.h" #include "protoc/sv_rcon.pb.h" #include "protoc/cl_rcon.pb.h" #include "mathlib/sha256.h" #include "common/igameserverdata.h" + +//----------------------------------------------------------------------------- +// Purpose: NETCON systems init +//----------------------------------------------------------------------------- +CRConServer::CRConServer() + : m_bInitialized(false) + , m_nConnIndex(0) +{ + m_pAdr2 = new CNetAdr2(); + m_pSocket = new CSocketCreator(); +} + //----------------------------------------------------------------------------- // Purpose: NETCON systems init //----------------------------------------------------------------------------- @@ -122,32 +135,70 @@ void CRConServer::RunFrame(void) } //----------------------------------------------------------------------------- -// Purpose: send message +// Purpose: send message to all connected sockets // Input : *svMessage - //----------------------------------------------------------------------------- void CRConServer::Send(const std::string& svMessage) const { - int nCount = m_pSocket->GetAcceptedSocketCount(); - - for (int i = nCount - 1; i >= 0; i--) + if (int nCount = m_pSocket->GetAcceptedSocketCount()) { - CConnectedNetConsoleData* pData = m_pSocket->GetAcceptedSocketData(i); + std::ostringstream ssSendBuf; - if (pData->m_bAuthorized) + ssSendBuf << static_cast(static_cast(svMessage.size()) >> 24); + ssSendBuf << static_cast(static_cast(svMessage.size()) >> 16); + ssSendBuf << static_cast(static_cast(svMessage.size()) >> 8 ); + ssSendBuf << static_cast(static_cast(svMessage.size())); + ssSendBuf << svMessage; + + for (int i = nCount - 1; i >= 0; i--) { - std::string svFinal = this->Serialize(svMessage, "", sv_rcon::response_t::SERVERDATA_RESPONSE_CONSOLE_LOG); - ::send(pData->m_hSocket, svFinal.c_str(), static_cast(svFinal.size()), MSG_NOSIGNAL); + CConnectedNetConsoleData* pData = m_pSocket->GetAcceptedSocketData(i); + + if (pData->m_bAuthorized) + { + size_t nMsgCount = (ssSendBuf.str().size() + MAX_NETCONSOLE_INPUT_LEN - 1) / MAX_NETCONSOLE_INPUT_LEN; + size_t nDataSize = ssSendBuf.str().size(); + size_t nPos = 0; + + for (size_t j = 0; j < nMsgCount; j++) + { + size_t nSize = std::min(MAX_NETCONSOLE_INPUT_LEN, nDataSize); + nDataSize -= nSize; + string svFinal = ssSendBuf.str().substr(nPos, nSize); + + ::send(pData->m_hSocket, svFinal.data(), static_cast(svFinal.size()), MSG_NOSIGNAL); + nPos += nSize; + } + } } } } +//----------------------------------------------------------------------------- +// Purpose: send message to specific connected socket +// Input : hSocket - +// *svMessage - +//----------------------------------------------------------------------------- +void CRConServer::Send(SocketHandle_t hSocket, const std::string& svMessage) const +{ + std::ostringstream ssSendBuf; + + ssSendBuf << static_cast(static_cast(svMessage.size()) >> 24); + ssSendBuf << static_cast(static_cast(svMessage.size()) >> 16); + ssSendBuf << static_cast(static_cast(svMessage.size()) >> 8 ); + ssSendBuf << static_cast(static_cast(svMessage.size())); + ssSendBuf << svMessage; + + ::send(hSocket, ssSendBuf.str().data(), static_cast(ssSendBuf.str().size()), MSG_NOSIGNAL); +} + //----------------------------------------------------------------------------- // Purpose: receive message //----------------------------------------------------------------------------- void CRConServer::Recv(void) { int nCount = m_pSocket->GetAcceptedSocketCount(); - static char szRecvBuf[MAX_NETCONSOLE_INPUT_LEN]{}; + static char szRecvBuf[1024]{}; for (m_nConnIndex = nCount - 1; m_nConnIndex >= 0; m_nConnIndex--) { @@ -155,13 +206,12 @@ void CRConServer::Recv(void) {////////////////////////////////////////////// if (this->CheckForBan(pData)) { - std::string svNoAuth = this->Serialize(s_pszBannedMessage, "", sv_rcon::response_t::SERVERDATA_RESPONSE_AUTH); - ::send(pData->m_hSocket, svNoAuth.c_str(), static_cast(svNoAuth.size()), MSG_NOSIGNAL); + this->Send(pData->m_hSocket, this->Serialize(s_pszBannedMessage, "", sv_rcon::response_t::SERVERDATA_RESPONSE_AUTH)); this->CloseConnection(); continue; } - int nPendingLen = ::recv(pData->m_hSocket, szRecvBuf, sizeof(szRecvBuf), MSG_PEEK); + int nPendingLen = ::recv(pData->m_hSocket, szRecvBuf, sizeof(char), MSG_PEEK); if (nPendingLen == SOCKET_ERROR && m_pSocket->IsSocketBlocking()) { continue; @@ -178,9 +228,7 @@ void CRConServer::Recv(void) while (nReadLen > 0) { - memset(szRecvBuf, '\0', sizeof(szRecvBuf)); int nRecvLen = ::recv(pData->m_hSocket, szRecvBuf, MIN(sizeof(szRecvBuf), nReadLen), MSG_NOSIGNAL); - if (nRecvLen == 0) // Socket was closed. { this->CloseConnection(); @@ -188,6 +236,7 @@ void CRConServer::Recv(void) } if (nRecvLen < 0 && !m_pSocket->IsSocketBlocking()) { + Error(eDLL_T::SERVER, "RCON Cmd: recv error (%s)\n", NET_ErrorString(WSAGetLastError())); break; } @@ -229,7 +278,7 @@ std::string CRConServer::Serialize(const std::string& svRspBuf, const std::strin break; } } - return sv_response.SerializeAsString().append("\r"); + return sv_response.SerializeAsString(); } //----------------------------------------------------------------------------- @@ -240,7 +289,7 @@ std::string CRConServer::Serialize(const std::string& svRspBuf, const std::strin cl_rcon::request CRConServer::Deserialize(const std::string& svBuf) const { cl_rcon::request cl_request; - cl_request.ParseFromArray(svBuf.c_str(), static_cast(svBuf.size())); + cl_request.ParseFromArray(svBuf.data(), static_cast(svBuf.size())); return cl_request; } @@ -249,9 +298,6 @@ cl_rcon::request CRConServer::Deserialize(const std::string& svBuf) const // Purpose: authenticate new connections // Input : *cl_request - // *pData - -// Todo : implement logic for key exchange instead so we never network our -// password in plain text over the wire. create a cvar for this so user could -// also opt out and use legacy authentication instead for older RCON clients //----------------------------------------------------------------------------- void CRConServer::Authenticate(const cl_rcon::request& cl_request, CConnectedNetConsoleData* pData) { @@ -259,16 +305,15 @@ void CRConServer::Authenticate(const cl_rcon::request& cl_request, CConnectedNet { return; } - else + else // Authorize. { if (this->Comparator(cl_request.requestbuf())) { pData->m_bAuthorized = true; m_pSocket->CloseListenSocket(); - this->CloseNonAuthConnection(); - std::string svAuth = this->Serialize(s_pszAuthMessage, "", sv_rcon::response_t::SERVERDATA_RESPONSE_AUTH); - ::send(pData->m_hSocket, svAuth.c_str(), static_cast(svAuth.size()), MSG_NOSIGNAL); + this->CloseNonAuthConnection(); + this->Send(pData->m_hSocket, this->Serialize(s_pszAuthMessage, "", sv_rcon::response_t::SERVERDATA_RESPONSE_AUTH)); } else // Bad password. { @@ -278,8 +323,7 @@ void CRConServer::Authenticate(const cl_rcon::request& cl_request, CConnectedNet DevMsg(eDLL_T::SERVER, "Bad RCON password attempt from '%s'\n", netAdr2.GetIPAndPort().c_str()); } - std::string svWrongPass = this->Serialize(s_pszWrongPwMessage, "", sv_rcon::response_t::SERVERDATA_RESPONSE_AUTH); - ::send(pData->m_hSocket, svWrongPass.c_str(), static_cast(svWrongPass.size()), MSG_NOSIGNAL); + this->Send(pData->m_hSocket, this->Serialize(s_pszWrongPwMessage, "", sv_rcon::response_t::SERVERDATA_RESPONSE_AUTH)); pData->m_bAuthorized = false; pData->m_bValidated = false; @@ -311,38 +355,70 @@ bool CRConServer::Comparator(std::string svPassword) const } //----------------------------------------------------------------------------- -// Purpose: handles input command buffer -// Input : *pszIn - +// Purpose: parses input response buffer using length-prefix framing +// Input : *pRecvBuf - // nRecvLen - // *pData - //----------------------------------------------------------------------------- -void CRConServer::ProcessBuffer(const char* pszIn, int nRecvLen, CConnectedNetConsoleData* pData) +void CRConServer::ProcessBuffer(const char* pRecvBuf, int nRecvLen, CConnectedNetConsoleData* pData) { - while (nRecvLen) + while (nRecvLen > 0) { - switch (*pszIn) + if (pData->m_nPayloadLen) { - case '\r': - { - if (pData->m_nCharsInCommandBuffer) + if (pData->m_nPayloadRead < pData->m_nPayloadLen) { - cl_rcon::request cl_request = this->Deserialize(pData->m_pszInputCommandBuffer); - this->ProcessMessage(cl_request); + pData->m_RecvBuffer[pData->m_nPayloadRead++] = *pRecvBuf; + + pRecvBuf++; + nRecvLen--; } - pData->m_nCharsInCommandBuffer = 0; - break; - } - default: - { - if (pData->m_nCharsInCommandBuffer < MAX_NETCONSOLE_INPUT_LEN - 1) + if (pData->m_nPayloadRead == pData->m_nPayloadLen) { - pData->m_pszInputCommandBuffer[pData->m_nCharsInCommandBuffer++] = *pszIn; + this->ProcessMessage(this->Deserialize(std::string( + reinterpret_cast(pData->m_RecvBuffer.data()), pData->m_nPayloadLen))); + + pData->m_nPayloadLen = 0; + pData->m_nPayloadRead = 0; } - break; } + else if (pData->m_nPayloadRead < sizeof(int)) // Read size field. + { + pData->m_RecvBuffer[pData->m_nPayloadRead++] = *pRecvBuf; + + pRecvBuf++; + nRecvLen--; + } + else // Build prefix. + { + pData->m_nPayloadLen = static_cast( + pData->m_RecvBuffer[0] << 24 | + pData->m_RecvBuffer[1] << 16 | + pData->m_RecvBuffer[2] << 8 | + pData->m_RecvBuffer[3]); + pData->m_nPayloadRead = 0; + + if (!pData->m_bAuthorized) + { + if (pData->m_nPayloadLen > MAX_NETCONSOLE_INPUT_LEN) + { + this->CloseConnection(); // Sending large messages while not authenticated. + break; + } + } + + if (pData->m_nPayloadLen < 0) + { + Error(eDLL_T::SERVER, "RCON Cmd: sync error (%d)\n", pData->m_nPayloadLen); + this->CloseConnection(); // Out of sync (irrecoverable). + + break; + } + else + { + pData->m_RecvBuffer.resize(pData->m_nPayloadLen); + } } - pszIn++; - nRecvLen--; } } @@ -358,8 +434,7 @@ void CRConServer::ProcessMessage(const cl_rcon::request& cl_request) && cl_request.requesttype() != cl_rcon::request_t::SERVERDATA_REQUEST_AUTH) { // Notify net console that authentication is required. - std::string svMessage = this->Serialize(s_pszNoAuthMessage, "", sv_rcon::response_t::SERVERDATA_RESPONSE_AUTH); - ::send(pData->m_hSocket, svMessage.c_str(), static_cast(svMessage.size()), MSG_NOSIGNAL); + this->Send(pData->m_hSocket, this->Serialize(s_pszNoAuthMessage, "", sv_rcon::response_t::SERVERDATA_RESPONSE_AUTH)); pData->m_bValidated = false; pData->m_nIgnoredMessage++; @@ -373,12 +448,18 @@ void CRConServer::ProcessMessage(const cl_rcon::request& cl_request) break; } case cl_rcon::request_t::SERVERDATA_REQUEST_EXECCOMMAND: + { + if (pData->m_bAuthorized) // Only execute if auth was succesfull. + { + this->Execute(cl_request, false); + } + break; + } case cl_rcon::request_t::SERVERDATA_REQUEST_SETVALUE: { - // Only execute if auth was succesfull. if (pData->m_bAuthorized) { - this->Execute(cl_request); + this->Execute(cl_request, true); } break; } @@ -400,15 +481,16 @@ void CRConServer::ProcessMessage(const cl_rcon::request& cl_request) //----------------------------------------------------------------------------- // Purpose: execute commands issued from net console // Input : *cl_request - +// bConVar - //----------------------------------------------------------------------------- -void CRConServer::Execute(const cl_rcon::request& cl_request) const +void CRConServer::Execute(const cl_rcon::request& cl_request, bool bConVar) const { ConVar* pConVar = g_pCVar->FindVar(cl_request.requestbuf().c_str()); - if (pConVar) + if (pConVar) // Set value without running the callback. { pConVar->SetValue(cl_request.requestval().c_str()); } - else // Execute command with "". + else if (!bConVar) // Execute command with "". { Cbuf_AddText(Cbuf_GetCurrentPlayer(), cl_request.requestbuf().c_str(), cmd_source_t::kCommandSrcCode); Cbuf_Execute(); diff --git a/r5dev/engine/server/sv_rcon.h b/r5dev/engine/server/sv_rcon.h index 550f6eb0..fa187b15 100644 --- a/r5dev/engine/server/sv_rcon.h +++ b/r5dev/engine/server/sv_rcon.h @@ -4,14 +4,15 @@ #include "protoc/sv_rcon.pb.h" #include "protoc/cl_rcon.pb.h" -constexpr char s_pszNoAuthMessage[] = "This server is password protected for console access. Must send 'PASS ' command.\n\r"; -constexpr char s_pszWrongPwMessage[] = "Password incorrect.\n\r"; -constexpr char s_pszBannedMessage[] = "Go away.\n\r"; -constexpr char s_pszAuthMessage[] = "RCON authentication succesfull.\n\r"; +constexpr char s_pszNoAuthMessage[] = "This server is password protected for console access. Authenticate with 'PASS ' command.\n"; +constexpr char s_pszWrongPwMessage[] = "Admin password incorrect.\n"; +constexpr char s_pszBannedMessage[] = "Go away.\n"; +constexpr char s_pszAuthMessage[] = "RCON authentication successfull.\n"; class CRConServer { public: + CRConServer(); ~CRConServer() { delete m_pAdr2; delete m_pSocket; } void Init(void); @@ -22,6 +23,7 @@ public: void RunFrame(void); void Send(const std::string& svMessage) const; + void Send(SocketHandle_t hSocket, const std::string& svMessage) const; void Recv(void); std::string Serialize(const std::string& svRspBuf, const std::string& svRspVal, sv_rcon::response_t response_t) const; @@ -33,7 +35,7 @@ public: void ProcessBuffer(const char* pszIn, int nRecvLen, CConnectedNetConsoleData* pData); void ProcessMessage(const cl_rcon::request& cl_request); - void Execute(const cl_rcon::request& cl_request) const; + void Execute(const cl_rcon::request& cl_request, bool bConVar) const; bool CheckForBan(CConnectedNetConsoleData* pData); void CloseConnection(void); @@ -41,10 +43,10 @@ public: private: - bool m_bInitialized = false; - int m_nConnIndex = 0; - CNetAdr2* m_pAdr2 = new CNetAdr2(); - CSocketCreator* m_pSocket = new CSocketCreator(); + bool m_bInitialized; + int m_nConnIndex; + CNetAdr2* m_pAdr2; + CSocketCreator* m_pSocket; std::vector m_vBannedAddress; std::string m_svPasswordHash; }; diff --git a/r5dev/netconsole/netconsole.cpp b/r5dev/netconsole/netconsole.cpp index 80769d41..f52c36b1 100644 --- a/r5dev/netconsole/netconsole.cpp +++ b/r5dev/netconsole/netconsole.cpp @@ -14,6 +14,29 @@ #include "engine/net.h" #include "netconsole/netconsole.h" +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CNetCon::CNetCon(void) + : m_bInitialized(false) + , m_bNoColor(false) + , m_bQuitApplication(false) + , m_abPromptConnect(true) + , m_abConnEstablished(false) +{ + m_pNetAdr2 = new CNetAdr2("localhost", "37015"); + m_pSocket = new CSocketCreator(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CNetCon::~CNetCon(void) +{ + delete m_pNetAdr2; + delete m_pSocket; +} + //----------------------------------------------------------------------------- // Purpose: WSA and NETCON systems init // Output : true on success, false otherwise @@ -127,9 +150,6 @@ void CNetCon::UserInput(void) { if (vSubStrings.size() > 2) { - printf("%s\n", vSubStrings[1].c_str()); - printf("%s\n", vSubStrings[2].c_str()); - std::string svSerialized = this->Serialize(vSubStrings[1], vSubStrings[2], cl_rcon::request_t::SERVERDATA_REQUEST_SETVALUE); this->Send(svSerialized); } @@ -236,8 +256,8 @@ bool CNetCon::Connect(const std::string& svInAdr, const std::string& svInPort) //----------------------------------------------------------------------------- void CNetCon::Disconnect(void) { - ::closesocket(m_pSocket->GetAcceptedSocketHandle(0)); - m_abPromptConnect = true; + m_pSocket->CloseAcceptedSocket(0); + m_abPromptConnect = true; m_abConnEstablished = false; } @@ -247,7 +267,16 @@ void CNetCon::Disconnect(void) //----------------------------------------------------------------------------- void CNetCon::Send(const std::string& svMessage) const { - int nSendResult = ::send(m_pSocket->GetAcceptedSocketData(0)->m_hSocket, svMessage.c_str(), svMessage.size(), MSG_NOSIGNAL); + std::ostringstream ssSendBuf; + + ssSendBuf << static_cast(static_cast(svMessage.size()) >> 24); + ssSendBuf << static_cast(static_cast(svMessage.size()) >> 16); + ssSendBuf << static_cast(static_cast(svMessage.size()) >> 8 ); + ssSendBuf << static_cast(static_cast(svMessage.size())); + ssSendBuf << svMessage; + + int nSendResult = ::send(m_pSocket->GetAcceptedSocketData(0)->m_hSocket, + ssSendBuf.str().data(), static_cast(ssSendBuf.str().size()), MSG_NOSIGNAL); if (nSendResult == SOCKET_ERROR) { std::cout << "Failed to send message: (SOCKET_ERROR)" << std::endl; @@ -259,10 +288,11 @@ void CNetCon::Send(const std::string& svMessage) const //----------------------------------------------------------------------------- void CNetCon::Recv(void) { - static char szRecvBuf[MAX_NETCONSOLE_INPUT_LEN]{}; + static char szRecvBuf[1024]; + CConnectedNetConsoleData* pData = m_pSocket->GetAcceptedSocketData(0); {////////////////////////////////////////////// - int nPendingLen = ::recv(m_pSocket->GetAcceptedSocketData(0)->m_hSocket, szRecvBuf, sizeof(szRecvBuf), MSG_PEEK); + int nPendingLen = ::recv(pData->m_hSocket, szRecvBuf, sizeof(char), MSG_PEEK); if (nPendingLen == SOCKET_ERROR && m_pSocket->IsSocketBlocking()) { return; @@ -276,13 +306,11 @@ void CNetCon::Recv(void) }////////////////////////////////////////////// u_long nReadLen; // Find out how much we have to read. - ::ioctlsocket(m_pSocket->GetAcceptedSocketData(0)->m_hSocket, FIONREAD, &nReadLen); + ::ioctlsocket(pData->m_hSocket, FIONREAD, &nReadLen); while (nReadLen > 0) { - memset(szRecvBuf, '\0', sizeof(szRecvBuf)); - int nRecvLen = ::recv(m_pSocket->GetAcceptedSocketData(0)->m_hSocket, szRecvBuf, MIN(sizeof(szRecvBuf), nReadLen), MSG_NOSIGNAL); - + int nRecvLen = ::recv(pData->m_hSocket, szRecvBuf, MIN(sizeof(szRecvBuf), nReadLen), MSG_NOSIGNAL); if (nRecvLen == 0 && m_abConnEstablished) // Socket was closed. { this->Disconnect(); @@ -291,50 +319,71 @@ void CNetCon::Recv(void) } if (nRecvLen < 0 && !m_pSocket->IsSocketBlocking()) { + std::cout << "RCON Cmd: recv error (" << NET_ErrorString(WSAGetLastError()) << ")" << std::endl; break; } nReadLen -= nRecvLen; // Process what we've got. - this->ProcessBuffer(szRecvBuf, nRecvLen); + this->ProcessBuffer(szRecvBuf, nRecvLen, pData); } } //----------------------------------------------------------------------------- -// Purpose: handles input response buffer -// Input : *pszIn - +// Purpose: parses input response buffer using length-prefix framing +// Input : *pRecvBuf - // nRecvLen - +// *pData - //----------------------------------------------------------------------------- -void CNetCon::ProcessBuffer(const char* pszIn, int nRecvLen) const +void CNetCon::ProcessBuffer(const char* pRecvBuf, int nRecvLen, CConnectedNetConsoleData* pData) { - int nCharsInRespondBuffer = 0; - char szInputRespondBuffer[MAX_NETCONSOLE_INPUT_LEN]{}; - - while (nRecvLen) + while (nRecvLen > 0) { - switch (*pszIn) + if (pData->m_nPayloadLen) { - case '\r': - { - if (nCharsInRespondBuffer) + if (pData->m_nPayloadRead < pData->m_nPayloadLen) { - sv_rcon::response sv_response = this->Deserialize(szInputRespondBuffer); - this->ProcessMessage(sv_response); - } - nCharsInRespondBuffer = 0; - break; - } + pData->m_RecvBuffer[pData->m_nPayloadRead++] = *pRecvBuf; - default: - { - if (nCharsInRespondBuffer < MAX_NETCONSOLE_INPUT_LEN - 1) - { - szInputRespondBuffer[nCharsInRespondBuffer++] = *pszIn; + pRecvBuf++; + nRecvLen--; + } + if (pData->m_nPayloadRead == pData->m_nPayloadLen) + { + this->ProcessMessage(this->Deserialize(std::string( + reinterpret_cast(pData->m_RecvBuffer.data()), pData->m_nPayloadLen))); + + pData->m_nPayloadLen = 0; + pData->m_nPayloadRead = 0; } - break; } + else if (pData->m_nPayloadRead < sizeof(int)) // Read size field. + { + pData->m_RecvBuffer[pData->m_nPayloadRead++] = *pRecvBuf; + + pRecvBuf++; + nRecvLen--; + } + else // Build prefix. + { + pData->m_nPayloadLen = static_cast( + pData->m_RecvBuffer[0] << 24 | + pData->m_RecvBuffer[1] << 16 | + pData->m_RecvBuffer[2] << 8 | + pData->m_RecvBuffer[3]); + pData->m_nPayloadRead = 0; + + if (pData->m_nPayloadLen < 0) + { + std::cout << "RCON Cmd: sync error (" << pData->m_nPayloadLen << ")" << std::endl; + this->Disconnect(); // Out of sync (irrecoverable). + + break; + } + else + { + pData->m_RecvBuffer.resize(pData->m_nPayloadLen); + } } - pszIn++; - nRecvLen--; } } @@ -347,25 +396,25 @@ void CNetCon::ProcessMessage(const sv_rcon::response& sv_response) const static std::regex rxAnsiExp("\\\033\\[.*?m"); switch (sv_response.responsetype()) { - case sv_rcon::response_t::SERVERDATA_RESPONSE_AUTH: - case sv_rcon::response_t::SERVERDATA_RESPONSE_CONSOLE_LOG: + case sv_rcon::response_t::SERVERDATA_RESPONSE_AUTH: + case sv_rcon::response_t::SERVERDATA_RESPONSE_CONSOLE_LOG: + { + std::string svOut = sv_response.responsebuf(); + if (m_bNoColor) { - std::string svOut = sv_response.responsebuf(); - if (m_bNoColor) - { - svOut = std::regex_replace(svOut, rxAnsiExp, ""); - } - else - { - svOut.append(g_svReset.c_str()); - } - std::cout << svOut.c_str(); - break; + svOut = std::regex_replace(svOut, rxAnsiExp, ""); } - default: + else { - break; + svOut.append(g_svReset); } + std::cout << svOut; + break; + } + default: + { + break; + } } } @@ -385,20 +434,20 @@ std::string CNetCon::Serialize(const std::string& svReqBuf, const std::string& s switch (request_t) { - case cl_rcon::request_t::SERVERDATA_REQUEST_SETVALUE: - case cl_rcon::request_t::SERVERDATA_REQUEST_AUTH: - { - cl_request.set_requestbuf(svReqBuf); - cl_request.set_requestval(svReqVal); - break; - } - case cl_rcon::request_t::SERVERDATA_REQUEST_EXECCOMMAND: - { - cl_request.set_requestbuf(svReqBuf); - break; - } + case cl_rcon::request_t::SERVERDATA_REQUEST_SETVALUE: + case cl_rcon::request_t::SERVERDATA_REQUEST_AUTH: + { + cl_request.set_requestbuf(svReqBuf); + cl_request.set_requestval(svReqVal); + break; } - return cl_request.SerializeAsString().append("\r"); + case cl_rcon::request_t::SERVERDATA_REQUEST_EXECCOMMAND: + { + cl_request.set_requestbuf(svReqBuf); + break; + } + } + return cl_request.SerializeAsString(); } //----------------------------------------------------------------------------- @@ -409,7 +458,7 @@ std::string CNetCon::Serialize(const std::string& svReqBuf, const std::string& s sv_rcon::response CNetCon::Deserialize(const std::string& svBuf) const { sv_rcon::response sv_response; - sv_response.ParseFromArray(svBuf.c_str(), static_cast(svBuf.size())); + sv_response.ParseFromArray(svBuf.data(), static_cast(svBuf.size())); return sv_response; } diff --git a/r5dev/netconsole/netconsole.h b/r5dev/netconsole/netconsole.h index 27ed534d..93f707d5 100644 --- a/r5dev/netconsole/netconsole.h +++ b/r5dev/netconsole/netconsole.h @@ -12,7 +12,8 @@ constexpr const char* NETCON_VERSION = "2.0.0.1"; class CNetCon { public: - ~CNetCon() { delete m_pNetAdr2; delete m_pSocket; } + CNetCon(void); + ~CNetCon(void); bool Init(void); bool Shutdown(void); @@ -29,19 +30,19 @@ public: void Send(const std::string& svMessage) const; void Recv(void); - void ProcessBuffer(const char* pszIn, int nRecvLen) const; + void ProcessBuffer(const char* pRecvBuf, int nRecvLen, CConnectedNetConsoleData* pData); void ProcessMessage(const sv_rcon::response& sv_response) const; std::string Serialize(const std::string& svReqBuf, const std::string& svReqVal, cl_rcon::request_t request_t) const; sv_rcon::response Deserialize(const std::string& svBuf) const; private: - CNetAdr2* m_pNetAdr2 = new CNetAdr2("localhost", "37015"); - CSocketCreator* m_pSocket = new CSocketCreator(); + CNetAdr2* m_pNetAdr2; + CSocketCreator* m_pSocket; - bool m_bInitialized = false; - bool m_bNoColor = false; - bool m_bQuitApplication = false; - std::atomic m_abPromptConnect{ true }; - std::atomic m_abConnEstablished{ false }; + bool m_bInitialized; + bool m_bNoColor; + bool m_bQuitApplication; + std::atomic m_abPromptConnect; + std::atomic m_abConnEstablished; }; \ No newline at end of file diff --git a/r5dev/squirrel/sqvm.cpp b/r5dev/squirrel/sqvm.cpp index 7fcd483f..d317f9ce 100644 --- a/r5dev/squirrel/sqvm.cpp +++ b/r5dev/squirrel/sqvm.cpp @@ -89,7 +89,7 @@ SQRESULT SQVM_PrintFunc(HSQUIRRELVM v, SQChar* fmt, ...) { wconsole->debug(vmStr); #ifdef DEDICATED - RCONServer()->Send(vmStr); + RCONServer()->Send(RCONServer()->Serialize(vmStr, "", sv_rcon::response_t::SERVERDATA_RESPONSE_CONSOLE_LOG)); #endif // DEDICATED } else @@ -135,7 +135,7 @@ SQRESULT SQVM_PrintFunc(HSQUIRRELVM v, SQChar* fmt, ...) vmStrAnsi.append(buf); wconsole->debug(vmStrAnsi); #ifdef DEDICATED - RCONServer()->Send(vmStrAnsi); + RCONServer()->Send(RCONServer()->Serialize(vmStrAnsi, "", sv_rcon::response_t::SERVERDATA_RESPONSE_CONSOLE_LOG)); #endif // DEDICATED } @@ -229,7 +229,7 @@ SQRESULT SQVM_WarningFunc(HSQUIRRELVM v, SQInteger a2, SQInteger a3, SQInteger* { wconsole->debug(vmStr); #ifdef DEDICATED - RCONServer()->Send(vmStr); + RCONServer()->Send(RCONServer()->Serialize(vmStr, "", sv_rcon::response_t::SERVERDATA_RESPONSE_CONSOLE_LOG)); #endif // DEDICATED } else @@ -239,7 +239,7 @@ SQRESULT SQVM_WarningFunc(HSQUIRRELVM v, SQInteger a2, SQInteger a3, SQInteger* vmStrAnsi.append(svConstructor); wconsole->debug(vmStrAnsi); #ifdef DEDICATED - RCONServer()->Send(vmStrAnsi); + RCONServer()->Send(RCONServer()->Serialize(vmStrAnsi, "", sv_rcon::response_t::SERVERDATA_RESPONSE_CONSOLE_LOG)); #endif // DEDICATED } diff --git a/r5dev/tier0/dbg.cpp b/r5dev/tier0/dbg.cpp index 293f0a79..a719166d 100644 --- a/r5dev/tier0/dbg.cpp +++ b/r5dev/tier0/dbg.cpp @@ -132,7 +132,7 @@ void DevMsg(eDLL_T context, const char* fmt, ...) { wconsole->debug(svOut); #ifdef DEDICATED - RCONServer()->Send(svOut); + RCONServer()->Send(RCONServer()->Serialize(svOut, "", sv_rcon::response_t::SERVERDATA_RESPONSE_CONSOLE_LOG)); #endif // DEDICATED } else @@ -147,7 +147,7 @@ void DevMsg(eDLL_T context, const char* fmt, ...) } wconsole->debug(svAnsiOut); #ifdef DEDICATED - RCONServer()->Send(svAnsiOut); + RCONServer()->Send(RCONServer()->Serialize(svAnsiOut, "", sv_rcon::response_t::SERVERDATA_RESPONSE_CONSOLE_LOG)); #endif // DEDICATED } @@ -240,7 +240,7 @@ void Warning(eDLL_T context, const char* fmt, ...) { wconsole->debug(svOut); #ifdef DEDICATED - RCONServer()->Send(svOut); + RCONServer()->Send(RCONServer()->Serialize(svOut, "", sv_rcon::response_t::SERVERDATA_RESPONSE_CONSOLE_LOG)); #endif // DEDICATED } else @@ -256,7 +256,7 @@ void Warning(eDLL_T context, const char* fmt, ...) } wconsole->debug(svAnsiOut); #ifdef DEDICATED - RCONServer()->Send(svAnsiOut); + RCONServer()->Send(RCONServer()->Serialize(svAnsiOut, "", sv_rcon::response_t::SERVERDATA_RESPONSE_CONSOLE_LOG)); #endif // DEDICATED } @@ -317,7 +317,7 @@ void Error(eDLL_T context, const char* fmt, ...) { wconsole->debug(svOut); #ifdef DEDICATED - RCONServer()->Send(svOut); + RCONServer()->Send(RCONServer()->Serialize(svOut, "", sv_rcon::response_t::SERVERDATA_RESPONSE_CONSOLE_LOG)); #endif // DEDICATED } else @@ -333,7 +333,7 @@ void Error(eDLL_T context, const char* fmt, ...) } wconsole->debug(svAnsiOut); #ifdef DEDICATED - RCONServer()->Send(svAnsiOut); + RCONServer()->Send(RCONServer()->Serialize(svAnsiOut, "", sv_rcon::response_t::SERVERDATA_RESPONSE_CONSOLE_LOG)); #endif // DEDICATED } diff --git a/r5dev/tier2/socketcreator.cpp b/r5dev/tier2/socketcreator.cpp index 824a04ee..263161e6 100644 --- a/r5dev/tier2/socketcreator.cpp +++ b/r5dev/tier2/socketcreator.cpp @@ -303,6 +303,8 @@ void CSocketCreator::CloseAcceptedSocket(int nIndex) AcceptedSocket_t& connected = m_hAcceptedSockets[nIndex]; ::closesocket(connected.m_hSocket); + delete connected.m_pData; + m_hAcceptedSockets.erase(m_hAcceptedSockets.begin() + nIndex); } @@ -315,6 +317,8 @@ void CSocketCreator::CloseAllAcceptedSockets(void) { AcceptedSocket_t& connected = m_hAcceptedSockets[i]; ::closesocket(connected.m_hSocket); + + delete connected.m_pData; } m_hAcceptedSockets.clear(); }