mirror of
https://github.com/znc/znc.git
synced 2026-03-28 17:42:41 +01:00
Create a connection queue including all the networks we need to connect
This commit is contained in:
@@ -95,6 +95,7 @@ public:
|
||||
bool IsIRCAway() const { return m_bIRCAway; }
|
||||
void SetIRCAway(bool b) { m_bIRCAway = b; }
|
||||
|
||||
bool Connect();
|
||||
/** This method will return whether the user is connected and authenticated to an IRC server.
|
||||
*/
|
||||
bool IsIRCConnected() const;
|
||||
|
||||
@@ -15,13 +15,15 @@
|
||||
#include <znc/Socket.h>
|
||||
#include <znc/Listener.h>
|
||||
#include <map>
|
||||
#include <list>
|
||||
|
||||
using std::map;
|
||||
using std::list;
|
||||
|
||||
class CListener;
|
||||
class CUser;
|
||||
class CIRCNetwork;
|
||||
class CConnectUserTimer;
|
||||
class CConnectQueueTimer;
|
||||
class CConfig;
|
||||
class CFile;
|
||||
|
||||
@@ -139,14 +141,18 @@ public:
|
||||
const VCString& GetMotd() const { return m_vsMotd; }
|
||||
// !MOTD
|
||||
|
||||
// Create a CIRCSocket. Return false if user cant connect
|
||||
bool ConnectNetwork(CIRCNetwork *pNetwork);
|
||||
// This creates a CConnectUserTimer if we haven't got one yet
|
||||
void EnableConnectUser();
|
||||
void DisableConnectUser();
|
||||
void AddServerThrottle(CString sName) { m_sConnectThrottle.AddItem(sName); }
|
||||
bool GetServerThrottle(CString sName) { return m_sConnectThrottle.GetItem(sName); }
|
||||
|
||||
// Never call this unless you are CConnectUserTimer::~CConnectUserTimer()
|
||||
void LeakConnectUser(CConnectUserTimer *pTimer);
|
||||
void AddNetworkToQueue(CIRCNetwork *pNetwork);
|
||||
list<CIRCNetwork*>& GetConnectionQueue() { return m_lpConnectQueue; }
|
||||
|
||||
// This creates a CConnectQueueTimer if we haven't got one yet
|
||||
void EnableConnectQueue();
|
||||
void DisableConnectQueue();
|
||||
|
||||
// Never call this unless you are CConnectQueueTimer::~CConnectQueueTimer()
|
||||
void LeakConnectQueueTimer(CConnectQueueTimer *pTimer);
|
||||
|
||||
static void DumpConfig(const CConfig* Config);
|
||||
|
||||
@@ -187,7 +193,8 @@ protected:
|
||||
CModules* m_pModules;
|
||||
unsigned long long m_uBytesRead;
|
||||
unsigned long long m_uBytesWritten;
|
||||
CConnectUserTimer *m_pConnectUserTimer;
|
||||
list<CIRCNetwork*> m_lpConnectQueue;
|
||||
CConnectQueueTimer *m_pConnectQueueTimer;
|
||||
TCacheMap<CString> m_sConnectThrottle;
|
||||
bool m_bProtectWebSessions;
|
||||
};
|
||||
|
||||
@@ -159,6 +159,9 @@ CIRCNetwork::~CIRCNetwork() {
|
||||
m_vChans.clear();
|
||||
|
||||
SetUser(NULL);
|
||||
|
||||
// Make sure we are not in the connection queue
|
||||
CZNC::Get().GetConnectionQueue().remove(this);
|
||||
}
|
||||
|
||||
void CIRCNetwork::DelServers() {
|
||||
@@ -810,6 +813,51 @@ CString CIRCNetwork::GetCurNick() const {
|
||||
return "";
|
||||
}
|
||||
|
||||
bool CIRCNetwork::Connect() {
|
||||
if (!m_pUser->GetIRCConnectEnabled() || m_pIRCSock || !HasServers())
|
||||
return false;
|
||||
|
||||
CServer *pServer = GetNextServer();
|
||||
if (!pServer)
|
||||
return false;
|
||||
|
||||
if (CZNC::Get().GetServerThrottle(pServer->GetName())) {
|
||||
CZNC::Get().AddNetworkToQueue(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
CZNC::Get().AddServerThrottle(pServer->GetName());
|
||||
|
||||
CIRCSock *pIRCSock = new CIRCSock(this);
|
||||
pIRCSock->SetPass(pServer->GetPass());
|
||||
|
||||
bool bSSL = false;
|
||||
#ifdef HAVE_LIBSSL
|
||||
if (pServer->IsSSL()) {
|
||||
bSSL = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
DEBUG("Connecting user/network [" << m_sName << "/" << m_sName << "]");
|
||||
|
||||
NETWORKMODULECALL(OnIRCConnecting(pIRCSock), m_pUser, this, NULL,
|
||||
DEBUG("Some module aborted the connection attempt");
|
||||
PutStatus("Some module aborted the connection attempt");
|
||||
delete pIRCSock;
|
||||
CZNC::Get().AddNetworkToQueue(this);
|
||||
return false;
|
||||
);
|
||||
|
||||
CString sSockName = "IRC::" + m_pUser->GetUserName() + "::" + m_sName;
|
||||
if (!CZNC::Get().GetManager().Connect(pServer->GetName(), pServer->GetPort(), sSockName, 120, bSSL, m_pUser->GetBindHost(), pIRCSock)) {
|
||||
PutStatus("Unable to connect. (Bad host?)");
|
||||
CZNC::Get().AddNetworkToQueue(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CIRCNetwork::IsIRCConnected() const {
|
||||
const CIRCSock* pSock = GetIRCSock();
|
||||
return (pSock && pSock->IsAuthed());
|
||||
@@ -832,7 +880,7 @@ void CIRCNetwork::IRCDisconnected() {
|
||||
void CIRCNetwork::CheckIRCConnect() {
|
||||
// Do we want to connect?
|
||||
if (m_pUser->GetIRCConnectEnabled() && GetIRCSock() == NULL)
|
||||
CZNC::Get().EnableConnectUser();
|
||||
CZNC::Get().AddNetworkToQueue(this);
|
||||
}
|
||||
|
||||
bool CIRCNetwork::PutIRC(const CString& sLine) {
|
||||
|
||||
191
src/znc.cpp
191
src/znc.cpp
@@ -15,7 +15,6 @@
|
||||
#include <znc/IRCNetwork.h>
|
||||
#include <znc/Listener.h>
|
||||
#include <znc/Config.h>
|
||||
#include <list>
|
||||
|
||||
static inline CString FormatBindError() {
|
||||
CString sError = (errno == 0 ? CString("unknown error, check the host name") : CString(strerror(errno)));
|
||||
@@ -34,7 +33,7 @@ CZNC::CZNC() {
|
||||
m_uBytesRead = 0;
|
||||
m_uBytesWritten = 0;
|
||||
m_uiMaxBufferSize = 500;
|
||||
m_pConnectUserTimer = NULL;
|
||||
m_pConnectQueueTimer = NULL;
|
||||
m_eConfigState = ECONFIG_NOTHING;
|
||||
m_TimeStarted = time(NULL);
|
||||
m_sConnectThrottle.SetTTL(30000);
|
||||
@@ -57,8 +56,8 @@ CZNC::~CZNC() {
|
||||
a->second->SetBeingDeleted(true);
|
||||
}
|
||||
|
||||
m_pConnectUserTimer = NULL;
|
||||
// This deletes m_pConnectUserTimer
|
||||
m_pConnectQueueTimer = NULL;
|
||||
// This deletes m_pConnectQueueTimer
|
||||
m_Manager.Cleanup();
|
||||
DeleteUsers();
|
||||
|
||||
@@ -103,54 +102,6 @@ bool CZNC::OnBoot() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CZNC::ConnectNetwork(CIRCNetwork *pNetwork) {
|
||||
CUser *pUser = pNetwork->GetUser();
|
||||
CString sSockName = "IRC::" + pUser->GetUserName() + "::" + pNetwork->GetName();
|
||||
CIRCSock* pIRCSock = pNetwork->GetIRCSock();
|
||||
|
||||
if (!pUser->GetIRCConnectEnabled())
|
||||
return false;
|
||||
|
||||
if (pIRCSock || !pNetwork->HasServers())
|
||||
return false;
|
||||
|
||||
CServer* pServer = pNetwork->GetNextServer();
|
||||
|
||||
if (!pServer)
|
||||
return false;
|
||||
|
||||
if (m_sConnectThrottle.GetItem(pServer->GetName()))
|
||||
return false;
|
||||
|
||||
m_sConnectThrottle.AddItem(pServer->GetName());
|
||||
|
||||
DEBUG("User [" << pUser->GetUserName() << "] is connecting to [" << pServer->GetString(false) << "] on network [" << pNetwork->GetName() << "]");
|
||||
pNetwork->PutStatus("Attempting to connect to [" + pServer->GetString(false) + "] ...");
|
||||
|
||||
pIRCSock = new CIRCSock(pNetwork);
|
||||
pIRCSock->SetPass(pServer->GetPass());
|
||||
|
||||
bool bSSL = false;
|
||||
#ifdef HAVE_LIBSSL
|
||||
if (pServer->IsSSL()) {
|
||||
bSSL = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
NETWORKMODULECALL(OnIRCConnecting(pIRCSock), pUser, pNetwork, NULL,
|
||||
DEBUG("Some module aborted the connection attempt");
|
||||
pUser->PutStatus("Some module aborted the connection attempt");
|
||||
delete pIRCSock;
|
||||
return false;
|
||||
);
|
||||
|
||||
if (!m_Manager.Connect(pServer->GetName(), pServer->GetPort(), sSockName, 120, bSSL, pUser->GetBindHost(), pIRCSock)) {
|
||||
pNetwork->PutStatus("Unable to connect. (Bad host?)");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CZNC::HandleUserDeletion()
|
||||
{
|
||||
map<CString, CUser*>::iterator it;
|
||||
@@ -297,7 +248,7 @@ void CZNC::DeleteUsers() {
|
||||
}
|
||||
|
||||
m_msUsers.clear();
|
||||
DisableConnectUser();
|
||||
DisableConnectQueue();
|
||||
}
|
||||
|
||||
bool CZNC::IsHostAllowed(const CString& sHostMask) const {
|
||||
@@ -1201,7 +1152,7 @@ bool CZNC::DoRehash(CString& sError)
|
||||
if (config.FindStringEntry("skin", sVal))
|
||||
SetSkinName(sVal);
|
||||
if (config.FindStringEntry("connectdelay", sVal))
|
||||
m_uiConnectDelay = sVal.ToUInt();
|
||||
SetConnectDelay(sVal.ToUInt());
|
||||
if (config.FindStringEntry("serverthrottle", sVal))
|
||||
m_sConnectThrottle.SetTTL(sVal.ToUInt() * 1000);
|
||||
if (config.FindStringEntry("anoniplimit", sVal))
|
||||
@@ -1351,11 +1302,6 @@ bool CZNC::DoRehash(CString& sError)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure that users that want to connect do so and also make sure a
|
||||
// new ConnectDelay setting is applied.
|
||||
DisableConnectUser();
|
||||
EnableConnectUser();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1771,119 +1717,94 @@ void CZNC::AuthUser(CSmartPtr<CAuthBase> AuthClass) {
|
||||
AuthClass->AcceptLogin(*pUser);
|
||||
}
|
||||
|
||||
class CConnectUserTimer : public CCron {
|
||||
class CConnectQueueTimer : public CCron {
|
||||
public:
|
||||
CConnectUserTimer(int iSecs) : CCron() {
|
||||
CConnectQueueTimer(int iSecs) : CCron() {
|
||||
SetName("Connect users");
|
||||
Start(iSecs);
|
||||
m_uiPosNextUser = 0;
|
||||
// Don't wait iSecs seconds for first timer run
|
||||
m_bRunOnNextCall = true;
|
||||
}
|
||||
virtual ~CConnectUserTimer() {
|
||||
virtual ~CConnectQueueTimer() {
|
||||
// This is only needed when ZNC shuts down:
|
||||
// CZNC::~CZNC() sets its CConnectUserTimer pointer to NULL and
|
||||
// CZNC::~CZNC() sets its CConnectQueueTimer pointer to NULL and
|
||||
// calls the manager's Cleanup() which destroys all sockets and
|
||||
// timers. If something calls CZNC::EnableConnectUser() here
|
||||
// timers. If something calls CZNC::EnableConnectQueue() here
|
||||
// (e.g. because a CIRCSock is destroyed), the socket manager
|
||||
// deletes that timer almost immediately, but CZNC now got a
|
||||
// dangling pointer to this timer which can crash later on.
|
||||
//
|
||||
// Unlikely but possible ;)
|
||||
CZNC::Get().LeakConnectUser(this);
|
||||
CZNC::Get().LeakConnectQueueTimer(this);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void RunJob() {
|
||||
unsigned int uiUserCount;
|
||||
bool bUsersLeft = false;
|
||||
const map<CString,CUser*>& mUsers = CZNC::Get().GetUserMap();
|
||||
map<CString,CUser*>::const_iterator it = mUsers.begin();
|
||||
list<CIRCNetwork*>& ConnectionQueue = CZNC::Get().GetConnectionQueue();
|
||||
|
||||
uiUserCount = CZNC::Get().GetUserMap().size();
|
||||
/* We store the end of the queue, so CIRCNetwork::Connect() can add
|
||||
* itself back to the queue and we wont end up in an infinite loop. */
|
||||
list<CIRCNetwork*>::iterator end = ConnectionQueue.end();
|
||||
list<CIRCNetwork*>::iterator it;
|
||||
|
||||
if (m_uiPosNextUser >= uiUserCount) {
|
||||
m_uiPosNextUser = 0;
|
||||
}
|
||||
for (it = ConnectionQueue.begin(); it != end;) {
|
||||
CIRCNetwork *pNetwork = *it;
|
||||
|
||||
for (unsigned int i = 0; i < m_uiPosNextUser; i++) {
|
||||
it++;
|
||||
}
|
||||
/* We must erase the network from the queue before we try to connect
|
||||
* because it may try to add the network to the queue (which would
|
||||
* fail if we were already in the queue) */
|
||||
it = ConnectionQueue.erase(it);
|
||||
|
||||
// Try to connect each user, if this doesnt work, abort
|
||||
for (unsigned int i = 0; i < uiUserCount; i++) {
|
||||
if (it == mUsers.end())
|
||||
it = mUsers.begin();
|
||||
|
||||
CUser* pUser = it->second;
|
||||
it++;
|
||||
m_uiPosNextUser = (m_uiPosNextUser + 1) % uiUserCount;
|
||||
|
||||
// Does this user want to connect?
|
||||
if (!pUser->GetIRCConnectEnabled())
|
||||
continue;
|
||||
|
||||
vector<CIRCNetwork*> vNetworks = pUser->GetNetworks();
|
||||
for (vector<CIRCNetwork*>::iterator it2 = vNetworks.begin(); it2 != vNetworks.end(); ++it2) {
|
||||
CIRCNetwork* pNetwork = *it2;
|
||||
|
||||
// Is this network disconnected?
|
||||
if (pNetwork->GetIRCSock() != NULL)
|
||||
continue;
|
||||
|
||||
// Does this user have any servers?
|
||||
if (!pNetwork->HasServers())
|
||||
continue;
|
||||
|
||||
// The timer runs until it once didn't find any users to connect
|
||||
bUsersLeft = true;
|
||||
|
||||
DEBUG("Connecting user [" << pUser->GetUserName() << "/" << pNetwork->GetName() << "]");
|
||||
|
||||
if (CZNC::Get().ConnectNetwork(pNetwork)) {
|
||||
// User connecting, wait until next time timer fires
|
||||
return;
|
||||
}
|
||||
if (pNetwork->Connect()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bUsersLeft == false) {
|
||||
DEBUG("ConnectUserTimer done");
|
||||
CZNC::Get().DisableConnectUser();
|
||||
if (ConnectionQueue.empty()) {
|
||||
DEBUG("ConnectQueueTimer done");
|
||||
CZNC::Get().DisableConnectQueue();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
size_t m_uiPosNextUser;
|
||||
};
|
||||
|
||||
void CZNC::SetConnectDelay(unsigned int i) {
|
||||
if (m_uiConnectDelay != i && m_pConnectUserTimer != NULL) {
|
||||
m_pConnectUserTimer->Start(i);
|
||||
if (m_uiConnectDelay != i && m_pConnectQueueTimer != NULL) {
|
||||
m_pConnectQueueTimer->Start(i);
|
||||
}
|
||||
m_uiConnectDelay = i;
|
||||
}
|
||||
|
||||
void CZNC::EnableConnectUser() {
|
||||
if (m_pConnectUserTimer != NULL)
|
||||
return;
|
||||
|
||||
m_pConnectUserTimer = new CConnectUserTimer(m_uiConnectDelay);
|
||||
GetManager().AddCron(m_pConnectUserTimer);
|
||||
void CZNC::EnableConnectQueue() {
|
||||
if (!m_pConnectQueueTimer) {
|
||||
m_pConnectQueueTimer = new CConnectQueueTimer(m_uiConnectDelay);
|
||||
GetManager().AddCron(m_pConnectQueueTimer);
|
||||
}
|
||||
}
|
||||
|
||||
void CZNC::DisableConnectUser() {
|
||||
if (m_pConnectUserTimer == NULL)
|
||||
return;
|
||||
|
||||
// This will kill the cron
|
||||
m_pConnectUserTimer->Stop();
|
||||
m_pConnectUserTimer = NULL;
|
||||
void CZNC::DisableConnectQueue() {
|
||||
if (m_pConnectQueueTimer) {
|
||||
// This will kill the cron
|
||||
m_pConnectQueueTimer->Stop();
|
||||
m_pConnectQueueTimer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void CZNC::LeakConnectUser(CConnectUserTimer *pTimer) {
|
||||
if (m_pConnectUserTimer == pTimer)
|
||||
m_pConnectUserTimer = NULL;
|
||||
void CZNC::AddNetworkToQueue(CIRCNetwork *pNetwork) {
|
||||
// Make sure we are not already in the queue
|
||||
for (list<CIRCNetwork*>::const_iterator it = m_lpConnectQueue.begin(); it != m_lpConnectQueue.end(); ++it) {
|
||||
if (*it == pNetwork) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
m_lpConnectQueue.push_back(pNetwork);
|
||||
EnableConnectQueue();
|
||||
}
|
||||
|
||||
void CZNC::LeakConnectQueueTimer(CConnectQueueTimer *pTimer) {
|
||||
if (m_pConnectQueueTimer == pTimer)
|
||||
m_pConnectQueueTimer = NULL;
|
||||
}
|
||||
|
||||
bool CZNC::WaitForChildLock() {
|
||||
|
||||
Reference in New Issue
Block a user