The InspIRCd Project
Home | Developers | Wiki | Forums | Bug Tracker | SVN | Download
Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members

xline.cpp

Go to the documentation of this file.
00001 /*       +------------------------------------+
00002  *       | Inspire Internet Relay Chat Daemon |
00003  *       +------------------------------------+
00004  *
00005  *  InspIRCd: (C) 2002-2008 InspIRCd Development Team
00006  * See: http://www.inspircd.org/wiki/index.php/Credits
00007  *
00008  * This program is free but copyrighted software; see
00009  *          the file COPYING for details.
00010  *
00011  * ---------------------------------------------------
00012  */
00013 
00014 /* $Core */
00015 
00016 #include "inspircd.h"
00017 #include "xline.h"
00018 #include "bancache.h"
00019 
00020 /*
00021  * This is now version 3 of the XLine subsystem, let's see if we can get it as nice and 
00022  * efficient as we can this time so we can close this file and never ever touch it again ..
00023  *
00024  * Background:
00025  *  Version 1 stored all line types in one list (one for g, one for z, etc). This was fine,
00026  *  but both version 1 and 2 suck at applying lines efficiently. That is, every time a new line
00027  *  was added, it iterated every existing line for every existing user. Ow. Expiry was also
00028  *  expensive, as the lists were NOT sorted.
00029  *
00030  *  Version 2 moved permanent lines into a seperate list from non-permanent to help optimize
00031  *  matching speed, but matched in the same way.
00032  *  Expiry was also sped up by sorting the list by expiry (meaning just remove the items at the
00033  *  head of the list that are outdated.)
00034  *
00035  * This was fine and good, but it looked less than ideal in code, and matching was still slower
00036  * than it could have been, something which we address here.
00037  *
00038  * VERSION 3:
00039  *  All lines are (as in v1) stored together -- no seperation of perm and non-perm. They are stored in
00040  *  a map of maps (first map is line type, second map is for quick lookup on add/delete/etc).
00041  *
00042  *  Expiry is *no longer* performed on a timer, and no longer uses a sorted list of any variety. This
00043  *  is now done by only checking for expiry when a line is accessed, meaning that expiry is no longer
00044  *  a resource intensive problem.
00045  *
00046  *  Application no longer tries to apply every single line on every single user - instead, now only lines
00047  *  added since the previous application are applied. This keeps S2S ADDLINE during burst nice and fast,
00048  *  while at the same time not slowing things the fuck down when we try adding a ban with lots of preexisting
00049  *  bans. :)
00050  */
00051 
00052 bool XLine::Matches(User *u)
00053 {
00054         return false;
00055 }
00056 
00057 /*
00058  * Checks what users match a given vector of ELines and sets their ban exempt flag accordingly.
00059  */
00060 void XLineManager::CheckELines()
00061 {
00062         ContainerIter n = lookup_lines.find("E");
00063 
00064         if (n == lookup_lines.end())
00065                 return;
00066 
00067         XLineLookup& ELines = n->second;
00068 
00069         if (ELines.empty())
00070                 return;
00071 
00072         for (std::vector<User*>::const_iterator u2 = ServerInstance->Users->local_users.begin(); u2 != ServerInstance->Users->local_users.end(); u2++)
00073         {
00074                 User* u = (User*)(*u2);
00075 
00076                 /* This uses safe iteration to ensure that if a line expires here, it doenst trash the iterator */
00077                 LookupIter safei;
00078 
00079                 for (LookupIter i = ELines.begin(); i != ELines.end(); )
00080                 {
00081                         safei = i;
00082                         safei++;
00083 
00084                         XLine *e = i->second;
00085                         u->exempt = e->Matches(u);
00086 
00087                         i = safei;
00088                 }
00089         }
00090 }
00091 
00092 
00093 XLineLookup* XLineManager::GetAll(const std::string &type)
00094 {
00095         ContainerIter n = lookup_lines.find(type);
00096 
00097         if (n == lookup_lines.end())
00098                 return NULL;
00099 
00100         LookupIter safei;
00101         const time_t current = ServerInstance->Time();
00102 
00103         /* Expire any dead ones, before sending */
00104         for (LookupIter x = n->second.begin(); x != n->second.end(); )
00105         {
00106                 safei = x;
00107                 safei++;
00108                 if (x->second->duration && current > x->second->expiry)
00109                 {
00110                         ExpireLine(n, x);
00111                 }
00112                 x = safei;
00113         }
00114 
00115         return &(n->second);
00116 }
00117 
00118 void XLineManager::DelAll(const std::string &type)
00119 {
00120         ContainerIter n = lookup_lines.find(type);
00121 
00122         if (n == lookup_lines.end())
00123                 return;
00124 
00125         LookupIter x;
00126 
00127         /* Delete all of a given type (this should probably use DelLine, but oh well) */
00128         while ((x = n->second.begin()) != n->second.end())
00129         {
00130                 ExpireLine(n, x);
00131         }
00132 }
00133 
00134 std::vector<std::string> XLineManager::GetAllTypes()
00135 {
00136         std::vector<std::string> items;
00137         for (ContainerIter x = lookup_lines.begin(); x != lookup_lines.end(); ++x)
00138                 items.push_back(x->first);
00139         return items;
00140 }
00141 
00142 IdentHostPair XLineManager::IdentSplit(const std::string &ident_and_host)
00143 {
00144         IdentHostPair n = std::make_pair<std::string,std::string>("*","*");
00145         std::string::size_type x = ident_and_host.find('@');
00146         if (x != std::string::npos)
00147         {
00148                 n.second = ident_and_host.substr(x + 1,ident_and_host.length());
00149                 n.first = ident_and_host.substr(0, x);
00150                 if (!n.first.length())
00151                         n.first.assign("*");
00152                 if (!n.second.length())
00153                         n.second.assign("*");
00154         }
00155         else
00156         {
00157                 n.first = "";
00158                 n.second = ident_and_host;
00159         }
00160 
00161         return n;
00162 }
00163 
00164 // adds a line
00165 
00166 bool XLineManager::AddLine(XLine* line, User* user)
00167 {
00168         /*IdentHostPair ih = IdentSplit(hostmask);*/
00169 
00170         ServerInstance->BanCache->RemoveEntries(line->type, false); // XXX perhaps remove ELines here?
00171 
00172         if (DelLine(line->Displayable(), line->type, user, true))
00173                 return false;
00174 
00175         /*ELine* item = new ELine(ServerInstance, ServerInstance->Time(), duration, source, reason, ih.first.c_str(), ih.second.c_str());*/
00176         XLineFactory* xlf = GetFactory(line->type);
00177         if (!xlf)
00178                 return false;
00179 
00180         if (xlf->AutoApplyToUserList(line))
00181                 pending_lines.push_back(line);
00182 
00183         lookup_lines[line->type][line->Displayable()] = line;
00184         line->OnAdd();
00185 
00186         FOREACH_MOD(I_OnAddLine,OnAddLine(user, line)); 
00187 
00188         return true;
00189 }
00190 
00191 // deletes a line, returns true if the line existed and was removed
00192 
00193 bool XLineManager::DelLine(const char* hostmask, const std::string &type, User* user, bool simulate)
00194 {
00195         ContainerIter x = lookup_lines.find(type);
00196 
00197         if (x == lookup_lines.end())
00198                 return false;
00199 
00200         LookupIter y = x->second.find(hostmask);
00201 
00202         if (y == x->second.end())
00203                 return false;
00204 
00205         if (simulate)
00206                 return true;
00207 
00208         ServerInstance->BanCache->RemoveEntries(y->second->type, true);
00209 
00210         FOREACH_MOD(I_OnDelLine,OnDelLine(user, y->second));
00211 
00212         y->second->Unset();
00213 
00214         std::vector<XLine*>::iterator pptr = std::find(pending_lines.begin(), pending_lines.end(), y->second);
00215         if (pptr != pending_lines.end())
00216                 pending_lines.erase(pptr);
00217 
00218         delete y->second;
00219         x->second.erase(y);
00220 
00221         return true;
00222 }
00223 
00224 
00225 void ELine::Unset()
00226 {
00227         /* remove exempt from everyone and force recheck after deleting eline */
00228         for (std::vector<User*>::const_iterator u2 = ServerInstance->Users->local_users.begin(); u2 != ServerInstance->Users->local_users.end(); u2++)
00229         {
00230                 User* u = (User*)(*u2);
00231                 u->exempt = false;
00232         }
00233 
00234         ServerInstance->XLines->CheckELines();
00235 }
00236 
00237 // returns a pointer to the reason if a nickname matches a qline, NULL if it didnt match
00238 
00239 XLine* XLineManager::MatchesLine(const std::string &type, User* user)
00240 {
00241         ContainerIter x = lookup_lines.find(type);
00242 
00243         if (x == lookup_lines.end())
00244                 return NULL;
00245 
00246         const time_t current = ServerInstance->Time();
00247 
00248         LookupIter safei;
00249 
00250         for (LookupIter i = x->second.begin(); i != x->second.end(); )
00251         {
00252                 safei = i;
00253                 safei++;
00254 
00255                 if (i->second->Matches(user))
00256                 {
00257                         if (i->second->duration && current > i->second->expiry)
00258                         {
00259                                 /* Expire the line, return nothing */
00260                                 ExpireLine(x, i);
00261                                 /* Continue, there may be another that matches
00262                                  * (thanks aquanight)
00263                                  */
00264                                 i = safei;
00265                                 continue;
00266                         }
00267                         else
00268                                 return i->second;
00269                 }
00270 
00271                 i = safei;
00272         }
00273         return NULL;
00274 }
00275 
00276 XLine* XLineManager::MatchesLine(const std::string &type, const std::string &pattern)
00277 {
00278         ContainerIter x = lookup_lines.find(type);
00279 
00280         if (x == lookup_lines.end())
00281                 return NULL;
00282 
00283         const time_t current = ServerInstance->Time();
00284 
00285          LookupIter safei;
00286 
00287         for (LookupIter i = x->second.begin(); i != x->second.end(); )
00288         {
00289                 safei = i;
00290                 safei++;
00291 
00292                 if (i->second->Matches(pattern))
00293                 {
00294                         if (i->second->duration && current > i->second->expiry)
00295                         {
00296                                 /* Expire the line, return nothing */
00297                                 ExpireLine(x, i);
00298                                 /* See above */
00299                                 i = safei;
00300                                 continue;
00301                         }
00302                         else
00303                                 return i->second;
00304                 }
00305 
00306                 i = safei;
00307         }
00308         return NULL;
00309 }
00310 
00311 // removes lines that have expired
00312 void XLineManager::ExpireLine(ContainerIter container, LookupIter item)
00313 {
00314         FOREACH_MOD(I_OnExpireLine, OnExpireLine(item->second));
00315 
00316         item->second->DisplayExpiry();
00317         item->second->Unset();
00318 
00319         /* TODO: Can we skip this loop by having a 'pending' field in the XLine class, which is set when a line
00320          * is pending, cleared when it is no longer pending, so we skip over this loop if its not pending?
00321          * -- Brain
00322          */
00323         std::vector<XLine*>::iterator pptr = std::find(pending_lines.begin(), pending_lines.end(), item->second);
00324         if (pptr != pending_lines.end())
00325                 pending_lines.erase(pptr);
00326 
00327         delete item->second;
00328         container->second.erase(item);
00329 }
00330 
00331 
00332 // applies lines, removing clients and changing nicks etc as applicable
00333 void XLineManager::ApplyLines()
00334 {
00335         for (std::vector<User*>::const_iterator u2 = ServerInstance->Users->local_users.begin(); u2 != ServerInstance->Users->local_users.end(); u2++)
00336         {
00337                 User* u = (User*)(*u2);
00338 
00339                 for (std::vector<XLine *>::iterator i = pending_lines.begin(); i != pending_lines.end(); i++)
00340                 {
00341                         XLine *x = *i;
00342                         if (x->Matches(u))
00343                                 x->Apply(u);
00344                 }
00345         }
00346 
00347         pending_lines.clear();
00348 }
00349 
00350 void XLineManager::InvokeStats(const std::string &type, int numeric, User* user, string_list &results)
00351 {
00352         std::string sn = ServerInstance->Config->ServerName;
00353 
00354         ContainerIter n = lookup_lines.find(type);
00355 
00356         time_t current = ServerInstance->Time();
00357 
00358         LookupIter safei;
00359 
00360         if (n != lookup_lines.end())
00361         {
00362                 XLineLookup& list = n->second;
00363                 for (LookupIter i = list.begin(); i != list.end(); )
00364                 {
00365                         safei = i;
00366                         safei++;
00367 
00368                         if (i->second->duration && current > i->second->expiry)
00369                         {
00370                                 ExpireLine(n, i);
00371                         }
00372                         else
00373                                 results.push_back(sn+" "+ConvToStr(numeric)+" "+user->nick+" :"+i->second->Displayable()+" "+
00374                                         ConvToStr(i->second->set_time)+" "+ConvToStr(i->second->duration)+" "+std::string(i->second->source)+" :"+(i->second->reason));
00375                         i = safei;
00376                 }
00377         }
00378 }
00379 
00380 
00381 XLineManager::XLineManager(InspIRCd* Instance) : ServerInstance(Instance)
00382 {
00383         GFact = new GLineFactory(Instance);
00384         EFact = new ELineFactory(Instance);
00385         KFact = new KLineFactory(Instance);
00386         QFact = new QLineFactory(Instance);
00387         ZFact = new ZLineFactory(Instance);
00388 
00389         RegisterFactory(GFact);
00390         RegisterFactory(EFact);
00391         RegisterFactory(KFact);
00392         RegisterFactory(QFact);
00393         RegisterFactory(ZFact);
00394 }
00395 
00396 XLineManager::~XLineManager()
00397 {
00398         UnregisterFactory(GFact);
00399         UnregisterFactory(EFact);
00400         UnregisterFactory(KFact);
00401         UnregisterFactory(QFact);
00402         UnregisterFactory(ZFact);
00403 
00404         delete GFact;
00405         delete EFact;
00406         delete KFact;
00407         delete QFact;
00408         delete ZFact;
00409 
00410         // Delete all existing XLines
00411         for (XLineContainer::iterator i = lookup_lines.begin(); i != lookup_lines.end(); i++)
00412         {
00413                 for (XLineLookup::iterator j = i->second.begin(); j != i->second.end(); j++)
00414                 {
00415                         delete j->second;
00416                 }
00417                 i->second.clear();
00418         }
00419         lookup_lines.clear();
00420         
00421 }
00422 
00423 void XLine::Apply(User* u)
00424 {
00425 }
00426 
00427 bool XLine::IsBurstable()
00428 {
00429         return true;
00430 }
00431 
00432 void XLine::DefaultApply(User* u, const std::string &line, bool bancache)
00433 {
00434         char sreason[MAXBUF];
00435         snprintf(sreason, MAXBUF, "%s-Lined: %s", line.c_str(), this->reason);
00436         if (*ServerInstance->Config->MoronBanner)
00437                 u->WriteServ("NOTICE %s :*** %s", u->nick.c_str(), ServerInstance->Config->MoronBanner);
00438 
00439         if (ServerInstance->Config->HideBans)
00440                 ServerInstance->Users->QuitUser(u, line + "-Lined", sreason);
00441         else
00442                 ServerInstance->Users->QuitUser(u, sreason);
00443 
00444 
00445         if (bancache)
00446         {
00447                 ServerInstance->Logs->Log("BANCACHE", DEBUG, std::string("BanCache: Adding positive hit (") + line + ") for " + u->GetIPString());
00448                 if (this->duration > 0)
00449                         ServerInstance->BanCache->AddHit(u->GetIPString(), this->type, line + "-Lined: " + this->reason, this->duration);
00450                 else
00451                         ServerInstance->BanCache->AddHit(u->GetIPString(), this->type, line + "-Lined: " + this->reason);
00452         }
00453 }
00454 
00455 bool KLine::Matches(User *u)
00456 {
00457         if (u->exempt)
00458                 return false;
00459 
00460         if (InspIRCd::Match(u->ident, this->identmask))
00461         {
00462                 if (InspIRCd::MatchCIDR(u->host, this->hostmask) ||
00463                     InspIRCd::MatchCIDR(u->GetIPString(), this->hostmask))
00464                 {
00465                         return true;
00466                 }
00467         }
00468 
00469         return false;
00470 }
00471 
00472 void KLine::Apply(User* u)
00473 {
00474         DefaultApply(u, "K", (strcmp(this->identmask, "*") == 0) ? true : false);
00475 }
00476 
00477 bool GLine::Matches(User *u)
00478 {
00479         if (u->exempt)
00480                 return false;
00481 
00482         if (InspIRCd::Match(u->ident, this->identmask))
00483         {
00484                 if (InspIRCd::MatchCIDR(u->host, this->hostmask) ||
00485                     InspIRCd::MatchCIDR(u->GetIPString(), this->hostmask))
00486                 {
00487                         return true;
00488                 }
00489         }
00490 
00491         return false;
00492 }
00493 
00494 void GLine::Apply(User* u)
00495 {       
00496         DefaultApply(u, "G", (strcmp(this->identmask, "*") == 0) ? true : false);
00497 }
00498 
00499 bool ELine::Matches(User *u)
00500 {
00501         if (u->exempt)
00502                 return false;
00503 
00504         if (InspIRCd::Match(u->ident, this->identmask))
00505         {
00506                 if (InspIRCd::MatchCIDR(u->host, this->hostmask) ||
00507                     InspIRCd::MatchCIDR(u->GetIPString(), this->hostmask))
00508                 {
00509                         return true;
00510                 }
00511         }
00512 
00513         return false;
00514 }
00515 
00516 bool ZLine::Matches(User *u)
00517 {
00518         if (u->exempt)
00519                 return false;
00520 
00521         if (InspIRCd::MatchCIDR(u->GetIPString(), this->ipaddr))
00522                 return true;
00523         else
00524                 return false;
00525 }
00526 
00527 void ZLine::Apply(User* u)
00528 {       
00529         DefaultApply(u, "Z", true);
00530 }
00531 
00532 
00533 bool QLine::Matches(User *u)
00534 {
00535         if (u->exempt)
00536                 return false;
00537 
00538         if (InspIRCd::Match(u->nick, this->nick))
00539                 return true;
00540 
00541         return false;
00542 }
00543 
00544 void QLine::Apply(User* u)
00545 {       
00546         /* Force to uuid on apply of qline, no need to disconnect any more :) */
00547         u->ForceNickChange(u->uuid.c_str());
00548 }
00549 
00550 
00551 bool ZLine::Matches(const std::string &str)
00552 {
00553         if (InspIRCd::MatchCIDR(str, this->ipaddr))
00554                 return true;
00555         else
00556                 return false;
00557 }
00558 
00559 bool QLine::Matches(const std::string &str)
00560 {
00561         if (InspIRCd::Match(str, this->nick))
00562                 return true;
00563 
00564         return false;
00565 }
00566 
00567 bool ELine::Matches(const std::string &str)
00568 {
00569         return (InspIRCd::MatchCIDR(str, matchtext));
00570 }
00571 
00572 bool KLine::Matches(const std::string &str)
00573 {
00574         return (InspIRCd::MatchCIDR(str.c_str(), matchtext));
00575 }
00576 
00577 bool GLine::Matches(const std::string &str)
00578 {
00579         return (InspIRCd::MatchCIDR(str, matchtext));
00580 }
00581 
00582 void ELine::OnAdd()
00583 {
00584         /* When adding one eline, only check the one eline */
00585         for (std::vector<User*>::const_iterator u2 = ServerInstance->Users->local_users.begin(); u2 != ServerInstance->Users->local_users.end(); u2++)
00586         {
00587                 User* u = (User*)(*u2);
00588                 if (this->Matches(u))
00589                         u->exempt = true;
00590         }
00591 }
00592 
00593 void ELine::DisplayExpiry()
00594 {
00595         ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed E-Line %s@%s (set by %s %ld seconds ago)",this->identmask,this->hostmask,this->source,this->duration);
00596 }
00597 
00598 void QLine::DisplayExpiry()
00599 {
00600         ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed Q-Line %s (set by %s %ld seconds ago)",this->nick,this->source,this->duration);
00601 }
00602 
00603 void ZLine::DisplayExpiry()
00604 {
00605         ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed Z-Line %s (set by %s %ld seconds ago)",this->ipaddr,this->source,this->duration);
00606 }
00607 
00608 void KLine::DisplayExpiry()
00609 {
00610         ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed K-Line %s@%s (set by %s %ld seconds ago)",this->identmask,this->hostmask,this->source,this->duration);
00611 }
00612 
00613 void GLine::DisplayExpiry()
00614 {
00615         ServerInstance->SNO->WriteToSnoMask('x',"Expiring timed G-Line %s@%s (set by %s %ld seconds ago)",this->identmask,this->hostmask,this->source,this->duration);
00616 }
00617 
00618 const char* ELine::Displayable()
00619 {
00620         return matchtext.c_str();
00621 }
00622 
00623 const char* KLine::Displayable()
00624 {
00625         return matchtext.c_str();
00626 }
00627 
00628 const char* GLine::Displayable()
00629 {
00630         return matchtext.c_str();
00631 }
00632 
00633 const char* ZLine::Displayable()
00634 {
00635         return ipaddr;
00636 }
00637 
00638 const char* QLine::Displayable()
00639 {
00640         return nick;
00641 }
00642 
00643 bool KLine::IsBurstable()
00644 {
00645         return false;
00646 }
00647 
00648 bool XLineManager::RegisterFactory(XLineFactory* xlf)
00649 {
00650         XLineFactMap::iterator n = line_factory.find(xlf->GetType());
00651 
00652         if (n != line_factory.end())
00653                 return false;
00654 
00655         line_factory[xlf->GetType()] = xlf;
00656 
00657         return true;
00658 }
00659 
00660 bool XLineManager::UnregisterFactory(XLineFactory* xlf)
00661 {
00662         XLineFactMap::iterator n = line_factory.find(xlf->GetType());
00663 
00664         if (n == line_factory.end())
00665                 return false;
00666 
00667         line_factory.erase(n);
00668 
00669         return true;
00670 }
00671 
00672 XLineFactory* XLineManager::GetFactory(const std::string &type)
00673 {
00674         XLineFactMap::iterator n = line_factory.find(type);
00675 
00676         if (n == line_factory.end())
00677                 return NULL;
00678 
00679         return n->second;
00680 }
00681