diff --git a/src/Components/Modules/RCon.cpp b/src/Components/Modules/RCon.cpp index b7c0ab20..6bcd6f8c 100644 --- a/src/Components/Modules/RCon.cpp +++ b/src/Components/Modules/RCon.cpp @@ -2,6 +2,8 @@ namespace Components { + std::unordered_map RCon::RateLimit; + RCon::Container RCon::RconContainer; Utils::Cryptography::ECC::Key RCon::RconKey; @@ -71,6 +73,43 @@ namespace Components }); } + bool RCon::RateLimitCheck(const Network::Address& address, const int time) + { + auto ip = address.getIP(); + + if (!RateLimit.contains(ip.full)) + { + RateLimit[ip.full] = 0; + } + + const auto lastTime = RateLimit[ip.full]; + + // Only one request every 500ms + if (lastTime && (time - lastTime) < 500) + { + return false; // Flooding + } + + RateLimit[ip.full] = time; + return true; + } + + void RCon::RateLimitCleanup(const int time) + { + for (auto i = RateLimit.begin(); i != RateLimit.end();) + { + // No longer at risk of flooding, remove + if ((time - i->second) > 500) + { + i = RateLimit.erase(i); + } + else + { + ++i; + } + } + } + RCon::RCon() { AddCommands(); @@ -126,6 +165,14 @@ namespace Components Network::OnClientPacket("rcon", [](const Network::Address& address, [[maybe_unused]] const std::string& data) { + const auto time = Game::Sys_Milliseconds(); + if (!RateLimitCheck(address, time)) + { + return; + } + + RateLimitCleanup(time); + std::string data_ = data; Utils::String::Trim(data_); diff --git a/src/Components/Modules/RCon.hpp b/src/Components/Modules/RCon.hpp index 78b07dea..22c8cfc0 100644 --- a/src/Components/Modules/RCon.hpp +++ b/src/Components/Modules/RCon.hpp @@ -29,6 +29,8 @@ namespace Components static Utils::Cryptography::ECC::Key GetKeyInternal(); }; + static std::unordered_map RateLimit; + static Container RconContainer; static Utils::Cryptography::ECC::Key RconKey; @@ -38,5 +40,8 @@ namespace Components static Dvar::Var RconLogRequests; static void AddCommands(); + + static bool RateLimitCheck(const Network::Address& address, int time); + static void RateLimitCleanup(int time); }; }