From c98abf00a5401995f8b27836af1cc6453c9569be Mon Sep 17 00:00:00 2001 From: Alexey Sokolov Date: Wed, 21 Mar 2012 19:48:26 +0700 Subject: [PATCH] Implement protection from flood. For ZNC-server connection --- include/znc/Config.h | 10 ++++++++ include/znc/IRCNetwork.h | 8 +++++++ include/znc/IRCSock.h | 12 ++++++++++ src/IRCNetwork.cpp | 28 +++++++++++++++++++++++ src/IRCSock.cpp | 49 +++++++++++++++++++++++++++++++++++++--- src/znc.cpp | 4 ++-- 6 files changed, 106 insertions(+), 5 deletions(-) diff --git a/include/znc/Config.h b/include/znc/Config.h index 990e4592..e66ee76c 100644 --- a/include/znc/Config.h +++ b/include/znc/Config.h @@ -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()) { diff --git a/include/znc/IRCNetwork.h b/include/znc/IRCNetwork.h index 1895472d..401b7b2b 100644 --- a/include/znc/IRCNetwork.h +++ b/include/znc/IRCNetwork.h @@ -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; diff --git a/include/znc/IRCSock.h b/include/znc/IRCSock.h index a30a1172..873b171a 100644 --- a/include/znc/IRCSock.h +++ b/include/znc/IRCSock.h @@ -13,6 +13,9 @@ #include #include +#include +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 m_vsSendQueue; + short int m_iSendsAllowed; + unsigned short int m_uFloodBurst; + double m_fFloodRate; + bool m_bFloodProtection; + + friend class CIRCFloodTimer; }; #endif // !_IRCSOCK_H diff --git a/src/IRCNetwork.cpp b/src/IRCNetwork.cpp index 7d0378d7..ebee49b8 100644 --- a/src/IRCNetwork.cpp +++ b/src/IRCNetwork.cpp @@ -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 DoubleOptions[] = { + { "floodrate", &CIRCNetwork::SetFloodRate }, + }; + size_t numDoubleOptions = sizeof(DoubleOptions) / sizeof(DoubleOptions[0]); + TOption 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(); diff --git a/src/IRCSock.cpp b/src/IRCSock.cpp index d6ddf48a..cf101aa2 100644 --- a/src/IRCSock.cpp +++ b/src/IRCSock.cpp @@ -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) { diff --git a/src/znc.cpp b/src/znc.cpp index 5718d206..6fca4063 100644 --- a/src/znc.cpp +++ b/src/znc.cpp @@ -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); } }