Create a connection queue including all the networks we need to connect

This commit is contained in:
Kyle Fuller
2011-10-04 15:17:42 +00:00
parent 0f0591b648
commit a4aedc90d3
4 changed files with 122 additions and 145 deletions

View File

@@ -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;

View File

@@ -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;
};

View File

@@ -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) {

View File

@@ -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() {