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