Implement protection from flood.

For ZNC-server connection
This commit is contained in:
Alexey Sokolov
2012-03-21 19:48:26 +07:00
parent 3aa6b581fc
commit c98abf00a5
6 changed files with 106 additions and 5 deletions
+10
View File
@@ -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()) {
+8
View File
@@ -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;
+12
View File
@@ -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
+28
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
}