Skip to content

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
...
  • 4 commits
  • 11 files changed
  • 0 commit comments
  • 1 contributor
View
10 include/znc/Config.h
@@ -114,6 +114,16 @@ class CConfig {
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()) {
View
18 include/znc/Csocket.h
@@ -362,16 +362,18 @@ class CCron
virtual ~CCron() {}
//! This is used by the Job Manager, and not you directly
- void run( time_t & iNow );
+ void run( timeval & tNow );
/**
* @param TimeSequence how often to run in seconds
* @param iMaxCycles how many times to run, 0 makes it run forever
*/
- void StartMaxCycles( int TimeSequence, u_int iMaxCycles );
+ void StartMaxCycles( double fTimeSequence, u_int iMaxCycles );
+ void StartMaxCycles( const timeval& tTimeSequence, u_int iMaxCycles );
//! starts and runs infinity amount of times
- void Start( int TimeSequence );
+ void Start( double fTimeSequence );
+ void Start( const timeval& TimeSequence );
//! call this to turn off your cron, it will be removed
void Stop();
@@ -382,7 +384,7 @@ class CCron
//! removes the pause on RunJon
void UnPause();
- int GetInterval() const;
+ timeval GetInterval() const;
u_int GetMaxCycles() const;
u_int GetCyclesLeft() const;
@@ -393,7 +395,7 @@ class CCron
void SetName( const CS_STRING & sName );
//! returns the timestamp of the next estimated run time. Note that it may not run at this EXACT time, but it will run at least at this time or after
- time_t GetNextRun() const { return( m_iTime ); }
+ timeval GetNextRun() const { return( m_tTime ); }
public:
@@ -404,9 +406,9 @@ class CCron
bool m_bRunOnNextCall; //!< if set to true, RunJob() gets called on next invocation of run() despite the timeout
private:
- time_t m_iTime;
+ timeval m_tTime;
bool m_bActive, m_bPause;
- int m_iTimeSequence;
+ timeval m_tTimeSequence;
u_int m_iMaxCycles, m_iCycles;
CS_STRING m_sName;
};
@@ -1494,7 +1496,7 @@ class CSocketManager : public std::vector<Csock *>, public CSockCommon
*/
void Select( std::map<Csock *, EMessages> & mpeSocks );
- time_t GetDynamicSleepTime( time_t iNow, time_t iMaxResolution = 3600 ) const;
+ timeval GetDynamicSleepTime( const timeval& tNow, const timeval& tMaxResolution ) const;
//! internal use only
virtual void SelectSock( std::map<Csock *, EMessages> & mpeSocks, EMessages eErrno, Csock * pcSock );
View
8 include/znc/IRCNetwork.h
@@ -137,6 +137,11 @@ class CIRCNetwork {
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 @@ class CIRCNetwork {
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;
View
12 include/znc/IRCSock.h
@@ -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 @@ class CIRCSock : public CZNCSock {
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 @@ class CIRCSock : public CZNCSock {
// 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 @@ class CIRCSock : public CZNCSock {
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
View
10 modules/data/webadmin/tmpl/add_edit_network.tmpl
@@ -57,6 +57,16 @@
</textarea></div>
<br /><span class="info">One server per line, host [[+]port] [password]</span>
</div>
+
+ <div class="subsection">
+ <div class="inputlabel">Flood protection rate:</div>
+ <div><input type="text" name="floodrate" value="<? VAR FloodRate ?>" />Set to -1 to disable flood protection</div>
+ </div>
+
+ <div class="subsection">
+ <div class="inputlabel">Flood protection burst:</div>
+ <div><input type="number" name="floodburst" value="<? VAR FloodBurst ?>" min="0" /></div>
+ </div>
<div style="clear: both;"></div>
</div>
</div>
View
8 modules/webadmin.cpp
@@ -678,6 +678,9 @@ class CWebAdminMod : public CModule {
Tmpl["Ident"] = pNetwork->GetIdent();
Tmpl["RealName"] = pNetwork->GetRealName();
+ Tmpl["FloodRate"] = CString(pNetwork->GetFloodRate());
+ Tmpl["FloodBurst"] = CString(pNetwork->GetFloodBurst());
+
Tmpl["IRCConnectEnabled"] = CString(pNetwork->GetIRCConnectEnabled());
const vector<CServer*>& vServers = pNetwork->GetServers();
@@ -715,6 +718,8 @@ class CWebAdminMod : public CModule {
Tmpl["Action"] = "addnetwork";
Tmpl["Title"] = "Add Network for User [" + pUser->GetUserName() + "]";
Tmpl["IRCConnectEnabled"] = "true";
+ Tmpl["FloodRate"] = "1.0";
+ Tmpl["FloodBurst"] = "4";
}
return true;
@@ -743,6 +748,9 @@ class CWebAdminMod : public CModule {
pNetwork->SetIRCConnectEnabled(WebSock.GetParam("doconnect").ToBool());
+ pNetwork->SetFloodRate(WebSock.GetParam("floodrate").ToDouble());
+ pNetwork->SetFloodBurst(WebSock.GetParam("floodburst").ToUInt());
+
VCString vsArgs;
pNetwork->DelServers();
View
98 src/Csocket.cpp
@@ -627,21 +627,22 @@ CCron::CCron()
m_iCycles = 0;
m_iMaxCycles = 0;
m_bActive = true;
- m_iTime = 0;
- m_iTimeSequence = 60;
+ timerclear( &m_tTime );
+ m_tTimeSequence.tv_sec = 60;
+ m_tTimeSequence.tv_usec = 0;
m_bPause = false;
m_bRunOnNextCall = false;
}
-void CCron::run( time_t & iNow )
+void CCron::run( timeval & tNow )
{
if( m_bPause )
return;
- if( iNow == 0 )
- iNow = time( NULL );
+ if( !timerisset( &tNow ) )
+ gettimeofday( &tNow, NULL );
- if(( m_bActive ) && ( iNow >= m_iTime || m_bRunOnNextCall ) )
+ if(( m_bActive ) && ( !timercmp( &tNow, &m_tTime, < ) || m_bRunOnNextCall ) )
{
m_bRunOnNextCall = false; // Setting this here because RunJob() could set it back to true
RunJob();
@@ -649,22 +650,37 @@ void CCron::run( time_t & iNow )
if(( m_iMaxCycles > 0 ) && ( ++m_iCycles >= m_iMaxCycles ) )
m_bActive = false;
else
- m_iTime = iNow + m_iTimeSequence;
+ timeradd( &tNow, &m_tTimeSequence, &m_tTime );
}
}
-void CCron::StartMaxCycles( int TimeSequence, u_int iMaxCycles )
+void CCron::StartMaxCycles( double fTimeSequence, u_int iMaxCycles )
{
- m_iTimeSequence = TimeSequence;
- m_iTime = time( NULL ) + m_iTimeSequence;
+ timeval tNow;
+ m_tTimeSequence.tv_sec = (time_t) fTimeSequence;
+ m_tTimeSequence.tv_usec = (suseconds_t) ((fTimeSequence - (time_t) fTimeSequence) * 1000000);
+ gettimeofday( &tNow, NULL );
+ timeradd( &tNow, &m_tTimeSequence, &m_tTime );
m_iMaxCycles = iMaxCycles;
}
-void CCron::Start( int TimeSequence )
+void CCron::StartMaxCycles( const timeval& tTimeSequence, u_int iMaxCycles )
{
- m_iTimeSequence = TimeSequence;
- m_iTime = time( NULL ) + m_iTimeSequence;
- m_iMaxCycles = 0;
+ timeval tNow;
+ m_tTimeSequence = tTimeSequence;
+ gettimeofday( &tNow, NULL );
+ timeradd( &tNow, &m_tTimeSequence, &m_tTime );
+ m_iMaxCycles = iMaxCycles;
+}
+
+void CCron::Start( double fTimeSequence )
+{
+ StartMaxCycles( fTimeSequence, 0 );
+}
+
+void CCron::Start( const timeval& tTimeSequence )
+{
+ StartMaxCycles( tTimeSequence, 0 );
}
void CCron::Stop()
@@ -682,7 +698,7 @@ void CCron::UnPause()
m_bPause = false;
}
-int CCron::GetInterval() const { return( m_iTimeSequence ); }
+timeval CCron::GetInterval() const { return( m_tTimeSequence ); }
u_int CCron::GetMaxCycles() const { return( m_iMaxCycles ); }
u_int CCron::GetCyclesLeft() const { return(( m_iMaxCycles > m_iCycles ? ( m_iMaxCycles - m_iCycles ) : 0 ) ); }
@@ -765,7 +781,8 @@ void CSockCommon::AssignFDs( std::map< int, short > & miiReadyFds, struct timeva
void CSockCommon::Cron()
{
- time_t iNow = 0;
+ timeval tNow;
+ timerclear( &tNow );
for( vector<CCron *>::size_type a = 0; a < m_vcCrons.size(); a++ )
{
@@ -777,7 +794,7 @@ void CSockCommon::Cron()
m_vcCrons.erase( m_vcCrons.begin() + a-- );
}
else
- pcCron->run( iNow );
+ pcCron->run( tNow );
}
}
@@ -2893,9 +2910,13 @@ void CSocketManager::DynamicSelectLoop( u_long iLowerBounds, u_long iUpperBounds
if( m_errno == SELECT_TIMEOUT )
{
// only do this if the previous call to select was a timeout
- time_t iNow = time( NULL );
- u_long iSelectTimeout = GetDynamicSleepTime( iNow, iMaxResolution );
- iSelectTimeout *= 1000000;
+ timeval tMaxResolution;
+ timeval tNow;
+ tMaxResolution.tv_sec = iMaxResolution;
+ tMaxResolution.tv_usec = 0;
+ gettimeofday( &tNow, NULL );
+ timeval tSelectTimeout = GetDynamicSleepTime( tNow, tMaxResolution );
+ u_long iSelectTimeout = tSelectTimeout.tv_sec * 1000000 + tSelectTimeout.tv_usec;
iSelectTimeout = std::max( iLowerBounds, iSelectTimeout );
iSelectTimeout = std::min( iSelectTimeout, iUpperBounds );
if( iLowerBounds != iSelectTimeout )
@@ -3478,9 +3499,18 @@ void CSocketManager::Select( std::map<Csock *, EMessages> & mpeSocks )
}
}
-time_t CSocketManager::GetDynamicSleepTime( time_t iNow, time_t iMaxResolution ) const
+inline void MinimizeTime( timeval& min, const timeval& another )
+{
+ if( timercmp( &min, &another, > ) )
+ {
+ min = another;
+ }
+}
+
+timeval CSocketManager::GetDynamicSleepTime( const timeval& tNow, const timeval& tMaxResolution ) const
{
- time_t iNextRunTime = iNow + iMaxResolution;
+ timeval tNextRunTime;
+ timeradd( &tNow, &tMaxResolution, &tNextRunTime );
std::vector<Csock *>::const_iterator it;
// This is safe, because we don't modify the vector.
std::vector<Csock *>::const_iterator it_end = this->end();
@@ -3490,29 +3520,37 @@ time_t CSocketManager::GetDynamicSleepTime( time_t iNow, time_t iMaxResolution )
Csock* pSock = *it;
if( pSock->GetConState() != Csock::CST_OK )
- iNextRunTime = iNow; // this is in a nebulous state, need to let it proceed like normal
+ tNextRunTime = tNow; // this is in a nebulous state, need to let it proceed like normal
time_t iTimeoutInSeconds = pSock->GetTimeout();
if( iTimeoutInSeconds > 0 )
{
- time_t iNextTimeout = pSock->GetNextCheckTimeout( iNow );
- iNextRunTime = std::min( iNextRunTime, iNextTimeout );
+ timeval tNextTimeout;
+ tNextTimeout.tv_sec = pSock->GetNextCheckTimeout( 0 ); // TODO convert socket timeouts to timeval too?
+ tNextTimeout.tv_usec = 0;
+ MinimizeTime( tNextRunTime, tNextTimeout );
}
const std::vector<CCron *> & vCrons = pSock->GetCrons();
std::vector<CCron *>::const_iterator cit;
std::vector<CCron *>::const_iterator cit_end = vCrons.end();
for( cit = vCrons.begin(); cit != cit_end; cit++ )
- iNextRunTime = std::min( iNextRunTime, ( *cit )->GetNextRun() );
+ MinimizeTime( tNextRunTime, ( *cit )->GetNextRun() );
}
std::vector<CCron *>::const_iterator cit;
std::vector<CCron *>::const_iterator cit_end = m_vcCrons.end();
for( cit = m_vcCrons.begin(); cit != cit_end; cit++ )
- iNextRunTime = std::min( iNextRunTime, ( *cit )->GetNextRun() );
+ MinimizeTime( tNextRunTime, ( *cit )->GetNextRun() );
- if( iNextRunTime < iNow )
- return( 0 ); // smallest unit possible
- return( std::min( iNextRunTime - iNow, iMaxResolution ) );
+ timeval tReturnValue;
+ if( timercmp( &tNextRunTime, &tNow, < ) )
+ {
+ timerclear( &tReturnValue );
+ return( tReturnValue ); // smallest unit possible
+ }
+ timersub( &tNextRunTime, &tNow, &tReturnValue );
+ MinimizeTime( tReturnValue, tMaxResolution );
+ return( tReturnValue );
}
void CSocketManager::SelectSock( std::map<Csock *, EMessages> & mpeSocks, EMessages eErrno, Csock * pcSock )
View
28 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<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();
View
49 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) {
View
3 src/Modules.cpp
@@ -303,10 +303,11 @@ void CModule::ListTimers() {
for (it = m_sTimers.begin(); it != m_sTimers.end(); ++it) {
CTimer* pTimer = *it;
unsigned int uCycles = pTimer->GetCyclesLeft();
+ timeval Interval = pTimer->GetInterval();
Table.AddRow();
Table.SetCell("Name", pTimer->GetName());
- Table.SetCell("Secs", CString(pTimer->GetInterval()));
+ Table.SetCell("Secs", CString(Interval.tv_sec) + "seconds" + (Interval.tv_usec ? " " + CString(Interval.tv_usec) + " microseconds" : ""));
Table.SetCell("Cycles", ((uCycles) ? CString(uCycles) : "INF"));
Table.SetCell("Description", pTimer->GetDescription());
}
View
4 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);
}
}

No commit comments for this range

Something went wrong with that request. Please try again.