-
Notifications
You must be signed in to change notification settings - Fork 1
/
Listener.cpp
146 lines (119 loc) · 4.42 KB
/
Listener.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/*
* Copyright (C) 2004-2011 See the AUTHORS file for details.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include "Listener.h"
#include "znc.h"
CListener::~CListener() {
if (m_pListener)
CZNC::Get().GetManager().DelSockByAddr(m_pListener);
}
bool CListener::Listen() {
if (!m_uPort || m_pListener) {
errno = EINVAL;
return false;
}
m_pListener = new CRealListener(this);
bool bSSL = false;
#ifdef HAVE_LIBSSL
if (IsSSL()) {
bSSL = true;
m_pListener->SetPemLocation(CZNC::Get().GetPemLocation());
}
#endif
// If e.g. getaddrinfo() fails, the following might not set errno.
// Make sure there is a consistent error message, not something random
// which might even be "Error: Success".
errno = EINVAL;
return CZNC::Get().GetManager().ListenHost(m_uPort, "_LISTENER", m_sBindHost, bSSL, SOMAXCONN,
m_pListener, 0, m_eAddr);
}
void CListener::ResetRealListener() {
m_pListener = NULL;
}
CRealListener::~CRealListener() {
m_pParent->ResetRealListener();
}
bool CRealListener::ConnectionFrom(const CString& sHost, unsigned short uPort) {
bool bHostAllowed = CZNC::Get().IsHostAllowed(sHost);
DEBUG(GetSockName() << " == ConnectionFrom(" << sHost << ", " << uPort << ") [" << (bHostAllowed ? "Allowed" : "Not allowed") << "]");
return bHostAllowed;
}
Csock* CRealListener::GetSockObj(const CString& sHost, unsigned short uPort) {
CIncomingConnection *pClient = new CIncomingConnection(sHost, uPort, m_pParent->GetAcceptType());
if (CZNC::Get().AllowConnectionFrom(sHost)) {
GLOBALMODULECALL(OnClientConnect(pClient, sHost, uPort), NOTHING);
} else {
pClient->Write(":irc.znc.in 464 unknown-nick :Too many anonymous connections from your IP\r\n");
pClient->Close(Csock::CLT_AFTERWRITE);
GLOBALMODULECALL(OnFailedLogin("", sHost), NOTHING);
}
return pClient;
}
void CRealListener::SockError(int iErrno) {
DEBUG(GetSockName() << " == SockError(" << strerror(iErrno) << ")");
if (iErrno == EMFILE) {
// We have too many open fds, let's close this listening port to be able to continue
// to work, next rehash will (try to) reopen it.
CZNC::Get().Broadcast("We hit the FD limit, closing listening socket on ["
+ GetLocalIP() + " : " + CString(GetLocalPort()) + "]");
CZNC::Get().Broadcast("An admin has to rehash to reopen the listening port");
Close();
}
}
CIncomingConnection::CIncomingConnection(const CString& sHostname, unsigned short uPort, CListener::EAcceptType eAcceptType) : CZNCSock(sHostname, uPort) {
m_eAcceptType = eAcceptType;
// The socket will time out in 120 secs, no matter what.
// This has to be fixed up later, if desired.
SetTimeout(120, 0);
EnableReadLine();
}
void CIncomingConnection::ReachedMaxBuffer() {
if (GetCloseType() != CLT_DONT)
return; // Already closing
// We don't actually SetMaxBufferThreshold() because that would be
// inherited by sockets after SwapSockByAddr().
if (GetInternalReadBuffer().length() <= 4096)
return;
// We should never get here with legitimate requests :/
Close();
}
void CIncomingConnection::ReadLine(const CString& sLine) {
bool bIsHTTP = (sLine.WildCmp("GET * HTTP/1.?\r\n") || sLine.WildCmp("POST * HTTP/1.?\r\n"));
bool bAcceptHTTP = (m_eAcceptType == CListener::ACCEPT_ALL)
|| (m_eAcceptType == CListener::ACCEPT_HTTP);
bool bAcceptIRC = (m_eAcceptType == CListener::ACCEPT_ALL)
|| (m_eAcceptType == CListener::ACCEPT_IRC);
Csock *pSock = NULL;
if (!bIsHTTP) {
// Let's assume it's an IRC connection
if (!bAcceptIRC) {
Write("ERROR :We don't take kindly to your types around here!\r\n");
Close(CLT_AFTERWRITE);
DEBUG("Refused IRC connection to non IRC port");
return;
}
pSock = new CClient();
CZNC::Get().GetManager().SwapSockByAddr(pSock, this);
// And don't forget to give it some sane name / timeout
pSock->SetSockName("USR::???");
} else {
// This is a HTTP request, let the webmods handle it
if (!bAcceptHTTP) {
Write("HTTP/1.0 403 Access Denied\r\n\r\nWeb Access is not enabled.\r\n");
Close(CLT_AFTERWRITE);
DEBUG("Refused HTTP connection to non HTTP port");
return;
}
pSock = new CWebSock();
CZNC::Get().GetManager().SwapSockByAddr(pSock, this);
// And don't forget to give it some sane name / timeout
pSock->SetSockName("WebMod::Client");
}
// TODO can we somehow get rid of this?
pSock->ReadLine(sLine);
pSock->PushBuff("", 0, true);
}