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

« back to all changes in this revision

Viewing changes to src/Socket.cpp

  • Committer: Package Import Robot
  • Author(s): Patrick Matthäi
  • Date: 2013-05-06 09:18:27 UTC
  • mfrom: (21.1.5 experimental)
  • Revision ID: package-import@ubuntu.com-20130506091827-08sixjiyy3hjfx6b
Tags: 1.0-4
* Change section from znc-tcl to interpreters.
* Uploading to unstable.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 ///////////////////