mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
Initial implementation of the bulk ban check system. This implementation takes a snapshot of the currently connected clients, sends it up to the master server, and the master server returns anything within this list that is marked 'banned'. The server would then kick the player from the server. This commit also removes the global banned list cache, as the bulk checking system offers a lot more freedom regarding banning specific players and have it sync across all available servers.
411 lines
11 KiB
C++
411 lines
11 KiB
C++
//=====================================================================================//
|
|
//
|
|
// Purpose: Implementation of the CBanSystem class.
|
|
//
|
|
// $NoKeywords: $
|
|
//=====================================================================================//
|
|
|
|
#include "core/stdafx.h"
|
|
#include "engine/net.h"
|
|
#include "engine/client/client.h"
|
|
#include "filesystem/filesystem.h"
|
|
#include "networksystem/bansystem.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: loads and parses the banned list
|
|
//-----------------------------------------------------------------------------
|
|
void CBanSystem::Load(void)
|
|
{
|
|
if (IsBanListValid())
|
|
m_vBanList.clear();
|
|
|
|
FileHandle_t pFile = FileSystem()->Open("banlist.json", "rt");
|
|
if (!pFile)
|
|
return;
|
|
|
|
uint32_t nLen = FileSystem()->Size(pFile);
|
|
char* pBuf = MemAllocSingleton()->Alloc<char>(nLen + 1);
|
|
|
|
int nRead = FileSystem()->Read(pBuf, nLen, pFile);
|
|
FileSystem()->Close(pFile);
|
|
|
|
pBuf[nRead] = '\0'; // Null terminate the string buffer containing our banned list.
|
|
|
|
try
|
|
{
|
|
nlohmann::json jsIn = nlohmann::json::parse(pBuf);
|
|
|
|
size_t nTotalBans = 0;
|
|
if (!jsIn.is_null())
|
|
{
|
|
if (!jsIn["totalBans"].is_null())
|
|
nTotalBans = jsIn["totalBans"].get<size_t>();
|
|
}
|
|
|
|
for (size_t i = 0; i < nTotalBans; i++)
|
|
{
|
|
nlohmann::json jsEntry = jsIn[std::to_string(i)];
|
|
if (!jsEntry.is_null())
|
|
{
|
|
string svIpAddress = jsEntry["ipAddress"].get<string>();
|
|
uint64_t nNucleusID = jsEntry["nucleusId"].get<uint64_t>();
|
|
|
|
m_vBanList.push_back(std::make_pair(svIpAddress, nNucleusID));
|
|
}
|
|
}
|
|
}
|
|
catch (const std::exception& ex)
|
|
{
|
|
Warning(eDLL_T::SERVER, "%s: Exception while parsing banned list:\n%s\n", __FUNCTION__, ex.what());
|
|
}
|
|
|
|
MemAllocSingleton()->Free(pBuf);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: saves the banned list
|
|
//-----------------------------------------------------------------------------
|
|
void CBanSystem::Save(void) const
|
|
{
|
|
FileHandle_t pFile = FileSystem()->Open("banlist.json", "wt", "PLATFORM");
|
|
if (!pFile)
|
|
{
|
|
Error(eDLL_T::SERVER, NO_ERROR, "%s - Unable to write to '%s' (read-only?)\n", __FUNCTION__, "banlist.json");
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
nlohmann::json jsOut;
|
|
for (size_t i = 0; i < m_vBanList.size(); i++)
|
|
{
|
|
jsOut[std::to_string(i)]["ipAddress"] = m_vBanList[i].first;
|
|
jsOut[std::to_string(i)]["nucleusId"] = m_vBanList[i].second;
|
|
}
|
|
|
|
jsOut["totalBans"] = m_vBanList.size();
|
|
string svJsOut = jsOut.dump(4);
|
|
|
|
FileSystem()->Write(svJsOut.data(), int(svJsOut.size()), pFile);
|
|
}
|
|
catch (const std::exception& ex)
|
|
{
|
|
Warning(eDLL_T::SERVER, "%s: Exception while parsing banned list:\n%s\n", __FUNCTION__, ex.what());
|
|
}
|
|
|
|
FileSystem()->Close(pFile);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: adds a banned player entry to the banned list
|
|
// Input : &svIpAddress -
|
|
// nNucleusID -
|
|
//-----------------------------------------------------------------------------
|
|
bool CBanSystem::AddEntry(const string& svIpAddress, const uint64_t nNucleusID)
|
|
{
|
|
Assert(!svIpAddress.empty());
|
|
|
|
if (IsBanListValid())
|
|
{
|
|
auto it = std::find(m_vBanList.begin(), m_vBanList.end(), std::make_pair(svIpAddress, nNucleusID));
|
|
if (it == m_vBanList.end())
|
|
{
|
|
m_vBanList.push_back(std::make_pair(svIpAddress, nNucleusID));
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_vBanList.push_back(std::make_pair(svIpAddress, nNucleusID));
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: deletes an entry in the banned list
|
|
// Input : &svIpAddress -
|
|
// nNucleusID -
|
|
//-----------------------------------------------------------------------------
|
|
bool CBanSystem::DeleteEntry(const string& svIpAddress, const uint64_t nNucleusID)
|
|
{
|
|
Assert(!svIpAddress.empty());
|
|
|
|
if (IsBanListValid())
|
|
{
|
|
auto it = std::find_if(m_vBanList.begin(), m_vBanList.end(),
|
|
[&](const pair<const string, const uint64_t>& element)
|
|
{ return (svIpAddress.compare(element.first) == NULL || element.second == nNucleusID); });
|
|
|
|
if (it != m_vBanList.end())
|
|
{
|
|
m_vBanList.erase(it);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: checks if specified ip address or nucleus id is banned
|
|
// Input : &svIpAddress -
|
|
// nNucleusID -
|
|
// Output : true if banned, false if not banned
|
|
//-----------------------------------------------------------------------------
|
|
bool CBanSystem::IsBanned(const string& svIpAddress, const uint64_t nNucleusID) const
|
|
{
|
|
for (size_t i = 0; i < m_vBanList.size(); i++)
|
|
{
|
|
const string& ipAddress = m_vBanList[i].first;
|
|
const uint64_t nucleusID = m_vBanList[i].second;
|
|
|
|
if (ipAddress.empty() ||
|
|
!nucleusID) // Cannot be null.
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (ipAddress.compare(svIpAddress) == NULL ||
|
|
nNucleusID == nucleusID)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: checks if banned list is valid
|
|
//-----------------------------------------------------------------------------
|
|
bool CBanSystem::IsBanListValid(void) const
|
|
{
|
|
return !m_vBanList.empty();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: kicks a player by given name
|
|
// Input : *playerName -
|
|
// *reason -
|
|
//-----------------------------------------------------------------------------
|
|
void CBanSystem::KickPlayerByName(const char* playerName, const char* reason)
|
|
{
|
|
if (!VALID_CHARSTAR(playerName))
|
|
return;
|
|
|
|
AuthorPlayerByName(playerName, false);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: kicks a player by given handle or id
|
|
// Input : *playerHandle -
|
|
// *reason -
|
|
//-----------------------------------------------------------------------------
|
|
void CBanSystem::KickPlayerById(const char* playerHandle, const char* reason)
|
|
{
|
|
if (!VALID_CHARSTAR(playerHandle))
|
|
return;
|
|
|
|
AuthorPlayerById(playerHandle, false);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: bans a player by given name
|
|
// Input : *playerName -
|
|
// *reason -
|
|
//-----------------------------------------------------------------------------
|
|
void CBanSystem::BanPlayerByName(const char* playerName, const char* reason)
|
|
{
|
|
if (!VALID_CHARSTAR(playerName))
|
|
return;
|
|
|
|
AuthorPlayerByName(playerName, true);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: bans a player by given handle or id
|
|
// Input : *playerHandle -
|
|
// *reason -
|
|
//-----------------------------------------------------------------------------
|
|
void CBanSystem::BanPlayerById(const char* playerHandle, const char* reason)
|
|
{
|
|
if (!VALID_CHARSTAR(playerHandle))
|
|
return;
|
|
|
|
AuthorPlayerById(playerHandle, true);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: unbans a player by given nucleus id or ip address
|
|
// Input : &svCriteria -
|
|
//-----------------------------------------------------------------------------
|
|
void CBanSystem::UnbanPlayer(const string& svCriteria)
|
|
{
|
|
try
|
|
{
|
|
bool bSave = false;
|
|
if (StringIsDigit(svCriteria)) // Check if we have an ip address or nucleus id.
|
|
{
|
|
if (DeleteEntry("<<invalid>>", std::stoll(svCriteria))) // Delete ban entry.
|
|
{
|
|
bSave = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (DeleteEntry(svCriteria, 0)) // Delete ban entry.
|
|
{
|
|
bSave = true;
|
|
}
|
|
}
|
|
|
|
if (bSave)
|
|
{
|
|
Save(); // Save modified vector to file.
|
|
DevMsg(eDLL_T::SERVER, "Removed '%s' from banned list\n", svCriteria.c_str());
|
|
}
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
Error(eDLL_T::SERVER, NO_ERROR, "%s - %s\n", __FUNCTION__, e.what());
|
|
return;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: authors player by given name
|
|
// Input : *playerName -
|
|
// shouldBan - (only kicks if false)
|
|
// *reason -
|
|
//-----------------------------------------------------------------------------
|
|
void CBanSystem::AuthorPlayerByName(const char* playerName, const bool shouldBan, const char* reason)
|
|
{
|
|
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);
|
|
if (!pClient)
|
|
continue;
|
|
|
|
CNetChan* pNetChan = pClient->GetNetChan();
|
|
if (!pNetChan)
|
|
continue;
|
|
|
|
if (strlen(pNetChan->GetName()) > 0)
|
|
{
|
|
if (strcmp(playerName, pNetChan->GetName()) == NULL) // Our wanted name?
|
|
{
|
|
if (shouldBan && AddEntry(pNetChan->GetAddress(), pClient->GetNucleusID()) && !bSave)
|
|
bSave = true;
|
|
|
|
pClient->Disconnect(REP_MARK_BAD, reason);
|
|
bDisconnect = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bSave)
|
|
{
|
|
Save();
|
|
DevMsg(eDLL_T::SERVER, "Added '%s' to banned list\n", playerName);
|
|
}
|
|
else if (bDisconnect)
|
|
{
|
|
DevMsg(eDLL_T::SERVER, "Kicked '%s' from server\n", playerName);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: authors player by given nucleus id or ip address
|
|
// Input : *playerHandle -
|
|
// shouldBan - (only kicks if false)
|
|
// *reason -
|
|
//-----------------------------------------------------------------------------
|
|
void CBanSystem::AuthorPlayerById(const char* playerHandle, const bool shouldBan, const char* reason)
|
|
{
|
|
Assert(VALID_CHARSTAR(playerHandle));
|
|
|
|
try
|
|
{
|
|
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);
|
|
if (!pClient)
|
|
continue;
|
|
|
|
CNetChan* pNetChan = pClient->GetNetChan();
|
|
if (!pNetChan)
|
|
continue;
|
|
|
|
if (bOnlyDigits)
|
|
{
|
|
uint64_t nTargetID = static_cast<uint64_t>(std::stoll(playerHandle));
|
|
if (nTargetID > static_cast<uint64_t>(MAX_PLAYERS)) // Is it a possible nucleusID?
|
|
{
|
|
uint64_t nNucleusID = pClient->GetNucleusID();
|
|
if (nNucleusID != nTargetID)
|
|
continue;
|
|
}
|
|
else // If its not try by handle.
|
|
{
|
|
uint64_t nClientID = static_cast<uint64_t>(pClient->GetHandle());
|
|
if (nClientID != nTargetID)
|
|
continue;
|
|
}
|
|
|
|
if (shouldBan && AddEntry(pNetChan->GetAddress(), pClient->GetNucleusID()) && !bSave)
|
|
bSave = true;
|
|
|
|
|
|
|
|
pClient->Disconnect(REP_MARK_BAD, reason);
|
|
bDisconnect = true;
|
|
}
|
|
else
|
|
{
|
|
if (strcmp(playerHandle, pNetChan->GetAddress()) != NULL)
|
|
continue;
|
|
|
|
if (shouldBan && AddEntry(pNetChan->GetAddress(), pClient->GetNucleusID()) && !bSave)
|
|
bSave = true;
|
|
|
|
pClient->Disconnect(REP_MARK_BAD, reason);
|
|
bDisconnect = true;
|
|
}
|
|
}
|
|
|
|
if (bSave)
|
|
{
|
|
Save();
|
|
DevMsg(eDLL_T::SERVER, "Added '%s' to banned list\n", playerHandle);
|
|
}
|
|
else if (bDisconnect)
|
|
{
|
|
DevMsg(eDLL_T::SERVER, "Kicked '%s' from server\n", playerHandle);
|
|
}
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
Error(eDLL_T::SERVER, NO_ERROR, "%s - %s\n", __FUNCTION__, e.what());
|
|
return;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
CBanSystem* g_pBanSystem = new CBanSystem();
|