sphere-source-x/src/network/CIPHistoryManager.cpp

147 lines
4.4 KiB
C++

//#include "../common/CScriptParserBufs.h" // included in the precompiled header via CExpression.h
#include "../game/CServer.h"
#include "../game/CServerConfig.h"
#include "../game/CWorldGameTime.h"
#include "CIPHistoryManager.h"
#include <algorithm>
/***************************************************************************
*
*
* struct HistoryIP Simple interface for IP history maintainence
*
*
***************************************************************************/
void HistoryIP::update(void)
{
// reset ttl
m_iTTLSeconds = g_Cfg.m_iNetHistoryTTLSeconds;
}
bool HistoryIP::checkPing(void)
{
// ip is pinging, check if blocked
update();
return (m_fBlocked || (m_iPings++ >= g_Cfg.m_iNetMaxPings));
}
void HistoryIP::setBlocked(bool isBlocked, int64 timeoutSeconds)
{
// block ip
ADDTOCALLSTACK("HistoryIP:setBlocked");
if (isBlocked == true)
{
CScriptTriggerArgsPtr pScriptArgs = CScriptParserBufs::GetCScriptTriggerArgsPtr();
pScriptArgs->Init(m_ip.GetAddrStr());
pScriptArgs->m_iN1 = timeoutSeconds;
g_Serv.r_Call("f_onserver_blockip", pScriptArgs, &g_Serv );
timeoutSeconds = pScriptArgs->m_iN1;
}
m_fBlocked = isBlocked;
if (isBlocked && timeoutSeconds >= 0)
m_iBlockExpireMs = CWorldGameTime::GetCurrentTime().GetTimeRaw() + (timeoutSeconds * MSECS_PER_SEC);
else
m_iBlockExpireMs = 0;
}
/***************************************************************************
*
*
* class IPHistoryManager Holds IP records (bans, pings, etc)
*
*
***************************************************************************/
IPHistoryManager::IPHistoryManager(void)
{
m_lastDecayTime = 0;
}
IPHistoryManager::~IPHistoryManager(void)
{
m_ips.clear();
}
void IPHistoryManager::tick(void)
{
// periodic events
ADDTOCALLSTACK("IPHistoryManager::tick");
// check if ttl should decay (only do this once every second)
const bool decayTTL = (!(m_lastDecayTime > 0) || CWorldGameTime::GetCurrentTime().GetTimeDiff(m_lastDecayTime) > 1 * MSECS_PER_SEC);
if (decayTTL)
m_lastDecayTime = CWorldGameTime::GetCurrentTime().GetTimeRaw();
for (IPHistoryList::iterator it = m_ips.begin(), end = m_ips.end(); it != end; ++it)
{
if (it->m_fBlocked)
{
// blocked ips don't decay, but check if the ban has expired
if (it->m_iBlockExpireMs > 0 && (CWorldGameTime::GetCurrentTime().GetTimeRaw() > it->m_iBlockExpireMs))
it->setBlocked(false);
}
else if (decayTTL)
{
if (it->m_iAliveSuccessfulConnections == 0 && it->m_iPendingConnectionRequests == 0)
{
// start to forget about clients who aren't connected
if (it->m_iTTLSeconds >= 0)
--it->m_iTTLSeconds;
}
// wait a 5th of TTL between each ping decay, but do not wait less than 30 seconds
if (it->m_iPings > 0 && --it->m_iPingDecay < 0)
{
--it->m_iPings;
it->m_iPingDecay = std::max(30, g_Cfg.m_iNetHistoryTTLSeconds / 5);
}
}
}
// clear old ip history
std::erase_if(m_ips, [](HistoryIP const& elem) {
return elem.m_iTTLSeconds < 0;
});
}
HistoryIP& IPHistoryManager::getHistoryForIP(const CSocketAddressIP& ip) noexcept
{
// get history for an ip
// find existing entry
for (IPHistoryList::iterator it = m_ips.begin(), end = m_ips.end(); it != end; ++it)
{
if (it->m_ip == ip)
return *it;
}
// create a new entry
HistoryIP hist = {
.m_ip = ip,
.m_fBlocked = 0,
.m_iPingDecay = 30,
.m_iPings = 0,
.m_iConnectionRequests = 0,
.m_iPendingConnectionRequests = 0,
.m_iAliveSuccessfulConnections = 0,
.m_iTTLSeconds = 0, // updated by update() method
.m_iTimeLastConnectedMs = 0, // set by the caller, if needed
.m_iBlockExpireMs = 0,
};
hist.update();
return m_ips.emplace_back(std::move(hist));
}
HistoryIP& IPHistoryManager::getHistoryForIP(const char* ip)
{
// get history for an ip
ADDTOCALLSTACK("IPHistoryManager::getHistoryForIP");
CSocketAddressIP me(ip);
return getHistoryForIP(me);
}