~ubuntu-branches/ubuntu/trusty/znc/trusty

1.4.1 by Patrick Matthäi
Import upstream version 1.0
1
/*
2
 * Copyright (C) 2004-2012  See the AUTHORS file for details.
3
 *
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.
7
 */
8
9
#include <znc/Socket.h>
10
#include <znc/Modules.h>
11
#include <znc/User.h>
12
#include <znc/IRCNetwork.h>
13
#include <znc/znc.h>
14
#include <signal.h>
15
16
/* We should need 2 DNS threads (host, bindhost) per IRC connection */
17
static const size_t MAX_IDLE_THREADS = 2;
18
19
unsigned int CSockManager::GetAnonConnectionCount(const CString &sIP) const {
20
	const_iterator it;
21
	unsigned int ret = 0;
22
23
	for (it = begin(); it != end(); ++it) {
24
		Csock *pSock = *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::") {
28
			ret++;
29
		}
30
	}
31
32
	DEBUG("There are [" << ret << "] clients from [" << sIP << "]");
33
34
	return ret;
35
}
36
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);
39
	if (ret == 0)
40
		sIP.TrimPrefix("::ffff:");
41
	return ret;
42
}
43
44
#ifdef HAVE_THREADED_DNS
45
class CSockManager::CTDNSMonitorFD : public CSMonitorFD {
46
	CSockManager* m_Manager;
47
public:
48
	CTDNSMonitorFD(CSockManager* mgr) {
49
		m_Manager = mgr;
50
		Add(mgr->m_iTDNSpipe[0], ECT_Read);
51
	}
52
53
	virtual bool FDsThatTriggered(const std::map<int, short>& miiReadyFds) {
54
		if (miiReadyFds.find(m_Manager->m_iTDNSpipe[0])->second) {
55
			m_Manager->RetrieveTDNSResult();
56
		}
57
		return true;
58
	}
59
};
60
61
bool CSockManager::ThreadNeeded(struct TDNSStatus* threadStatus)
62
{
63
	// We should keep a number of idle threads alive
64
	if (threadStatus->num_idle > MAX_IDLE_THREADS)
65
		return false;
66
	// If ZNC is shutting down, all threads should exit
67
	return !threadStatus->done;
68
}
69
70
void* CSockManager::TDNSThread(void* argument) {
71
	TDNSStatus *threadStatus = (TDNSStatus *) argument;
72
73
	pthread_mutex_lock(&threadStatus->mutex);
74
	threadStatus->num_threads++;
75
	threadStatus->num_idle++;
76
	while (true) {
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.
79
		 */
80
		while (threadStatus->jobs.empty()) {
81
			if (!ThreadNeeded(threadStatus))
82
				break;
83
			pthread_cond_wait(&threadStatus->cond, &threadStatus->mutex);
84
		}
85
86
		if (!ThreadNeeded(threadStatus))
87
			break;
88
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);
95
96
		/* Now do the actual work */
97
		DoDNS(job);
98
99
		pthread_mutex_lock(&threadStatus->mutex);
100
		threadStatus->num_idle++;
101
	}
102
	assert(threadStatus->num_threads > 0 && threadStatus->num_idle > 0);
103
	threadStatus->num_threads--;
104
	threadStatus->num_idle--;
105
	pthread_mutex_unlock(&threadStatus->mutex);
106
	return NULL;
107
}
108
109
void CSockManager::DoDNS(TDNSArg *arg) {
110
	int iCount = 0;
111
	while (true) {
112
		addrinfo hints;
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) {
120
			break;
121
		}
122
123
		iCount++;
124
		if (iCount > 5) {
125
			arg->iRes = ETIMEDOUT;
126
			break;
127
		}
128
		sleep(5); // wait 5 seconds before next try
129
	}
130
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);
136
	if (w != need) {
137
		DEBUG("Something bad happened during write() to a pipe from TDNSThread, wrote " << w << " bytes: " << strerror(errno));
138
		exit(1);
139
	}
140
}
141
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;
146
	arg->task      = task;
147
	arg->fd        = m_iTDNSpipe[1];
148
	arg->bBind     = bBind;
149
	arg->iRes      = 0;
150
	arg->aiResult  = NULL;
151
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);
159
		return;
160
	}
161
	pthread_mutex_unlock(&m_threadStatus.mutex);
162
163
	pthread_attr_t attr;
164
	if (pthread_attr_init(&attr)) {
165
		CString sError = "Couldn't init pthread_attr for " + sHostname;
166
		DEBUG(sError);
167
		CZNC::Get().Broadcast(sError, /* bAdminOnly = */ true);
168
		SetTDNSThreadFinished(task, bBind, NULL);
169
		return;
170
	}
171
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
172
173
	pthread_t thr;
174
	sigset_t old_sigmask;
175
	sigset_t 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.
179
	 */
180
	if (pthread_sigmask(SIG_SETMASK, &sigmask, &old_sigmask)) {
181
		CString sError = "Couldn't block signals";
182
		DEBUG(sError);
183
		CZNC::Get().Broadcast(sError, /* bAdminOnly = */ true);
184
		delete arg;
185
		pthread_attr_destroy(&attr);
186
		SetTDNSThreadFinished(task, bBind, NULL);
187
		return;
188
	}
189
	if (pthread_create(&thr, &attr, TDNSThread, &m_threadStatus)) {
190
		CString sError = "Couldn't create thread for " + sHostname;
191
		DEBUG(sError);
192
		CZNC::Get().Broadcast(sError, /* bAdminOnly = */ true);
193
		delete arg;
194
		pthread_attr_destroy(&attr);
195
		SetTDNSThreadFinished(task, bBind, NULL);
196
		return;
197
	}
198
	if (pthread_sigmask(SIG_SETMASK, &old_sigmask, NULL)) {
199
		CString sError = "Couldn't unblock signals";
200
		DEBUG(sError);
201
		CZNC::Get().Broadcast(sError, /* bAdminOnly = */ true);
202
		delete arg;
203
		pthread_attr_destroy(&attr);
204
		SetTDNSThreadFinished(task, bBind, NULL);
205
		return;
206
	}
207
	pthread_attr_destroy(&attr);
208
}
209
210
void CSockManager::SetTDNSThreadFinished(TDNSTask* task, bool bBind, addrinfo* aiResult) {
211
	if (bBind) {
212
		task->aiBind = aiResult;
213
		task->bDoneBind = true;
214
	} else {
215
		task->aiTarget = aiResult;
216
		task->bDoneTarget = true;
217
	}
218
219
	// Now that something is done, check if everything we needed is done
220
	if (!task->bDoneBind || !task->bDoneTarget) {
221
		return;
222
	}
223
224
	// All needed DNS is done, now collect the results
225
	addrinfo* aiTarget = NULL;
226
	addrinfo* aiBind = NULL;
227
228
	try {
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;
233
234
		addrinfo* aiTarget6 = NULL;
235
		addrinfo* aiBind6 = NULL;
236
#ifdef HAVE_IPV6
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;
241
#endif
242
243
		if (!aiTarget4 && !aiTarget6) {
244
			throw "Can't resolve server hostname";
245
		} else if (task->sBindhost.empty()) {
246
#ifdef HAVE_IPV6
247
			aiTarget = task->aiTarget;
248
#else
249
			aiTarget = aiTarget4;
250
#endif
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;
255
			aiBind = aiBind6;
256
		} else if (aiBind4 && aiTarget4) {
257
			aiTarget = aiTarget4;
258
			aiBind = aiBind4;
259
		} else {
260
			throw "Address family of bindhost doesn't match address family of server";
261
		}
262
263
		CString sBindhost;
264
		CString sTargetHost;
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);
268
			sBindhost = s;
269
		}
270
		char s[INET6_ADDRSTRLEN] = {};
271
		getnameinfo(aiTarget->ai_addr, aiTarget->ai_addrlen, s, sizeof(s), NULL, 0, NI_NUMERICHOST);
272
		sTargetHost = s;
273
274
		DEBUG("TDNS: " << task->sSockName << ", connecting to [" << sTargetHost << "] using bindhost [" << sBindhost << "]");
275
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);
281
		delete task->pcSock;
282
	}
283
284
	if (task->aiTarget) freeaddrinfo(task->aiTarget);
285
	if (task->aiBind) freeaddrinfo(task->aiBind);
286
287
	delete task;
288
}
289
290
void CSockManager::RetrieveTDNSResult() {
291
	TDNSArg* a = NULL;
292
	size_t readed = 0;
293
	size_t need = sizeof(TDNSArg*);
294
	char* x = (char*)&a;
295
	while (readed < need) {
296
		ssize_t r = read(m_iTDNSpipe[0], x, need - readed);
297
		if (-1 == r) {
298
			DEBUG("Something bad happened during read() from a pipe when getting result of TDNSThread: " << strerror(errno));
299
			exit(1);
300
		}
301
		readed += r;
302
		x += r;
303
	}
304
	TDNSTask* task = a->task;
305
	if (0 != a->iRes) {
306
		DEBUG("Error in threaded DNS: " << gai_strerror(a->iRes));
307
		if (a->aiResult) {
308
			DEBUG("And aiResult is not NULL...");
309
		}
310
		a->aiResult = NULL; // just for case. Maybe to call freeaddrinfo()?
311
	}
312
	SetTDNSThreadFinished(task, a->bBind, a->aiResult);
313
	delete a;
314
}
315
#endif /* HAVE_THREADED_DNS */
316
317
CSockManager::CSockManager() {
318
#ifdef HAVE_THREADED_DNS
319
	int m = pthread_mutex_init(&m_threadStatus.mutex, NULL);
320
	if (m) {
321
		CUtils::PrintError("Can't initialize mutex: " + CString(strerror(errno)));
322
		exit(1);
323
	}
324
	m = pthread_cond_init(&m_threadStatus.cond, NULL);
325
	if (m) {
326
		CUtils::PrintError("Can't initialize condition variable: " + CString(strerror(errno)));
327
		exit(1);
328
	}
329
330
	m_threadStatus.num_threads = 0;
331
	m_threadStatus.num_idle = 0;
332
	m_threadStatus.done = false;
333
334
	if (pipe(m_iTDNSpipe)) {
335
		DEBUG("Ouch, can't open pipe for threaded DNS resolving: " << strerror(errno));
336
		exit(1);
337
	}
338
339
	MonitorFD(new CTDNSMonitorFD(this));
340
#endif
341
}
342
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);
351
		usleep(100);
352
		pthread_mutex_lock(&m_threadStatus.mutex);
353
	}
354
	pthread_mutex_unlock(&m_threadStatus.mutex);
355
	pthread_cond_destroy(&m_threadStatus.cond);
356
	pthread_mutex_destroy(&m_threadStatus.mutex);
357
#endif
358
}
359
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;
365
	task->iPort       = iPort;
366
	task->sSockName   = sSockName;
367
	task->iTimeout    = iTimeout;
368
	task->bSSL        = bSSL;
369
	task->sBindhost   = sBindHost;
370
	task->pcSock      = pcSock;
371
	task->aiTarget    = NULL;
372
	task->aiBind      = NULL;
373
	task->bDoneTarget = false;
374
	if (sBindHost.empty()) {
375
		task->bDoneBind = true;
376
	} else {
377
		task->bDoneBind = false;
378
		StartTDNSThread(task, true);
379
	}
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);
384
#endif
385
}
386
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);
389
390
	C.SetSockName(sSockName);
391
	C.SetIsSSL(bSSL);
392
	C.SetBindHost(sBindHost);
393
394
	TSocketManager<CZNCSock>::Connect(C, pcSock);
395
}
396
397
398
/////////////////// CSocket ///////////////////
399
CSocket::CSocket(CModule* pModule) : CZNCSock() {
400
	m_pModule = pModule;
401
	if (m_pModule) m_pModule->AddSocket(this);
402
	EnableReadLine();
403
	SetMaxBufferThreshold(10240);
404
}
405
406
CSocket::CSocket(CModule* pModule, const CString& sHostname, unsigned short uPort, int iTimeout) : CZNCSock(sHostname, uPort, iTimeout) {
407
	m_pModule = pModule;
408
	if (m_pModule) m_pModule->AddSocket(this);
409
	EnableReadLine();
410
	SetMaxBufferThreshold(10240);
411
}
412
413
CSocket::~CSocket() {
414
	CUser *pUser = NULL;
415
416
	// CWebSock could cause us to have a NULL pointer here
417
	if (m_pModule) {
418
		pUser = m_pModule->GetUser();
419
		m_pModule->UnlinkSocket(this);
420
	}
421
422
	if (pUser && m_pModule && (m_pModule->GetType() != CModInfo::GlobalModule)) {
423
		pUser->AddBytesWritten(GetBytesWritten());
424
		pUser->AddBytesRead(GetBytesRead());
425
	} else {
426
		CZNC::Get().AddBytesWritten(GetBytesWritten());
427
		CZNC::Get().AddBytesRead(GetBytesRead());
428
	}
429
}
430
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!");
434
	Close();
435
}
436
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.
441
		Close();
442
	}
443
}
444
445
bool CSocket::ConnectionFrom(const CString& sHost, unsigned short uPort) {
446
	return CZNC::Get().AllowConnectionFrom(sHost);
447
}
448
449
bool CSocket::Connect(const CString& sHostname, unsigned short uPort, bool bSSL, unsigned int uTimeout) {
450
	if (!m_pModule) {
451
		DEBUG("ERROR: CSocket::Connect called on instance without m_pModule handle!");
452
		return false;
453
	}
454
455
	CUser* pUser = m_pModule->GetUser();
456
	CString sSockName = "MOD::C::" + m_pModule->GetModName();
457
	CString sBindHost;
458
459
	if (pUser) {
460
		sSockName += "::" + pUser->GetUserName();
461
		sBindHost = pUser->GetBindHost();
462
		CIRCNetwork* pNetwork = m_pModule->GetNetwork();
463
		if (pNetwork) {
464
			sSockName += "::" + pNetwork->GetName();
465
			sBindHost = pNetwork->GetBindHost();
466
		}
467
	}
468
469
	// Don't overwrite the socket name if one is already set
470
	if (!GetSockName().empty()) {
471
		sSockName = GetSockName();
472
	}
473
474
	m_pModule->GetManager()->Connect(sHostname, uPort, sSockName, uTimeout, bSSL, sBindHost, this);
475
	return true;
476
}
477
478
bool CSocket::Listen(unsigned short uPort, bool bSSL, unsigned int uTimeout) {
479
	if (!m_pModule) {
480
		DEBUG("ERROR: CSocket::Listen called on instance without m_pModule handle!");
481
		return false;
482
	}
483
484
	CUser* pUser = m_pModule->GetUser();
485
	CString sSockName = "MOD::L::" + m_pModule->GetModName();
486
487
	if (pUser) {
488
		sSockName += "::" + pUser->GetUserName();
489
	}
490
	// Don't overwrite the socket name if one is already set
491
	if (!GetSockName().empty()) {
492
		sSockName = GetSockName();
493
	}
494
495
	return m_pModule->GetManager()->ListenAll(uPort, sSockName, bSSL, SOMAXCONN, this);
496
}
497
498
CModule* CSocket::GetModule() const { return m_pModule; }
499
/////////////////// !CSocket ///////////////////