aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NetConsole/netconsole.cpp195
-rw-r--r--NetConsole/netconsole.h24
-rw-r--r--NorthstarDedicatedTest/cl_rcon.cpp142
-rw-r--r--NorthstarDedicatedTest/cl_rcon.h12
-rw-r--r--NorthstarDedicatedTest/host_state.cpp8
-rw-r--r--NorthstarDedicatedTest/igameserverdata.h26
-rw-r--r--NorthstarDedicatedTest/logging.cpp4
-rw-r--r--NorthstarDedicatedTest/rcon_shared.cpp23
-rw-r--r--NorthstarDedicatedTest/socketcreator.cpp15
-rw-r--r--NorthstarDedicatedTest/squirrel.cpp4
-rw-r--r--NorthstarDedicatedTest/sv_rcon.cpp228
-rw-r--r--NorthstarDedicatedTest/sv_rcon.h25
12 files changed, 478 insertions, 228 deletions
diff --git a/NetConsole/netconsole.cpp b/NetConsole/netconsole.cpp
index 4df47647..7dfec29b 100644
--- a/NetConsole/netconsole.cpp
+++ b/NetConsole/netconsole.cpp
@@ -13,9 +13,19 @@
#include "netconsole.h"
//-----------------------------------------------------------------------------
-// Purpose: destructor
+// Purpose:
//-----------------------------------------------------------------------------
-CNetCon::~CNetCon()
+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;
@@ -27,12 +37,12 @@ CNetCon::~CNetCon()
//-----------------------------------------------------------------------------
bool CNetCon::Init(void)
{
- WSAData wsaData{};
+ WSAData wsaData {};
int nError = ::WSAStartup(MAKEWORD(2, 2), &wsaData);
if (nError != 0)
{
- spdlog::error("Failed to start Winsock via WSAStartup: ({})", NET_ErrorString(WSAGetLastError()));
+ spdlog::error("Failed to start Winsock via WSAStartup: ({:s})", NET_ErrorString(WSAGetLastError()));
return false;
}
@@ -56,7 +66,7 @@ bool CNetCon::Shutdown(void)
int nError = ::WSACleanup();
if (nError != 0)
{
- spdlog::error("Failed to stop winsock via WSACleanup: ({})", NET_ErrorString(WSAGetLastError()));
+ spdlog::error("Failed to stop winsock via WSACleanup: ({:s})", NET_ErrorString(WSAGetLastError()));
return false;
}
return true;
@@ -71,7 +81,7 @@ void CNetCon::TermSetup(void)
HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE);
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
- CONSOLE_SCREEN_BUFFER_INFOEX sbInfoEx{};
+ CONSOLE_SCREEN_BUFFER_INFOEX sbInfoEx {};
COLORREF storedBG = sbInfoEx.ColorTable[0];
sbInfoEx.cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX);
@@ -101,22 +111,23 @@ void CNetCon::UserInput(void)
this->Disconnect();
return;
}
- size_t nPos = svInput.find(" ");
- if (!svInput.empty() && nPos > 0 && nPos < svInput.size() && nPos != svInput.size())
- {
- std::string svSecondArg = svInput.substr(nPos + 1);
- std::string svFirstArg = svInput;
- svFirstArg = svFirstArg.erase(svFirstArg.find(" "));
- if (strcmp(svFirstArg.c_str(), "PASS") == 0) // Auth with RCON server.
+ std::vector<std::string> vSubStrings = StringSplit(svInput, ' ', 2);
+ if (vSubStrings.size() > 1)
+ {
+ if (strcmp(vSubStrings[0].c_str(), "PASS") == 0) // Auth with RCON server.
{
- std::string svSerialized = this->Serialize(svSecondArg, "", cl_rcon::request_t::SERVERDATA_REQUEST_AUTH);
+ std::string svSerialized = this->Serialize(vSubStrings[1], "", cl_rcon::request_t::SERVERDATA_REQUEST_AUTH);
this->Send(svSerialized);
}
- else if (strcmp(svFirstArg.c_str(), "SET") == 0) // Set value query.
+ else if (strcmp(vSubStrings[0].c_str(), "SET") == 0) // Set value query.
{
- std::string svSerialized = this->Serialize(svFirstArg, svSecondArg, cl_rcon::request_t::SERVERDATA_REQUEST_SETVALUE);
- this->Send(svSerialized);
+ if (vSubStrings.size() > 2)
+ {
+ std::string svSerialized =
+ this->Serialize(vSubStrings[1], vSubStrings[2], cl_rcon::request_t::SERVERDATA_REQUEST_SETVALUE);
+ this->Send(svSerialized);
+ }
}
else // Execute command query.
{
@@ -124,7 +135,7 @@ void CNetCon::UserInput(void)
this->Send(svSerialized);
}
}
- else // Single arg command query.
+ else if (!svInput.empty()) // Single arg command query.
{
std::string svSerialized = this->Serialize(svInput.c_str(), "", cl_rcon::request_t::SERVERDATA_REQUEST_EXECCOMMAND);
this->Send(svSerialized);
@@ -132,16 +143,19 @@ void CNetCon::UserInput(void)
}
else // Setup connection from input.
{
- size_t nPos = svInput.find(" ");
- if (!svInput.empty() && nPos > 0 && nPos < svInput.size() && nPos != svInput.size())
+ if (!svInput.empty())
{
- std::string svInPort = svInput.substr(nPos + 1);
- std::string svInAdr = svInput.erase(svInput.find(" "));
-
- if (!this->Connect(svInAdr, svInPort))
+ std::string::size_type nPos = svInput.find(' ');
+ if (nPos > 0 && nPos < svInput.size() && nPos != svInput.size())
{
- m_abPromptConnect = true;
- return;
+ std::string svInPort = svInput.substr(nPos + 1);
+ std::string svInAdr = svInput.erase(svInput.find(' '));
+
+ if (!this->Connect(svInAdr, svInPort))
+ {
+ m_abPromptConnect = true;
+ return;
+ }
}
}
else // Initialize as [127.0.0.1]:37015.
@@ -180,7 +194,10 @@ void CNetCon::RunFrame(void)
// Purpose: checks if application should be terminated
// Output : true for termination, false otherwise
//-----------------------------------------------------------------------------
-bool CNetCon::ShouldQuit(void) const { return this->m_bQuitApplication; }
+bool CNetCon::ShouldQuit(void) const
+{
+ return this->m_bQuitApplication;
+}
//-----------------------------------------------------------------------------
// Purpose: connect to specified address and port
@@ -201,7 +218,7 @@ bool CNetCon::Connect(const std::string& svInAdr, const std::string& svInPort)
spdlog::warn("Failed to connect. Error: (SOCKET_ERROR). Verify IP and PORT.");
return false;
}
- spdlog::info("Connected to: {}", m_pNetAdr2->GetIPAndPort());
+ spdlog::info("Connected to: {:s}", m_pNetAdr2->GetIPAndPort());
m_abConnEstablished = true;
return true;
@@ -212,7 +229,7 @@ bool CNetCon::Connect(const std::string& svInAdr, const std::string& svInPort)
//-----------------------------------------------------------------------------
void CNetCon::Disconnect(void)
{
- ::closesocket(m_pSocket->GetAcceptedSocketHandle(0));
+ m_pSocket->CloseAcceptedSocket(0);
m_abPromptConnect = true;
m_abConnEstablished = false;
}
@@ -223,7 +240,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<uint8_t>(static_cast<int>(svMessage.size()) >> 24);
+ ssSendBuf << static_cast<uint8_t>(static_cast<int>(svMessage.size()) >> 16);
+ ssSendBuf << static_cast<uint8_t>(static_cast<int>(svMessage.size()) >> 8);
+ ssSendBuf << static_cast<uint8_t>(static_cast<int>(svMessage.size()));
+ ssSendBuf << svMessage;
+
+ int nSendResult = ::send(
+ m_pSocket->GetAcceptedSocketData(0)->m_hSocket, ssSendBuf.str().data(), static_cast<int>(ssSendBuf.str().size()), MSG_NOSIGNAL);
if (nSendResult == SOCKET_ERROR)
{
spdlog::warn("Failed to send message: (SOCKET_ERROR)");
@@ -235,10 +261,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;
@@ -252,13 +279,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();
@@ -267,50 +292,68 @@ void CNetCon::Recv(void)
}
if (nRecvLen < 0 && !m_pSocket->IsSocketBlocking())
{
+ spdlog::error("RCON Cmd: recv error ({:s})", 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
-// 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);
+ pData->m_RecvBuffer[pData->m_nPayloadRead++] = *pRecvBuf;
+
+ pRecvBuf++;
+ nRecvLen--;
+ }
+ if (pData->m_nPayloadRead == pData->m_nPayloadLen)
+ {
+ this->ProcessMessage(
+ this->Deserialize(std::string(reinterpret_cast<char*>(pData->m_RecvBuffer.data()), pData->m_nPayloadLen)));
+
+ pData->m_nPayloadLen = 0;
+ pData->m_nPayloadRead = 0;
}
- nCharsInRespondBuffer = 0;
- break;
}
+ else if (pData->m_nPayloadRead < sizeof(int)) // Read size field.
+ {
+ pData->m_RecvBuffer[pData->m_nPayloadRead++] = *pRecvBuf;
- default:
+ pRecvBuf++;
+ nRecvLen--;
+ }
+ else // Build prefix.
{
- if (nCharsInRespondBuffer < MAX_NETCONSOLE_INPUT_LEN - 1)
+ pData->m_nPayloadLen = static_cast<int>(
+ 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)
{
- szInputRespondBuffer[nCharsInRespondBuffer++] = *pszIn;
+ spdlog::error("RCON Cmd: sync error ({:d})", pData->m_nPayloadLen);
+ this->Disconnect(); // Out of sync (irrecoverable).
+
+ break;
+ }
+ else
+ {
+ pData->m_RecvBuffer.resize(pData->m_nPayloadLen);
}
- break;
- }
}
- pszIn++;
- nRecvLen--;
}
}
@@ -327,7 +370,7 @@ void CNetCon::ProcessMessage(const sv_rcon::response& sv_response) const
{
std::string svOut = sv_response.responsebuf();
svOut.erase(std::remove(svOut.begin(), svOut.end(), '\n'), svOut.end());
- spdlog::info(svOut.c_str());
+ spdlog::info(svOut);
break;
}
default:
@@ -366,7 +409,7 @@ std::string CNetCon::Serialize(const std::string& svReqBuf, const std::string& s
break;
}
}
- return cl_request.SerializeAsString().append("\r");
+ return cl_request.SerializeAsString();
}
//-----------------------------------------------------------------------------
@@ -377,7 +420,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<int>(svBuf.size()));
+ sv_response.ParseFromArray(svBuf.data(), static_cast<int>(svBuf.size()));
return sv_response;
}
@@ -417,4 +460,32 @@ int main(int argc, char* argv[])
}
return ERROR_SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// For splitting a string into substrings by delimiter.
+std::vector<std::string>
+StringSplit(std::string svInput, char cDelim, size_t nMax) // Move this to a dedicated string util file if one ever gets created.
+{
+ std::string svSubString;
+ std::vector<std::string> vSubStrings;
+
+ svInput = svInput + cDelim;
+
+ for (size_t i = 0; i < svInput.size(); i++)
+ {
+ if (i != (svInput.size() - 1) && vSubStrings.size() >= nMax || svInput[i] != cDelim)
+ {
+ svSubString += svInput[i];
+ }
+ else
+ {
+ if (svSubString.size() != 0)
+ {
+ vSubStrings.push_back(svSubString);
+ }
+ svSubString.clear();
+ }
+ }
+ return vSubStrings;
} \ No newline at end of file
diff --git a/NetConsole/netconsole.h b/NetConsole/netconsole.h
index dbd6eb2c..729c37b0 100644
--- a/NetConsole/netconsole.h
+++ b/NetConsole/netconsole.h
@@ -12,7 +12,8 @@ constexpr const char* NETCON_VERSION = "2.0.0.1";
class CNetCon
{
public:
- ~CNetCon();
+ CNetCon(void);
+ ~CNetCon(void);
bool Init(void);
bool Shutdown(void);
@@ -29,18 +30,21 @@ class CNetCon
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();
-
- bool m_bInitialized = false;
- bool m_bQuitApplication = false;
- std::atomic<bool> m_abPromptConnect{true};
- std::atomic<bool> m_abConnEstablished{false};
-}; \ No newline at end of file
+ CNetAdr2* m_pNetAdr2;
+ CSocketCreator* m_pSocket;
+
+ bool m_bInitialized;
+ bool m_bNoColor;
+ bool m_bQuitApplication;
+ std::atomic<bool> m_abPromptConnect;
+ std::atomic<bool> m_abConnEstablished;
+};
+
+std::vector<std::string> StringSplit(std::string svInput, char cDelim, size_t nMax); \ No newline at end of file
diff --git a/NorthstarDedicatedTest/cl_rcon.cpp b/NorthstarDedicatedTest/cl_rcon.cpp
index 839f40bd..8278fa03 100644
--- a/NorthstarDedicatedTest/cl_rcon.cpp
+++ b/NorthstarDedicatedTest/cl_rcon.cpp
@@ -12,12 +12,22 @@
#include "sv_rcon.pb.h"
#include "cl_rcon.pb.h"
#include "cl_rcon.h"
+#include "net.h"
#include "igameserverdata.h"
//-----------------------------------------------------------------------------
-// Purpose: destructor
+// Purpose:
//-----------------------------------------------------------------------------
-CRConClient::~CRConClient()
+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;
@@ -28,6 +38,10 @@ CRConClient::~CRConClient()
//-----------------------------------------------------------------------------
void CRConClient::Init(void)
{
+ if (!m_bInitialized)
+ {
+ this->SetPassword(CVar_rcon_password->GetString());
+ }
m_bInitialized = true;
}
@@ -43,6 +57,24 @@ void CRConClient::Shutdown(void)
}
//-----------------------------------------------------------------------------
+// Purpose: changes the password
+// Input : *pszPassword -
+// Output : true on success, false otherwise
+//-----------------------------------------------------------------------------
+bool CRConClient::SetPassword(const char* pszPassword)
+{
+ if (std::strlen(pszPassword) < 8)
+ {
+ if (std::strlen(pszPassword) > 0)
+ {
+ spdlog::info("Remote server access requires a password of at least 8 characters");
+ }
+ return false;
+ }
+ return true;
+}
+
+//-----------------------------------------------------------------------------
// Purpose: client rcon main processing loop
//-----------------------------------------------------------------------------
void CRConClient::RunFrame(void)
@@ -67,10 +99,10 @@ bool CRConClient::Connect(void)
if (m_pSocket->ConnectSocket(*m_pNetAdr2, true) == SOCKET_ERROR)
{
- spdlog::info("Connection to RCON server '{}' failed: (SOCKET_ERROR)", m_pNetAdr2->GetIPAndPort());
+ spdlog::info("Connection to RCON server '{:s}' failed: (SOCKET_ERROR)", m_pNetAdr2->GetIPAndPort());
return false;
}
- spdlog::info("Connected to: {}", m_pNetAdr2->GetIPAndPort().c_str());
+ spdlog::info("Connected to: {:s}", m_pNetAdr2->GetIPAndPort());
m_bConnEstablished = true;
return true;
@@ -92,10 +124,10 @@ bool CRConClient::Connect(const std::string& svInAdr, const std::string& svInPor
if (m_pSocket->ConnectSocket(*m_pNetAdr2, true) == SOCKET_ERROR)
{
- spdlog::info("Connection to RCON server '{}' failed: (SOCKET_ERROR)", m_pNetAdr2->GetIPAndPort());
+ spdlog::info("Connection to RCON server '{:s}' failed: (SOCKET_ERROR)", m_pNetAdr2->GetIPAndPort());
return false;
}
- spdlog::info("Connected to: {}", m_pNetAdr2->GetIPAndPort().c_str());
+ spdlog::info("Connected to: {:s}", m_pNetAdr2->GetIPAndPort());
m_bConnEstablished = true;
return true;
@@ -106,7 +138,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;
}
@@ -116,7 +148,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<uint8_t>(static_cast<int>(svMessage.size()) >> 24);
+ ssSendBuf << static_cast<uint8_t>(static_cast<int>(svMessage.size()) >> 16);
+ ssSendBuf << static_cast<uint8_t>(static_cast<int>(svMessage.size()) >> 8);
+ ssSendBuf << static_cast<uint8_t>(static_cast<int>(svMessage.size()));
+ ssSendBuf << svMessage;
+
+ int nSendResult = ::send(
+ m_pSocket->GetAcceptedSocketData(0)->m_hSocket, ssSendBuf.str().data(), static_cast<int>(ssSendBuf.str().size()), MSG_NOSIGNAL);
if (nSendResult == SOCKET_ERROR)
{
spdlog::info("Failed to send RCON message: (SOCKET_ERROR)");
@@ -128,10 +169,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;
@@ -145,13 +187,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();
@@ -160,50 +200,68 @@ void CRConClient::Recv(void)
}
if (nRecvLen < 0 && !m_pSocket->IsSocketBlocking())
{
+ spdlog::error("RCON Cmd: recv error ({:s})", 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);
+ pData->m_RecvBuffer[pData->m_nPayloadRead++] = *pRecvBuf;
+
+ pRecvBuf++;
+ nRecvLen--;
+ }
+ if (pData->m_nPayloadRead == pData->m_nPayloadLen)
+ {
+ this->ProcessMessage(
+ this->Deserialize(std::string(reinterpret_cast<char*>(pData->m_RecvBuffer.data()), pData->m_nPayloadLen)));
+
+ pData->m_nPayloadLen = 0;
+ pData->m_nPayloadRead = 0;
}
- nCharsInRespondBuffer = 0;
- break;
}
+ else if (pData->m_nPayloadRead < sizeof(int)) // Read size field.
+ {
+ pData->m_RecvBuffer[pData->m_nPayloadRead++] = *pRecvBuf;
- default:
+ pRecvBuf++;
+ nRecvLen--;
+ }
+ else // Build prefix.
{
- if (nCharsInRespondBuffer < MAX_NETCONSOLE_INPUT_LEN - 1)
+ pData->m_nPayloadLen = static_cast<int>(
+ 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)
{
- szInputRespondBuffer[nCharsInRespondBuffer++] = *pszIn;
+ spdlog::error("RCON Cmd: sync error ({:d})", pData->m_nPayloadLen);
+ this->Disconnect(); // Out of sync (irrecoverable).
+
+ break;
+ }
+ else
+ {
+ pData->m_RecvBuffer.resize(pData->m_nPayloadLen);
}
- break;
- }
}
- pszIn++;
- nRecvLen--;
}
}
@@ -220,14 +278,14 @@ void CRConClient::ProcessMessage(const sv_rcon::response& sv_response) const
case sv_rcon::response_t::SERVERDATA_RESPONSE_AUTH:
{
svOut.erase(std::remove(svOut.begin(), svOut.end(), '\n'), svOut.end());
- spdlog::info("{}", svOut.c_str());
+ spdlog::info("{:s}", svOut);
break;
}
case sv_rcon::response_t::SERVERDATA_RESPONSE_CONSOLE_LOG:
{
// !TODO: Network the enum to differentiate script/engine logs.
svOut.erase(std::remove(svOut.begin(), svOut.end(), '\n'), svOut.end());
- spdlog::info("{}", svOut.c_str());
+ spdlog::info("{:s}", svOut);
break;
}
default:
@@ -266,7 +324,7 @@ std::string CRConClient::Serialize(const std::string& svReqBuf, const std::strin
break;
}
}
- return cl_request.SerializeAsString().append("\r");
+ return cl_request.SerializeAsString();
}
//-----------------------------------------------------------------------------
@@ -277,7 +335,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<int>(svBuf.size()));
+ sv_response.ParseFromArray(svBuf.data(), static_cast<int>(svBuf.size()));
return sv_response;
}
@@ -301,3 +359,7 @@ bool CRConClient::IsConnected(void) const
}
///////////////////////////////////////////////////////////////////////////////
CRConClient* g_pRConClient = new CRConClient();
+CRConClient* RCONClient()
+{
+ return g_pRConClient;
+} \ No newline at end of file
diff --git a/NorthstarDedicatedTest/cl_rcon.h b/NorthstarDedicatedTest/cl_rcon.h
index 1b9e2dec..aedc579e 100644
--- a/NorthstarDedicatedTest/cl_rcon.h
+++ b/NorthstarDedicatedTest/cl_rcon.h
@@ -7,12 +7,13 @@
class CRConClient
{
public:
- CRConClient(void) {};
+ CRConClient(void);
~CRConClient(void);
void Init(void);
void Shutdown(void);
+ bool SetPassword(const char* pszPassword);
void RunFrame(void);
bool Connect(void);
@@ -22,7 +23,7 @@ class CRConClient
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;
@@ -32,10 +33,11 @@ class CRConClient
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;
};
-extern CRConClient* g_pRConClient; \ No newline at end of file
+extern CRConClient* g_pRConClient;
+CRConClient* RCONClient(); \ No newline at end of file
diff --git a/NorthstarDedicatedTest/host_state.cpp b/NorthstarDedicatedTest/host_state.cpp
index 11138479..39fb931d 100644
--- a/NorthstarDedicatedTest/host_state.cpp
+++ b/NorthstarDedicatedTest/host_state.cpp
@@ -19,24 +19,24 @@ void CHostState__FrameUpdateHook(CHostState* thisptr)
{
Cbuf_AddText(Cbuf_GetCurrentPlayer(), "exec rcon_server", cmd_source_t::kCommandSrcCode);
Cbuf_Execute();
- g_pRConServer->Init();
+ RCONServer()->Init();
}
else
{
Cbuf_AddText(Cbuf_GetCurrentPlayer(), "exec rcon_client", cmd_source_t::kCommandSrcCode);
Cbuf_Execute();
- g_pRConClient->Init();
+ RCONClient()->Init();
}
bInitialized = true;
}
if (IsDedicated())
{
- g_pRConServer->RunFrame();
+ RCONServer()->RunFrame();
}
else
{
- g_pRConClient->RunFrame();
+ RCONClient()->RunFrame();
}
CHostState__FrameUpdate(thisptr);
}
diff --git a/NorthstarDedicatedTest/igameserverdata.h b/NorthstarDedicatedTest/igameserverdata.h
index 9cc6c759..0cd96080 100644
--- a/NorthstarDedicatedTest/igameserverdata.h
+++ b/NorthstarDedicatedTest/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<uint8_t> m_RecvBuffer;
CConnectedNetConsoleData(SocketHandle_t hSocket = -1)
{
- m_nCharsInCommandBuffer = 0;
- m_bAuthorized = 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/NorthstarDedicatedTest/logging.cpp b/NorthstarDedicatedTest/logging.cpp
index 7b9f34c1..c4565d69 100644
--- a/NorthstarDedicatedTest/logging.cpp
+++ b/NorthstarDedicatedTest/logging.cpp
@@ -380,7 +380,7 @@ void EngineSpewFuncHook(void* engineServer, SpewType_t type, const char* format,
{
char sendbuf[2048] {};
snprintf(sendbuf, sizeof(sendbuf), "[SERVER %s] %s", typeStr, formatted);
- g_pRConServer->Send(sendbuf);
+ RCONServer()->Send(RCONServer()->Serialize(sendbuf, "", sv_rcon::response_t::SERVERDATA_RESPONSE_CONSOLE_LOG));
}
}
}
@@ -407,7 +407,7 @@ void Status_ConMsg_Hook(const char* text, ...)
{
if (CVar_sv_rcon_sendlogs->GetBool())
{
- g_pRConServer->Send(formatted);
+ RCONServer()->Send(RCONServer()->Serialize(formatted, "", sv_rcon::response_t::SERVERDATA_RESPONSE_CONSOLE_LOG));
}
}
}
diff --git a/NorthstarDedicatedTest/rcon_shared.cpp b/NorthstarDedicatedTest/rcon_shared.cpp
index c4dc4c6c..fc220cb2 100644
--- a/NorthstarDedicatedTest/rcon_shared.cpp
+++ b/NorthstarDedicatedTest/rcon_shared.cpp
@@ -32,42 +32,41 @@ void _RCON_CmdQuery_f(const CCommand& args)
if (args.ArgC() < 2)
{
- if (g_pRConClient->IsInitialized() && !g_pRConClient->IsConnected())
+ if (RCONClient()->IsInitialized() && !RCONClient()->IsConnected())
{
- g_pRConClient->Connect();
+ RCONClient()->Connect();
}
}
else
{
- if (!g_pRConClient->IsInitialized())
+ if (!RCONClient()->IsInitialized())
{
spdlog::warn("Failed to issue command to RCON server: uninitialized\n");
return;
}
- else if (g_pRConClient->IsConnected())
+ else if (RCONClient()->IsConnected())
{
if (strcmp(args.Arg(1), "PASS") == 0) // Auth with RCON server using rcon_password ConVar value.
{
std::string svCmdQuery;
if (args.ArgC() > 2)
{
- svCmdQuery = g_pRConClient->Serialize(args.Arg(2), "", cl_rcon::request_t::SERVERDATA_REQUEST_AUTH);
+ svCmdQuery = RCONClient()->Serialize(args.Arg(2), "", cl_rcon::request_t::SERVERDATA_REQUEST_AUTH);
}
else // Use 'rcon_password' ConVar as password.
{
- svCmdQuery = g_pRConClient->Serialize(CVar_rcon_password->GetString(), "", cl_rcon::request_t::SERVERDATA_REQUEST_AUTH);
+ svCmdQuery = RCONClient()->Serialize(CVar_rcon_password->GetString(), "", cl_rcon::request_t::SERVERDATA_REQUEST_AUTH);
}
- g_pRConClient->Send(svCmdQuery);
+ RCONClient()->Send(svCmdQuery);
return;
}
else if (strcmp(args.Arg(1), "disconnect") == 0) // Disconnect from RCON server.
{
- g_pRConClient->Disconnect();
+ RCONClient()->Disconnect();
return;
}
- std::string svCmdQuery = g_pRConClient->Serialize(args.ArgS(), "", cl_rcon::request_t::SERVERDATA_REQUEST_EXECCOMMAND);
- g_pRConClient->Send(svCmdQuery);
+ RCONClient()->Send(RCONClient()->Serialize(args.ArgS(), "", cl_rcon::request_t::SERVERDATA_REQUEST_EXECCOMMAND));
return;
}
else
@@ -91,9 +90,9 @@ void _RCON_Disconnect_f(const CCommand& args)
{
return;
}
- if (g_pRConClient->IsConnected())
+ if (RCONClient()->IsConnected())
{
- g_pRConClient->Disconnect();
+ RCONClient()->Disconnect();
spdlog::info("User closed RCON connection");
}
}
diff --git a/NorthstarDedicatedTest/socketcreator.cpp b/NorthstarDedicatedTest/socketcreator.cpp
index 03b5b76f..d500eace 100644
--- a/NorthstarDedicatedTest/socketcreator.cpp
+++ b/NorthstarDedicatedTest/socketcreator.cpp
@@ -254,7 +254,7 @@ int CSocketCreator::OnSocketAccepted(SocketHandle_t hSocket, CNetAdr2 netAdr2)
m_hAcceptedSockets.push_back(pNewEntry);
- int nIndex = (int)m_hAcceptedSockets.size() - 1;
+ int nIndex = static_cast<int>(m_hAcceptedSockets.size()) - 1;
return nIndex;
}
@@ -271,6 +271,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);
}
@@ -284,12 +286,15 @@ void CSocketCreator::CloseAllAcceptedSockets(void)
{
AcceptedSocket_t& connected = m_hAcceptedSockets[i];
::closesocket(connected.m_hSocket);
+
+ delete connected.m_pData;
}
m_hAcceptedSockets.clear();
}
//-----------------------------------------------------------------------------
// Purpose: returns true if the listening socket is created and listening
+// Output : bool
//-----------------------------------------------------------------------------
bool CSocketCreator::IsListening(void) const
{
@@ -298,6 +303,7 @@ bool CSocketCreator::IsListening(void) const
//-----------------------------------------------------------------------------
// Purpose: returns true if the socket would block because of the last socket command
+// Output : bool
//-----------------------------------------------------------------------------
bool CSocketCreator::IsSocketBlocking(void) const
{
@@ -306,6 +312,7 @@ bool CSocketCreator::IsSocketBlocking(void) const
//-----------------------------------------------------------------------------
// Purpose: returns accepted socket count
+// Output : int
//-----------------------------------------------------------------------------
int CSocketCreator::GetAcceptedSocketCount(void) const
{
@@ -314,6 +321,8 @@ int CSocketCreator::GetAcceptedSocketCount(void) const
//-----------------------------------------------------------------------------
// Purpose: returns accepted socket handle
+// Input : nIndex -
+// Output : SocketHandle_t
//-----------------------------------------------------------------------------
SocketHandle_t CSocketCreator::GetAcceptedSocketHandle(int nIndex) const
{
@@ -322,6 +331,8 @@ SocketHandle_t CSocketCreator::GetAcceptedSocketHandle(int nIndex) const
//-----------------------------------------------------------------------------
// Purpose: returns accepted socket address
+// Input : nIndex -
+// Output : const CNetAdr2&
//-----------------------------------------------------------------------------
const CNetAdr2& CSocketCreator::GetAcceptedSocketAddress(int nIndex) const
{
@@ -330,6 +341,8 @@ const CNetAdr2& CSocketCreator::GetAcceptedSocketAddress(int nIndex) const
//-----------------------------------------------------------------------------
// Purpose: returns accepted socket data
+// Input : nIndex -
+// Output : CConnectedNetConsoleData*
//-----------------------------------------------------------------------------
CConnectedNetConsoleData* CSocketCreator::GetAcceptedSocketData(int nIndex) const
{
diff --git a/NorthstarDedicatedTest/squirrel.cpp b/NorthstarDedicatedTest/squirrel.cpp
index cba0d880..4ff7ee9d 100644
--- a/NorthstarDedicatedTest/squirrel.cpp
+++ b/NorthstarDedicatedTest/squirrel.cpp
@@ -276,7 +276,7 @@ template <ScriptContext context> SQInteger SQPrintHook(void* sqvm, char* fmt, ..
char sendbuf[1024] {};
snprintf(sendbuf, sizeof(sendbuf), "[%s SCRIPT] %s", GetContextName(context), buf);
- g_pRConServer->Send(sendbuf);
+ RCONServer()->Send(RCONServer()->Serialize(sendbuf, "", sv_rcon::response_t::SERVERDATA_RESPONSE_CONSOLE_LOG));
}
}
}
@@ -355,7 +355,7 @@ template <ScriptContext context> void ScriptCompileErrorHook(void* sqvm, const c
{
if (CVar_sv_rcon_sendlogs->GetBool())
{
- g_pRConServer->Send(buffer);
+ RCONServer()->Send(RCONServer()->Serialize(buffer, "", sv_rcon::response_t::SERVERDATA_RESPONSE_CONSOLE_LOG));
}
}
diff --git a/NorthstarDedicatedTest/sv_rcon.cpp b/NorthstarDedicatedTest/sv_rcon.cpp
index 527e5f5e..ef855af5 100644
--- a/NorthstarDedicatedTest/sv_rcon.cpp
+++ b/NorthstarDedicatedTest/sv_rcon.cpp
@@ -8,6 +8,7 @@
#include "concommand.h"
#include "cvar.h"
#include "convar.h"
+#include "net.h"
#include "NetAdr2.h"
#include "socketcreator.h"
#include "rcon_shared.h"
@@ -19,34 +20,39 @@
#include "igameserverdata.h"
//-----------------------------------------------------------------------------
-// Purpose: destructor
+// Purpose:
//-----------------------------------------------------------------------------
-CRConServer::~CRConServer()
+CRConServer::CRConServer(void) : m_bInitialized(false), m_nConnIndex(0)
{
- delete m_pNetAdr2;
- delete m_pSocket;
+ m_pAdr2 = new CNetAdr2();
+ m_pSocket = new CSocketCreator();
}
//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CRConServer::~CRConServer(void)
+{
+ delete m_pAdr2;
+ delete m_pSocket;
+}
+//-----------------------------------------------------------------------------
// Purpose: NETCON systems init
//-----------------------------------------------------------------------------
void CRConServer::Init(void)
{
- if (std::strlen(CVar_rcon_password->GetString()) < 8)
+ if (!m_bInitialized)
{
- if (std::strlen(CVar_rcon_password->GetString()) > 0)
+ if (!this->SetPassword(CVar_rcon_password->GetString()))
{
- spdlog::info("Remote server access requires a password of at least 8 characters");
+ return;
}
- this->Shutdown();
- return;
}
static ConVar* hostport = g_pCVar->FindVar("hostport");
- m_pNetAdr2->SetIPAndPort(CVar_rcon_address->GetString(), hostport->GetString());
- m_pSocket->CreateListenSocket(*m_pNetAdr2, false);
- m_svPasswordHash = sha256(CVar_rcon_password->GetString());
+ m_pAdr2->SetIPAndPort(CVar_rcon_address->GetString(), hostport->GetString());
+ m_pSocket->CreateListenSocket(*m_pAdr2, false);
spdlog::info("Remote server access initialized");
m_bInitialized = true;
@@ -93,9 +99,35 @@ void CRConServer::Think(void)
{
if (!m_pSocket->IsListening())
{
- m_pSocket->CreateListenSocket(*m_pNetAdr2, false);
+ m_pSocket->CreateListenSocket(*m_pAdr2, false);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: changes the password
+// Input : *pszPassword -
+// Output : true on success, false otherwise
+//-----------------------------------------------------------------------------
+bool CRConServer::SetPassword(const char* pszPassword)
+{
+ m_bInitialized = false;
+ m_pSocket->CloseAllAcceptedSockets();
+
+ if (std::strlen(pszPassword) < 8)
+ {
+ if (std::strlen(pszPassword) > 0)
+ {
+ spdlog::warn("Remote server access requires a password of at least 8 characters");
}
+ this->Shutdown();
+ return false;
}
+ m_svPasswordHash = sha256(pszPassword);
+ spdlog::info("Password hash ('{:s}')", m_svPasswordHash);
+
+ m_bInitialized = true;
+ return true;
}
//-----------------------------------------------------------------------------
@@ -112,32 +144,57 @@ 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<uint8_t>(static_cast<int>(svMessage.size()) >> 24);
+ ssSendBuf << static_cast<uint8_t>(static_cast<int>(svMessage.size()) >> 16);
+ ssSendBuf << static_cast<uint8_t>(static_cast<int>(svMessage.size()) >> 8);
+ ssSendBuf << static_cast<uint8_t>(static_cast<int>(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<int>(svFinal.size()), MSG_NOSIGNAL);
+ CConnectedNetConsoleData* pData = m_pSocket->GetAcceptedSocketData(i);
+ if (pData->m_bAuthorized)
+ {
+ ::send(pData->m_hSocket, ssSendBuf.str().data(), static_cast<int>(ssSendBuf.str().size()), MSG_NOSIGNAL);
+ }
}
}
}
//-----------------------------------------------------------------------------
+// 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<uint8_t>(static_cast<int>(svMessage.size()) >> 24);
+ ssSendBuf << static_cast<uint8_t>(static_cast<int>(svMessage.size()) >> 16);
+ ssSendBuf << static_cast<uint8_t>(static_cast<int>(svMessage.size()) >> 8);
+ ssSendBuf << static_cast<uint8_t>(static_cast<int>(svMessage.size()));
+ ssSendBuf << svMessage;
+
+ ::send(hSocket, ssSendBuf.str().data(), static_cast<int>(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--)
{
@@ -145,13 +202,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<int>(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;
@@ -168,9 +224,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();
@@ -178,6 +232,7 @@ void CRConServer::Recv(void)
}
if (nRecvLen < 0 && !m_pSocket->IsSocketBlocking())
{
+ spdlog::error("RCON Cmd: recv error ({:s})", NET_ErrorString(WSAGetLastError()));
break;
}
@@ -219,7 +274,7 @@ std::string CRConServer::Serialize(const std::string& svRspBuf, const std::strin
break;
}
}
- return sv_response.SerializeAsString().append("\r");
+ return sv_response.SerializeAsString();
}
//-----------------------------------------------------------------------------
@@ -230,7 +285,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<int>(svBuf.size()));
+ cl_request.ParseFromArray(svBuf.data(), static_cast<int>(svBuf.size()));
return cl_request;
}
@@ -239,9 +294,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)
{
@@ -249,27 +301,25 @@ 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<int>(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.
{
CNetAdr2 netAdr2 = m_pSocket->GetAcceptedSocketAddress(m_nConnIndex);
if (CVar_sv_rcon_debug->GetBool())
{
- spdlog::info("Bad RCON password attempt from '{}'", netAdr2.GetIPAndPort().c_str());
+ spdlog::info("Bad RCON password attempt from '{:s}'", netAdr2.GetIPAndPort());
}
- std::string svWrongPass = this->Serialize(s_pszWrongPwMessage, "", sv_rcon::response_t::SERVERDATA_RESPONSE_AUTH);
- ::send(pData->m_hSocket, svWrongPass.c_str(), static_cast<int>(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;
@@ -289,8 +339,8 @@ bool CRConServer::Comparator(std::string svPassword) const
if (CVar_sv_rcon_debug->GetBool())
{
spdlog::info("+---------------------------------------------------------------------------+");
- spdlog::info("] Server: '{}'[", m_svPasswordHash.c_str());
- spdlog::info("] Client: '{}'[", svPassword.c_str());
+ spdlog::info("] Server: '{:s}'[", m_svPasswordHash);
+ spdlog::info("] Client: '{:s}'[", svPassword);
spdlog::info("+---------------------------------------------------------------------------+");
}
if (memcmp(svPassword.c_str(), m_svPasswordHash.c_str(), SHA256::DIGEST_SIZE) == 0)
@@ -301,38 +351,67 @@ 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;
+ if (pData->m_nPayloadRead == pData->m_nPayloadLen)
+ {
+ this->ProcessMessage(
+ this->Deserialize(std::string(reinterpret_cast<char*>(pData->m_RecvBuffer.data()), pData->m_nPayloadLen)));
+
+ pData->m_nPayloadLen = 0;
+ pData->m_nPayloadRead = 0;
+ }
+ }
+ else if (pData->m_nPayloadRead < sizeof(int)) // Read size field.
+ {
+ pData->m_RecvBuffer[pData->m_nPayloadRead++] = *pRecvBuf;
+
+ pRecvBuf++;
+ nRecvLen--;
}
- default:
+ else // Build prefix.
{
- if (pData->m_nCharsInCommandBuffer < MAX_NETCONSOLE_INPUT_LEN - 1)
+ pData->m_nPayloadLen = static_cast<int>(
+ 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)
{
- pData->m_pszInputCommandBuffer[pData->m_nCharsInCommandBuffer++] = *pszIn;
+ spdlog::error("RCON Cmd: sync error ({:d})", pData->m_nPayloadLen);
+ this->CloseConnection(); // Out of sync (irrecoverable).
+
+ break;
+ }
+ else
+ {
+ pData->m_RecvBuffer.resize(pData->m_nPayloadLen);
}
- break;
- }
}
- pszIn++;
- nRecvLen--;
}
}
@@ -347,8 +426,7 @@ void CRConServer::ProcessMessage(const cl_rcon::request& cl_request)
if (!pData->m_bAuthorized && 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<int>(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++;
@@ -362,12 +440,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;
}
@@ -389,15 +473,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 "<val>".
+ else if (!bConVar) // Execute command with "<val>".
{
Cbuf_AddText(Cbuf_GetCurrentPlayer(), cl_request.requestbuf().c_str(), cmd_source_t::kCommandSrcCode);
Cbuf_Execute();
@@ -435,7 +520,7 @@ bool CRConServer::CheckForBan(CConnectedNetConsoleData* pData)
return false;
}
- spdlog::info("Banned '{}' for RCON hacking attempts", netAdr2.GetIPAndPort().c_str());
+ spdlog::info("Banned '{:s}' for RCON hacking attempts", netAdr2.GetIPAndPort());
m_vBannedAddress.push_back(netAdr2.GetIP(true));
return true;
}
@@ -452,7 +537,7 @@ void CRConServer::CloseConnection(void) // NETMGR
{
// Inform server owner when authenticated connection has been closed.
CNetAdr2 netAdr2 = m_pSocket->GetAcceptedSocketAddress(m_nConnIndex);
- spdlog::info("Net console '{}' closed RCON connection", netAdr2.GetIPAndPort().c_str());
+ spdlog::info("Net console '{:s}' closed RCON connection", netAdr2.GetIPAndPort());
}
m_pSocket->CloseAcceptedSocket(m_nConnIndex);
}
@@ -463,7 +548,6 @@ void CRConServer::CloseConnection(void) // NETMGR
void CRConServer::CloseNonAuthConnection(void)
{
int nCount = m_pSocket->GetAcceptedSocketCount();
-
for (int i = nCount - 1; i >= 0; i--)
{
CConnectedNetConsoleData* pData = m_pSocket->GetAcceptedSocketData(i);
@@ -476,3 +560,7 @@ void CRConServer::CloseNonAuthConnection(void)
}
///////////////////////////////////////////////////////////////////////////////
CRConServer* g_pRConServer = new CRConServer();
+CRConServer* RCONServer()
+{
+ return g_pRConServer;
+}
diff --git a/NorthstarDedicatedTest/sv_rcon.h b/NorthstarDedicatedTest/sv_rcon.h
index ec762d25..8d89c615 100644
--- a/NorthstarDedicatedTest/sv_rcon.h
+++ b/NorthstarDedicatedTest/sv_rcon.h
@@ -5,23 +5,27 @@
#include "cl_rcon.pb.h"
#include "igameserverdata.h"
-constexpr char s_pszNoAuthMessage[] = "This server is password protected for console access. Must send 'PASS <password>' 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 <password>' 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();
void Init(void);
void Shutdown(void);
+ bool SetPassword(const char* pszPassword);
void Think(void);
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,18 +37,19 @@ class CRConServer
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);
void CloseNonAuthConnection(void);
private:
- bool m_bInitialized = false;
- int m_nConnIndex = 0;
- CNetAdr2* m_pNetAdr2 = new CNetAdr2();
- CSocketCreator* m_pSocket = new CSocketCreator();
+ bool m_bInitialized;
+ int m_nConnIndex;
+ CNetAdr2* m_pAdr2;
+ CSocketCreator* m_pSocket;
std::vector<std::string> m_vBannedAddress;
std::string m_svPasswordHash;
};
-extern CRConServer* g_pRConServer; \ No newline at end of file
+extern CRConServer* g_pRConServer;
+CRConServer* RCONServer(); \ No newline at end of file