summaryrefslogtreecommitdiff
path: root/engine/sv_ipratelimit.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /engine/sv_ipratelimit.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'engine/sv_ipratelimit.cpp')
-rw-r--r--engine/sv_ipratelimit.cpp139
1 files changed, 139 insertions, 0 deletions
diff --git a/engine/sv_ipratelimit.cpp b/engine/sv_ipratelimit.cpp
new file mode 100644
index 0000000..30da65d
--- /dev/null
+++ b/engine/sv_ipratelimit.cpp
@@ -0,0 +1,139 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Handles all the functions for implementing remote access to the engine
+//
+//=============================================================================//
+
+#include "sv_ipratelimit.h"
+#include "filesystem.h"
+#include "sv_log.h"
+
+static ConVar sv_logblocks("sv_logblocks", "0", 0, "If true when log when a query is blocked (can cause very large log files)");
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CIPRateLimit::CIPRateLimit(ConVar *maxSec, ConVar *maxWindow, ConVar *maxSecGlobal)
+: m_IPTree( 0, START_TREE_SIZE, LessIP),
+ m_maxSec( maxSec ),
+ m_maxWindow( maxWindow ),
+ m_maxSecGlobal( maxSecGlobal )
+{
+ m_iGlobalCount = 0;
+ m_lLastTime = -1;
+ m_flFlushTime = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CIPRateLimit::~CIPRateLimit()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sort func for rb tree
+//-----------------------------------------------------------------------------
+bool CIPRateLimit::LessIP( const struct CIPRateLimit::iprate_s &lhs, const struct CIPRateLimit::iprate_s &rhs )
+{
+ return ( lhs.ip < rhs.ip );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: return false and potentially log a warning if this IP has exceeded limits
+//-----------------------------------------------------------------------------
+bool CIPRateLimit::CheckIP( netadr_t adr )
+{
+ bool ret = CheckIPInternal(adr);
+ if ( !ret && sv_logblocks.GetBool() == true )
+ {
+ g_Log.Printf("Traffic from %s was blocked for exceeding rate limits\n", adr.ToString() );
+ }
+ return ret;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: return false if this IP has exceeded limits
+//-----------------------------------------------------------------------------
+bool CIPRateLimit::CheckIPInternal( netadr_t adr )
+{
+ // check the global count first in case we are being spammed
+ m_iGlobalCount++;
+ long curTime = (long)Plat_FloatTime();
+
+ if( (curTime - m_lLastTime) > m_maxWindow->GetFloat() )
+ {
+ m_lLastTime = curTime;
+ m_iGlobalCount = 1;
+ }
+
+ float query_rate = static_cast<float>( m_iGlobalCount ) / m_maxWindow->GetFloat(); // add one so the bottom is never zero
+ if( m_maxSecGlobal->GetFloat() > FLT_EPSILON && query_rate > m_maxSecGlobal->GetFloat() )
+ {
+ return false;
+ }
+
+
+ // check the per ip rate (do this first, so one person dosing doesn't add to the global max rate
+ ip_t clientIP;
+ memcpy( &clientIP, adr.ip, sizeof(ip_t) );
+
+ if( m_IPTree.Count() > MAX_TREE_SIZE && curTime > ( m_flFlushTime + FLUSH_TIMEOUT/4 ) ) // if we have stored too many items
+ {
+ m_flFlushTime = curTime;
+ ip_t tmp = m_IPTree.LastInorder(); // we step BACKWARD's through the tree
+ int i = m_IPTree.FirstInorder();
+ while( (m_IPTree.Count() > (2*MAX_TREE_SIZE)/3) && i < m_IPTree.MaxElement() ) // trim 1/3 the entries from the tree and only traverse the max nodes
+ {
+ if ( !m_IPTree.IsValidIndex( tmp ) )
+ break;
+
+ if ( (curTime - m_IPTree[ tmp ].lastTime) > FLUSH_TIMEOUT &&
+ m_IPTree[ tmp ].ip != clientIP )
+ {
+ ip_t removeIPT = tmp;
+ tmp = m_IPTree.PrevInorder( tmp );
+ m_IPTree.RemoveAt( removeIPT );
+ continue;
+ }
+ i++;
+ tmp = m_IPTree.PrevInorder( tmp );
+ }
+ }
+
+ // now find the entry and check if its within our rate limits
+ struct iprate_s findEntry = { clientIP };
+ ip_t entry = m_IPTree.Find( findEntry );
+
+ if( m_IPTree.IsValidIndex( entry ) )
+ {
+ m_IPTree[ entry ].count++; // a new hit
+
+ if( (curTime - m_IPTree[ entry ].lastTime) > m_maxWindow->GetFloat() )
+ {
+ m_IPTree[ entry ].lastTime = curTime;
+ m_IPTree[ entry ].count = 1;
+ }
+ else
+ {
+ float flQueryRate = static_cast<float>( m_IPTree[ entry ].count) / m_maxWindow->GetFloat(); // add one so the bottom is never zero
+ if( flQueryRate > m_maxSec->GetFloat() )
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ // not found, insert this new guy
+ struct iprate_s newEntry;
+ newEntry.count = 1;
+ newEntry.lastTime = curTime;
+ newEntry.ip = clientIP;
+ m_IPTree.Insert( newEntry );
+ }
+
+
+ return true;
+}