]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/xline.cpp
51b968a5f6e31bfe85e56beec0684e9374197aea
[user/henk/code/inspircd.git] / src / xline.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2009 InspIRCd Development Team
6  * See: http://wiki.inspircd.org/Credits
7  *
8  * This program is free but copyrighted software; see
9  *          the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #include "inspircd.h"
15 #include "xline.h"
16 #include "bancache.h"
17
18 /** An XLineFactory specialized to generate GLine* pointers
19  */
20 class GLineFactory : public XLineFactory
21 {
22  public:
23         GLineFactory() : XLineFactory("G") { }
24
25         /** Generate a GLine
26          */
27         XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask)
28         {
29                 IdentHostPair ih = ServerInstance->XLines->IdentSplit(xline_specific_mask);
30                 return new GLine(set_time, duration, source, reason, ih.first, ih.second);
31         }
32 };
33
34 /** An XLineFactory specialized to generate ELine* pointers
35  */
36 class ELineFactory : public XLineFactory
37 {
38  public:
39         ELineFactory() : XLineFactory("E") { }
40
41         /** Generate an ELine
42          */
43         XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask)
44         {
45                 IdentHostPair ih = ServerInstance->XLines->IdentSplit(xline_specific_mask);
46                 return new ELine(set_time, duration, source, reason, ih.first, ih.second);
47         }
48 };
49
50 /** An XLineFactory specialized to generate KLine* pointers
51  */
52 class KLineFactory : public XLineFactory
53 {
54  public:
55         KLineFactory() : XLineFactory("K") { }
56
57         /** Generate a KLine
58          */
59         XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask)
60         {
61                 IdentHostPair ih = ServerInstance->XLines->IdentSplit(xline_specific_mask);
62                 return new KLine(set_time, duration, source, reason, ih.first, ih.second);
63         }
64 };
65
66 /** An XLineFactory specialized to generate QLine* pointers
67  */
68 class QLineFactory : public XLineFactory
69 {
70  public:
71         QLineFactory() : XLineFactory("Q") { }
72
73         /** Generate a QLine
74          */
75         XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask)
76         {
77                 return new QLine(set_time, duration, source, reason, xline_specific_mask);
78         }
79 };
80
81 /** An XLineFactory specialized to generate ZLine* pointers
82  */
83 class ZLineFactory : public XLineFactory
84 {
85  public:
86         ZLineFactory() : XLineFactory("Z") { }
87
88         /** Generate a ZLine
89          */
90         XLine* Generate(time_t set_time, long duration, std::string source, std::string reason, std::string xline_specific_mask)
91         {
92                 return new ZLine(set_time, duration, source, reason, xline_specific_mask);
93         }
94 };
95
96
97 /*
98  * This is now version 3 of the XLine subsystem, let's see if we can get it as nice and
99  * efficient as we can this time so we can close this file and never ever touch it again ..
100  *
101  * Background:
102  *  Version 1 stored all line types in one list (one for g, one for z, etc). This was fine,
103  *  but both version 1 and 2 suck at applying lines efficiently. That is, every time a new line
104  *  was added, it iterated every existing line for every existing user. Ow. Expiry was also
105  *  expensive, as the lists were NOT sorted.
106  *
107  *  Version 2 moved permanent lines into a seperate list from non-permanent to help optimize
108  *  matching speed, but matched in the same way.
109  *  Expiry was also sped up by sorting the list by expiry (meaning just remove the items at the
110  *  head of the list that are outdated.)
111  *
112  * This was fine and good, but it looked less than ideal in code, and matching was still slower
113  * than it could have been, something which we address here.
114  *
115  * VERSION 3:
116  *  All lines are (as in v1) stored together -- no seperation of perm and non-perm. They are stored in
117  *  a map of maps (first map is line type, second map is for quick lookup on add/delete/etc).
118  *
119  *  Expiry is *no longer* performed on a timer, and no longer uses a sorted list of any variety. This
120  *  is now done by only checking for expiry when a line is accessed, meaning that expiry is no longer
121  *  a resource intensive problem.
122  *
123  *  Application no longer tries to apply every single line on every single user - instead, now only lines
124  *  added since the previous application are applied. This keeps S2S ADDLINE during burst nice and fast,
125  *  while at the same time not slowing things the fuck down when we try adding a ban with lots of preexisting
126  *  bans. :)
127  */
128
129 bool XLine::Matches(User *u)
130 {
131         return false;
132 }
133
134 /*
135  * Checks what users match a given vector of ELines and sets their ban exempt flag accordingly.
136  */
137 void XLineManager::CheckELines()
138 {
139         ContainerIter n = lookup_lines.find("E");
140
141         if (n == lookup_lines.end())
142                 return;
143
144         XLineLookup& ELines = n->second;
145
146         if (ELines.empty())
147                 return;
148
149         for (std::vector<LocalUser*>::const_iterator u2 = ServerInstance->Users->local_users.begin(); u2 != ServerInstance->Users->local_users.end(); u2++)
150         {
151                 User* u = (User*)(*u2);
152
153                 /* This uses safe iteration to ensure that if a line expires here, it doenst trash the iterator */
154                 LookupIter safei;
155
156                 for (LookupIter i = ELines.begin(); i != ELines.end(); )
157                 {
158                         safei = i;
159                         safei++;
160
161                         XLine *e = i->second;
162                         u->exempt = e->Matches(u);
163
164                         i = safei;
165                 }
166         }
167 }
168
169
170 XLineLookup* XLineManager::GetAll(const std::string &type)
171 {
172         ContainerIter n = lookup_lines.find(type);
173
174         if (n == lookup_lines.end())
175                 return NULL;
176
177         LookupIter safei;
178         const time_t current = ServerInstance->Time();
179
180         /* Expire any dead ones, before sending */
181         for (LookupIter x = n->second.begin(); x != n->second.end(); )
182         {
183                 safei = x;
184                 safei++;
185                 if (x->second->duration && current > x->second->expiry)
186                 {
187                         ExpireLine(n, x);
188                 }
189                 x = safei;
190         }
191
192         return &(n->second);
193 }
194
195 void XLineManager::DelAll(const std::string &type)
196 {
197         ContainerIter n = lookup_lines.find(type);
198
199         if (n == lookup_lines.end())
200                 return;
201
202         LookupIter x;
203
204         /* Delete all of a given type (this should probably use DelLine, but oh well) */
205         while ((x = n->second.begin()) != n->second.end())
206         {
207                 ExpireLine(n, x);
208         }
209 }
210
211 std::vector<std::string> XLineManager::GetAllTypes()
212 {
213         std::vector<std::string> items;
214         for (ContainerIter x = lookup_lines.begin(); x != lookup_lines.end(); ++x)
215                 items.push_back(x->first);
216         return items;
217 }
218
219 IdentHostPair XLineManager::IdentSplit(const std::string &ident_and_host)
220 {
221         IdentHostPair n = std::make_pair<std::string,std::string>("*","*");
222         std::string::size_type x = ident_and_host.find('@');
223         if (x != std::string::npos)
224         {
225                 n.second = ident_and_host.substr(x + 1,ident_and_host.length());
226                 n.first = ident_and_host.substr(0, x);
227                 if (!n.first.length())
228                         n.first.assign("*");
229                 if (!n.second.length())
230                         n.second.assign("*");
231         }
232         else
233         {
234                 n.first = "";
235                 n.second = ident_and_host;
236         }
237
238         return n;
239 }
240
241 // adds a line
242
243 bool XLineManager::AddLine(XLine* line, User* user)
244 {
245         ServerInstance->BanCache->RemoveEntries(line->type, false); // XXX perhaps remove ELines here?
246
247         if (line->duration && ServerInstance->Time() > line->expiry)
248                 return false; // Don't apply expired XLines.
249
250         /* Don't apply duplicate xlines */
251         ContainerIter x = lookup_lines.find(line->type);
252         if (x != lookup_lines.end())
253         {
254                 LookupIter i = x->second.find(line->Displayable());
255                 if (i != x->second.end())
256                 {
257                         return false;
258                 }
259         }
260
261         /*ELine* item = new ELine(ServerInstance->Time(), duration, source, reason, ih.first.c_str(), ih.second.c_str());*/
262         XLineFactory* xlf = GetFactory(line->type);
263         if (!xlf)
264                 return false;
265
266         if (xlf->AutoApplyToUserList(line))
267                 pending_lines.push_back(line);
268
269         lookup_lines[line->type][line->Displayable()] = line;
270         line->OnAdd();
271
272         FOREACH_MOD(I_OnAddLine,OnAddLine(user, line));
273
274         return true;
275 }
276
277 // deletes a line, returns true if the line existed and was removed
278
279 bool XLineManager::DelLine(const char* hostmask, const std::string &type, User* user, bool simulate)
280 {
281         ContainerIter x = lookup_lines.find(type);
282
283         if (x == lookup_lines.end())
284                 return false;
285
286         LookupIter y = x->second.find(hostmask);
287
288         if (y == x->second.end())
289                 return false;
290
291         if (simulate)
292                 return true;
293
294         ServerInstance->BanCache->RemoveEntries(y->second->type, true);
295
296         FOREACH_MOD(I_OnDelLine,OnDelLine(user, y->second));
297
298         y->second->Unset();
299
300         std::vector<XLine*>::iterator pptr = std::find(pending_lines.begin(), pending_lines.end(), y->second);
301         if (pptr != pending_lines.end())
302                 pending_lines.erase(pptr);
303
304         delete y->second;
305         x->second.erase(y);
306
307         return true;
308 }
309
310
311 void ELine::Unset()
312 {
313         /* remove exempt from everyone and force recheck after deleting eline */
314         for (std::vector<LocalUser*>::const_iterator u2 = ServerInstance->Users->local_users.begin(); u2 != ServerInstance->Users->local_users.end(); u2++)
315         {
316                 User* u = (User*)(*u2);
317                 u->exempt = false;
318         }
319
320         ServerInstance->XLines->CheckELines();
321 }
322
323 // returns a pointer to the reason if a nickname matches a qline, NULL if it didnt match
324
325 XLine* XLineManager::MatchesLine(const std::string &type, User* user)
326 {
327         ContainerIter x = lookup_lines.find(type);
328
329         if (x == lookup_lines.end())
330                 return NULL;
331
332         const time_t current = ServerInstance->Time();
333
334         LookupIter safei;
335
336         for (LookupIter i = x->second.begin(); i != x->second.end(); )
337         {
338                 safei = i;
339                 safei++;
340
341                 if (i->second->duration && current > i->second->expiry)
342                 {
343                         /* Expire the line, proceed to next one */
344                         ExpireLine(x, i);
345                         i = safei;
346                         continue;
347                 }
348
349                 if (i->second->Matches(user))
350                 {
351                         return i->second;
352                 }
353
354                 i = safei;
355         }
356         return NULL;
357 }
358
359 XLine* XLineManager::MatchesLine(const std::string &type, const std::string &pattern)
360 {
361         ContainerIter x = lookup_lines.find(type);
362
363         if (x == lookup_lines.end())
364                 return NULL;
365
366         const time_t current = ServerInstance->Time();
367
368          LookupIter safei;
369
370         for (LookupIter i = x->second.begin(); i != x->second.end(); )
371         {
372                 safei = i;
373                 safei++;
374
375                 if (i->second->Matches(pattern))
376                 {
377                         if (i->second->duration && current > i->second->expiry)
378                         {
379                                 /* Expire the line, return nothing */
380                                 ExpireLine(x, i);
381                                 /* See above */
382                                 i = safei;
383                                 continue;
384                         }
385                         else
386                                 return i->second;
387                 }
388
389                 i = safei;
390         }
391         return NULL;
392 }
393
394 // removes lines that have expired
395 void XLineManager::ExpireLine(ContainerIter container, LookupIter item)
396 {
397         FOREACH_MOD(I_OnExpireLine, OnExpireLine(item->second));
398
399         item->second->DisplayExpiry();
400         item->second->Unset();
401
402         /* TODO: Can we skip this loop by having a 'pending' field in the XLine class, which is set when a line
403          * is pending, cleared when it is no longer pending, so we skip over this loop if its not pending?
404          * -- Brain
405          */
406         std::vector<XLine*>::iterator pptr = std::find(pending_lines.begin(), pending_lines.end(), item->second);
407         if (pptr != pending_lines.end())
408                 pending_lines.erase(pptr);
409
410         delete item->second;
411         container->second.erase(item);
412 }
413
414
415 // applies lines, removing clients and changing nicks etc as applicable
416 void XLineManager::ApplyLines()
417 {
418         std::vector<LocalUser*>::reverse_iterator u2 = ServerInstance->Users->local_users.rbegin();
419         while (u2 != ServerInstance->Users->local_users.rend())
420         {
421                 User* u = *u2++;
422
423                 // Don't ban people who are exempt.
424                 if (u->exempt)
425                         continue;
426
427                 for (std::vector<XLine *>::iterator i = pending_lines.begin(); i != pending_lines.end(); i++)
428                 {
429                         XLine *x = *i;
430                         if (x->Matches(u))
431                                 x->Apply(u);
432                 }
433         }
434
435         pending_lines.clear();
436 }
437
438 void XLineManager::InvokeStats(const std::string &type, int numeric, User* user, string_list &results)
439 {
440         std::string sn = ServerInstance->Config->ServerName;
441
442         ContainerIter n = lookup_lines.find(type);
443
444         time_t current = ServerInstance->Time();
445
446         LookupIter safei;
447
448         if (n != lookup_lines.end())
449         {
450                 XLineLookup& list = n->second;
451                 for (LookupIter i = list.begin(); i != list.end(); )
452                 {
453                         safei = i;
454                         safei++;
455
456                         if (i->second->duration && current > i->second->expiry)
457                         {
458                                 ExpireLine(n, i);
459                         }
460                         else
461                                 results.push_back(sn+" "+ConvToStr(numeric)+" "+user->nick+" :"+i->second->Displayable()+" "+
462                                         ConvToStr(i->second->set_time)+" "+ConvToStr(i->second->duration)+" "+std::string(i->second->source)+" :"+(i->second->reason));
463                         i = safei;
464                 }
465         }
466 }
467
468
469 XLineManager::XLineManager()
470 {
471         GLineFactory* GFact;
472         ELineFactory* EFact;
473         KLineFactory* KFact;
474         QLineFactory* QFact;
475         ZLineFactory* ZFact;
476
477
478         GFact = new GLineFactory;
479         EFact = new ELineFactory;
480         KFact = new KLineFactory;
481         QFact = new QLineFactory;
482         ZFact = new ZLineFactory;
483
484         RegisterFactory(GFact);
485         RegisterFactory(EFact);
486         RegisterFactory(KFact);
487         RegisterFactory(QFact);
488         RegisterFactory(ZFact);
489 }
490
491 XLineManager::~XLineManager()
492 {
493         const char gekqz[] = "GEKQZ";
494         for(unsigned int i=0; i < sizeof(gekqz); i++)
495         {
496                 XLineFactory* xlf = GetFactory(std::string(1, gekqz[i]));
497                 if (xlf)
498                         UnregisterFactory(xlf);
499                 delete xlf;
500         }
501
502         // Delete all existing XLines
503         for (XLineContainer::iterator i = lookup_lines.begin(); i != lookup_lines.end(); i++)
504         {
505                 for (XLineLookup::iterator j = i->second.begin(); j != i->second.end(); j++)
506                 {
507                         delete j->second;
508                 }
509                 i->second.clear();
510         }
511         lookup_lines.clear();
512
513 }
514
515 void XLine::Apply(User* u)
516 {
517 }
518
519 bool XLine::IsBurstable()
520 {
521         return true;
522 }
523
524 void XLine::DefaultApply(User* u, const std::string &line, bool bancache)
525 {
526         char sreason[MAXBUF];
527         snprintf(sreason, MAXBUF, "%s-Lined: %s", line.c_str(), this->reason.c_str());
528         if (!ServerInstance->Config->MoronBanner.empty())
529                 u->WriteServ("NOTICE %s :*** %s", u->nick.c_str(), ServerInstance->Config->MoronBanner.c_str());
530
531         if (ServerInstance->Config->HideBans)
532                 ServerInstance->Users->QuitUser(u, line + "-Lined", sreason);
533         else
534                 ServerInstance->Users->QuitUser(u, sreason);
535
536
537         if (bancache)
538         {
539                 ServerInstance->Logs->Log("BANCACHE", DEBUG, std::string("BanCache: Adding positive hit (") + line + ") for " + u->GetIPString());
540                 if (this->duration > 0)
541                         ServerInstance->BanCache->AddHit(u->GetIPString(), this->type, line + "-Lined: " + this->reason, this->duration);
542                 else
543                         ServerInstance->BanCache->AddHit(u->GetIPString(), this->type, line + "-Lined: " + this->reason);
544         }
545 }
546
547 bool KLine::Matches(User *u)
548 {
549         if (u->exempt)
550                 return false;
551
552         if (InspIRCd::Match(u->ident, this->identmask, ascii_case_insensitive_map))
553         {
554                 if (InspIRCd::MatchCIDR(u->host, this->hostmask, ascii_case_insensitive_map) ||
555                     InspIRCd::MatchCIDR(u->GetIPString(), this->hostmask, ascii_case_insensitive_map))
556                 {
557                         return true;
558                 }
559         }
560
561         return false;
562 }
563
564 void KLine::Apply(User* u)
565 {
566         DefaultApply(u, "K", (this->identmask ==  "*") ? true : false);
567 }
568
569 bool GLine::Matches(User *u)
570 {
571         if (u->exempt)
572                 return false;
573
574         if (InspIRCd::Match(u->ident, this->identmask, ascii_case_insensitive_map))
575         {
576                 if (InspIRCd::MatchCIDR(u->host, this->hostmask, ascii_case_insensitive_map) ||
577                     InspIRCd::MatchCIDR(u->GetIPString(), this->hostmask, ascii_case_insensitive_map))
578                 {
579                         return true;
580                 }
581         }
582
583         return false;
584 }
585
586 void GLine::Apply(User* u)
587 {
588         DefaultApply(u, "G", (this->identmask == "*") ? true : false);
589 }
590
591 bool ELine::Matches(User *u)
592 {
593         if (u->exempt)
594                 return false;
595
596         if (InspIRCd::Match(u->ident, this->identmask, ascii_case_insensitive_map))
597         {
598                 if (InspIRCd::MatchCIDR(u->host, this->hostmask, ascii_case_insensitive_map) ||
599                     InspIRCd::MatchCIDR(u->GetIPString(), this->hostmask, ascii_case_insensitive_map))
600                 {
601                         return true;
602                 }
603         }
604
605         return false;
606 }
607
608 bool ZLine::Matches(User *u)
609 {
610         if (u->exempt)
611                 return false;
612
613         if (InspIRCd::MatchCIDR(u->GetIPString(), this->ipaddr))
614                 return true;
615         else
616                 return false;
617 }
618
619 void ZLine::Apply(User* u)
620 {
621         DefaultApply(u, "Z", true);
622 }
623
624
625 bool QLine::Matches(User *u)
626 {
627         if (InspIRCd::Match(u->nick, this->nick))
628                 return true;
629
630         return false;
631 }
632
633 void QLine::Apply(User* u)
634 {
635         /* Force to uuid on apply of qline, no need to disconnect any more :) */
636         u->ForceNickChange(u->uuid.c_str());
637 }
638
639
640 bool ZLine::Matches(const std::string &str)
641 {
642         if (InspIRCd::MatchCIDR(str, this->ipaddr))
643                 return true;
644         else
645                 return false;
646 }
647
648 bool QLine::Matches(const std::string &str)
649 {
650         if (InspIRCd::Match(str, this->nick))
651                 return true;
652
653         return false;
654 }
655
656 bool ELine::Matches(const std::string &str)
657 {
658         return (InspIRCd::MatchCIDR(str, matchtext));
659 }
660
661 bool KLine::Matches(const std::string &str)
662 {
663         return (InspIRCd::MatchCIDR(str.c_str(), matchtext));
664 }
665
666 bool GLine::Matches(const std::string &str)
667 {
668         return (InspIRCd::MatchCIDR(str, matchtext));
669 }
670
671 void ELine::OnAdd()
672 {
673         /* When adding one eline, only check the one eline */
674         for (std::vector<LocalUser*>::const_iterator u2 = ServerInstance->Users->local_users.begin(); u2 != ServerInstance->Users->local_users.end(); u2++)
675         {
676                 User* u = (User*)(*u2);
677                 if (this->Matches(u))
678                         u->exempt = true;
679         }
680 }
681
682 void ELine::DisplayExpiry()
683 {
684         ServerInstance->SNO->WriteToSnoMask('x',"Removing expired E-Line %s@%s (set by %s %ld seconds ago)",
685                 identmask.c_str(),hostmask.c_str(),source.c_str(),(long)(ServerInstance->Time() - this->set_time));
686 }
687
688 void QLine::DisplayExpiry()
689 {
690         ServerInstance->SNO->WriteToSnoMask('x',"Removing expired Q-Line %s (set by %s %ld seconds ago)",
691                 nick.c_str(),source.c_str(),(long)(ServerInstance->Time() - this->set_time));
692 }
693
694 void ZLine::DisplayExpiry()
695 {
696         ServerInstance->SNO->WriteToSnoMask('x',"Removing expired Z-Line %s (set by %s %ld seconds ago)",
697                 ipaddr.c_str(),source.c_str(),(long)(ServerInstance->Time() - this->set_time));
698 }
699
700 void KLine::DisplayExpiry()
701 {
702         ServerInstance->SNO->WriteToSnoMask('x',"Removing expired K-Line %s@%s (set by %s %ld seconds ago)",
703                 identmask.c_str(),hostmask.c_str(),source.c_str(),(long)(ServerInstance->Time() - this->set_time));
704 }
705
706 void GLine::DisplayExpiry()
707 {
708         ServerInstance->SNO->WriteToSnoMask('x',"Removing expired G-Line %s@%s (set by %s %ld seconds ago)",
709                 identmask.c_str(),hostmask.c_str(),source.c_str(),(long)(ServerInstance->Time() - this->set_time));
710 }
711
712 const char* ELine::Displayable()
713 {
714         return matchtext.c_str();
715 }
716
717 const char* KLine::Displayable()
718 {
719         return matchtext.c_str();
720 }
721
722 const char* GLine::Displayable()
723 {
724         return matchtext.c_str();
725 }
726
727 const char* ZLine::Displayable()
728 {
729         return ipaddr.c_str();
730 }
731
732 const char* QLine::Displayable()
733 {
734         return nick.c_str();
735 }
736
737 bool KLine::IsBurstable()
738 {
739         return false;
740 }
741
742 bool XLineManager::RegisterFactory(XLineFactory* xlf)
743 {
744         XLineFactMap::iterator n = line_factory.find(xlf->GetType());
745
746         if (n != line_factory.end())
747                 return false;
748
749         line_factory[xlf->GetType()] = xlf;
750
751         return true;
752 }
753
754 bool XLineManager::UnregisterFactory(XLineFactory* xlf)
755 {
756         XLineFactMap::iterator n = line_factory.find(xlf->GetType());
757
758         if (n == line_factory.end())
759                 return false;
760
761         line_factory.erase(n);
762
763         return true;
764 }
765
766 XLineFactory* XLineManager::GetFactory(const std::string &type)
767 {
768         XLineFactMap::iterator n = line_factory.find(type);
769
770         if (n == line_factory.end())
771                 return NULL;
772
773         return n->second;
774 }