Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

139 lines
4.3 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Handles all the functions for implementing remote access to the engine
  4. //
  5. //=============================================================================//
  6. #include "sv_ipratelimit.h"
  7. #include "filesystem.h"
  8. #include "sv_log.h"
  9. static ConVar sv_logblocks("sv_logblocks", "0", 0, "If true when log when a query is blocked (can cause very large log files)");
  10. //-----------------------------------------------------------------------------
  11. // Purpose: Constructor
  12. //-----------------------------------------------------------------------------
  13. CIPRateLimit::CIPRateLimit(ConVar *maxSec, ConVar *maxWindow, ConVar *maxSecGlobal)
  14. : m_IPTree( 0, START_TREE_SIZE, LessIP),
  15. m_maxSec( maxSec ),
  16. m_maxWindow( maxWindow ),
  17. m_maxSecGlobal( maxSecGlobal )
  18. {
  19. m_iGlobalCount = 0;
  20. m_lLastTime = -1;
  21. m_flFlushTime = 0;
  22. }
  23. //-----------------------------------------------------------------------------
  24. // Purpose: Destructor
  25. //-----------------------------------------------------------------------------
  26. CIPRateLimit::~CIPRateLimit()
  27. {
  28. }
  29. //-----------------------------------------------------------------------------
  30. // Purpose: sort func for rb tree
  31. //-----------------------------------------------------------------------------
  32. bool CIPRateLimit::LessIP( const struct CIPRateLimit::iprate_s &lhs, const struct CIPRateLimit::iprate_s &rhs )
  33. {
  34. return ( lhs.ip < rhs.ip );
  35. }
  36. //-----------------------------------------------------------------------------
  37. // Purpose: return false and potentially log a warning if this IP has exceeded limits
  38. //-----------------------------------------------------------------------------
  39. bool CIPRateLimit::CheckIP( netadr_t adr )
  40. {
  41. bool ret = CheckIPInternal(adr);
  42. if ( !ret && sv_logblocks.GetBool() == true )
  43. {
  44. g_Log.Printf("Traffic from %s was blocked for exceeding rate limits\n", adr.ToString() );
  45. }
  46. return ret;
  47. }
  48. //-----------------------------------------------------------------------------
  49. // Purpose: return false if this IP has exceeded limits
  50. //-----------------------------------------------------------------------------
  51. bool CIPRateLimit::CheckIPInternal( netadr_t adr )
  52. {
  53. // check the global count first in case we are being spammed
  54. m_iGlobalCount++;
  55. long curTime = (long)Plat_FloatTime();
  56. if( (curTime - m_lLastTime) > m_maxWindow->GetFloat() )
  57. {
  58. m_lLastTime = curTime;
  59. m_iGlobalCount = 1;
  60. }
  61. float query_rate = static_cast<float>( m_iGlobalCount ) / m_maxWindow->GetFloat(); // add one so the bottom is never zero
  62. if( m_maxSecGlobal->GetFloat() > FLT_EPSILON && query_rate > m_maxSecGlobal->GetFloat() )
  63. {
  64. return false;
  65. }
  66. // check the per ip rate (do this first, so one person dosing doesn't add to the global max rate
  67. ip_t clientIP;
  68. memcpy( &clientIP, adr.ip, sizeof(ip_t) );
  69. if( m_IPTree.Count() > MAX_TREE_SIZE && curTime > ( m_flFlushTime + FLUSH_TIMEOUT/4 ) ) // if we have stored too many items
  70. {
  71. m_flFlushTime = curTime;
  72. ip_t tmp = m_IPTree.LastInorder(); // we step BACKWARD's through the tree
  73. int i = m_IPTree.FirstInorder();
  74. 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
  75. {
  76. if ( !m_IPTree.IsValidIndex( tmp ) )
  77. break;
  78. if ( (curTime - m_IPTree[ tmp ].lastTime) > FLUSH_TIMEOUT &&
  79. m_IPTree[ tmp ].ip != clientIP )
  80. {
  81. ip_t removeIPT = tmp;
  82. tmp = m_IPTree.PrevInorder( tmp );
  83. m_IPTree.RemoveAt( removeIPT );
  84. continue;
  85. }
  86. i++;
  87. tmp = m_IPTree.PrevInorder( tmp );
  88. }
  89. }
  90. // now find the entry and check if its within our rate limits
  91. struct iprate_s findEntry = { clientIP };
  92. ip_t entry = m_IPTree.Find( findEntry );
  93. if( m_IPTree.IsValidIndex( entry ) )
  94. {
  95. m_IPTree[ entry ].count++; // a new hit
  96. if( (curTime - m_IPTree[ entry ].lastTime) > m_maxWindow->GetFloat() )
  97. {
  98. m_IPTree[ entry ].lastTime = curTime;
  99. m_IPTree[ entry ].count = 1;
  100. }
  101. else
  102. {
  103. float flQueryRate = static_cast<float>( m_IPTree[ entry ].count) / m_maxWindow->GetFloat(); // add one so the bottom is never zero
  104. if( flQueryRate > m_maxSec->GetFloat() )
  105. {
  106. return false;
  107. }
  108. }
  109. }
  110. else
  111. {
  112. // not found, insert this new guy
  113. struct iprate_s newEntry;
  114. newEntry.count = 1;
  115. newEntry.lastTime = curTime;
  116. newEntry.ip = clientIP;
  117. m_IPTree.Insert( newEntry );
  118. }
  119. return true;
  120. }