2
* Copyright (C) 2004-2012 See the AUTHORS file for details.
4
* This program is free software; you can redistribute it and/or modify it
5
* under the terms of the GNU General Public License version 2 as published
6
* by the Free Software Foundation.
9
#include <znc/Socket.h>
10
#include <znc/Modules.h>
12
#include <znc/IRCNetwork.h>
16
/* We should need 2 DNS threads (host, bindhost) per IRC connection */
17
static const size_t MAX_IDLE_THREADS = 2;
19
unsigned int CSockManager::GetAnonConnectionCount(const CString &sIP) const {
23
for (it = begin(); it != end(); ++it) {
25
// Logged in CClients have "USR::<username>" as their sockname
26
if (pSock->GetType() == Csock::INBOUND && pSock->GetRemoteIP() == sIP
27
&& pSock->GetSockName().Left(5) != "USR::") {
32
DEBUG("There are [" << ret << "] clients from [" << sIP << "]");
37
int CZNCSock::ConvertAddress(const struct sockaddr_storage * pAddr, socklen_t iAddrLen, CS_STRING & sIP, u_short * piPort) {
38
int ret = Csock::ConvertAddress(pAddr, iAddrLen, sIP, piPort);
40
sIP.TrimPrefix("::ffff:");
44
#ifdef HAVE_THREADED_DNS
45
class CSockManager::CTDNSMonitorFD : public CSMonitorFD {
46
CSockManager* m_Manager;
48
CTDNSMonitorFD(CSockManager* mgr) {
50
Add(mgr->m_iTDNSpipe[0], ECT_Read);
53
virtual bool FDsThatTriggered(const std::map<int, short>& miiReadyFds) {
54
if (miiReadyFds.find(m_Manager->m_iTDNSpipe[0])->second) {
55
m_Manager->RetrieveTDNSResult();
61
bool CSockManager::ThreadNeeded(struct TDNSStatus* threadStatus)
63
// We should keep a number of idle threads alive
64
if (threadStatus->num_idle > MAX_IDLE_THREADS)
66
// If ZNC is shutting down, all threads should exit
67
return !threadStatus->done;
70
void* CSockManager::TDNSThread(void* argument) {
71
TDNSStatus *threadStatus = (TDNSStatus *) argument;
73
pthread_mutex_lock(&threadStatus->mutex);
74
threadStatus->num_threads++;
75
threadStatus->num_idle++;
77
/* Wait for a DNS job for us to do. This is a while()-loop
78
* because POSIX allows spurious wakeups from pthread_cond_wait.
80
while (threadStatus->jobs.empty()) {
81
if (!ThreadNeeded(threadStatus))
83
pthread_cond_wait(&threadStatus->cond, &threadStatus->mutex);
86
if (!ThreadNeeded(threadStatus))
89
/* Figure out a DNS job to do */
90
assert(threadStatus->num_idle > 0);
91
TDNSArg *job = threadStatus->jobs.front();
92
threadStatus->jobs.pop_front();
93
threadStatus->num_idle--;
94
pthread_mutex_unlock(&threadStatus->mutex);
96
/* Now do the actual work */
99
pthread_mutex_lock(&threadStatus->mutex);
100
threadStatus->num_idle++;
102
assert(threadStatus->num_threads > 0 && threadStatus->num_idle > 0);
103
threadStatus->num_threads--;
104
threadStatus->num_idle--;
105
pthread_mutex_unlock(&threadStatus->mutex);
109
void CSockManager::DoDNS(TDNSArg *arg) {
113
memset(&hints, 0, sizeof(hints));
114
hints.ai_family = AF_UNSPEC;
115
hints.ai_socktype = SOCK_STREAM;
116
hints.ai_protocol = IPPROTO_TCP;
117
hints.ai_flags = AI_ADDRCONFIG;
118
arg->iRes = getaddrinfo(arg->sHostname.c_str(), NULL, &hints, &arg->aiResult);
119
if (EAGAIN != arg->iRes) {
125
arg->iRes = ETIMEDOUT;
128
sleep(5); // wait 5 seconds before next try
131
size_t need = sizeof(TDNSArg*);
132
char* x = (char*)&arg;
133
// This write() must succeed because POSIX guarantees that writes of
134
// less than PIPE_BUF are atomic (and PIPE_BUF is at least 512).
135
size_t w = write(arg->fd, x, need);
137
DEBUG("Something bad happened during write() to a pipe from TDNSThread, wrote " << w << " bytes: " << strerror(errno));
142
void CSockManager::StartTDNSThread(TDNSTask* task, bool bBind) {
143
CString sHostname = bBind ? task->sBindhost : task->sHostname;
144
TDNSArg* arg = new TDNSArg;
145
arg->sHostname = sHostname;
147
arg->fd = m_iTDNSpipe[1];
150
arg->aiResult = NULL;
152
pthread_mutex_lock(&m_threadStatus.mutex);
153
m_threadStatus.jobs.push_back(arg);
154
/* Do we need a new DNS thread? */
155
if (m_threadStatus.num_idle > 0) {
156
/* Nope, there is one waiting for a job */
157
pthread_cond_signal(&m_threadStatus.cond);
158
pthread_mutex_unlock(&m_threadStatus.mutex);
161
pthread_mutex_unlock(&m_threadStatus.mutex);
164
if (pthread_attr_init(&attr)) {
165
CString sError = "Couldn't init pthread_attr for " + sHostname;
167
CZNC::Get().Broadcast(sError, /* bAdminOnly = */ true);
168
SetTDNSThreadFinished(task, bBind, NULL);
171
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
174
sigset_t old_sigmask;
176
sigfillset(&sigmask);
177
/* Block all signals. The thread will inherit our signal mask and thus
178
* won't ever try to handle any signals.
180
if (pthread_sigmask(SIG_SETMASK, &sigmask, &old_sigmask)) {
181
CString sError = "Couldn't block signals";
183
CZNC::Get().Broadcast(sError, /* bAdminOnly = */ true);
185
pthread_attr_destroy(&attr);
186
SetTDNSThreadFinished(task, bBind, NULL);
189
if (pthread_create(&thr, &attr, TDNSThread, &m_threadStatus)) {
190
CString sError = "Couldn't create thread for " + sHostname;
192
CZNC::Get().Broadcast(sError, /* bAdminOnly = */ true);
194
pthread_attr_destroy(&attr);
195
SetTDNSThreadFinished(task, bBind, NULL);
198
if (pthread_sigmask(SIG_SETMASK, &old_sigmask, NULL)) {
199
CString sError = "Couldn't unblock signals";
201
CZNC::Get().Broadcast(sError, /* bAdminOnly = */ true);
203
pthread_attr_destroy(&attr);
204
SetTDNSThreadFinished(task, bBind, NULL);
207
pthread_attr_destroy(&attr);
210
void CSockManager::SetTDNSThreadFinished(TDNSTask* task, bool bBind, addrinfo* aiResult) {
212
task->aiBind = aiResult;
213
task->bDoneBind = true;
215
task->aiTarget = aiResult;
216
task->bDoneTarget = true;
219
// Now that something is done, check if everything we needed is done
220
if (!task->bDoneBind || !task->bDoneTarget) {
224
// All needed DNS is done, now collect the results
225
addrinfo* aiTarget = NULL;
226
addrinfo* aiBind = NULL;
229
addrinfo* aiTarget4 = task->aiTarget;
230
addrinfo* aiBind4 = task->aiBind;
231
while (aiTarget4 && aiTarget4->ai_family != AF_INET) aiTarget4 = aiTarget4->ai_next;
232
while (aiBind4 && aiBind4->ai_family != AF_INET) aiBind4 = aiBind4->ai_next;
234
addrinfo* aiTarget6 = NULL;
235
addrinfo* aiBind6 = NULL;
237
aiTarget6 = task->aiTarget;
238
aiBind6 = task->aiBind;
239
while (aiTarget6 && aiTarget6->ai_family != AF_INET6) aiTarget6 = aiTarget6->ai_next;
240
while (aiBind6 && aiBind6->ai_family != AF_INET6) aiBind6 = aiBind6->ai_next;
243
if (!aiTarget4 && !aiTarget6) {
244
throw "Can't resolve server hostname";
245
} else if (task->sBindhost.empty()) {
247
aiTarget = task->aiTarget;
249
aiTarget = aiTarget4;
251
} else if (!aiBind4 && !aiBind6) {
252
throw "Can't resolve bind hostname. Try /znc clearbindhost and /znc clearuserbindhost";
253
} else if (aiBind6 && aiTarget6) {
254
aiTarget = aiTarget6;
256
} else if (aiBind4 && aiTarget4) {
257
aiTarget = aiTarget4;
260
throw "Address family of bindhost doesn't match address family of server";
265
if (!task->sBindhost.empty()) {
266
char s[INET6_ADDRSTRLEN] = {};
267
getnameinfo(aiBind->ai_addr, aiBind->ai_addrlen, s, sizeof(s), NULL, 0, NI_NUMERICHOST);
270
char s[INET6_ADDRSTRLEN] = {};
271
getnameinfo(aiTarget->ai_addr, aiTarget->ai_addrlen, s, sizeof(s), NULL, 0, NI_NUMERICHOST);
274
DEBUG("TDNS: " << task->sSockName << ", connecting to [" << sTargetHost << "] using bindhost [" << sBindhost << "]");
276
FinishConnect(sTargetHost, task->iPort, task->sSockName, task->iTimeout, task->bSSL, sBindhost, task->pcSock);
277
} catch (const char* s) {
278
DEBUG(task->sSockName << ", dns resolving error: " << s);
279
task->pcSock->SetSockName(task->sSockName);
280
task->pcSock->SockError(-1, s);
284
if (task->aiTarget) freeaddrinfo(task->aiTarget);
285
if (task->aiBind) freeaddrinfo(task->aiBind);
290
void CSockManager::RetrieveTDNSResult() {
293
size_t need = sizeof(TDNSArg*);
295
while (readed < need) {
296
ssize_t r = read(m_iTDNSpipe[0], x, need - readed);
298
DEBUG("Something bad happened during read() from a pipe when getting result of TDNSThread: " << strerror(errno));
304
TDNSTask* task = a->task;
306
DEBUG("Error in threaded DNS: " << gai_strerror(a->iRes));
308
DEBUG("And aiResult is not NULL...");
310
a->aiResult = NULL; // just for case. Maybe to call freeaddrinfo()?
312
SetTDNSThreadFinished(task, a->bBind, a->aiResult);
315
#endif /* HAVE_THREADED_DNS */
317
CSockManager::CSockManager() {
318
#ifdef HAVE_THREADED_DNS
319
int m = pthread_mutex_init(&m_threadStatus.mutex, NULL);
321
CUtils::PrintError("Can't initialize mutex: " + CString(strerror(errno)));
324
m = pthread_cond_init(&m_threadStatus.cond, NULL);
326
CUtils::PrintError("Can't initialize condition variable: " + CString(strerror(errno)));
330
m_threadStatus.num_threads = 0;
331
m_threadStatus.num_idle = 0;
332
m_threadStatus.done = false;
334
if (pipe(m_iTDNSpipe)) {
335
DEBUG("Ouch, can't open pipe for threaded DNS resolving: " << strerror(errno));
339
MonitorFD(new CTDNSMonitorFD(this));
343
CSockManager::~CSockManager() {
344
#ifdef HAVE_THREADED_DNS
345
/* Anyone has an idea how this can be done less ugly? */
346
pthread_mutex_lock(&m_threadStatus.mutex);
347
m_threadStatus.done = true;
348
while (m_threadStatus.num_threads > 0) {
349
pthread_cond_broadcast(&m_threadStatus.cond);
350
pthread_mutex_unlock(&m_threadStatus.mutex);
352
pthread_mutex_lock(&m_threadStatus.mutex);
354
pthread_mutex_unlock(&m_threadStatus.mutex);
355
pthread_cond_destroy(&m_threadStatus.cond);
356
pthread_mutex_destroy(&m_threadStatus.mutex);
360
void CSockManager::Connect(const CString& sHostname, u_short iPort, const CString& sSockName, int iTimeout, bool bSSL, const CString& sBindHost, CZNCSock *pcSock) {
361
#ifdef HAVE_THREADED_DNS
362
DEBUG("TDNS: initiating resolving of [" << sHostname << "] and bindhost [" << sBindHost << "]");
363
TDNSTask* task = new TDNSTask;
364
task->sHostname = sHostname;
366
task->sSockName = sSockName;
367
task->iTimeout = iTimeout;
369
task->sBindhost = sBindHost;
370
task->pcSock = pcSock;
371
task->aiTarget = NULL;
373
task->bDoneTarget = false;
374
if (sBindHost.empty()) {
375
task->bDoneBind = true;
377
task->bDoneBind = false;
378
StartTDNSThread(task, true);
380
StartTDNSThread(task, false);
381
#else /* HAVE_THREADED_DNS */
382
// Just let Csocket handle DNS itself
383
FinishConnect(sHostname, iPort, sSockName, iTimeout, bSSL, sBindHost, pcSock);
387
void CSockManager::FinishConnect(const CString& sHostname, u_short iPort, const CString& sSockName, int iTimeout, bool bSSL, const CString& sBindHost, CZNCSock *pcSock) {
388
CSConnection C(sHostname, iPort, iTimeout);
390
C.SetSockName(sSockName);
392
C.SetBindHost(sBindHost);
394
TSocketManager<CZNCSock>::Connect(C, pcSock);
398
/////////////////// CSocket ///////////////////
399
CSocket::CSocket(CModule* pModule) : CZNCSock() {
401
if (m_pModule) m_pModule->AddSocket(this);
403
SetMaxBufferThreshold(10240);
406
CSocket::CSocket(CModule* pModule, const CString& sHostname, unsigned short uPort, int iTimeout) : CZNCSock(sHostname, uPort, iTimeout) {
408
if (m_pModule) m_pModule->AddSocket(this);
410
SetMaxBufferThreshold(10240);
413
CSocket::~CSocket() {
416
// CWebSock could cause us to have a NULL pointer here
418
pUser = m_pModule->GetUser();
419
m_pModule->UnlinkSocket(this);
422
if (pUser && m_pModule && (m_pModule->GetType() != CModInfo::GlobalModule)) {
423
pUser->AddBytesWritten(GetBytesWritten());
424
pUser->AddBytesRead(GetBytesRead());
426
CZNC::Get().AddBytesWritten(GetBytesWritten());
427
CZNC::Get().AddBytesRead(GetBytesRead());
431
void CSocket::ReachedMaxBuffer() {
432
DEBUG(GetSockName() << " == ReachedMaxBuffer()");
433
if (m_pModule) m_pModule->PutModule("Some socket reached its max buffer limit and was closed!");
437
void CSocket::SockError(int iErrno, const CString& sDescription) {
438
DEBUG(GetSockName() << " == SockError(" << sDescription << ", " << strerror(iErrno) << ")");
439
if (iErrno == EMFILE) {
440
// We have too many open fds, this can cause a busy loop.
445
bool CSocket::ConnectionFrom(const CString& sHost, unsigned short uPort) {
446
return CZNC::Get().AllowConnectionFrom(sHost);
449
bool CSocket::Connect(const CString& sHostname, unsigned short uPort, bool bSSL, unsigned int uTimeout) {
451
DEBUG("ERROR: CSocket::Connect called on instance without m_pModule handle!");
455
CUser* pUser = m_pModule->GetUser();
456
CString sSockName = "MOD::C::" + m_pModule->GetModName();
460
sSockName += "::" + pUser->GetUserName();
461
sBindHost = pUser->GetBindHost();
462
CIRCNetwork* pNetwork = m_pModule->GetNetwork();
464
sSockName += "::" + pNetwork->GetName();
465
sBindHost = pNetwork->GetBindHost();
469
// Don't overwrite the socket name if one is already set
470
if (!GetSockName().empty()) {
471
sSockName = GetSockName();
474
m_pModule->GetManager()->Connect(sHostname, uPort, sSockName, uTimeout, bSSL, sBindHost, this);
478
bool CSocket::Listen(unsigned short uPort, bool bSSL, unsigned int uTimeout) {
480
DEBUG("ERROR: CSocket::Listen called on instance without m_pModule handle!");
484
CUser* pUser = m_pModule->GetUser();
485
CString sSockName = "MOD::L::" + m_pModule->GetModName();
488
sSockName += "::" + pUser->GetUserName();
490
// Don't overwrite the socket name if one is already set
491
if (!GetSockName().empty()) {
492
sSockName = GetSockName();
495
return m_pModule->GetManager()->ListenAll(uPort, sSockName, bSSL, SOMAXCONN, this);
498
CModule* CSocket::GetModule() const { return m_pModule; }
499
/////////////////// !CSocket ///////////////////