]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/usermanager.cpp
Allow modules to prevent a failed connection from being closed.
[user/henk/code/inspircd.git] / src / usermanager.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
5  *   Copyright (C) 2008 Dennis Friis <peavey@inspircd.org>
6  *   Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
7  *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
8  *
9  * This file is part of InspIRCd.  InspIRCd is free software: you can
10  * redistribute it and/or modify it under the terms of the GNU General Public
11  * License as published by the Free Software Foundation, version 2.
12  *
13  * This program is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22
23 #include "inspircd.h"
24 #include "xline.h"
25 #include "iohook.h"
26
27 namespace
28 {
29         class WriteCommonQuit : public User::ForEachNeighborHandler
30         {
31                 ClientProtocol::Messages::Quit quitmsg;
32                 ClientProtocol::Event quitevent;
33                 ClientProtocol::Messages::Quit operquitmsg;
34                 ClientProtocol::Event operquitevent;
35
36                 void Execute(LocalUser* user) CXX11_OVERRIDE
37                 {
38                         user->Send(user->IsOper() ? operquitevent : quitevent);
39                 }
40
41          public:
42                 WriteCommonQuit(User* user, const std::string& msg, const std::string& opermsg)
43                         : quitmsg(user, msg)
44                         , quitevent(ServerInstance->GetRFCEvents().quit, quitmsg)
45                         , operquitmsg(user, opermsg)
46                         , operquitevent(ServerInstance->GetRFCEvents().quit, operquitmsg)
47                 {
48                         user->ForEachNeighbor(*this, false);
49                 }
50         };
51
52         void CheckPingTimeout(LocalUser* user)
53         {
54                 // Check if it is time to ping the user yet.
55                 if (ServerInstance->Time() < user->nextping)
56                         return;
57
58                 // This user didn't answer the last ping, remove them.
59                 if (!user->lastping)
60                 {
61                         ModResult res;
62                         FIRST_MOD_RESULT(OnConnectionFail, res, (user, I_ERR_TIMEOUT));
63                         if (res == MOD_RES_ALLOW)
64                         {
65                                 // A module is preventing this user from being timed out.
66                                 user->lastping = 1;
67                                 user->nextping = ServerInstance->Time() + user->MyClass->GetPingTime();
68                                 return;
69                         }
70
71                         time_t secs = ServerInstance->Time() - (user->nextping - user->MyClass->GetPingTime());
72                         const std::string message = "Ping timeout: " + ConvToStr(secs) + (secs != 1 ? " seconds" : " second");
73                         ServerInstance->Users.QuitUser(user, message);
74                         return;
75                 }
76
77                 // Send a ping to the client.
78                 ClientProtocol::Messages::Ping ping;
79                 user->Send(ServerInstance->GetRFCEvents().ping, ping);
80                 user->lastping = 0;
81                 user->nextping = ServerInstance->Time() + user->MyClass->GetPingTime();
82         }
83
84         void CheckRegistrationTimeout(LocalUser* user)
85         {
86                 if (user->GetClass() && (ServerInstance->Time() > (user->signon + user->GetClass()->GetRegTimeout())))
87                 {
88                         // Either the user did not send NICK/USER or a module blocked registration in
89                         // OnCheckReady until the client timed out.
90                         ServerInstance->Users.QuitUser(user, "Registration timeout");
91                 }
92         }
93
94         void CheckModulesReady(LocalUser* user)
95         {
96                 ModResult res;
97                 FIRST_MOD_RESULT(OnCheckReady, res, (user));
98                 if (res == MOD_RES_PASSTHRU)
99                 {
100                         // User has sent NICK/USER and modules are ready.
101                         user->FullConnect();
102                         return;
103                 }
104
105                 // If the user has been quit in OnCheckReady then we shouldn't quit
106                 // them again for having a registration timeout.
107                 if (!user->quitting)
108                         CheckRegistrationTimeout(user);
109         }
110 }
111
112 UserManager::UserManager()
113         : already_sent_id(0)
114         , unregistered_count(0)
115 {
116 }
117
118 UserManager::~UserManager()
119 {
120         for (user_hash::iterator i = clientlist.begin(); i != clientlist.end(); ++i)
121         {
122                 delete i->second;
123         }
124 }
125
126 void UserManager::AddUser(int socket, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
127 {
128         // User constructor allocates a new UUID for the user and inserts it into the uuidlist
129         LocalUser* const New = new LocalUser(socket, client, server);
130         UserIOHandler* eh = &New->eh;
131
132         ServerInstance->Logs->Log("USERS", LOG_DEBUG, "New user fd: %d", socket);
133
134         this->unregistered_count++;
135         this->clientlist[New->nick] = New;
136         this->AddClone(New);
137         this->local_users.push_front(New);
138         FOREACH_MOD(OnUserInit, (New));
139
140         if (!SocketEngine::AddFd(eh, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE))
141         {
142                 ServerInstance->Logs->Log("USERS", LOG_DEBUG, "Internal error on new connection");
143                 this->QuitUser(New, "Internal error handling connection");
144                 return;
145         }
146
147         // If this listener has an IO hook provider set then tell it about the connection
148         for (ListenSocket::IOHookProvList::iterator i = via->iohookprovs.begin(); i != via->iohookprovs.end(); ++i)
149         {
150                 ListenSocket::IOHookProvRef& iohookprovref = *i;
151                 if (!iohookprovref)
152                         continue;
153
154                 iohookprovref->OnAccept(eh, client, server);
155                 // IOHook could have encountered a fatal error, e.g. if the TLS ClientHello was already in the queue and there was no common TLS version
156                 if (!eh->getError().empty())
157                 {
158                         QuitUser(New, eh->getError());
159                         return;
160                 }
161         }
162
163         if (this->local_users.size() > ServerInstance->Config->SoftLimit)
164         {
165                 ServerInstance->SNO->WriteToSnoMask('a', "Warning: softlimit value has been reached: %d clients", ServerInstance->Config->SoftLimit);
166                 this->QuitUser(New,"No more connections allowed");
167                 return;
168         }
169
170         // First class check. We do this again in LocalUser::FullConnect() after DNS is done, and NICK/USER is received.
171         New->SetClass();
172         // If the user doesn't have an acceptable connect class CheckClass() quits them
173         New->CheckClass(ServerInstance->Config->CCOnConnect);
174         if (New->quitting)
175                 return;
176
177         /*
178          * even with bancache, we still have to keep User::exempt current.
179          * besides that, if we get a positive bancache hit, we still won't fuck
180          * them over if they are exempt. -- w00t
181          */
182         New->exempt = (ServerInstance->XLines->MatchesLine("E",New) != NULL);
183
184         BanCacheHit* const b = ServerInstance->BanCache.GetHit(New->GetIPString());
185         if (b)
186         {
187                 if (!b->Type.empty() && !New->exempt)
188                 {
189                         /* user banned */
190                         ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCache: Positive hit for " + New->GetIPString());
191                         if (!ServerInstance->Config->XLineMessage.empty())
192                                 New->WriteNumeric(ERR_YOUREBANNEDCREEP, ServerInstance->Config->XLineMessage);
193
194                         if (ServerInstance->Config->HideBans)
195                                 this->QuitUser(New, b->Type + "-lined", &b->Reason);
196                         else
197                                 this->QuitUser(New, b->Reason);
198                         return;
199                 }
200                 else
201                 {
202                         ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCache: Negative hit for " + New->GetIPString());
203                 }
204         }
205         else
206         {
207                 if (!New->exempt)
208                 {
209                         XLine* r = ServerInstance->XLines->MatchesLine("Z",New);
210
211                         if (r)
212                         {
213                                 r->Apply(New);
214                                 return;
215                         }
216                 }
217         }
218
219         if (ServerInstance->Config->RawLog)
220                 New->WriteNotice("*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.");
221
222         FOREACH_MOD(OnSetUserIP, (New));
223         if (!New->quitting)
224                 FOREACH_MOD(OnUserPostInit, (New));
225 }
226
227 void UserManager::QuitUser(User* user, const std::string& quitmessage, const std::string* operquitmessage)
228 {
229         if (user->quitting)
230         {
231                 ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Tried to quit quitting user: " + user->nick);
232                 return;
233         }
234
235         if (IS_SERVER(user))
236         {
237                 ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Tried to quit server user: " + user->nick);
238                 return;
239         }
240
241         std::string quitmsg(quitmessage);
242         std::string operquitmsg;
243         if (operquitmessage)
244                 operquitmsg.assign(*operquitmessage);
245
246         LocalUser* const localuser = IS_LOCAL(user);
247         if (localuser)
248         {
249                 ModResult MOD_RESULT;
250                 FIRST_MOD_RESULT(OnUserPreQuit, MOD_RESULT, (localuser, quitmsg, operquitmsg));
251                 if (MOD_RESULT == MOD_RES_DENY)
252                         return;
253         }
254
255         if (quitmsg.length() > ServerInstance->Config->Limits.MaxQuit)
256                 quitmsg.erase(ServerInstance->Config->Limits.MaxQuit + 1);
257
258         if (operquitmsg.empty())
259                 operquitmsg.assign(quitmsg);
260         else if (operquitmsg.length() > ServerInstance->Config->Limits.MaxQuit)
261                 operquitmsg.erase(ServerInstance->Config->Limits.MaxQuit + 1);
262
263         user->quitting = true;
264         ServerInstance->Logs->Log("USERS", LOG_DEBUG, "QuitUser: %s=%s '%s'", user->uuid.c_str(), user->nick.c_str(), quitmessage.c_str());
265         if (localuser)
266         {
267                 ClientProtocol::Messages::Error errormsg(InspIRCd::Format("Closing link: (%s@%s) [%s]", user->ident.c_str(), user->GetRealHost().c_str(), operquitmsg.c_str()));
268                 localuser->Send(ServerInstance->GetRFCEvents().error, errormsg);
269         }
270
271         ServerInstance->GlobalCulls.AddItem(user);
272
273         if (user->registered == REG_ALL)
274         {
275                 FOREACH_MOD(OnUserQuit, (user, quitmsg, operquitmsg));
276                 WriteCommonQuit(user, quitmsg, operquitmsg);
277         }
278         else
279                 unregistered_count--;
280
281         if (IS_LOCAL(user))
282         {
283                 LocalUser* lu = IS_LOCAL(user);
284                 FOREACH_MOD(OnUserDisconnect, (lu));
285                 lu->eh.Close();
286
287                 if (lu->registered == REG_ALL)
288                         ServerInstance->SNO->WriteToSnoMask('q',"Client exiting: %s (%s) [%s]", user->GetFullRealHost().c_str(), user->GetIPString().c_str(), operquitmsg.c_str());
289                 local_users.erase(lu);
290         }
291
292         if (!clientlist.erase(user->nick))
293                 ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Nick not found in clientlist, cannot remove: " + user->nick);
294
295         uuidlist.erase(user->uuid);
296         user->PurgeEmptyChannels();
297         user->UnOper();
298 }
299
300 void UserManager::AddClone(User* user)
301 {
302         CloneCounts& counts = clonemap[user->GetCIDRMask()];
303         counts.global++;
304         if (IS_LOCAL(user))
305                 counts.local++;
306 }
307
308 void UserManager::RemoveCloneCounts(User *user)
309 {
310         CloneMap::iterator it = clonemap.find(user->GetCIDRMask());
311         if (it != clonemap.end())
312         {
313                 CloneCounts& counts = it->second;
314                 counts.global--;
315                 if (counts.global == 0)
316                 {
317                         // No more users from this IP, remove entry from the map
318                         clonemap.erase(it);
319                         return;
320                 }
321
322                 if (IS_LOCAL(user))
323                         counts.local--;
324         }
325 }
326
327 void UserManager::RehashCloneCounts()
328 {
329         clonemap.clear();
330
331         const user_hash& hash = ServerInstance->Users.GetUsers();
332         for (user_hash::const_iterator i = hash.begin(); i != hash.end(); ++i)
333         {
334                 User* u = i->second;
335                 AddClone(u);
336         }
337 }
338
339 const UserManager::CloneCounts& UserManager::GetCloneCounts(User* user) const
340 {
341         CloneMap::const_iterator it = clonemap.find(user->GetCIDRMask());
342         if (it != clonemap.end())
343                 return it->second;
344         else
345                 return zeroclonecounts;
346 }
347
348 void UserManager::ServerNoticeAll(const char* text, ...)
349 {
350         std::string message;
351         VAFORMAT(message, text, text);
352         ClientProtocol::Messages::Privmsg msg(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, ServerInstance->Config->ServerName, message, MSG_NOTICE);
353         ClientProtocol::Event msgevent(ServerInstance->GetRFCEvents().privmsg, msg);
354
355         for (LocalList::const_iterator i = local_users.begin(); i != local_users.end(); ++i)
356         {
357                 LocalUser* user = *i;
358                 user->Send(msgevent);
359         }
360 }
361
362 /**
363  * This function is called once a second from the mainloop.
364  * It is intended to do background checking on all the users, e.g. do
365  * ping checks, registration timeouts, etc.
366  */
367 void UserManager::DoBackgroundUserStuff()
368 {
369         for (LocalList::iterator i = local_users.begin(); i != local_users.end(); )
370         {
371                 // It's possible that we quit the user below due to ping timeout etc. and QuitUser() removes it from the list
372                 LocalUser* curr = *i;
373                 ++i;
374
375                 if (curr->CommandFloodPenalty || curr->eh.getSendQSize())
376                 {
377                         unsigned int rate = curr->MyClass->GetCommandRate();
378                         if (curr->CommandFloodPenalty > rate)
379                                 curr->CommandFloodPenalty -= rate;
380                         else
381                                 curr->CommandFloodPenalty = 0;
382                         curr->eh.OnDataReady();
383                 }
384
385                 switch (curr->registered)
386                 {
387                         case REG_ALL:
388                                 CheckPingTimeout(curr);
389                                 break;
390
391                         case REG_NICKUSER:
392                                 CheckModulesReady(curr);
393                                 break;
394
395                         default:
396                                 CheckRegistrationTimeout(curr);
397                                 break;
398                 }
399         }
400 }
401
402 already_sent_t UserManager::NextAlreadySentId()
403 {
404         if (++already_sent_id == 0)
405         {
406                 // Wrapped around, reset the already_sent ids of all users
407                 already_sent_id = 1;
408                 for (LocalList::iterator i = local_users.begin(); i != local_users.end(); ++i)
409                 {
410                         LocalUser* user = *i;
411                         user->already_sent = 0;
412                 }
413         }
414         return already_sent_id;
415 }