X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fusers.cpp;h=b81cd6d912d2bd6ca3bc8bbdc3833183144f12ae;hb=8f7f74cf0f297e2b8476fc4c670515f8940580ea;hp=bdceba1f5038a7587df82004a0843dfadb8f595e;hpb=743c10e4ca5acd45e670b335a07b5bf3ab754ab6;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/src/users.cpp b/src/users.cpp index bdceba1f5..b81cd6d91 100644 --- a/src/users.cpp +++ b/src/users.cpp @@ -21,10 +21,18 @@ #include "bancache.h" #include "commands/cmd_whowas.h" -static unsigned long already_sent[MAX_DESCRIPTORS] = {0}; - /* XXX: Used for speeding up WriteCommon operations */ -unsigned long uniq_id = 0; +unsigned long uniq_id = 1; + +static unsigned long* already_sent = NULL; + + +void InitializeAlreadySent(SocketEngine* SE) +{ + already_sent = new unsigned long[SE->GetMaxFds()]; + memset(already_sent, 0, SE->GetMaxFds() * sizeof(unsigned long)); +} + std::string User::ProcessNoticeMasks(const char *sm) { @@ -91,7 +99,7 @@ void User::StartDNSLookup() try { bool cached; - const char* sip = this->GetIPString(); + const char* sip = this->GetIPString(false); /* Special case for 4in6 (Have i mentioned i HATE 4in6?) */ if (!strncmp(sip, "0::ffff:", 8)) @@ -193,9 +201,10 @@ User::User(InspIRCd* Instance, const std::string &uid) : ServerInstance(Instance Visibility = NULL; ip = NULL; MyClass = NULL; + io = NULL; + AllowedUserModes = NULL; + AllowedChanModes = NULL; AllowedOperCommands = NULL; - memset(AllowedUserModes, 0, sizeof(AllowedUserModes)); - memset(AllowedChanModes, 0, sizeof(AllowedChanModes)); chans.clear(); invites.clear(); memset(modes,0,sizeof(modes)); @@ -223,7 +232,7 @@ User::~User() if (this->MyClass) { this->MyClass->RefCount--; - ServerInstance->Logs->Log("USERS", DEBUG, "User destructor -- connect refcount now: %u", this->MyClass->RefCount); + ServerInstance->Logs->Log("USERS", DEBUG, "User destructor -- connect refcount now: %lu", this->MyClass->RefCount); } if (this->AllowedOperCommands) { @@ -231,6 +240,18 @@ User::~User() AllowedOperCommands = NULL; } + if (this->AllowedUserModes) + { + delete[] AllowedUserModes; + AllowedUserModes = NULL; + } + + if (this->AllowedChanModes) + { + delete[] AllowedChanModes; + AllowedChanModes = NULL; + } + this->InvalidateCache(); this->DecrementModes(); @@ -295,8 +316,11 @@ char* User::MakeHostIP() void User::CloseSocket() { - ServerInstance->SE->Shutdown(this, 2); - ServerInstance->SE->Close(this); + if (this->fd > -1) + { + ServerInstance->SE->Shutdown(this, 2); + ServerInstance->SE->Close(this); + } } char* User::GetFullHost() @@ -447,6 +471,9 @@ bool User::HasModePermission(unsigned char mode, ModeType type) if (!IS_OPER(this)) return false; + if (!AllowedUserModes || !AllowedChanModes) + return false; + return ((type == MODETYPE_USER ? AllowedUserModes : AllowedChanModes))[(mode - 'A')]; } @@ -480,42 +507,68 @@ bool User::HasPermission(const std::string &command) return false; } -/** NOTE: We cannot pass a const reference to this method. - * The string is changed by the workings of the method, - * so that if we pass const ref, we end up copying it to - * something we can change anyway. Makes sense to just let - * the compiler do that copy for us. - */ -bool User::AddBuffer(std::string a) +bool User::AddBuffer(const std::string &a) { - try - { - std::string::size_type i = a.rfind('\r'); + std::string::size_type start = 0; + std::string::size_type i = a.find('\r'); + /* + * The old implementation here took a copy, and rfind() on \r, removing as it found them, before + * copying a second time onto the recvq. That's ok, but involves three copies minimum (recv() to buffer, + * buffer to here, here to recvq) - The new method now copies twice (recv() to buffer, buffer to recvq). + * + * We use find() instead of rfind() for clarity, however unlike the old code, our scanning of the string is + * contiguous: as we specify a startpoint, we never see characters we have scanned previously, making this + * marginally faster in cases with a number of \r hidden early on in the buffer. + * + * How it works: + * Start at first pos of string, find first \r, append everything in the chunk (excluding \r) to recvq. Set + * i ahead of the \r, search for next \r, add next chunk to buffer... repeat. + * -- w00t (7 may, 2008) + */ + if (i == std::string::npos) + { + // no \r that we need to dance around, just add to buffer + recvq.append(a); + } + else + { + // While we can find the end of a chunk to add while (i != std::string::npos) { - a.erase(i, 1); - i = a.rfind('\r'); - } + // Append the chunk that we have + recvq.append(a, start, (i - start)); - if (a.length()) - recvq.append(a); + // Start looking for the next one + start = i + 1; + i = a.find('\r', start); + } - if (this->MyClass && (recvq.length() > this->MyClass->GetRecvqMax())) + if (start != a.length()) { - this->SetWriteError("RecvQ exceeded"); - ServerInstance->SNO->WriteToSnoMask('A', "User %s RecvQ of %d exceeds connect class maximum of %d",this->nick,recvq.length(),this->MyClass->GetRecvqMax()); - return false; + /* + * This is here to catch a corner case when we get something like: + * NICK w0 + * 0t\r\nU + * SER ... + * in successive calls to us. + * + * Without this conditional, the 'U' on the second case will be dropped, + * which is most *certainly* not the behaviour we want! + * -- w00t + */ + recvq.append(a, start, (a.length() - start)); } - - return true; } - catch (...) + if (this->MyClass && (recvq.length() > this->MyClass->GetRecvqMax())) { - ServerInstance->Logs->Log("USERS", DEBUG,"Exception in User::AddBuffer()"); + this->SetWriteError("RecvQ exceeded"); + ServerInstance->SNO->WriteToSnoMask('A', "User %s RecvQ of %lu exceeds connect class maximum of %lu",this->nick,(unsigned long int)recvq.length(),this->MyClass->GetRecvqMax()); return false; } + + return true; } bool User::BufferIsReady() @@ -581,7 +634,7 @@ void User::AddWriteBuf(const std::string &data) * to repeatedly add the text to the sendq! */ this->SetWriteError("SendQ exceeded"); - ServerInstance->SNO->WriteToSnoMask('A', "User %s SendQ of %d exceeds connect class maximum of %d",this->nick,sendq.length() + data.length(),this->MyClass->GetSendqMax()); + ServerInstance->SNO->WriteToSnoMask('A', "User %s SendQ of %lu exceeds connect class maximum of %lu",this->nick,(unsigned long int)sendq.length() + data.length(),this->MyClass->GetSendqMax()); return; } @@ -683,6 +736,15 @@ void User::Oper(const std::string &opertype, const std::string &opername) else AllowedOperCommands = new std::map; + if (!AllowedChanModes) + AllowedChanModes = new bool[64]; + + if (!AllowedUserModes) + AllowedUserModes = new bool[64]; + + memset(AllowedUserModes, 0, 64); + memset(AllowedChanModes, 0, 64); + char* Classes = strdup(iter_opertype->second); char* myclass = strtok_r(Classes," ",&savept); while (myclass) @@ -703,7 +765,7 @@ void User::Oper(const std::string &opertype, const std::string &opername) { if (*c == '*') { - memset(this->AllowedUserModes, (int)(true), sizeof(this->AllowedUserModes)); + memset(this->AllowedUserModes, (int)(true), 64); } else { @@ -714,7 +776,7 @@ void User::Oper(const std::string &opertype, const std::string &opername) { if (*c == '*') { - memset(this->AllowedChanModes, (int)(true), sizeof(this->AllowedChanModes)); + memset(this->AllowedChanModes, (int)(true), 64); } else { @@ -753,8 +815,11 @@ void User::UnOper() } } - const char* parameters[] = { this->nick, moderemove.c_str() }; - ServerInstance->Parser->CallHandler("MODE", parameters, 2, this); + std::vector parameters; + parameters.push_back(this->nick); + parameters.push_back(moderemove); + + ServerInstance->Parser->CallHandler("MODE", parameters, this); /* unset their oper type (what IS_OPER checks), and remove +o */ *this->oper = 0; @@ -768,22 +833,18 @@ void User::UnOper() delete AllowedOperCommands; AllowedOperCommands = NULL; } - } -} - -void User::QuitUser(InspIRCd* Instance, User *user, const std::string &quitreason, const char* operreason) -{ - Instance->Logs->Log("USERS", DEBUG,"QuitUser: %s '%s'", user->nick, quitreason.c_str()); - user->Write("ERROR :Closing link (%s@%s) [%s]", user->ident, user->host, *operreason ? operreason : quitreason.c_str()); - user->quietquit = false; - user->quitmsg = quitreason; - - if (!*operreason) - user->operquitmsg = quitreason; - else - user->operquitmsg = operreason; + if (AllowedUserModes) + { + delete[] AllowedUserModes; + AllowedUserModes = NULL; + } + if (AllowedChanModes) + { + delete[] AllowedChanModes; + AllowedChanModes = NULL; + } - Instance->GlobalCulls.AddItem(user); + } } /* adds or updates an entry in the whowas list */ @@ -807,18 +868,18 @@ void User::CheckClass() if ((!a) || (a->GetType() == CC_DENY)) { - User::QuitUser(ServerInstance, this, "Unauthorised connection"); + ServerInstance->Users->QuitUser(this, "Unauthorised connection"); return; } else if ((a->GetMaxLocal()) && (ServerInstance->Users->LocalCloneCount(this) > a->GetMaxLocal())) { - User::QuitUser(ServerInstance, this, "No more connections allowed from your host via this connect class (local)"); + ServerInstance->Users->QuitUser(this, "No more connections allowed from your host via this connect class (local)"); ServerInstance->SNO->WriteToSnoMask('A', "WARNING: maximum LOCAL connections (%ld) exceeded for IP %s", a->GetMaxLocal(), this->GetIPString()); return; } else if ((a->GetMaxGlobal()) && (ServerInstance->Users->GlobalCloneCount(this) > a->GetMaxGlobal())) { - User::QuitUser(ServerInstance, this, "No more connections allowed from your host via this connect class (global)"); + ServerInstance->Users->QuitUser(this, "No more connections allowed from your host via this connect class (global)"); ServerInstance->SNO->WriteToSnoMask('A', "WARNING: maximum GLOBAL connections (%ld) exceeded for IP %s", a->GetMaxGlobal(), this->GetIPString()); return; } @@ -828,6 +889,25 @@ void User::CheckClass() this->MaxChans = a->GetMaxChans(); } +void User::CheckLines() +{ + const char* check[] = { "G" , "K", NULL }; + + if (!this->exempt) + { + for (int n = 0; check[n]; ++n) + { + XLine *r = ServerInstance->XLines->MatchesLine(check[n], this); + + if (r) + { + r->Apply(this); + return; + } + } + } +} + void User::FullConnect() { ServerInstance->stats->statsConnects++; @@ -846,37 +926,19 @@ void User::FullConnect() */ if (this->MyClass && !this->MyClass->GetPass().empty() && !this->haspassed) { - User::QuitUser(ServerInstance, this, "Invalid password"); + ServerInstance->Users->QuitUser(this, "Invalid password"); return; } - if (!this->exempt) - { - GLine *r = (GLine *)ServerInstance->XLines->MatchesLine("G", this); - - if (r) - { - r->Apply(this); - return; - } - - KLine *n = (KLine *)ServerInstance->XLines->MatchesLine("K", this); - - if (n) - { - n->Apply(this); - return; - } - } + CheckLines(); this->WriteServ("NOTICE Auth :Welcome to \002%s\002!",ServerInstance->Config->Network); this->WriteNumeric(001, "%s :Welcome to the %s IRC Network %s!%s@%s",this->nick, ServerInstance->Config->Network, this->nick, this->ident, this->host); - this->WriteNumeric(002, "%s :Your host is %s, running version %s",this->nick,ServerInstance->Config->ServerName,VERSION); + this->WriteNumeric(002, "%s :Your host is %s, running version InspIRCd-1.2",this->nick,ServerInstance->Config->ServerName); this->WriteNumeric(003, "%s :This server was created %s %s", this->nick, __TIME__, __DATE__); - this->WriteNumeric(004, "%s %s %s %s %s %s", this->nick, ServerInstance->Config->ServerName, VERSION, ServerInstance->Modes->UserModeList().c_str(), ServerInstance->Modes->ChannelModeList().c_str(), ServerInstance->Modes->ParaModeList().c_str()); + this->WriteNumeric(004, "%s %s InspIRCd-1.2 %s %s %s", this->nick, ServerInstance->Config->ServerName, ServerInstance->Modes->UserModeList().c_str(), ServerInstance->Modes->ChannelModeList().c_str(), ServerInstance->Modes->ParaModeList().c_str()); ServerInstance->Config->Send005(this); - this->WriteNumeric(42, "%s %s :your unique ID", this->nick, this->uuid); @@ -888,9 +950,9 @@ void User::FullConnect() /* Trigger LUSERS output, give modules a chance too */ int MOD_RESULT = 0; - FOREACH_RESULT(I_OnPreCommand, OnPreCommand("LUSERS", NULL, 0, this, true, "LUSERS")); + FOREACH_RESULT(I_OnPreCommand, OnPreCommand("LUSERS", std::vector(), this, true, "LUSERS")); if (!MOD_RESULT) - ServerInstance->CallCommandHandler("LUSERS", NULL, 0, this); + ServerInstance->CallCommandHandler("LUSERS", std::vector(), this); /* * We don't set REG_ALL until triggering OnUserConnect, so some module events don't spew out stuff @@ -944,46 +1006,31 @@ void User::InvalidateCache() bool User::ForceNickChange(const char* newnick) { - /* - * XXX this makes no sense.. - * why do we do nothing for change on users not REG_ALL? - * why do we trigger events twice for everyone previously (and just them now) - * i think the first if () needs removing totally, or? -- w00t - */ - if (this->registered != REG_ALL) - { - int MOD_RESULT = 0; - - this->InvalidateCache(); + int MOD_RESULT = 0; - FOREACH_RESULT(I_OnUserPreNick,OnUserPreNick(this, newnick)); + this->InvalidateCache(); - if (MOD_RESULT) - { - ServerInstance->stats->statsCollisions++; - return false; - } + FOREACH_RESULT(I_OnUserPreNick,OnUserPreNick(this, newnick)); - if (ServerInstance->XLines->MatchesLine("Q",newnick)) - { - ServerInstance->stats->statsCollisions++; - return false; - } + if (MOD_RESULT) + { + ServerInstance->stats->statsCollisions++; + return false; } - else + + std::deque dummy; + Command* nickhandler = ServerInstance->Parser->GetHandler("NICK"); + if (nickhandler) // wtfbbq, when would this not be here { - std::deque dummy; - Command* nickhandler = ServerInstance->Parser->GetHandler("NICK"); - if (nickhandler) // wtfbbq, when would this not be here - { - nickhandler->HandleInternal(1, dummy); - bool result = (ServerInstance->Parser->CallHandler("NICK", &newnick, 1, this) == CMD_SUCCESS); - nickhandler->HandleInternal(0, dummy); - return result; - } + std::vector parameters; + nickhandler->HandleInternal(1, dummy); + parameters.push_back(newnick); + bool result = (ServerInstance->Parser->CallHandler("NICK", parameters, this) == CMD_SUCCESS); + nickhandler->HandleInternal(0, dummy); + return result; } - // Unreachable. + // Unreachable, we hope return false; } @@ -1059,7 +1106,7 @@ int User::GetProtocolFamily() * XXX the duplication here is horrid.. * do we really need two methods doing essentially the same thing? */ -const char* User::GetIPString() +const char* User::GetIPString(bool translate4in6) { static char buf[1024]; @@ -1083,6 +1130,12 @@ const char* User::GetIPString() { strlcpy(&temp[1], buf, sizeof(temp) - 1); *temp = '0'; + if (translate4in6 && !strncmp(temp, "0::ffff:", 8)) + { + this->cachedip = temp + 8; + return temp + 8; + } + this->cachedip = temp; return temp; } @@ -1130,14 +1183,14 @@ void User::Write(std::string text) return; } - if (ServerInstance->Config->GetIOHook(this->GetPort())) + if (this->io) { /* XXX: The lack of buffering here is NOT a bug, modules implementing this interface have to * implement their own buffering mechanisms */ try { - ServerInstance->Config->GetIOHook(this->GetPort())->OnRawSocketWrite(this->fd, text.data(), text.length()); + this->io->OnRawSocketWrite(this->fd, text.data(), text.length()); } catch (CoreException& modexcept) { @@ -1286,6 +1339,9 @@ void User::WriteCommon(const std::string &text) uniq_id++; + if (!already_sent) + InitializeAlreadySent(ServerInstance->SE); + /* We dont want to be doing this n times, just once */ snprintf(tb,MAXBUF,":%s %s",this->GetFullHost(),text.c_str()); std::string out = tb; @@ -1340,6 +1396,10 @@ void User::WriteCommonQuit(const std::string &normal_text, const std::string &op return; uniq_id++; + + if (!already_sent) + InitializeAlreadySent(ServerInstance->SE); + snprintf(tb1,MAXBUF,":%s QUIT :%s",this->GetFullHost(),normal_text.c_str()); snprintf(tb2,MAXBUF,":%s QUIT :%s",this->GetFullHost(),oper_text.c_str()); std::string out1 = tb1; @@ -1371,6 +1431,10 @@ void User::WriteCommonExcept(const std::string &text) return; uniq_id++; + + if (!already_sent) + InitializeAlreadySent(ServerInstance->SE); + snprintf(tb1,MAXBUF,":%s %s",this->GetFullHost(),text.c_str()); out1 = tb1; @@ -1394,7 +1458,7 @@ void User::WriteCommonExcept(const std::string &text) void User::WriteWallOps(const std::string &text) { - if (!IS_OPER(this) && IS_LOCAL(this)) + if (!IS_LOCAL(this)) return; std::string wallop("WALLOPS :"); @@ -1410,6 +1474,9 @@ void User::WriteWallOps(const std::string &text) void User::WriteWallOps(const char* text, ...) { + if (!IS_LOCAL(this)) + return; + char textbuffer[MAXBUF]; va_list argsPtr; @@ -1672,7 +1739,7 @@ ConnectClass* User::SetClass(const std::string &explicit_name) /* deny change if change will take class over the limit */ if (found->limit && (found->RefCount + 1 >= found->limit)) { - ServerInstance->Logs->Log("USERS", DEBUG, "OOPS: Connect class limit (%u) hit, denying", found->limit); + ServerInstance->Logs->Log("USERS", DEBUG, "OOPS: Connect class limit (%lu) hit, denying", found->limit); return this->MyClass; } @@ -1682,12 +1749,12 @@ ConnectClass* User::SetClass(const std::string &explicit_name) if (found == this->MyClass) // no point changing this shit :P return this->MyClass; this->MyClass->RefCount--; - ServerInstance->Logs->Log("USERS", DEBUG, "Untying user from connect class -- refcount: %u", this->MyClass->RefCount); + ServerInstance->Logs->Log("USERS", DEBUG, "Untying user from connect class -- refcount: %lu", this->MyClass->RefCount); } this->MyClass = found; this->MyClass->RefCount++; - ServerInstance->Logs->Log("USERS", DEBUG, "User tied to new class -- connect refcount now: %u", this->MyClass->RefCount); + ServerInstance->Logs->Log("USERS", DEBUG, "User tied to new class -- connect refcount now: %lu", this->MyClass->RefCount); } return this->MyClass; @@ -1806,7 +1873,7 @@ void User::HandleEvent(EventType et, int errornum) { if (!WriteError.empty()) { - User::QuitUser(ServerInstance, this, GetWriteError()); + ServerInstance->Users->QuitUser(this, GetWriteError()); } } }