2022-02-27 07:53:44 -05:00
|
|
|
#include <STDInclude.hpp>
|
2017-01-19 16:23:59 -05:00
|
|
|
|
|
|
|
namespace Components
|
|
|
|
{
|
2022-06-28 03:26:43 -04:00
|
|
|
// Have only one instance of IW4x read/write the file
|
|
|
|
std::unique_lock<Utils::NamedMutex> Bans::Lock()
|
2017-01-19 16:23:59 -05:00
|
|
|
{
|
2022-06-28 03:26:43 -04:00
|
|
|
static Utils::NamedMutex mutex{"iw4x-ban-list-lock"};
|
|
|
|
std::unique_lock lock{mutex};
|
|
|
|
return lock;
|
|
|
|
}
|
2021-12-17 13:49:25 -05:00
|
|
|
|
2022-06-28 03:26:43 -04:00
|
|
|
bool Bans::IsBanned(const banEntry& entry)
|
|
|
|
{
|
|
|
|
BanList list;
|
|
|
|
LoadBans(&list);
|
2017-01-19 16:23:59 -05:00
|
|
|
|
2017-02-18 03:42:55 -05:00
|
|
|
if (entry.first.bits)
|
2017-01-19 16:23:59 -05:00
|
|
|
{
|
2022-06-04 08:59:14 -04:00
|
|
|
for (const auto& idEntry : list.idList)
|
2017-01-19 16:23:59 -05:00
|
|
|
{
|
2017-02-18 03:42:55 -05:00
|
|
|
if (idEntry.bits == entry.first.bits)
|
2017-01-19 16:23:59 -05:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (entry.second.full)
|
|
|
|
{
|
2022-06-28 03:26:43 -04:00
|
|
|
for (const auto& ipEntry : list.ipList)
|
2017-01-19 16:23:59 -05:00
|
|
|
{
|
|
|
|
if (ipEntry.full == entry.second.full)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-06-28 03:26:43 -04:00
|
|
|
void Bans::InsertBan(const banEntry& entry)
|
2017-01-19 16:23:59 -05:00
|
|
|
{
|
2022-06-28 03:26:43 -04:00
|
|
|
BanList list;
|
|
|
|
LoadBans(&list);
|
2017-01-19 16:23:59 -05:00
|
|
|
|
2017-02-18 03:42:55 -05:00
|
|
|
if (entry.first.bits)
|
2017-01-19 16:23:59 -05:00
|
|
|
{
|
|
|
|
bool found = false;
|
2022-06-28 03:26:43 -04:00
|
|
|
for (const auto& idEntry : list.idList)
|
2017-01-19 16:23:59 -05:00
|
|
|
{
|
2017-02-18 03:42:55 -05:00
|
|
|
if (idEntry.bits == entry.first.bits)
|
2017-01-19 16:23:59 -05:00
|
|
|
{
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found)
|
|
|
|
{
|
|
|
|
list.idList.push_back(entry.first);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (entry.second.full)
|
|
|
|
{
|
|
|
|
bool found = false;
|
2022-06-28 03:26:43 -04:00
|
|
|
for (const auto& ipEntry : list.ipList)
|
2017-01-19 16:23:59 -05:00
|
|
|
{
|
|
|
|
if (ipEntry.full == entry.second.full)
|
|
|
|
{
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found)
|
|
|
|
{
|
|
|
|
list.ipList.push_back(entry.second);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-28 03:26:43 -04:00
|
|
|
SaveBans(&list);
|
2018-10-13 11:37:37 -04:00
|
|
|
}
|
|
|
|
|
2022-06-28 03:26:43 -04:00
|
|
|
void Bans::SaveBans(const BanList* list)
|
2018-10-13 11:37:37 -04:00
|
|
|
{
|
2022-06-28 03:26:43 -04:00
|
|
|
assert(list != nullptr);
|
|
|
|
|
|
|
|
const auto _ = Lock();
|
2018-10-13 11:37:37 -04:00
|
|
|
|
2017-01-19 16:23:59 -05:00
|
|
|
std::vector<std::string> idVector;
|
|
|
|
std::vector<std::string> ipVector;
|
|
|
|
|
2022-06-13 11:32:45 -04:00
|
|
|
for (const auto& idEntry : list->idList)
|
2017-01-19 16:23:59 -05:00
|
|
|
{
|
2022-06-13 11:32:45 -04:00
|
|
|
idVector.emplace_back(Utils::String::VA("%llX", idEntry.bits));
|
2017-01-19 16:23:59 -05:00
|
|
|
}
|
|
|
|
|
2022-06-13 11:32:45 -04:00
|
|
|
for (const auto& ipEntry : list->ipList)
|
2017-01-19 16:23:59 -05:00
|
|
|
{
|
2022-06-13 11:32:45 -04:00
|
|
|
ipVector.emplace_back(Utils::String::VA("%u.%u.%u.%u",
|
2017-01-19 16:23:59 -05:00
|
|
|
ipEntry.bytes[0] & 0xFF,
|
|
|
|
ipEntry.bytes[1] & 0xFF,
|
|
|
|
ipEntry.bytes[2] & 0xFF,
|
|
|
|
ipEntry.bytes[3] & 0xFF));
|
|
|
|
}
|
|
|
|
|
2022-06-13 11:32:45 -04:00
|
|
|
const json11::Json bans = json11::Json::object
|
2017-01-19 16:23:59 -05:00
|
|
|
{
|
|
|
|
{ "ip", ipVector },
|
|
|
|
{ "id", idVector },
|
|
|
|
};
|
|
|
|
|
2022-06-28 03:26:43 -04:00
|
|
|
FileSystem::FileWriter ("bans.json").write(bans.dump());
|
2017-01-19 16:23:59 -05:00
|
|
|
}
|
|
|
|
|
2022-06-28 03:26:43 -04:00
|
|
|
void Bans::LoadBans(BanList* list)
|
2017-01-19 16:23:59 -05:00
|
|
|
{
|
2022-06-28 03:26:43 -04:00
|
|
|
assert(list != nullptr);
|
|
|
|
|
|
|
|
const auto _ = Lock();
|
2017-01-19 16:23:59 -05:00
|
|
|
|
|
|
|
FileSystem::File bans("bans.json");
|
|
|
|
|
2022-06-28 03:26:43 -04:00
|
|
|
if (!bans.exists())
|
2017-01-19 16:23:59 -05:00
|
|
|
{
|
2022-06-28 03:26:43 -04:00
|
|
|
Logger::Debug("bans.json does not exist");
|
|
|
|
return;
|
|
|
|
}
|
2017-01-19 16:23:59 -05:00
|
|
|
|
2022-06-28 03:26:43 -04:00
|
|
|
std::string error;
|
|
|
|
const auto banData = json11::Json::parse(bans.getBuffer(), error);
|
2017-01-19 16:23:59 -05:00
|
|
|
|
2022-06-28 03:26:43 -04:00
|
|
|
if (!error.empty())
|
|
|
|
{
|
|
|
|
Logger::PrintError(Game::CON_CHANNEL_ERROR, "Failed to parse bans.json: {}\n", error);
|
|
|
|
return;
|
|
|
|
}
|
2017-01-19 16:23:59 -05:00
|
|
|
|
2022-06-28 03:26:43 -04:00
|
|
|
if (!banData.is_object())
|
|
|
|
{
|
|
|
|
Logger::Debug("bans.json contains invalid data");
|
|
|
|
return;
|
|
|
|
}
|
2017-01-19 16:23:59 -05:00
|
|
|
|
2022-06-28 03:26:43 -04:00
|
|
|
const auto& idList = banData["id"];
|
|
|
|
const auto& ipList = banData["ip"];
|
2017-01-19 16:23:59 -05:00
|
|
|
|
2022-06-28 03:26:43 -04:00
|
|
|
if (idList.is_array())
|
|
|
|
{
|
|
|
|
for (auto &idEntry : idList.array_items())
|
|
|
|
{
|
|
|
|
if (idEntry.is_string())
|
2017-01-19 16:23:59 -05:00
|
|
|
{
|
2022-06-28 03:26:43 -04:00
|
|
|
SteamID id;
|
|
|
|
id.bits = strtoull(idEntry.string_value().data(), nullptr, 16);
|
|
|
|
|
|
|
|
list->idList.push_back(id);
|
2017-01-19 16:23:59 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-28 03:26:43 -04:00
|
|
|
if (ipList.is_array())
|
2017-01-19 16:23:59 -05:00
|
|
|
{
|
2022-06-28 03:26:43 -04:00
|
|
|
for (auto &ipEntry : ipList.array_items())
|
|
|
|
{
|
|
|
|
if (ipEntry.is_string())
|
|
|
|
{
|
|
|
|
Network::Address addr(ipEntry.string_value());
|
2017-01-19 16:23:59 -05:00
|
|
|
|
2022-06-28 03:26:43 -04:00
|
|
|
list->ipList.push_back(addr.getIP());
|
|
|
|
}
|
|
|
|
}
|
2017-01-19 16:23:59 -05:00
|
|
|
}
|
2022-06-28 03:26:43 -04:00
|
|
|
}
|
2017-01-19 16:23:59 -05:00
|
|
|
|
2022-06-28 03:26:43 -04:00
|
|
|
void Bans::BanClient(Game::client_t* cl, const std::string& reason)
|
|
|
|
{
|
2017-01-19 16:23:59 -05:00
|
|
|
SteamID guid;
|
2022-06-28 03:26:43 -04:00
|
|
|
guid.bits = cl->steamID;
|
2017-01-19 16:23:59 -05:00
|
|
|
|
2022-06-28 03:26:43 -04:00
|
|
|
InsertBan({guid, cl->netchan.remoteAddress.ip});
|
2017-01-19 16:23:59 -05:00
|
|
|
|
2022-06-28 03:26:43 -04:00
|
|
|
Game::SV_DropClient(cl, reason.data(), true);
|
2017-01-19 16:23:59 -05:00
|
|
|
}
|
|
|
|
|
2018-10-13 11:37:37 -04:00
|
|
|
void Bans::UnbanClient(SteamID id)
|
|
|
|
{
|
2022-06-28 03:26:43 -04:00
|
|
|
BanList list;
|
|
|
|
LoadBans(&list);
|
2018-10-13 11:37:37 -04:00
|
|
|
|
2022-06-28 03:26:43 -04:00
|
|
|
const auto entry = std::find_if(list.idList.begin(), list.idList.end(), [&id](const SteamID& entry)
|
2018-10-13 11:37:37 -04:00
|
|
|
{
|
|
|
|
return id.bits == entry.bits;
|
|
|
|
});
|
|
|
|
|
|
|
|
if (entry != list.idList.end())
|
|
|
|
{
|
|
|
|
list.idList.erase(entry);
|
|
|
|
}
|
|
|
|
|
2022-06-28 03:26:43 -04:00
|
|
|
SaveBans(&list);
|
2018-10-13 11:37:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Bans::UnbanClient(Game::netIP_t ip)
|
|
|
|
{
|
2022-06-28 03:26:43 -04:00
|
|
|
BanList list;
|
|
|
|
LoadBans(&list);
|
2018-10-13 11:37:37 -04:00
|
|
|
|
2022-06-28 03:26:43 -04:00
|
|
|
const auto entry = std::find_if(list.ipList.begin(), list.ipList.end(), [&ip](const Game::netIP_t& entry)
|
2018-10-13 11:37:37 -04:00
|
|
|
{
|
|
|
|
return ip.full == entry.full;
|
|
|
|
});
|
|
|
|
|
|
|
|
if (entry != list.ipList.end())
|
|
|
|
{
|
|
|
|
list.ipList.erase(entry);
|
|
|
|
}
|
|
|
|
|
2022-06-28 03:26:43 -04:00
|
|
|
SaveBans(&list);
|
2018-10-13 11:37:37 -04:00
|
|
|
}
|
|
|
|
|
2017-01-19 16:23:59 -05:00
|
|
|
Bans::Bans()
|
|
|
|
{
|
2022-06-28 03:26:43 -04:00
|
|
|
Command::Add("banClient", [](Command::Params* params)
|
2017-01-19 16:23:59 -05:00
|
|
|
{
|
2022-06-28 03:26:43 -04:00
|
|
|
if (!Dvar::Var("sv_running").get<bool>())
|
|
|
|
{
|
|
|
|
Logger::Print("Server is not running.\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (params->size() < 2)
|
|
|
|
{
|
|
|
|
Logger::Print("{} <client number> : permanently ban a client\n", params->get(0));
|
|
|
|
return;
|
|
|
|
}
|
2017-01-19 16:23:59 -05:00
|
|
|
|
2022-06-28 03:26:43 -04:00
|
|
|
const auto* input = params->get(1);
|
2017-01-19 16:23:59 -05:00
|
|
|
|
2022-06-28 03:26:43 -04:00
|
|
|
for (auto i = 0; input[i] != '\0'; ++i)
|
|
|
|
{
|
|
|
|
if (input[i] < '0' || input[i] > '9')
|
|
|
|
{
|
|
|
|
Logger::Print("Bad slot number: {}\n", input);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto num = std::atoi(input);
|
|
|
|
|
|
|
|
if (num < 0 || num >= *Game::svs_clientCount)
|
|
|
|
{
|
|
|
|
Logger::Print("Bad client slot: {}\n", num);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto* cl = &Game::svs_clients[num];
|
|
|
|
if (cl->state == Game::CS_FREE)
|
|
|
|
{
|
|
|
|
Logger::Print("Client {} is not active\n", num);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::string reason = params->size() < 3 ? "EXE_ERR_BANNED_PERM" : params->join(2);
|
|
|
|
Bans::BanClient(&Game::svs_clients[num], reason);
|
2017-01-19 16:23:59 -05:00
|
|
|
});
|
|
|
|
|
2022-06-28 03:26:43 -04:00
|
|
|
Command::Add("unbanClient", [](Command::Params* params)
|
2018-10-13 11:37:37 -04:00
|
|
|
{
|
2022-06-28 03:26:43 -04:00
|
|
|
if (!Dvar::Var("sv_running").get<bool>())
|
|
|
|
{
|
|
|
|
Logger::Print("Server is not running.\n");
|
|
|
|
return;
|
|
|
|
}
|
2018-10-13 11:37:37 -04:00
|
|
|
|
2022-06-28 03:26:43 -04:00
|
|
|
if (params->size() < 3)
|
|
|
|
{
|
|
|
|
Logger::Print("{} <type> <ip or guid>\n", params->get(0));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto* type = params->get(1);
|
2018-10-13 11:37:37 -04:00
|
|
|
|
|
|
|
if (type == "ip"s)
|
|
|
|
{
|
|
|
|
Network::Address address(params->get(2));
|
2022-06-28 03:26:43 -04:00
|
|
|
UnbanClient(address.getIP());
|
2018-10-13 11:37:37 -04:00
|
|
|
|
2022-06-12 17:07:53 -04:00
|
|
|
Logger::Print("Unbanned IP {}\n", params->get(2));
|
2018-10-13 11:37:37 -04:00
|
|
|
|
|
|
|
}
|
|
|
|
else if (type == "guid"s)
|
|
|
|
{
|
|
|
|
SteamID id;
|
|
|
|
id.bits = strtoull(params->get(2), nullptr, 16);
|
|
|
|
|
2022-06-28 03:26:43 -04:00
|
|
|
UnbanClient(id);
|
2018-10-13 11:37:37 -04:00
|
|
|
|
2022-06-12 17:07:53 -04:00
|
|
|
Logger::Print("Unbanned GUID {}\n", params->get(2));
|
2018-10-13 11:37:37 -04:00
|
|
|
}
|
|
|
|
});
|
2017-01-19 16:23:59 -05:00
|
|
|
}
|
|
|
|
}
|