diff --git a/r5dev/engine/host_state.cpp b/r5dev/engine/host_state.cpp index 629bc452..e5969603 100644 --- a/r5dev/engine/host_state.cpp +++ b/r5dev/engine/host_state.cpp @@ -268,9 +268,10 @@ void CHostState::Think(void) const statsTimer.Start(); } - if (banListTimer.GetDurationInProgress().GetSeconds() > sv_banlistRefreshRate->GetDouble()) + if (sv_globalBanlist->GetBool() && + banListTimer.GetDurationInProgress().GetSeconds() > sv_banlistRefreshRate->GetDouble()) { - g_pBanSystem->BanListCheck(); + SV_CheckForBan(); banListTimer.Start(); } #ifdef DEDICATED diff --git a/r5dev/engine/server/sv_main.cpp b/r5dev/engine/server/sv_main.cpp index 05913471..ea7b72d7 100644 --- a/r5dev/engine/server/sv_main.cpp +++ b/r5dev/engine/server/sv_main.cpp @@ -8,6 +8,7 @@ #include "tier0/frametask.h" #include "tier1/cvar.h" #include "engine/server/sv_main.h" +#include "engine/client/client.h" #include "networksystem/pylon.h" #include "networksystem/bansystem.h" @@ -23,12 +24,68 @@ void SV_IsClientBanned(const string& svIPAddr, const uint64_t nNucleusID) { if (!ThreadInMainThread()) { - g_TaskScheduler->Dispatch([svError, nNucleusID] + g_TaskScheduler->Dispatch([svError, svIPAddr] { - g_pBanSystem->AddConnectionRefuse(svError, nNucleusID); // Add to the vector. + g_pBanSystem->KickPlayerById(svIPAddr.c_str(), svError.c_str()); }, 0); } - Warning(eDLL_T::SERVER, "Added '%s' to refused list ('%llu' is banned from the master server!)\n", svIPAddr.c_str(), nNucleusID); + + //Warning(eDLL_T::SERVER, "Added '%s' to refused list ('%llu' is banned from the master server!)\n", svIPAddr.c_str(), nNucleusID); + } +} + +void SV_ProcessBulkCheck(const BannedVec_t& bannedVec) +{ + BannedVec_t outBannedVec; + g_pMasterServer->GetBannedList(bannedVec, outBannedVec); + + if (!ThreadInMainThread()) + { + g_TaskScheduler->Dispatch([outBannedVec] + { + SV_CheckForBan(&outBannedVec); + }, 0); + } +} + +void SV_CheckForBan(const BannedVec_t* pBannedVec /*= nullptr*/) +{ + BannedVec_t bannedVec; + + for (int c = 0; c < MAX_PLAYERS; c++) // Loop through all possible client instances. + { + CClient* pClient = g_pClient->GetClient(c); + if (!pClient) + continue; + + CNetChan* pNetChan = pClient->GetNetChan(); + if (!pNetChan) + continue; + + if (!pClient->IsConnected()) + continue; + + const char* szIPAddr = pNetChan->GetAddress(); + const uint64_t nNucleusID = pClient->GetNucleusID(); + + if (!pBannedVec) + bannedVec.push_back(std::make_pair(szIPAddr, nNucleusID)); + else + { + for (auto& it : *pBannedVec) + { + if (it.second == pClient->GetNucleusID()) + { + Warning(eDLL_T::SERVER, "Removing client '%s' from slot '%i' ('%llu' is banned from this server!)\n", szIPAddr, c, nNucleusID); + pClient->Disconnect(Reputation_t::REP_MARK_BAD, "%s", it.first.c_str()); + } + } + } + } + + if (!pBannedVec && !bannedVec.empty()) + { + std::thread(&SV_ProcessBulkCheck, bannedVec).detach(); } } diff --git a/r5dev/engine/server/sv_main.h b/r5dev/engine/server/sv_main.h index 6f15d7fa..3d7bf8f3 100644 --- a/r5dev/engine/server/sv_main.h +++ b/r5dev/engine/server/sv_main.h @@ -28,6 +28,7 @@ void SV_InitGameDLL(); void SV_ShutdownGameDLL(); bool SV_ActivateServer(); void SV_IsClientBanned(const string& svIPAddr, const uint64_t nNucleusID); +void SV_CheckForBan(const BannedVec_t* pBannedVec = nullptr); /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/networksystem/bansystem.cpp b/r5dev/networksystem/bansystem.cpp index 4bc7ee8f..40d4c640 100644 --- a/r5dev/networksystem/bansystem.cpp +++ b/r5dev/networksystem/bansystem.cpp @@ -140,9 +140,7 @@ bool CBanSystem::DeleteEntry(const string& svIpAddress, const uint64_t nNucleusI if (it != m_vBanList.end()) { - DeleteConnectionRefuse(it->second); m_vBanList.erase(it); - return true; } } @@ -150,88 +148,6 @@ bool CBanSystem::DeleteEntry(const string& svIpAddress, const uint64_t nNucleusI return false; } -//----------------------------------------------------------------------------- -// Purpose: adds a connect refuse entry to the refused list -// Input : &svError - -// nNucleusID - -//----------------------------------------------------------------------------- -bool CBanSystem::AddConnectionRefuse(const string& svError, const uint64_t nNucleusID) -{ - if (IsRefuseListValid()) - { - auto it = std::find_if(m_vRefuseList.begin(), m_vRefuseList.end(), - [&](const pair& element) { return element.second == nNucleusID; }); - - if (it == m_vRefuseList.end()) - { - m_vRefuseList.push_back(std::make_pair(svError, nNucleusID)); - return true; - } - } - else - { - m_vRefuseList.push_back(std::make_pair(svError, nNucleusID)); - return true; - } - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: deletes an entry in the refused list -// Input : nNucleusID - -//----------------------------------------------------------------------------- -bool CBanSystem::DeleteConnectionRefuse(const uint64_t nNucleusID) -{ - if (IsRefuseListValid()) - { - auto it = std::find_if(m_vRefuseList.begin(), m_vRefuseList.end(), - [&](const pair& element) { return element.second == nNucleusID; }); - - if (it != m_vRefuseList.end()) - { - m_vRefuseList.erase(it); - return true; - } - } - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: Check refuse list and kill netchan connection. -//----------------------------------------------------------------------------- -void CBanSystem::BanListCheck(void) -{ - if (!IsRefuseListValid()) - return; - - for (size_t i = 0; i < m_vRefuseList.size(); i++) - { - for (int c = 0; c < MAX_PLAYERS; c++) // Loop through all possible client instances. - { - CClient* pClient = g_pClient->GetClient(c); - if (!pClient) - continue; - - CNetChan* pNetChan = pClient->GetNetChan(); - if (!pNetChan) - continue; - - if (!pClient->IsConnected()) - continue; - - if (pClient->GetNucleusID() != m_vRefuseList[i].second) - continue; - - string svIpAddress = pNetChan->GetAddress(); - - Warning(eDLL_T::SERVER, "Removing client '%s' from slot '%i' ('%llu' is banned from this server!)\n", svIpAddress.c_str(), c, pClient->GetNucleusID()); - pClient->Disconnect(Reputation_t::REP_MARK_BAD, "%s", m_vRefuseList[i].first.c_str()); - } - } -} - //----------------------------------------------------------------------------- // Purpose: checks if specified ip address or nucleus id is banned // Input : &svIpAddress - @@ -261,14 +177,6 @@ bool CBanSystem::IsBanned(const string& svIpAddress, const uint64_t nNucleusID) return false; } -//----------------------------------------------------------------------------- -// Purpose: checks if refused list is valid -//----------------------------------------------------------------------------- -bool CBanSystem::IsRefuseListValid(void) const -{ - return !m_vRefuseList.empty(); -} - //----------------------------------------------------------------------------- // Purpose: checks if banned list is valid //----------------------------------------------------------------------------- @@ -279,50 +187,54 @@ bool CBanSystem::IsBanListValid(void) const //----------------------------------------------------------------------------- // Purpose: kicks a player by given name -// Input : &svPlayerName - +// Input : *playerName - +// *reason - //----------------------------------------------------------------------------- -void CBanSystem::KickPlayerByName(const string& svPlayerName) +void CBanSystem::KickPlayerByName(const char* playerName, const char* reason) { - if (svPlayerName.empty()) + if (!VALID_CHARSTAR(playerName)) return; - AuthorPlayerByName(svPlayerName, false); + AuthorPlayerByName(playerName, false); } //----------------------------------------------------------------------------- // Purpose: kicks a player by given handle or id -// Input : &svHandle - +// Input : *playerHandle - +// *reason - //----------------------------------------------------------------------------- -void CBanSystem::KickPlayerById(const string& svHandle) +void CBanSystem::KickPlayerById(const char* playerHandle, const char* reason) { - if (svHandle.empty()) + if (!VALID_CHARSTAR(playerHandle)) return; - AuthorPlayerById(svHandle, false); + AuthorPlayerById(playerHandle, false); } //----------------------------------------------------------------------------- // Purpose: bans a player by given name -// Input : &svPlayerName - +// Input : *playerName - +// *reason - //----------------------------------------------------------------------------- -void CBanSystem::BanPlayerByName(const string& svPlayerName) +void CBanSystem::BanPlayerByName(const char* playerName, const char* reason) { - if (svPlayerName.empty()) + if (!VALID_CHARSTAR(playerName)) return; - AuthorPlayerByName(svPlayerName, true); + AuthorPlayerByName(playerName, true); } //----------------------------------------------------------------------------- // Purpose: bans a player by given handle or id -// Input : &svHandle - +// Input : *playerHandle - +// *reason - //----------------------------------------------------------------------------- -void CBanSystem::BanPlayerById(const string& svHandle) +void CBanSystem::BanPlayerById(const char* playerHandle, const char* reason) { - if (svHandle.empty()) + if (!VALID_CHARSTAR(playerHandle)) return; - AuthorPlayerById(svHandle, true); + AuthorPlayerById(playerHandle, true); } //----------------------------------------------------------------------------- @@ -364,15 +276,19 @@ void CBanSystem::UnbanPlayer(const string& svCriteria) //----------------------------------------------------------------------------- // Purpose: authors player by given name -// Input : &svPlayerName - -// bBan - (only kicks if false) +// Input : *playerName - +// shouldBan - (only kicks if false) +// *reason - //----------------------------------------------------------------------------- -void CBanSystem::AuthorPlayerByName(const string& svPlayerName, const bool bBan) +void CBanSystem::AuthorPlayerByName(const char* playerName, const bool shouldBan, const char* reason) { - Assert(!svPlayerName.empty()); + Assert(VALID_CHARSTAR(playerName)); bool bDisconnect = false; bool bSave = false; + if (!reason) + reason = shouldBan ? "Banned from server" : "Kicked from server"; + for (int i = 0; i < MAX_PLAYERS; i++) { CClient* pClient = g_pClient->GetClient(i); @@ -385,12 +301,12 @@ void CBanSystem::AuthorPlayerByName(const string& svPlayerName, const bool bBan) if (strlen(pNetChan->GetName()) > 0) { - if (svPlayerName.compare(pNetChan->GetName()) == NULL) // Our wanted name? + if (strcmp(playerName, pNetChan->GetName()) == NULL) // Our wanted name? { - if (bBan && AddEntry(pNetChan->GetAddress(), pClient->GetNucleusID()) && !bSave) + if (shouldBan && AddEntry(pNetChan->GetAddress(), pClient->GetNucleusID()) && !bSave) bSave = true; - pClient->Disconnect(REP_MARK_BAD, bBan ? "Banned from server" : "Kicked from server"); + pClient->Disconnect(REP_MARK_BAD, reason); bDisconnect = true; } } @@ -399,29 +315,33 @@ void CBanSystem::AuthorPlayerByName(const string& svPlayerName, const bool bBan) if (bSave) { Save(); - DevMsg(eDLL_T::SERVER, "Added '%s' to banned list\n", svPlayerName.c_str()); + DevMsg(eDLL_T::SERVER, "Added '%s' to banned list\n", playerName); } else if (bDisconnect) { - DevMsg(eDLL_T::SERVER, "Kicked '%s' from server\n", svPlayerName.c_str()); + DevMsg(eDLL_T::SERVER, "Kicked '%s' from server\n", playerName); } } //----------------------------------------------------------------------------- // Purpose: authors player by given nucleus id or ip address -// Input : &svHandle - -// bBan - (only kicks if false) +// Input : *playerHandle - +// shouldBan - (only kicks if false) +// *reason - //----------------------------------------------------------------------------- -void CBanSystem::AuthorPlayerById(const string& svHandle, const bool bBan) +void CBanSystem::AuthorPlayerById(const char* playerHandle, const bool shouldBan, const char* reason) { - Assert(!svHandle.empty()); + Assert(VALID_CHARSTAR(playerHandle)); try { - bool bOnlyDigits = StringIsDigit(svHandle); + bool bOnlyDigits = StringIsDigit(playerHandle); bool bDisconnect = false; bool bSave = false; + if (!reason) + reason = shouldBan ? "Banned from server" : "Kicked from server"; + for (int i = 0; i < MAX_PLAYERS; i++) { CClient* pClient = g_pClient->GetClient(i); @@ -434,7 +354,7 @@ void CBanSystem::AuthorPlayerById(const string& svHandle, const bool bBan) if (bOnlyDigits) { - uint64_t nTargetID = static_cast(std::stoll(svHandle)); + uint64_t nTargetID = static_cast(std::stoll(playerHandle)); if (nTargetID > static_cast(MAX_PLAYERS)) // Is it a possible nucleusID? { uint64_t nNucleusID = pClient->GetNucleusID(); @@ -448,21 +368,23 @@ void CBanSystem::AuthorPlayerById(const string& svHandle, const bool bBan) continue; } - if (bBan && AddEntry(pNetChan->GetAddress(), pClient->GetNucleusID()) && !bSave) + if (shouldBan && AddEntry(pNetChan->GetAddress(), pClient->GetNucleusID()) && !bSave) bSave = true; - pClient->Disconnect(REP_MARK_BAD, bBan ? "Banned from server" : "Kicked from server"); + + + pClient->Disconnect(REP_MARK_BAD, reason); bDisconnect = true; } else { - if (svHandle.compare(pNetChan->GetAddress()) != NULL) + if (strcmp(playerHandle, pNetChan->GetAddress()) != NULL) continue; - if (bBan && AddEntry(pNetChan->GetAddress(), pClient->GetNucleusID()) && !bSave) + if (shouldBan && AddEntry(pNetChan->GetAddress(), pClient->GetNucleusID()) && !bSave) bSave = true; - pClient->Disconnect(REP_MARK_BAD, bBan ? "Banned from server" : "Kicked from server"); + pClient->Disconnect(REP_MARK_BAD, reason); bDisconnect = true; } } @@ -470,11 +392,11 @@ void CBanSystem::AuthorPlayerById(const string& svHandle, const bool bBan) if (bSave) { Save(); - DevMsg(eDLL_T::SERVER, "Added '%s' to banned list\n", svHandle.c_str()); + DevMsg(eDLL_T::SERVER, "Added '%s' to banned list\n", playerHandle); } else if (bDisconnect) { - DevMsg(eDLL_T::SERVER, "Kicked '%s' from server\n", svHandle.c_str()); + DevMsg(eDLL_T::SERVER, "Kicked '%s' from server\n", playerHandle); } } catch (const std::exception& e) diff --git a/r5dev/networksystem/bansystem.h b/r5dev/networksystem/bansystem.h index 3874ac5b..c266bb9c 100644 --- a/r5dev/networksystem/bansystem.h +++ b/r5dev/networksystem/bansystem.h @@ -1,5 +1,7 @@ #pragma once +typedef vector> BannedVec_t; + class CBanSystem { public: @@ -9,29 +11,22 @@ public: bool AddEntry(const string& svIpAddress, const uint64_t nNucleusID); bool DeleteEntry(const string& svIpAddress, const uint64_t nNucleusID); - bool AddConnectionRefuse(const string& svError, const uint64_t nNucleusID); - bool DeleteConnectionRefuse(const uint64_t nNucleusID); - - void BanListCheck(void); - bool IsBanned(const string& svIpAddress, const uint64_t nNucleusID) const; - bool IsRefuseListValid(void) const; bool IsBanListValid(void) const; - void KickPlayerByName(const string& svPlayerName); - void KickPlayerById(const string& svHandle); + void KickPlayerByName(const char* playerName, const char* reason = nullptr); + void KickPlayerById(const char* playerHandle, const char* reason = nullptr); - void BanPlayerByName(const string& svPlayerName); - void BanPlayerById(const string& svHandle); + void BanPlayerByName(const char* playerName, const char* reason = nullptr); + void BanPlayerById(const char* playerHandle, const char* reason = nullptr); void UnbanPlayer(const string& svCriteria); private: - void AuthorPlayerByName(const string& svPlayerName, const bool bBan); - void AuthorPlayerById(const string& svHandle, const bool bBan); + void AuthorPlayerByName(const char* playerName, const bool bBan, const char* reason = nullptr); + void AuthorPlayerById(const char* playerHandle, const bool bBan, const char* reason = nullptr); - vector> m_vRefuseList; - vector> m_vBanList; + BannedVec_t m_vBanList; }; extern CBanSystem* g_pBanSystem; diff --git a/r5dev/networksystem/pylon.cpp b/r5dev/networksystem/pylon.cpp index 85a0000c..842e1643 100644 --- a/r5dev/networksystem/pylon.cpp +++ b/r5dev/networksystem/pylon.cpp @@ -209,12 +209,61 @@ bool CPylon::KeepAlive(const NetGameServer_t& netGameServer) } #endif // DEDICATED +//----------------------------------------------------------------------------- +// Purpose: Checks a list of clients for their banned status. +// Input : &inBannedVec - +// &outBannedVec - +// Output : True on success, false otherwise. +//----------------------------------------------------------------------------- +bool CPylon::GetBannedList(const BannedVec_t& inBannedVec, BannedVec_t& outBannedVec) const +{ + nlohmann::json arrayJson = nlohmann::json::array(); + + for (const auto& bannedPair : inBannedVec) + { + nlohmann::json player; + player["id"] = bannedPair.second; + player["ip"] = bannedPair.first; + arrayJson.push_back(player); + } + + nlohmann::json playerArray; + playerArray["players"] = arrayJson; + + string outMessage; + CURLINFO status; + + if (!SendRequest("/banlist/bulkCheck", playerArray, + arrayJson, outMessage, status, "banned bulk check error")) + { + return false; + } + + if (!arrayJson.contains("bannedPlayers")) + { + outMessage = Format("Invalid response with status: %d", int(status)); + return false; + } + + for (auto& obj : arrayJson["bannedPlayers"]) + { + outBannedVec.push_back( + std::make_pair( + obj.value("reason", "#DISCONNECT_BANNED"), + obj.value("id", uint64_t(0)) + ) + ); + } + + return true; +} + //----------------------------------------------------------------------------- // Purpose: Checks if client is banned on the comp server. // Input : &ipAddress - // nucleusId - // &outReason - <- contains banned reason if any. -// Output : Returns true if banned, false if not banned. +// Output : True if banned, false if not banned. //----------------------------------------------------------------------------- bool CPylon::CheckForBan(const string& ipAddress, const uint64_t nucleusId, string& outReason) const { diff --git a/r5dev/networksystem/pylon.h b/r5dev/networksystem/pylon.h index 39b3f279..c73eb909 100644 --- a/r5dev/networksystem/pylon.h +++ b/r5dev/networksystem/pylon.h @@ -1,5 +1,6 @@ #pragma once #include "thirdparty/curl/include/curl/curl.h" +#include "bansystem.h" #include "serverlisting.h" class CPylon @@ -8,6 +9,8 @@ public: vector GetServerList(string& outMessage) const; bool GetServerByToken(NetGameServer_t& slOutServer, string& outMessage, const string& svToken) const; bool PostServerHost(string& outMessage, string& svOutToken, const NetGameServer_t& netGameServer) const; + + bool GetBannedList(const BannedVec_t& inBannedVec, BannedVec_t& outBannedVec) const; bool CheckForBan(const string& ipAddress, const uint64_t nucleusId, string& outReason) const; void ExtractError(const nlohmann::json& resultBody, string& outMessage, CURLINFO status, const char* errorText = nullptr) const;