mirror of
https://github.com/znc/znc.git
synced 2026-06-28 05:51:50 +02:00
Implement protection from flood.
For ZNC-server connection
This commit is contained in:
@@ -114,6 +114,16 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FindDoubleEntry(const CString& sName, double& fRes, double fDefault = 0) {
|
||||
CString s;
|
||||
if (FindStringEntry(sName, s)) {
|
||||
fRes = s.ToDouble();
|
||||
return true;
|
||||
}
|
||||
fRes = fDefault;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FindSubConfig(const CString& sName, SubConfig& Config, bool bErase = true) {
|
||||
SubConfigMap::iterator it = m_SubConfigs.find(sName);
|
||||
if (it == m_SubConfigs.end()) {
|
||||
|
||||
@@ -137,6 +137,11 @@ public:
|
||||
void SetIdent(const CString& s);
|
||||
void SetRealName(const CString& s);
|
||||
|
||||
double GetFloodRate() const { return m_fFloodRate; }
|
||||
unsigned short int GetFloodBurst() const { return m_uFloodBurst; }
|
||||
void SetFloodRate(double fFloodRate) { m_fFloodRate = fFloodRate; }
|
||||
void SetFloodBurst(unsigned short int uFloodBurst) { m_uFloodBurst = uFloodBurst; }
|
||||
|
||||
CString ExpandString(const CString& sStr) const;
|
||||
CString& ExpandString(const CString& sStr, CString& sRet) const;
|
||||
private:
|
||||
@@ -170,6 +175,9 @@ protected:
|
||||
CNick m_IRCNick;
|
||||
bool m_bIRCAway;
|
||||
|
||||
double m_fFloodRate; ///< Set to -1 to disable protection.
|
||||
unsigned short int m_uFloodBurst;
|
||||
|
||||
CBuffer m_RawBuffer;
|
||||
CBuffer m_MotdBuffer;
|
||||
CBuffer m_QueryBuffer;
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
#include <znc/Socket.h>
|
||||
#include <znc/Nick.h>
|
||||
|
||||
#include <deque>
|
||||
using std::deque;
|
||||
|
||||
// Forward Declarations
|
||||
class CChan;
|
||||
class CUser;
|
||||
@@ -54,6 +57,7 @@ public:
|
||||
virtual void ReachedMaxBuffer();
|
||||
|
||||
void PutIRC(const CString& sLine);
|
||||
void PutIRCQuick(const CString& sLine); //!< Should be used for PONG only
|
||||
void ResetChans();
|
||||
void Quit(const CString& sQuitMsg = "");
|
||||
|
||||
@@ -102,6 +106,7 @@ private:
|
||||
// This is called when we connect and the nick we want is already taken
|
||||
void SendAltNick(const CString& sBadNick);
|
||||
void SendNextCap();
|
||||
void TrySend();
|
||||
protected:
|
||||
bool m_bAuthed;
|
||||
bool m_bNamesx;
|
||||
@@ -123,6 +128,13 @@ protected:
|
||||
static const time_t m_uCTCPFloodTime;
|
||||
static const unsigned int m_uCTCPFloodCount;
|
||||
MCString m_mISupport;
|
||||
deque<CString> m_vsSendQueue;
|
||||
short int m_iSendsAllowed;
|
||||
unsigned short int m_uFloodBurst;
|
||||
double m_fFloodRate;
|
||||
bool m_bFloodProtection;
|
||||
|
||||
friend class CIRCFloodTimer;
|
||||
};
|
||||
|
||||
#endif // !_IRCSOCK_H
|
||||
|
||||
@@ -49,6 +49,9 @@ CIRCNetwork::CIRCNetwork(CUser *pUser, const CString& sName) {
|
||||
m_sChanPrefixes = "";
|
||||
m_bIRCAway = false;
|
||||
|
||||
m_fFloodRate = 1;
|
||||
m_uFloodBurst = 4;
|
||||
|
||||
m_RawBuffer.SetLineCount(100, true); // This should be more than enough raws, especially since we are buffering the MOTD separately
|
||||
m_MotdBuffer.SetLineCount(200, true); // This should be more than enough motd lines
|
||||
m_QueryBuffer.SetLineCount(250, true);
|
||||
@@ -78,6 +81,9 @@ CIRCNetwork::CIRCNetwork(CUser *pUser, const CIRCNetwork &Network) {
|
||||
void CIRCNetwork::Clone(const CIRCNetwork& Network) {
|
||||
m_sName = Network.GetName();
|
||||
|
||||
m_fFloodRate = Network.GetFloodRate();
|
||||
m_uFloodBurst = Network.GetFloodBurst();
|
||||
|
||||
SetNick(Network.GetNick());
|
||||
SetAltNick(Network.GetAltNick());
|
||||
SetIdent(Network.GetIdent());
|
||||
@@ -247,6 +253,14 @@ bool CIRCNetwork::ParseConfig(CConfig *pConfig, CString& sError, bool bUpgrade)
|
||||
{ "ircconnectenabled", &CIRCNetwork::SetIRCConnectEnabled },
|
||||
};
|
||||
size_t numBoolOptions = sizeof(BoolOptions) / sizeof(BoolOptions[0]);
|
||||
TOption<double> DoubleOptions[] = {
|
||||
{ "floodrate", &CIRCNetwork::SetFloodRate },
|
||||
};
|
||||
size_t numDoubleOptions = sizeof(DoubleOptions) / sizeof(DoubleOptions[0]);
|
||||
TOption<short unsigned int> SUIntOptions[] = {
|
||||
{ "floodburst", &CIRCNetwork::SetFloodBurst },
|
||||
};
|
||||
size_t numSUIntOptions = sizeof(SUIntOptions) / sizeof(SUIntOptions[0]);
|
||||
|
||||
for (size_t i = 0; i < numStringOptions; i++) {
|
||||
CString sValue;
|
||||
@@ -260,6 +274,18 @@ bool CIRCNetwork::ParseConfig(CConfig *pConfig, CString& sError, bool bUpgrade)
|
||||
(this->*BoolOptions[i].pSetter)(sValue.ToBool());
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < numDoubleOptions; ++i) {
|
||||
double fValue;
|
||||
if (pConfig->FindDoubleEntry(DoubleOptions[i].name, fValue))
|
||||
(this->*DoubleOptions[i].pSetter)(fValue);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < numSUIntOptions; ++i) {
|
||||
unsigned int value;
|
||||
if (pConfig->FindUIntEntry(SUIntOptions[i].name, value))
|
||||
(this->*SUIntOptions[i].pSetter)(value);
|
||||
}
|
||||
|
||||
pConfig->FindStringVector("loadmodule", vsList);
|
||||
for (vit = vsList.begin(); vit != vsList.end(); ++vit) {
|
||||
CString sValue = *vit;
|
||||
@@ -348,6 +374,8 @@ CConfig CIRCNetwork::ToConfig() {
|
||||
}
|
||||
|
||||
config.AddKeyValuePair("IRCConnectEnabled", CString(GetIRCConnectEnabled()));
|
||||
config.AddKeyValuePair("FloodRate", CString(GetFloodRate()));
|
||||
config.AddKeyValuePair("FloodBurst", CString(GetFloodBurst()));
|
||||
|
||||
// Modules
|
||||
CModules& Mods = GetModules();
|
||||
|
||||
+46
-3
@@ -19,11 +19,35 @@
|
||||
const time_t CIRCSock::m_uCTCPFloodTime = 5;
|
||||
const unsigned int CIRCSock::m_uCTCPFloodCount = 5;
|
||||
|
||||
// It will be bad if user sets it to 0.00000000000001
|
||||
// If you want no flood protection, set network's flood rate to -1
|
||||
static const double FLOOD_MINIMAL_RATE = 0.3;
|
||||
|
||||
class CIRCFloodTimer : public CCron {
|
||||
CIRCSock* m_pSock;
|
||||
public:
|
||||
CIRCFloodTimer(CIRCSock* pSock) {
|
||||
m_pSock = pSock;
|
||||
StartMaxCycles(m_pSock->m_fFloodRate, 0);
|
||||
}
|
||||
virtual void RunJob() {
|
||||
if (m_pSock->m_iSendsAllowed < m_pSock->m_uFloodBurst) {
|
||||
m_pSock->m_iSendsAllowed++;
|
||||
}
|
||||
m_pSock->TrySend();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
CIRCSock::CIRCSock(CIRCNetwork* pNetwork) : CZNCSock() {
|
||||
m_pNetwork = pNetwork;
|
||||
m_bAuthed = false;
|
||||
m_bNamesx = false;
|
||||
m_bUHNames = false;
|
||||
m_fFloodRate = m_pNetwork->GetFloodRate();
|
||||
m_uFloodBurst = m_pNetwork->GetFloodBurst();
|
||||
m_bFloodProtection = m_fFloodRate > FLOOD_MINIMAL_RATE;
|
||||
m_iSendsAllowed = m_uFloodBurst;
|
||||
EnableReadLine();
|
||||
m_Nick.SetIdent(m_pNetwork->GetIdent());
|
||||
m_Nick.SetHost(m_pNetwork->GetUser()->GetBindHost());
|
||||
@@ -49,6 +73,9 @@ CIRCSock::CIRCSock(CIRCNetwork* pNetwork) : CZNCSock() {
|
||||
|
||||
// RFC says a line can have 512 chars max, but we don't care ;)
|
||||
SetMaxBufferThreshold(1024);
|
||||
if (m_bFloodProtection) {
|
||||
AddCron(new CIRCFloodTimer(this));
|
||||
}
|
||||
}
|
||||
|
||||
CIRCSock::~CIRCSock() {
|
||||
@@ -98,7 +125,7 @@ void CIRCSock::ReadLine(const CString& sData) {
|
||||
if (sLine.Equals("PING ", false, 5)) {
|
||||
// Generate a reply and don't forward this to any user,
|
||||
// we don't want any PING forwarded
|
||||
PutIRC("PONG " + sLine.substr(5));
|
||||
PutIRCQuick("PONG " + sLine.substr(5));
|
||||
return;
|
||||
} else if (sLine.Token(1).Equals("PONG")) {
|
||||
// Block PONGs, we already responded to the pings
|
||||
@@ -901,8 +928,24 @@ bool CIRCSock::OnChanMsg(CNick& Nick, const CString& sChan, CString& sMessage) {
|
||||
}
|
||||
|
||||
void CIRCSock::PutIRC(const CString& sLine) {
|
||||
DEBUG("(" << m_pNetwork->GetUser()->GetUserName() << "/" << m_pNetwork->GetName() << ") ZNC -> IRC [" << sLine << "]");
|
||||
Write(sLine + "\r\n");
|
||||
DEBUG("(" << m_pNetwork->GetUser()->GetUserName() << "/" << m_pNetwork->GetName() << ") ZNC -> IRC [" << sLine << "] (queued)");
|
||||
m_vsSendQueue.push_back(sLine);
|
||||
TrySend();
|
||||
}
|
||||
|
||||
void CIRCSock::PutIRCQuick(const CString& sLine) {
|
||||
DEBUG("(" << m_pNetwork->GetUser()->GetUserName() << "/" << m_pNetwork->GetName() << ") ZNC -> IRC [" << sLine << "] (queued to front)");
|
||||
m_vsSendQueue.push_front(sLine);
|
||||
TrySend();
|
||||
}
|
||||
|
||||
void CIRCSock::TrySend() {
|
||||
while (!m_vsSendQueue.empty() && (m_iSendsAllowed > 0 || !m_bFloodProtection)) {
|
||||
m_iSendsAllowed--;
|
||||
DEBUG("(" << m_pNetwork->GetUser()->GetUserName() << "/" << m_pNetwork->GetName() << ") ZNC -> IRC [" << m_vsSendQueue.front() << "]");
|
||||
Write(m_vsSendQueue.front() + "\r\n");
|
||||
m_vsSendQueue.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
void CIRCSock::SetNick(const CString& sNick) {
|
||||
|
||||
+2
-2
@@ -188,8 +188,8 @@ void CZNC::Loop() {
|
||||
}
|
||||
|
||||
// Csocket wants micro seconds
|
||||
// 500 msec to 600 sec
|
||||
m_Manager.DynamicSelectLoop(500 * 1000, 600 * 1000 * 1000);
|
||||
// 100 msec to 600 sec
|
||||
m_Manager.DynamicSelectLoop(100 * 1000, 600 * 1000 * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user