Merge pull request #595 from diamante0018/main

rcon: add flooding protection
This commit is contained in:
Maurice Heumann 2023-04-26 11:55:47 +02:00 committed by GitHub
commit 8346ed2c2c
10 changed files with 375 additions and 304 deletions

View File

@ -25,6 +25,8 @@ namespace auth
{ {
namespace namespace
{ {
const game::dvar_t* password;
std::array<uint64_t, 18> client_xuids{}; std::array<uint64_t, 18> client_xuids{};
std::string get_hdd_serial() std::string get_hdd_serial()
@ -324,6 +326,12 @@ namespace auth
} }
} }
void info_set_value_for_key_stub(char* s, const char* key, const char* value)
{
game::Info_SetValueForKey.call_safe(s, key, value);
game::Info_SetValueForKey.call_safe(s, "password", password->current.value.string);
}
struct component final : generic_component struct component final : generic_component
{ {
void post_unpack() override void post_unpack() override
@ -336,6 +344,11 @@ namespace auth
// Intercept SV_DirectConnect in SV_AddTestClient // Intercept SV_DirectConnect in SV_AddTestClient
utils::hook::call(game::select(0x1422490DC, 0x14052E582), direct_connect_bots_stub); utils::hook::call(game::select(0x1422490DC, 0x14052E582), direct_connect_bots_stub);
scheduler::once([]
{
password = game::register_dvar_string("password", "", game::DVAR_USERINFO, "password");
}, scheduler::pipeline::main);
// Patch steam id bit check // Patch steam id bit check
std::vector<std::pair<size_t, size_t>> patches{}; std::vector<std::pair<size_t, size_t>> patches{};
const auto p = [&patches](const size_t a, const size_t b) const auto p = [&patches](const size_t a, const size_t b)
@ -383,6 +396,8 @@ namespace auth
utils::hook::call(0x14134BF7D_g, send_connect_data_stub); utils::hook::call(0x14134BF7D_g, send_connect_data_stub);
utils::hook::call(0x14134BEFE_g, info_set_value_for_key_stub);
// Fix crash // Fix crash
utils::hook::set<uint8_t>(0x14134B970_g, 0xC3); utils::hook::set<uint8_t>(0x14134B970_g, 0xC3);
} }

View File

@ -13,7 +13,7 @@ namespace dvars_patches
{ {
void patch_dvars() void patch_dvars()
{ {
game::register_sessionmode_dvar_bool("com_pauseSupported", !game::is_server(), game::DVAR_SERVERINFO, "Whether is pause is ever supported by the game mode"); (void)game::register_sessionmode_dvar_bool("com_pauseSupported", !game::is_server(), game::DVAR_SERVERINFO, "Whether is pause is ever supported by the game mode");
} }
void patch_flags() void patch_flags()

View File

@ -2,10 +2,8 @@
#include "loader/component_loader.hpp" #include "loader/component_loader.hpp"
#include <game/game.hpp> #include <game/game.hpp>
#include <game/utils.hpp>
#include "network.hpp" #include "network.hpp"
#include "scheduler.hpp"
#include <utils/hook.hpp> #include <utils/hook.hpp>
@ -56,11 +54,6 @@ namespace patches
// make sure client's reliableAck are not negative // make sure client's reliableAck are not negative
sv_execute_client_messages_hook.create(game::select(0x14224A460, 0x14052F840), sv_execute_client_messages_stub); sv_execute_client_messages_hook.create(game::select(0x14224A460, 0x14052F840), sv_execute_client_messages_stub);
scheduler::once([]
{
game::register_dvar_string("password", "", game::DVAR_USERINFO, "password");
}, scheduler::pipeline::main);
} }
}; };
} }

View File

@ -7,7 +7,6 @@
#include "scheduler.hpp" #include "scheduler.hpp"
#include <utils/finally.hpp> #include <utils/finally.hpp>
#include <utils/string.hpp>
#include <game/utils.hpp> #include <game/utils.hpp>
@ -15,9 +14,13 @@ namespace rcon
{ {
namespace namespace
{ {
const game::dvar_t* rcon_timeout;
std::unordered_map<game::netadr_t, int> rate_limit_map;
std::optional<std::string> get_and_validate_rcon_command(const std::string& data) std::optional<std::string> get_and_validate_rcon_command(const std::string& data)
{ {
const command::params params{data.data()}; const command::params params{data};
if (params.size() <= 1) if (params.size() <= 1)
{ {
@ -52,8 +55,45 @@ namespace rcon
network::send(target, "print", console_buffer); network::send(target, "print", console_buffer);
} }
bool rate_limit_check(const game::netadr_t& address, const int time)
{
const auto last_time = rate_limit_map[address];
if (last_time && (time - last_time) < rcon_timeout->current.value.integer)
{
return false; // Flooding
}
rate_limit_map[address] = time;
return true;
}
void rate_limit_cleanup(const int time)
{
for (auto i = rate_limit_map.begin(); i != rate_limit_map.end();)
{
// No longer at risk of flooding, remove
if ((time - i->second) > rcon_timeout->current.value.integer)
{
i = rate_limit_map.erase(i);
}
else
{
++i;
}
}
}
void rcon_handler(const game::netadr_t& target, const network::data_view& data) void rcon_handler(const game::netadr_t& target, const network::data_view& data)
{ {
const auto time = game::Sys_Milliseconds();
if (!rate_limit_check(target, time))
{
return;
}
rate_limit_cleanup(time);
auto str_data = std::string(reinterpret_cast<const char*>(data.data()), data.size()); auto str_data = std::string(reinterpret_cast<const char*>(data.data()), data.size());
scheduler::once([target, s = std::move(str_data)] scheduler::once([target, s = std::move(str_data)]
{ {
@ -67,6 +107,8 @@ namespace rcon
void post_unpack() override void post_unpack() override
{ {
network::on("rcon", rcon_handler); network::on("rcon", rcon_handler);
rcon_timeout = game::register_dvar_int("rcon_timeout", 500, 100, 10000, game::DVAR_NONE, "");
} }
}; };
} }

View File

@ -143,20 +143,20 @@ namespace server_list
void request_servers(callback callback) void request_servers(callback callback)
{ {
master_state.access([&callback](state& s) master_state.access([&callback](state& s)
{
game::netadr_t addr{};
if (!get_master_server(addr))
{ {
game::netadr_t addr{}; return;
if (!get_master_server(addr)) }
{
return;
}
s.requesting = true; s.requesting = true;
s.address = addr; s.address = addr;
s.callback = std::move(callback); s.callback = std::move(callback);
s.query_start = std::chrono::high_resolution_clock::now(); s.query_start = std::chrono::high_resolution_clock::now();
network::send(s.address, "getservers", utils::string::va("T7 %i full empty", PROTOCOL)); network::send(s.address, "getservers", utils::string::va("T7 %i full empty", PROTOCOL));
}); });
} }
void add_favorite_server(game::netadr_t addr) void add_favorite_server(game::netadr_t addr)
@ -194,33 +194,33 @@ namespace server_list
void post_unpack() override void post_unpack() override
{ {
network::on("getServersResponse", [](const game::netadr_t& target, const network::data_view& data) network::on("getServersResponse", [](const game::netadr_t& target, const network::data_view& data)
{
master_state.access([&](state& s)
{ {
master_state.access([&](state& s) handle_server_list_response(target, data, s);
{
handle_server_list_response(target, data, s);
});
}); });
});
scheduler::loop([] scheduler::loop([]
{
master_state.access([](state& s)
{ {
master_state.access([](state& s) if (!s.requesting)
{ {
if (!s.requesting) return;
{ }
return;
}
const auto now = std::chrono::high_resolution_clock::now(); const auto now = std::chrono::high_resolution_clock::now();
if ((now - s.query_start) < 2s) if ((now - s.query_start) < 2s)
{ {
return; return;
} }
s.requesting = false; s.requesting = false;
s.callback(false, {}); s.callback(false, {});
s.callback = {}; s.callback = {};
}); });
}, scheduler::async, 200ms); }, scheduler::async, 200ms);
lua_serverinfo_to_table_hook.create(0x141F1FD10_g, lua_serverinfo_to_table_stub); lua_serverinfo_to_table_hook.create(0x141F1FD10_g, lua_serverinfo_to_table_stub);
@ -233,10 +233,10 @@ namespace server_list
void pre_destroy() override void pre_destroy() override
{ {
master_state.access([](state& s) master_state.access([](state& s)
{ {
s.requesting = false; s.requesting = false;
s.callback = {}; s.callback = {};
}); });
} }
}; };
} }

View File

@ -85,6 +85,7 @@ namespace game
// Info // Info
WEAK symbol<const char*(const char*, const char* key)> Info_ValueForKey{0x1422E87B0}; WEAK symbol<const char*(const char*, const char* key)> Info_ValueForKey{0x1422E87B0};
WEAK symbol<void(char* s, const char* key, const char* value)> Info_SetValueForKey{0x1422E8410};
// MSG // MSG
WEAK symbol<uint8_t(msg_t* msg)> MSG_ReadByte{0x142155450, 0x14050D1B0}; WEAK symbol<uint8_t(msg_t* msg)> MSG_ReadByte{0x142155450, 0x14050D1B0};
@ -97,7 +98,7 @@ namespace game
WEAK symbol<bool(char const*, netadr_t*)> NET_StringToAdr{0x142172780, 0x140515110}; WEAK symbol<bool(char const*, netadr_t*)> NET_StringToAdr{0x142172780, 0x140515110};
// Sys // Sys
WEAK symbol<int()> Sys_Milliseconds{0x142332870}; WEAK symbol<int()> Sys_Milliseconds{0x142332870, 0x1405972F0};
WEAK symbol<void()> Sys_ShowConsole{0x1423333C0, 0x140597E40}; WEAK symbol<void()> Sys_ShowConsole{0x1423333C0, 0x140597E40};
WEAK symbol<TLSData*()> Sys_GetTLS{0x1421837B0, 0x140525EB0}; WEAK symbol<TLSData*()> Sys_GetTLS{0x1421837B0, 0x140525EB0};
WEAK symbol<TLSData*()> Sys_IsDatabaseReady{0x142183A60}; WEAK symbol<TLSData*()> Sys_IsDatabaseReady{0x142183A60};
@ -124,6 +125,10 @@ namespace game
const char* description)> Dvar_RegisterBool{ const char* description)> Dvar_RegisterBool{
0x1422D0900, 0x14057B500 0x1422D0900, 0x14057B500
}; };
WEAK symbol<dvar_t*(dvarStrHash_t hash, const char* dvarName, int value, int min, int max, unsigned int flags,
const char* description)> Dvar_RegisterInt{
0x0, 0x14057B7B0
};
WEAK symbol<dvar_t*(dvarStrHash_t hash, const char* dvarName, float value, float min, float max, unsigned int flags, WEAK symbol<dvar_t*(dvarStrHash_t hash, const char* dvarName, float value, float min, float max, unsigned int flags,
const char* description)> Dvar_RegisterFloat{ const char* description)> Dvar_RegisterFloat{
0x0, 0x14057B6B0 0x0, 0x14057B6B0

View File

@ -45,7 +45,7 @@ namespace game
return dvar->current.value.enabled; return dvar->current.value.enabled;
} }
const dvar_t* register_sessionmode_dvar_bool(const char* dvar_name, const bool value, const int flags, const dvar_t* register_sessionmode_dvar_bool(const char* dvar_name, const bool value, const unsigned int flags,
const char* description, const eModes mode) const char* description, const eModes mode)
{ {
const auto hash = Dvar_GenerateHash(dvar_name); const auto hash = Dvar_GenerateHash(dvar_name);
@ -71,7 +71,7 @@ namespace game
return registered_dvar; return registered_dvar;
} }
const dvar_t* register_dvar_bool(const char* dvar_name, const bool value, const int flags, const char* description) const dvar_t* register_dvar_bool(const char* dvar_name, const bool value, const unsigned int flags, const char* description)
{ {
const auto hash = Dvar_GenerateHash(dvar_name); const auto hash = Dvar_GenerateHash(dvar_name);
auto* registered_dvar = Dvar_RegisterBool(hash, dvar_name, value, flags, description); auto* registered_dvar = Dvar_RegisterBool(hash, dvar_name, value, flags, description);
@ -84,7 +84,21 @@ namespace game
return registered_dvar; return registered_dvar;
} }
const dvar_t* register_dvar_float(const char* dvar_name, float value, float min, float max, const int flags, const dvar_t* register_dvar_int(const char* dvar_name, int value, int min, int max, const unsigned int flags,
const char* description)
{
const auto hash = Dvar_GenerateHash(dvar_name);
auto* registered_dvar = Dvar_RegisterInt(hash, dvar_name, value, min, max, flags, description);
if (registered_dvar)
{
registered_dvar->debugName = dvar_name;
}
return registered_dvar;
}
const dvar_t* register_dvar_float(const char* dvar_name, float value, float min, float max, const unsigned int flags,
const char* description) const char* description)
{ {
const auto hash = Dvar_GenerateHash(dvar_name); const auto hash = Dvar_GenerateHash(dvar_name);
@ -98,7 +112,7 @@ namespace game
return registered_dvar; return registered_dvar;
} }
const dvar_t* register_dvar_string(const char* dvar_name, const char* value, const int flags, const dvar_t* register_dvar_string(const char* dvar_name, const char* value, const unsigned int flags,
const char* description) const char* description)
{ {
const auto hash = Dvar_GenerateHash(dvar_name); const auto hash = Dvar_GenerateHash(dvar_name);
@ -112,7 +126,7 @@ namespace game
return registered_dvar; return registered_dvar;
} }
void dvar_add_flags(const char* dvar_name, const dvarFlags_e flags) void dvar_add_flags(const char* dvar_name, const unsigned int flags)
{ {
auto* dvar = Dvar_FindVar(dvar_name); auto* dvar = Dvar_FindVar(dvar_name);
@ -132,7 +146,7 @@ namespace game
dvar_to_change->flags |= flags; dvar_to_change->flags |= flags;
} }
void dvar_set_flags(const char* dvar_name, const dvarFlags_e flags) void dvar_set_flags(const char* dvar_name, const unsigned int flags)
{ {
auto* dvar = Dvar_FindVar(dvar_name); auto* dvar = Dvar_FindVar(dvar_name);

View File

@ -4,20 +4,21 @@
namespace game namespace game
{ {
std::string get_dvar_string(const char* dvar_name); [[nodiscard]] std::string get_dvar_string(const char* dvar_name);
int get_dvar_int(const char* dvar_name); [[nodiscard]] int get_dvar_int(const char* dvar_name);
bool get_dvar_bool(const char* dvar_name); [[nodiscard]] bool get_dvar_bool(const char* dvar_name);
const dvar_t* register_dvar_bool(const char* dvar_name, bool value, int flags, const char* description); [[nodiscard]] const dvar_t* register_dvar_bool(const char* dvar_name, bool value, unsigned int flags, const char* description);
const dvar_t* register_dvar_float(const char* dvar_name, float value, float min, float max, const int flags, const char* description); [[nodiscard]] const dvar_t* register_dvar_int(const char* dvar_name, int value, int min, int max, unsigned int flags, const char* description);
const dvar_t* register_sessionmode_dvar_bool(const char* dvar_name, bool value, int flags, const char* description, eModes mode = MODE_COUNT); [[nodiscard]] const dvar_t* register_dvar_float(const char* dvar_name, float value, float min, float max, unsigned int flags, const char* description);
const dvar_t* register_dvar_string(const char* dvar_name, const char* value, int flags, const char* description); [[nodiscard]] const dvar_t* register_sessionmode_dvar_bool(const char* dvar_name, bool value, unsigned int flags, const char* description, eModes mode = MODE_COUNT);
[[nodiscard]] const dvar_t* register_dvar_string(const char* dvar_name, const char* value, unsigned int flags, const char* description);
void dvar_add_flags(const char* dvar, dvarFlags_e flags); void dvar_add_flags(const char* dvar, unsigned int flags);
void dvar_set_flags(const char* dvar_name, dvarFlags_e flags); void dvar_set_flags(const char* dvar_name, unsigned int flags);
bool is_server_running(); [[nodiscard]] bool is_server_running();
size_t get_max_client_count(); [[nodiscard]] size_t get_max_client_count();
void foreach_client(const std::function<void(client_s&, size_t index)>& callback); void foreach_client(const std::function<void(client_s&, size_t index)>& callback);
void foreach_client(const std::function<void(client_s&)>& callback); void foreach_client(const std::function<void(client_s&)>& callback);

View File

@ -305,7 +305,8 @@ namespace steam
auto& servers_list = hRequest == favorites_request ? favorites_servers : internet_servers; auto& servers_list = hRequest == favorites_request ? favorites_servers : internet_servers;
static thread_local gameserveritem_t server_item{}; static thread_local gameserveritem_t server_item{};
return servers_list.access<gameserveritem_t*>([iServer](const servers& s) -> gameserveritem_t* { return servers_list.access<gameserveritem_t*>([iServer](const servers& s) -> gameserveritem_t*
{
if (iServer < 0 || static_cast<size_t>(iServer) >= s.size()) if (iServer < 0 || static_cast<size_t>(iServer) >= s.size())
{ {
return nullptr; return nullptr;
@ -378,17 +379,17 @@ namespace steam
party::query_server( party::query_server(
addr, [response](const bool success, const game::netadr_t& host, const ::utils::info_string& info, addr, [response](const bool success, const game::netadr_t& host, const ::utils::info_string& info,
const uint32_t ping) const uint32_t ping)
{
if (success)
{ {
if (success) auto server_item = create_server_item(host, info, ping, success);
{ response->ServerResponded(server_item);
auto server_item = create_server_item(host, info, ping, success); }
response->ServerResponded(server_item); else
} {
else response->ServerFailedToRespond();
{ }
response->ServerFailedToRespond(); });
}
});
return reinterpret_cast<void*>(static_cast<uint64_t>(7 + rand())); return reinterpret_cast<void*>(static_cast<uint64_t>(7 + rand()));
} }