diff --git a/rehlds/engine/host_cmd.cpp b/rehlds/engine/host_cmd.cpp index c72d1b5..a666016 100644 --- a/rehlds/engine/host_cmd.cpp +++ b/rehlds/engine/host_cmd.cpp @@ -2426,7 +2426,6 @@ void Host_Changelevel2_f(void) void Host_Version_f(void) { Con_Printf("Protocol version %i\nExe version %s (%s)\n", PROTOCOL_VERSION, gpszVersionString, gpszProductString); - Con_Printf("ReHLDS API version %i.%i\n", REHLDS_API_VERSION_MAJOR, REHLDS_API_VERSION_MINOR); #ifdef REHLDS_FIXES Con_Printf("Exe build: " __TIME__ " " __DATE__ " (%i)\n", build_number()); #else // REHLDS_FIXES @@ -2436,6 +2435,7 @@ void Host_Version_f(void) Con_Printf("Exe build: 10:03:21 Aug 8 2013 (%i)\n", build_number()); #endif // _WIN32 #endif // REHLDS_FIXES + Con_Printf("ReHLDS API version %i.%i\n", REHLDS_API_VERSION_MAJOR, REHLDS_API_VERSION_MINOR); } /* <3d516> ../engine/host_cmd.c:3382 */ diff --git a/rehlds/engine/net_ws.cpp b/rehlds/engine/net_ws.cpp index c1f124e..f72cd2a 100644 --- a/rehlds/engine/net_ws.cpp +++ b/rehlds/engine/net_ws.cpp @@ -314,7 +314,7 @@ qboolean NET_IsReservedAdr(netadr_t& a) } /* ../engine/net_ws.c:332 */ -qboolean NET_CompareBaseAdr(netadr_t& a, netadr_t& b) +qboolean NET_CompareBaseAdr(const netadr_t& a, const netadr_t& b) { if (a.type != b.type) { diff --git a/rehlds/engine/net_ws.h b/rehlds/engine/net_ws.h index ded79d0..2af6da9 100644 --- a/rehlds/engine/net_ws.h +++ b/rehlds/engine/net_ws.h @@ -234,7 +234,7 @@ NOXREF short unsigned int NET_HostToNetShort(short unsigned int us_in); qboolean NET_CompareAdr(netadr_t& a, netadr_t& b); qboolean NET_CompareClassBAdr(netadr_t& a, netadr_t& b); qboolean NET_IsReservedAdr(netadr_t& a); -qboolean NET_CompareBaseAdr(netadr_t& a, netadr_t& b); +qboolean NET_CompareBaseAdr(const netadr_t& a, const netadr_t& b); char *NET_AdrToString(const netadr_t& a); char *NET_BaseAdrToString(netadr_t& a); qboolean NET_StringToSockaddr(const char *s, struct sockaddr *sadr); diff --git a/rehlds/engine/server.h b/rehlds/engine/server.h index 559ef22..71820b4 100644 --- a/rehlds/engine/server.h +++ b/rehlds/engine/server.h @@ -652,7 +652,7 @@ int SV_FindEmptySlot(netadr_t *adr, int *pslot, client_t ** ppClient); void SV_ConnectClient(void); void SV_ConnectClient_internal(void); void SVC_Ping(void); -int SV_GetChallenge(netadr_t& adr); +int SV_GetChallenge(const netadr_t& adr); void SVC_GetChallenge(void); void SVC_ServiceChallenge(void); void SV_ResetModInfo(void); diff --git a/rehlds/engine/sv_main.cpp b/rehlds/engine/sv_main.cpp index ef4feff..0bdefc7 100644 --- a/rehlds/engine/sv_main.cpp +++ b/rehlds/engine/sv_main.cpp @@ -194,7 +194,11 @@ cvar_t max_queries_window = { "max_queries_window", "60", FCVAR_SERVER | FCVAR_P cvar_t sv_logblocks = { "sv_logblocks", "0", FCVAR_SERVER, 0.0f, NULL }; cvar_t sv_downloadurl = { "sv_downloadurl", "", FCVAR_PROTECTED, 0.0f, NULL }; cvar_t sv_allow_dlfile = { "sv_allow_dlfile", "1", 0, 0.0f, NULL }; -cvar_t sv_version = { "sv_version", "", 0, 0.0f, NULL }; +#ifdef REHLDS_FIXES +cvar_t sv_version = { "sv_version", "", FCVAR_SERVER, 0.0f, NULL }; +#else +cvar_t sv_version = {"sv_version", "", 0, 0.0f, NULL}; +#endif cvar_t sv_rcon_minfailures = { "sv_rcon_minfailures", "5", 0, 0.0f, NULL }; cvar_t sv_rcon_maxfailures = { "sv_rcon_maxfailures", "10", 0, 0.0f, NULL }; @@ -2418,7 +2422,7 @@ void SVC_Ping(void) NET_SendPacket(NS_SERVER, sizeof(data), data, net_from); } -int SV_GetChallenge(netadr_t& adr) +int SV_GetChallenge(const netadr_t& adr) { int i; #ifndef REHLDS_OPT_PEDANTIC diff --git a/rehlds/engine/sv_user.cpp b/rehlds/engine/sv_user.cpp index 450570e..51ee09f 100644 --- a/rehlds/engine/sv_user.cpp +++ b/rehlds/engine/sv_user.cpp @@ -1404,12 +1404,23 @@ void SV_RestoreMove(client_t *_host_client) /* ../engine/sv_user.c:1736 */ void SV_ParseStringCommand(client_t *pSenderClient) { + //check string commands rate for this player +#ifdef REHLDS_FIXES + g_StringCommandsRateLimiter.StringCommandIssued(pSenderClient - g_psvs.clients); + + if (!pSenderClient->active) { + return; //return if player was kicked + } +#endif + char *s = MSG_ReadString(); int ret = SV_ValidateClientCommand(s); switch (ret) { case 0: +#ifndef REHLDS_OPT_PEDANTIC if (Q_strlen(s) > 127) +#endif { s[127] = 0; } diff --git a/rehlds/public/rehlds/rehlds_api.h b/rehlds/public/rehlds/rehlds_api.h index 675d84d..f8ac00f 100644 --- a/rehlds/public/rehlds/rehlds_api.h +++ b/rehlds/public/rehlds/rehlds_api.h @@ -210,7 +210,7 @@ struct RehldsFuncs_t { void(*MSG_EndBitWriting)(sizebuf_t *buf); void*(*SZ_GetSpace)(sizebuf_t *buf, int length); cvar_t*(*GetCvarVars)(); - int (*SV_GetChallenge)(netadr_t& adr); + int (*SV_GetChallenge)(const netadr_t& adr); }; class IRehldsApi { diff --git a/rehlds/rehlds/rehlds_security.cpp b/rehlds/rehlds/rehlds_security.cpp index b0c307c..92b339d 100644 --- a/rehlds/rehlds/rehlds_security.cpp +++ b/rehlds/rehlds/rehlds_security.cpp @@ -2,17 +2,19 @@ cvar_t sv_rehlds_movecmdrate_max_avg = { "sv_rehlds_movecmdrate_max_avg", "750", 0, 750.0f, NULL }; cvar_t sv_rehlds_movecmdrate_max_burst = { "sv_rehlds_movecmdrate_max_burst", "3500", 0, 3500.0f, NULL }; +cvar_t sv_rehlds_stringcmdrate_max_avg = {"sv_rehlds_stringcmdrate_max_avg", "32", 0, 32.0f, NULL}; +cvar_t sv_rehlds_stringcmdrate_max_burst = {"sv_rehlds_stringcmdrate_max_burst", "80", 0, 80.0f, NULL}; CMoveCommandRateLimiter g_MoveCommandRateLimiter; +CStringCommandsRateLimiter g_StringCommandsRateLimiter; CMoveCommandRateLimiter::CMoveCommandRateLimiter() { memset(m_AverageMoveCmdRate, 0, sizeof(m_AverageMoveCmdRate)); memset(m_CurrentMoveCmds, 0, sizeof(m_CurrentMoveCmds)); - m_LastCheckTime = 0; + m_LastCheckTime = 0.0; } -void CMoveCommandRateLimiter::UpdateAverageRates(double currentTime) { - double dt = currentTime - m_LastCheckTime; +void CMoveCommandRateLimiter::UpdateAverageRates(double dt) { for (unsigned int i = 0; i < MAX_CLIENTS; i++) { m_AverageMoveCmdRate[i] = (2.0 * m_AverageMoveCmdRate[i] / 3.0) + m_CurrentMoveCmds[i] / dt / 3.0; m_CurrentMoveCmds[i] = 0; @@ -29,24 +31,16 @@ void CMoveCommandRateLimiter::Frame() { return; } - UpdateAverageRates(currentTime); + UpdateAverageRates(dt); m_LastCheckTime = currentTime; } void CMoveCommandRateLimiter::ClientConnected(unsigned int clientId) { - if (clientId >= (unsigned)g_psvs.maxclients) { - rehlds_syserror(__FUNCTION__": Invalid clientId %u", clientId); - } - m_CurrentMoveCmds[clientId] = 0; m_AverageMoveCmdRate[clientId] = 0.0f; } void CMoveCommandRateLimiter::MoveCommandsIssued(unsigned int clientId, unsigned int numCmds) { - if (clientId >= (unsigned)g_psvs.maxclients) { - rehlds_syserror(__FUNCTION__": Invalid clientId %u", clientId); - } - m_CurrentMoveCmds[clientId] += numCmds; CheckBurstRate(clientId); } @@ -79,10 +73,77 @@ void CMoveCommandRateLimiter::CheckAverageRate(unsigned int clientId) { } } +CStringCommandsRateLimiter::CStringCommandsRateLimiter() { + memset(m_AverageStringCmdRate, 0, sizeof(m_AverageStringCmdRate)); + memset(m_CurrentStringCmds, 0, sizeof(m_CurrentStringCmds)); + m_LastCheckTime = 0.0; +} + +void CStringCommandsRateLimiter::UpdateAverageRates(double dt) { + for (unsigned int i = 0; i < MAX_CLIENTS; i++) { + m_AverageStringCmdRate[i] = (2.0 * m_AverageStringCmdRate[i] / 3.0) + m_CurrentStringCmds[i] / dt / 3.0; + m_CurrentStringCmds[i] = 0; + + CheckAverageRate(i); + } +} + +void CStringCommandsRateLimiter::Frame() { + double currentTime = realtime; + double dt = currentTime - m_LastCheckTime; + + if (dt < 0.5) { //refresh avg. rate every 0.5 sec + return; + } + + UpdateAverageRates(dt); + m_LastCheckTime = currentTime; +} + +void CStringCommandsRateLimiter::ClientConnected(unsigned int clientId) { + m_CurrentStringCmds[clientId] = 0; + m_AverageStringCmdRate[clientId] = 0.0f; +} + +void CStringCommandsRateLimiter::StringCommandIssued(unsigned int clientId) { + m_CurrentStringCmds[clientId]++; + CheckBurstRate(clientId); +} + +void CStringCommandsRateLimiter::CheckBurstRate(unsigned int clientId) { + client_t* cl = &g_psvs.clients[clientId]; + if (!cl->active || sv_rehlds_stringcmdrate_max_burst.value <= 0.0f) { + return; + } + + double dt = realtime - m_LastCheckTime; + if (dt < 0.2) { + dt = 0.2; //small intervals may give too high rates + } + if ((m_CurrentStringCmds[clientId] / dt) > sv_rehlds_stringcmdrate_max_burst.value) { + Cbuf_AddText(va("addip %i %s\n", 5, NET_BaseAdrToString(cl->netchan.remote_address))); + SV_DropClient(cl, false, "Banned for string commands flooding (burst)"); + } +} + +void CStringCommandsRateLimiter::CheckAverageRate(unsigned int clientId) { + client_t* cl = &g_psvs.clients[clientId]; + if (!cl->active || sv_rehlds_stringcmdrate_max_burst.value <= 0.0f) { + return; + } + + if (m_AverageStringCmdRate[clientId] > sv_rehlds_stringcmdrate_max_avg.value) { + Cbuf_AddText(va("addip %i %s\n", 5, NET_BaseAdrToString(cl->netchan.remote_address))); + SV_DropClient(cl, false, "Banned for string commands flooding (Avg)"); + } +} + void Rehlds_Security_Init() { #ifdef REHLDS_FIXES Cvar_RegisterVariable(&sv_rehlds_movecmdrate_max_avg); Cvar_RegisterVariable(&sv_rehlds_movecmdrate_max_burst); + Cvar_RegisterVariable(&sv_rehlds_stringcmdrate_max_avg); + Cvar_RegisterVariable(&sv_rehlds_stringcmdrate_max_burst); #endif } @@ -92,11 +153,13 @@ void Rehlds_Security_Shutdown() { void Rehlds_Security_Frame() { #ifdef REHLDS_FIXES g_MoveCommandRateLimiter.Frame(); + g_StringCommandsRateLimiter.Frame(); #endif } void Rehlds_Security_ClientConnected(unsigned int clientId) { #ifdef REHLDS_FIXES g_MoveCommandRateLimiter.ClientConnected(clientId); + g_StringCommandsRateLimiter.ClientConnected(clientId); #endif } \ No newline at end of file diff --git a/rehlds/rehlds/rehlds_security.h b/rehlds/rehlds/rehlds_security.h index 0fd3ac0..c287314 100644 --- a/rehlds/rehlds/rehlds_security.h +++ b/rehlds/rehlds/rehlds_security.h @@ -22,6 +22,26 @@ private: extern CMoveCommandRateLimiter g_MoveCommandRateLimiter; +class CStringCommandsRateLimiter { +public: + CStringCommandsRateLimiter(); + void Frame(); + void StringCommandIssued(unsigned int clientId); + void ClientConnected(unsigned int clientId); + +private: + void UpdateAverageRates(double currentTime); + void CheckBurstRate(unsigned int clientId); + void CheckAverageRate(unsigned int clientId); + +private: + float m_AverageStringCmdRate[MAX_CLIENTS]; + int m_CurrentStringCmds[MAX_CLIENTS]; + double m_LastCheckTime; +}; + +extern CStringCommandsRateLimiter g_StringCommandsRateLimiter; + extern void Rehlds_Security_Init(); extern void Rehlds_Security_Shutdown(); extern void Rehlds_Security_Frame();