X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fusers.cpp;h=db23d5672219799857e99d67e84cdc1d7d6fff4c;hb=9d58c0986bfc4801a8d6388947f100317470bb5f;hp=43bf64f1c3a95ab75f275cdcbe28d8179022dfba;hpb=d7a0cd3db1e8b64a6f706f1831e645ad69aa7927;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/src/users.cpp b/src/users.cpp index 43bf64f1c..db23d5672 100644 --- a/src/users.cpp +++ b/src/users.cpp @@ -21,17 +21,18 @@ #include "bancache.h" #include "commands/cmd_whowas.h" +/* XXX: Used for speeding up WriteCommon operations */ +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, sizeof(already_sent)); + memset(already_sent, 0, SE->GetMaxFds() * sizeof(unsigned long)); } -/* XXX: Used for speeding up WriteCommon operations */ -unsigned long uniq_id = 1; std::string User::ProcessNoticeMasks(const char *sm) { @@ -98,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)) @@ -506,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 %lu exceeds connect class maximum of %lu",this->nick,(unsigned long int)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() @@ -788,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; @@ -859,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++; @@ -881,24 +930,7 @@ void User::FullConnect() 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); @@ -918,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 @@ -990,8 +1022,10 @@ bool User::ForceNickChange(const char* newnick) Command* nickhandler = ServerInstance->Parser->GetHandler("NICK"); if (nickhandler) // wtfbbq, when would this not be here { + std::vector parameters; nickhandler->HandleInternal(1, dummy); - bool result = (ServerInstance->Parser->CallHandler("NICK", &newnick, 1, this) == CMD_SUCCESS); + parameters.push_back(newnick); + bool result = (ServerInstance->Parser->CallHandler("NICK", parameters, this) == CMD_SUCCESS); nickhandler->HandleInternal(0, dummy); return result; } @@ -1072,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]; @@ -1096,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; } @@ -1655,14 +1695,20 @@ ConnectClass* User::SetClass(const std::string &explicit_name) if (!IS_LOCAL(this)) return NULL; + ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "Setting connect class for UID %s", this->uuid); + if (!explicit_name.empty()) { for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++) { ConnectClass* c = *i; - if (explicit_name == c->GetName() && !c->GetDisabled()) + if (c->GetDisabled()) + continue; // can't possibly match, removed from conf + + if (explicit_name == c->GetName()) { + ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "Explicitly set to %s", explicit_name.c_str()); found = c; } } @@ -1673,37 +1719,64 @@ ConnectClass* User::SetClass(const std::string &explicit_name) { ConnectClass* c = *i; - if (((match(this->GetIPString(),c->GetHost().c_str(),true)) || (match(this->host,c->GetHost().c_str())))) + if (c->GetType() == CC_ALLOW) { - if (c->GetPort()) - { - if (this->GetPort() == c->GetPort() && !c->GetDisabled()) - { - found = c; - } - else - continue; - } - else + ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "ALLOW %s %d %s", c->GetHost().c_str(), c->GetPort(), c->GetName().c_str()); + } + else + { + ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "DENY %s %d %s", c->GetHost().c_str(), c->GetPort(), c->GetName().c_str()); + } + + /* if it's disabled, we can't match this one. */ + if (c->GetDisabled()) + { + ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "Class disabled"); + continue; + } + + /* check if host matches.. */ + if (((!match(this->GetIPString(),c->GetHost(),true)) && (!match(this->host,c->GetHost())))) + { + ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "No host match (for %s)", c->GetHost().c_str()); + continue; + } + + /* + * deny change if change will take class over the limit check it HERE, not after we found a matching class, + * because we should attempt to find another class if this one doesn't match us. -- w00t + */ + if (c->limit && (c->RefCount + 1 >= c->limit)) + { + ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "OOPS: Connect class limit (%lu) hit, denying", c->limit); + continue; + } + + /* if it requires a port ... */ + if (c->GetPort()) + { + ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "Requires port (%d)", c->GetPort()); + + /* and our port doesn't match, fail. */ + if (this->GetPort() != c->GetPort()) { - if (!c->GetDisabled()) - found = c; + ServerInstance->Logs->Log("CONNECTCLASS", DEBUG, "Port match failed (%d)", this->GetPort()); + continue; } } + + /* we stop at the first class that meets ALL critera. */ + found = c; + break; } } - /* ensure we don't fuck things up refcount wise, only remove them from a class if we find a new one :P */ + /* + * Okay, assuming we found a class that matches.. switch us into that class, keeping refcounts up to date. + */ if (found) { - /* 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 (%lu) hit, denying", found->limit); - return this->MyClass; - } - - /* should always be valid, but just in case .. */ + /* only fiddle with refcounts if they are already in a class .. */ if (this->MyClass) { if (found == this->MyClass) // no point changing this shit :P