From b29a733c4feb44cd0596ab0374abde4b6cbd027f Mon Sep 17 00:00:00 2001 From: heinrich5991 Date: Fri, 22 Jan 2016 16:02:40 +0100 Subject: [PATCH 1/2] Add DoS protection to server info requests Only allow 10 requests per second before falling back to smaller server info responses. --- src/engine/server/server.cpp | 54 ++++++++++++++++++++++++++++++++---- src/engine/server/server.h | 7 ++++- 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index 6fe255b03..04f6d07cc 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -317,6 +317,10 @@ CServer::CServer() m_RconRestrict = -1; m_GeneratedRconPassword = 0; + m_ServerInfoFirstRequest = 0; + m_ServerInfoNumRequests = 0; + m_ServerInfoHighLoad = false; + Init(); } @@ -1266,12 +1270,35 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket) } } -void CServer::SendServerInfo(const NETADDR *pAddr, int Token, bool Extended, int Offset) +void CServer::SendServerInfoConnless(const NETADDR *pAddr, int Token, bool Extended) +{ + static const int MAX_REQUESTS_PER_SECOND = 10; + int64 Now = Tick(); + if(Now <= m_ServerInfoFirstRequest + TickSpeed()) + { + m_ServerInfoNumRequests++; + } + else + { + m_ServerInfoHighLoad = m_ServerInfoNumRequests > MAX_REQUESTS_PER_SECOND; + m_ServerInfoNumRequests = 1; + m_ServerInfoFirstRequest = Now; + } + + bool Short = m_ServerInfoNumRequests > MAX_REQUESTS_PER_SECOND || m_ServerInfoHighLoad; + SendServerInfo(pAddr, Token, Extended, 0, Short); +} + +void CServer::SendServerInfo(const NETADDR *pAddr, int Token, bool Extended, int Offset, bool Short) { CNetChunk Packet; CPacker p; char aBuf[128]; + Packet.m_ClientID = -1; + Packet.m_Address = *pAddr; + Packet.m_Flags = NETSENDFLAG_CONNLESS; + // count the players int PlayerCount = 0, ClientCount = 0; for(int i = 0; i < MAX_CLIENTS; i++) @@ -1346,6 +1373,14 @@ void CServer::SendServerInfo(const NETADDR *pAddr, int Token, bool Extended, int if (Extended) p.AddInt(Offset); + if(Short) + { + Packet.m_DataSize = p.Size(); + Packet.m_pData = p.Data(); + m_NetServer.Send(&Packet); + return; + } + int ClientsPerPacket = Extended ? 24 : VANILLA_MAX_CLIENTS; int Skip = Offset; int Take = ClientsPerPacket; @@ -1368,9 +1403,6 @@ void CServer::SendServerInfo(const NETADDR *pAddr, int Token, bool Extended, int } } - Packet.m_ClientID = -1; - Packet.m_Address = *pAddr; - Packet.m_Flags = NETSENDFLAG_CONNLESS; Packet.m_DataSize = p.Size(); Packet.m_pData = p.Data(); m_NetServer.Send(&Packet); @@ -1406,15 +1438,25 @@ void CServer::PumpNetwork() // stateless if(!m_Register.RegisterProcessPacket(&Packet)) { + bool ServerInfo = false; + bool Extended = false; + if(Packet.m_DataSize == sizeof(SERVERBROWSE_GETINFO)+1 && mem_comp(Packet.m_pData, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)) == 0) { - SendServerInfo(&Packet.m_Address, ((unsigned char *)Packet.m_pData)[sizeof(SERVERBROWSE_GETINFO)]); + ServerInfo = true; + Extended = false; } else if(Packet.m_DataSize == sizeof(SERVERBROWSE_GETINFO64)+1 && mem_comp(Packet.m_pData, SERVERBROWSE_GETINFO64, sizeof(SERVERBROWSE_GETINFO64)) == 0) { - SendServerInfo(&Packet.m_Address, ((unsigned char *)Packet.m_pData)[sizeof(SERVERBROWSE_GETINFO64)], true); + ServerInfo = true; + Extended = true; + } + if(ServerInfo) + { + int Token = ((unsigned char *)Packet.m_pData)[sizeof(SERVERBROWSE_GETINFO)]; + SendServerInfoConnless(&Packet.m_Address, Token, Extended); } } } diff --git a/src/engine/server/server.h b/src/engine/server/server.h index db8caf482..0d11e591c 100644 --- a/src/engine/server/server.h +++ b/src/engine/server/server.h @@ -184,6 +184,10 @@ public: int m_RconRestrict; + bool m_ServerInfoHighLoad; + int64 m_ServerInfoFirstRequest; + int m_ServerInfoNumRequests; + CServer(); int TrySetClientName(int ClientID, const char *pName); @@ -238,7 +242,8 @@ public: void ProcessClientPacket(CNetChunk *pPacket); - void SendServerInfo(const NETADDR *pAddr, int Token, bool Extended=false, int Offset=0); + void SendServerInfoConnless(const NETADDR *pAddr, int Token, bool Extended); + void SendServerInfo(const NETADDR *pAddr, int Token, bool Extended=false, int Offset=0, bool Short=false); void UpdateServerInfo(); void PumpNetwork(); From 96f0eddbb8909c931c39a143b9f6fa6c0259568d Mon Sep 17 00:00:00 2001 From: heinrich5991 Date: Fri, 22 Jan 2016 16:42:54 +0100 Subject: [PATCH 2/2] Add `sv_max_server_info_per_second` This controls how many complete server info responses are sent per second. --- src/engine/server/server.cpp | 6 +++--- src/engine/shared/config_variables.h | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index 04f6d07cc..0c4b09e1e 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -1272,7 +1272,7 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket) void CServer::SendServerInfoConnless(const NETADDR *pAddr, int Token, bool Extended) { - static const int MAX_REQUESTS_PER_SECOND = 10; + const int MaxRequests = g_Config.m_SvServerInfoPerSecond; int64 Now = Tick(); if(Now <= m_ServerInfoFirstRequest + TickSpeed()) { @@ -1280,12 +1280,12 @@ void CServer::SendServerInfoConnless(const NETADDR *pAddr, int Token, bool Exten } else { - m_ServerInfoHighLoad = m_ServerInfoNumRequests > MAX_REQUESTS_PER_SECOND; + m_ServerInfoHighLoad = m_ServerInfoNumRequests > MaxRequests; m_ServerInfoNumRequests = 1; m_ServerInfoFirstRequest = Now; } - bool Short = m_ServerInfoNumRequests > MAX_REQUESTS_PER_SECOND || m_ServerInfoHighLoad; + bool Short = m_ServerInfoNumRequests > MaxRequests || m_ServerInfoHighLoad; SendServerInfo(pAddr, Token, Extended, 0, Short); } diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index 945974382..2888cb7e9 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -150,6 +150,7 @@ MACRO_CONFIG_INT(SvVanillaAntiSpoof, sv_vanilla_antispoof, 1, 0, 1, CFGFLAG_SERV MACRO_CONFIG_INT(SvPlayerDemoRecord, sv_player_demo_record, 0, 0, 1, CFGFLAG_SERVER, "Automatically record demos for each player") MACRO_CONFIG_INT(SvDemoChat, sv_demo_chat, 0, 0, 1, CFGFLAG_SERVER, "Record chat for demos") +MACRO_CONFIG_INT(SvServerInfoPerSecond, sv_server_info_per_second, 10, 1, 1000, CFGFLAG_SERVER, "Maximum number of complete server info responses that are sent out per second") MACRO_CONFIG_STR(EcBindaddr, ec_bindaddr, 128, "localhost", CFGFLAG_ECON, "Address to bind the external console to. Anything but 'localhost' is dangerous") MACRO_CONFIG_INT(EcPort, ec_port, 0, 0, 0, CFGFLAG_ECON, "Port to use for the external console")