]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/usermanager.cpp
Close connections when an I/O hook is configured but not loaded.
[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                 {
153                         if (!iohookprovref.GetProvider().empty())
154                         {
155                                 ServerInstance->Logs->Log("USERS", LOG_DEBUG, "Non-existent I/O hook '%s' in <bind:%s> tag at %s",
156                                         iohookprovref.GetProvider().c_str(),
157                                         i == via->iohookprovs.begin() ? "hook" : "ssl",
158                                         via->bind_tag->getTagLocation().c_str());
159                                 this->QuitUser(New, "Internal error handling connection");
160                                 return;
161                         }
162                         continue;
163                 }
164
165                 iohookprovref->OnAccept(eh, client, server);
166
167                 // IOHook could have encountered a fatal error, e.g. if the TLS ClientHello
168                 // was already in the queue and there was no common TLS version.
169                 if (!eh->getError().empty())
170                 {
171                         QuitUser(New, eh->getError());
172                         return;
173                 }
174         }
175
176         if (this->local_users.size() > ServerInstance->Config->SoftLimit)
177         {
178                 ServerInstance->SNO->WriteToSnoMask('a', "Warning: softlimit value has been reached: %d clients", ServerInstance->Config->SoftLimit);
179                 this->QuitUser(New,"No more connections allowed");
180                 return;
181         }
182
183         // First class check. We do this again in LocalUser::FullConnect() after DNS is done, and NICK/USER is received.
184         New->SetClass();
185         // If the user doesn't have an acceptable connect class CheckClass() quits them
186         New->CheckClass(ServerInstance->Config->CCOnConnect);
187         if (New->quitting)
188                 return;
189
190         /*
191          * even with bancache, we still have to keep User::exempt current.
192          * besides that, if we get a positive bancache hit, we still won't fuck
193          * them over if they are exempt. -- w00t
194          */
195         New->exempt = (ServerInstance->XLines->MatchesLine("E",New) != NULL);
196
197         BanCacheHit* const b = ServerInstance->BanCache.GetHit(New->GetIPString());
198         if (b)
199         {
200                 if (!b->Type.empty() && !New->exempt)
201                 {
202                         /* user banned */
203                         ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCache: Positive hit for " + New->GetIPString());
204                         if (!ServerInstance->Config->XLineMessage.empty())
205                                 New->WriteNumeric(ERR_YOUREBANNEDCREEP, ServerInstance->Config->XLineMessage);
206
207                         if (ServerInstance->Config->HideBans)
208                                 this->QuitUser(New, b->Type + "-lined", &b->Reason);
209                         else
210                                 this->QuitUser(New, b->Reason);
211                         return;
212                 }
213                 else
214                 {
215                         ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCache: Negative hit for " + New->GetIPString());
216                 }
217         }
218         else
219         {
220                 if (!New->exempt)
221                 {
222                         XLine* r = ServerInstance->XLines->MatchesLine("Z",New);
223
224                         if (r)
225                         {
226                                 r->Apply(New);
227                                 return;
228                         }
229                 }
230         }
231
232         if (ServerInstance->Config->RawLog)
233                 New->WriteNotice("*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.");
234
235         FOREACH_MOD(OnSetUserIP, (New));
236         if (!New->quitting)
237                 FOREACH_MOD(OnUserPostInit, (New));
238 }
239
240 void UserManager::QuitUser(User* user, const std::string& quitmessage, const std::string* operquitmessage)
241 {
242         if (user->quitting)
243         {
244                 ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Tried to quit quitting user: " + user->nick);
245                 return;
246         }
247
248         if (IS_SERVER(user))
249         {
250                 ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Tried to quit server user: " + user->nick);
251                 return;
252         }
253
254         std::string quitmsg(quitmessage);
255         std::string operquitmsg;
256         if (operquitmessage)
257                 operquitmsg.assign(*operquitmessage);
258
259         LocalUser* const localuser = IS_LOCAL(user);
260         if (localuser)
261         {
262                 ModResult MOD_RESULT;
263                 FIRST_MOD_RESULT(OnUserPreQuit, MOD_RESULT, (localuser, quitmsg, operquitmsg));
264                 if (MOD_RESULT == MOD_RES_DENY)
265                         return;
266         }
267
268         if (quitmsg.length() > ServerInstance->Config->Limits.MaxQuit)
269                 quitmsg.erase(ServerInstance->Config->Limits.MaxQuit + 1);
270
271         if (operquitmsg.empty())
272                 operquitmsg.assign(quitmsg);
273         else if (operquitmsg.length() > ServerInstance->Config->Limits.MaxQuit)
274                 operquitmsg.erase(ServerInstance->Config->Limits.MaxQuit + 1);
275
276         user->quitting = true;
277         ServerInstance->Logs->Log("USERS", LOG_DEBUG, "QuitUser: %s=%s '%s'", user->uuid.c_str(), user->nick.c_str(), quitmessage.c_str());
278         if (localuser)
279         {
280                 ClientProtocol::Messages::Error errormsg(InspIRCd::Format("Closing link: (%s@%s) [%s]", user->ident.c_str(), user->GetRealHost().c_str(), operquitmsg.c_str()));
281                 localuser->Send(ServerInstance->GetRFCEvents().error, errormsg);
282         }
283
284         ServerInstance->GlobalCulls.AddItem(user);
285
286         if (user->registered == REG_ALL)
287         {
288                 FOREACH_MOD(OnUserQuit, (user, quitmsg, operquitmsg));
289                 WriteCommonQuit(user, quitmsg, operquitmsg);
290         }
291         else
292                 unregistered_count--;
293
294         if (IS_LOCAL(user))
295         {
296                 LocalUser* lu = IS_LOCAL(user);
297                 FOREACH_MOD(OnUserDisconnect, (lu));
298                 lu->eh.Close();
299
300                 if (lu->registered == REG_ALL)
301                         ServerInstance->SNO->WriteToSnoMask('q',"Client exiting: %s (%s) [%s]", user->GetFullRealHost().c_str(), user->GetIPString().c_str(), operquitmsg.c_str());
302                 local_users.erase(lu);
303         }
304
305         if (!clientlist.erase(user->nick))
306                 ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Nick not found in clientlist, cannot remove: " + user->nick);
307
308         uuidlist.erase(user->uuid);
309         user->PurgeEmptyChannels();
310         user->UnOper();
311 }
312
313 void UserManager::AddClone(User* user)
314 {
315         CloneCounts& counts = clonemap[user->GetCIDRMask()];
316         counts.global++;
317         if (IS_LOCAL(user))
318                 counts.local++;
319 }
320
321 void UserManager::RemoveCloneCounts(User *user)
322 {
323         CloneMap::iterator it = clonemap.find(user->GetCIDRMask());
324         if (it != clonemap.end())
325         {
326                 CloneCounts& counts = it->second;
327                 counts.global--;
328                 if (counts.global == 0)
329                 {
330                         // No more users from this IP, remove entry from the map
331                         clonemap.erase(it);
332                         return;
333                 }
334
335                 if (IS_LOCAL(user))
336                         counts.local--;
337         }
338 }
339
340 void UserManager::RehashCloneCounts()
341 {
342         clonemap.clear();
343
344         const user_hash& hash = ServerInstance->Users.GetUsers();
345         for (user_hash::const_iterator i = hash.begin(); i != hash.end(); ++i)
346         {
347                 User* u = i->second;
348                 AddClone(u);
349         }
350 }
351
352 const UserManager::CloneCounts& UserManager::GetCloneCounts(User* user) const
353 {
354         CloneMap::const_iterator it = clonemap.find(user->GetCIDRMask());
355         if (it != clonemap.end())
356                 return it->second;
357         else
358                 return zeroclonecounts;
359 }
360
361 void UserManager::ServerNoticeAll(const char* text, ...)
362 {
363         std::string message;
364         VAFORMAT(message, text, text);
365         ClientProtocol::Messages::Privmsg msg(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, ServerInstance->Config->ServerName, message, MSG_NOTICE);
366         ClientProtocol::Event msgevent(ServerInstance->GetRFCEvents().privmsg, msg);
367
368         for (LocalList::const_iterator i = local_users.begin(); i != local_users.end(); ++i)
369         {
370                 LocalUser* user = *i;
371                 user->Send(msgevent);
372         }
373 }
374
375 /**
376  * This function is called once a second from the mainloop.
377  * It is intended to do background checking on all the users, e.g. do
378  * ping checks, registration timeouts, etc.
379  */
380 void UserManager::DoBackgroundUserStuff()
381 {
382         for (LocalList::iterator i = local_users.begin(); i != local_users.end(); )
383         {
384                 // It's possible that we quit the user below due to ping timeout etc. and QuitUser() removes it from the list
385                 LocalUser* curr = *i;
386                 ++i;
387
388                 if (curr->CommandFloodPenalty || curr->eh.getSendQSize())
389                 {
390                         unsigned int rate = curr->MyClass->GetCommandRate();
391                         if (curr->CommandFloodPenalty > rate)
392                                 curr->CommandFloodPenalty -= rate;
393                         else
394                                 curr->CommandFloodPenalty = 0;
395                         curr->eh.OnDataReady();
396                 }
397
398                 switch (curr->registered)
399                 {
400                         case REG_ALL:
401                                 CheckPingTimeout(curr);
402                                 break;
403
404                         case REG_NICKUSER:
405                                 CheckModulesReady(curr);
406                                 break;
407
408                         default:
409                                 CheckRegistrationTimeout(curr);
410                                 break;
411                 }
412         }
413 }
414
415 already_sent_t UserManager::NextAlreadySentId()
416 {
417         if (++already_sent_id == 0)
418         {
419                 // Wrapped around, reset the already_sent ids of all users
420                 already_sent_id = 1;
421                 for (LocalList::iterator i = local_users.begin(); i != local_users.end(); ++i)
422                 {
423                         LocalUser* user = *i;
424                         user->already_sent = 0;
425                 }
426         }
427         return already_sent_id;
428 }