X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fusers.cpp;h=01a973deae90a7b444736f0572cdc25b78377a8b;hb=46f89e12959945a94f6ad81265351600badca723;hp=d8ed553a79dde4247bcd08d60e720e8b9ff740da;hpb=ffbd1eebf0b82bf40482879f410f58874030a695;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/src/users.cpp b/src/users.cpp index d8ed553a7..01a973dea 100644 --- a/src/users.cpp +++ b/src/users.cpp @@ -11,12 +11,11 @@ * --------------------------------------------------- */ -/* $Core: libIRCDusers */ +/* $Core */ #include "inspircd.h" #include #include "socketengine.h" -#include "wildcard.h" #include "xline.h" #include "bancache.h" #include "commands/cmd_whowas.h" @@ -82,7 +81,7 @@ std::string User::ProcessNoticeMasks(const char *sm) } } else - this->WriteNumeric(501, "%s %c :is unknown snomask char to me", this->nick, *c); + this->WriteNumeric(ERR_UNKNOWNSNOMASK, "%s %c :is unknown snomask char to me", this->nick.c_str(), *c); oldadding = adding; break; @@ -98,8 +97,9 @@ void User::StartDNSLookup() { try { - bool cached; + bool cached = false; const char* sip = this->GetIPString(false); + UserResolver *res_reverse; /* Special case for 4in6 (Have i mentioned i HATE 4in6?) */ if (!strncmp(sip, "0::ffff:", 8)) @@ -117,11 +117,15 @@ void User::StartDNSLookup() bool User::IsNoticeMaskSet(unsigned char sm) { + if (!isalpha(sm)) + return false; return (snomasks[sm-65]); } void User::SetNoticeMask(unsigned char sm, bool value) { + if (!isalpha(sm)) + return; snomasks[sm-65] = value; } @@ -140,28 +144,42 @@ const char* User::FormatNoticeMasks() return data; } - - bool User::IsModeSet(unsigned char m) { + if (!isalpha(m)) + return false; return (modes[m-65]); } void User::SetMode(unsigned char m, bool value) { + if (!isalpha(m)) + return; modes[m-65] = value; } -const char* User::FormatModes() +const char* User::FormatModes(bool showparameters) { static char data[MAXBUF]; + std::string params; int offset = 0; - for (int n = 0; n < 64; n++) + + for (unsigned char n = 0; n < 64; n++) { if (modes[n]) - data[offset++] = n+65; + { + data[offset++] = n + 65; + ModeHandler* mh = ServerInstance->Modes->FindMode(n + 65, MODETYPE_USER); + if (showparameters && mh && mh->GetNumParams(true)) + { + std::string p = mh->GetUserParameter(this); + if (p.length()) + params.append(" ").append(p); + } + } } data[offset] = 0; + strlcat(data, params.c_str(), MAXBUF); return data; } @@ -185,39 +203,31 @@ void User::DecrementModes() User::User(InspIRCd* Instance, const std::string &uid) : ServerInstance(Instance) { - *password = *nick = *ident = *host = *dhost = *fullname = *awaymsg = *oper = *uuid = 0; server = (char*)Instance->FindServerNamePtr(Instance->Config->ServerName); reset_due = ServerInstance->Time(); age = ServerInstance->Time(); Penalty = 0; lines_in = lastping = signon = idle_lastmsg = nping = registered = 0; - ChannelCount = timeout = bytes_in = bytes_out = cmds_in = cmds_out = 0; + bytes_in = bytes_out = cmds_in = cmds_out = 0; quietquit = OverPenalty = ExemptFromPenalty = quitting = exempt = haspassed = dns_done = false; fd = -1; recvq.clear(); sendq.clear(); - WriteError.clear(); - res_forward = res_reverse = NULL; Visibility = NULL; ip = NULL; MyClass = NULL; - io = NULL; AllowedUserModes = NULL; AllowedChanModes = NULL; AllowedOperCommands = NULL; chans.clear(); invites.clear(); - memset(modes,0,sizeof(modes)); - memset(snomasks,0,sizeof(snomasks)); - /* Invalidate cache */ - cached_fullhost = cached_hostip = cached_makehost = cached_fullrealhost = NULL; if (uid.empty()) - strlcpy(uuid, Instance->GetUID().c_str(), UUID_LENGTH); + uuid.assign(Instance->GetUID(), 0, UUID_LENGTH - 1); else - strlcpy(uuid, uid.c_str(), UUID_LENGTH); + uuid.assign(uid, 0, UUID_LENGTH - 1); - ServerInstance->Logs->Log("USERS", DEBUG,"New UUID for user: %s (%s)", uuid, uid.empty() ? "allocated new" : "used remote"); + ServerInstance->Logs->Log("USERS", DEBUG,"New UUID for user: %s (%s)", uuid.c_str(), uid.empty() ? "allocated new" : "used remote"); user_hash::iterator finduuid = Instance->Users->uuidlist->find(uuid); if (finduuid == Instance->Users->uuidlist->end()) @@ -274,42 +284,42 @@ User::~User() ServerInstance->Users->uuidlist->erase(uuid); } -char* User::MakeHost() +const std::string& User::MakeHost() { - if (this->cached_makehost) + if (!this->cached_makehost.empty()) return this->cached_makehost; char nhost[MAXBUF]; /* This is much faster than snprintf */ char* t = nhost; - for(char* n = ident; *n; n++) + for(const char* n = ident.c_str(); *n; n++) *t++ = *n; *t++ = '@'; - for(char* n = host; *n; n++) + for(const char* n = host.c_str(); *n; n++) *t++ = *n; *t = 0; - this->cached_makehost = strdup(nhost); + this->cached_makehost.assign(nhost); return this->cached_makehost; } -char* User::MakeHostIP() +const std::string& User::MakeHostIP() { - if (this->cached_hostip) + if (!this->cached_hostip.empty()) return this->cached_hostip; char ihost[MAXBUF]; /* This is much faster than snprintf */ char* t = ihost; - for(char* n = ident; *n; n++) + for(const char* n = ident.c_str(); *n; n++) *t++ = *n; *t++ = '@'; for(const char* n = this->GetIPString(); *n; n++) *t++ = *n; *t = 0; - this->cached_hostip = strdup(ihost); + this->cached_hostip = ihost; return this->cached_hostip; } @@ -323,24 +333,24 @@ void User::CloseSocket() } } -char* User::GetFullHost() +const std::string& User::GetFullHost() { - if (this->cached_fullhost) + if (!this->cached_fullhost.empty()) return this->cached_fullhost; char result[MAXBUF]; char* t = result; - for(char* n = nick; *n; n++) + for(const char* n = nick.c_str(); *n; n++) *t++ = *n; *t++ = '!'; - for(char* n = ident; *n; n++) + for(const char* n = ident.c_str(); *n; n++) *t++ = *n; *t++ = '@'; - for(char* n = dhost; *n; n++) + for(const char* n = dhost.c_str(); *n; n++) *t++ = *n; *t = 0; - this->cached_fullhost = strdup(result); + this->cached_fullhost = result; return this->cached_fullhost; } @@ -351,7 +361,7 @@ char* User::MakeWildHost() char* t = nresult; *t++ = '*'; *t++ = '!'; *t++ = '*'; *t++ = '@'; - for(char* n = dhost; *n; n++) + for(const char* n = dhost.c_str(); *n; n++) *t++ = *n; *t = 0; return nresult; @@ -372,24 +382,24 @@ int User::ReadData(void* buffer, size_t size) } -char* User::GetFullRealHost() +const std::string& User::GetFullRealHost() { - if (this->cached_fullrealhost) + if (!this->cached_fullrealhost.empty()) return this->cached_fullrealhost; char fresult[MAXBUF]; char* t = fresult; - for(char* n = nick; *n; n++) + for(const char* n = nick.c_str(); *n; n++) *t++ = *n; *t++ = '!'; - for(char* n = ident; *n; n++) + for(const char* n = ident.c_str(); *n; n++) *t++ = *n; *t++ = '@'; - for(char* n = host; *n; n++) + for(const char* n = host.c_str(); *n; n++) *t++ = *n; *t = 0; - this->cached_fullrealhost = strdup(fresult); + this->cached_fullrealhost = fresult; return this->cached_fullrealhost; } @@ -446,6 +456,8 @@ void User::InviteTo(const irc::string &channel, time_t invtimeout) { i->second = invtimeout; } + + return; } } invites.push_back(std::make_pair(channel, invtimeout)); @@ -507,42 +519,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()"); + ServerInstance->Users->QuitUser(this, "RecvQ exceeded"); + ServerInstance->SNO->WriteToSnoMask('A', "User %s RecvQ of %lu exceeds connect class maximum of %lu",this->nick.c_str(),(unsigned long int)recvq.length(),this->MyClass->GetRecvqMax()); return false; } + + return true; } bool User::BufferIsReady() @@ -597,7 +635,7 @@ std::string User::GetBuffer() void User::AddWriteBuf(const std::string &data) { - if (*this->GetWriteError()) + if (this->quitting) return; if (this->MyClass && (sendq.length() + data.length() > this->MyClass->GetSendqMax())) @@ -607,8 +645,8 @@ void User::AddWriteBuf(const std::string &data) * if we dont it'll recursively call here over and over again trying * to repeatedly add the text to the sendq! */ - this->SetWriteError("SendQ exceeded"); - 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()); + ServerInstance->Users->QuitUser(this, "SendQ exceeded"); + ServerInstance->SNO->WriteToSnoMask('A', "User %s SendQ of %lu exceeds connect class maximum of %lu",this->nick.c_str(),(unsigned long int)sendq.length() + data.length(),this->MyClass->GetSendqMax()); return; } @@ -621,172 +659,157 @@ void User::AddWriteBuf(const std::string &data) // send AS MUCH OF THE USERS SENDQ as we are able to (might not be all of it) void User::FlushWriteBuf() { - try + if (this->fd == FD_MAGIC_NUMBER) { - if ((this->fd == FD_MAGIC_NUMBER) || (*this->GetWriteError())) - { - sendq.clear(); - } - if ((sendq.length()) && (this->fd != FD_MAGIC_NUMBER)) - { - int old_sendq_length = sendq.length(); - int n_sent = ServerInstance->SE->Send(this, this->sendq.data(), this->sendq.length(), 0); + sendq.clear(); + return; + } - if (n_sent == -1) + if ((sendq.length()) && (this->fd != FD_MAGIC_NUMBER)) + { + int old_sendq_length = sendq.length(); + int n_sent = ServerInstance->SE->Send(this, this->sendq.data(), this->sendq.length(), 0); + + if (n_sent == -1) + { + if (errno == EAGAIN) { - if (errno == EAGAIN) - { - /* The socket buffer is full. This isnt fatal, - * try again later. - */ - this->ServerInstance->SE->WantWrite(this); - } - else - { - /* Fatal error, set write error and bail - */ - this->SetWriteError(errno ? strerror(errno) : "EOF from client"); - return; - } + /* The socket buffer is full. This isnt fatal, + * try again later. + */ + ServerInstance->SE->WantWrite(this); } else { - /* advance the queue */ - if (n_sent) - this->sendq = this->sendq.substr(n_sent); - /* update the user's stats counters */ - this->bytes_out += n_sent; - this->cmds_out++; - if (n_sent != old_sendq_length) - this->ServerInstance->SE->WantWrite(this); + /* Fatal error, set write error and bail */ + ServerInstance->Users->QuitUser(this, errno ? strerror(errno) : "Write error"); + return; } } + else + { + /* advance the queue */ + if (n_sent) + this->sendq = this->sendq.substr(n_sent); + /* update the user's stats counters */ + this->bytes_out += n_sent; + this->cmds_out++; + if (n_sent != old_sendq_length) + this->ServerInstance->SE->WantWrite(this); + } } - catch (...) - { - ServerInstance->Logs->Log("USERS", DEBUG,"Exception in User::FlushWriteBuf()"); - } - + /* note: NOT else if! */ if (this->sendq.empty()) { FOREACH_MOD(I_OnBufferFlushed,OnBufferFlushed(this)); } } -void User::SetWriteError(const std::string &error) -{ - // don't try to set the error twice, its already set take the first string. - if (this->WriteError.empty()) - this->WriteError = error; -} - -const char* User::GetWriteError() -{ - return this->WriteError.c_str(); -} - void User::Oper(const std::string &opertype, const std::string &opername) { char* mycmd; char* savept; char* savept2; - try - { - this->modes[UM_OPERATOR] = 1; - this->WriteServ("MODE %s :+o", this->nick); - FOREACH_MOD(I_OnOper, OnOper(this, opertype)); - ServerInstance->Logs->Log("OPER", DEFAULT, "%s!%s@%s opered as type: %s", this->nick, this->ident, this->host, opertype.c_str()); - strlcpy(this->oper, opertype.c_str(), NICKMAX - 1); - ServerInstance->Users->all_opers.push_back(this); + if (this->IsModeSet('o')) + this->UnOper(); - opertype_t::iterator iter_opertype = ServerInstance->Config->opertypes.find(this->oper); - if (iter_opertype != ServerInstance->Config->opertypes.end()) - { + this->modes[UM_OPERATOR] = 1; + this->WriteServ("MODE %s :+o", this->nick.c_str()); + FOREACH_MOD(I_OnOper, OnOper(this, opertype)); - if (AllowedOperCommands) - AllowedOperCommands->clear(); - else - AllowedOperCommands = new std::map; + ServerInstance->SNO->WriteToSnoMask('o',"%s (%s@%s) is now an IRC operator of type %s (using oper '%s')", this->nick.c_str(), this->ident.c_str(), this->host.c_str(), irc::Spacify(opertype.c_str()), opername.c_str()); + this->WriteNumeric(381, "%s :You are now %s %s", this->nick.c_str(), strchr("aeiouAEIOU", *opertype.c_str()) ? "an" : "a", irc::Spacify(opertype.c_str())); - if (!AllowedChanModes) - AllowedChanModes = new bool[64]; + ServerInstance->Logs->Log("OPER", DEFAULT, "%s!%s@%s opered as type: %s", this->nick.c_str(), this->ident.c_str(), this->host.c_str(), opertype.c_str()); + this->oper.assign(opertype, 0, 512); + ServerInstance->Users->all_opers.push_back(this); + + opertype_t::iterator iter_opertype = ServerInstance->Config->opertypes.find(this->oper.c_str()); + if (iter_opertype != ServerInstance->Config->opertypes.end()) + { + + if (AllowedOperCommands) + AllowedOperCommands->clear(); + else + AllowedOperCommands = new std::map; - if (!AllowedUserModes) - AllowedUserModes = new bool[64]; + if (!AllowedChanModes) + AllowedChanModes = new bool[64]; - memset(AllowedUserModes, 0, 64); - memset(AllowedChanModes, 0, 64); + if (!AllowedUserModes) + AllowedUserModes = new bool[64]; - char* Classes = strdup(iter_opertype->second); - char* myclass = strtok_r(Classes," ",&savept); - while (myclass) + memset(AllowedUserModes, 0, 64); + memset(AllowedChanModes, 0, 64); + + char* Classes = strdup(iter_opertype->second); + char* myclass = strtok_r(Classes," ",&savept); + while (myclass) + { + operclass_t::iterator iter_operclass = ServerInstance->Config->operclass.find(myclass); + if (iter_operclass != ServerInstance->Config->operclass.end()) { - operclass_t::iterator iter_operclass = ServerInstance->Config->operclass.find(myclass); - if (iter_operclass != ServerInstance->Config->operclass.end()) + char* CommandList = strdup(iter_operclass->second.commandlist); + mycmd = strtok_r(CommandList," ",&savept2); + while (mycmd) + { + this->AllowedOperCommands->insert(std::make_pair(mycmd, true)); + mycmd = strtok_r(NULL," ",&savept2); + } + free(CommandList); + this->AllowedUserModes['o' - 'A'] = true; // Call me paranoid if you want. + for (unsigned char* c = (unsigned char*)iter_operclass->second.umodelist; *c; ++c) { - char* CommandList = strdup(iter_operclass->second.commandlist); - mycmd = strtok_r(CommandList," ",&savept2); - while (mycmd) + if (*c == '*') { - this->AllowedOperCommands->insert(std::make_pair(mycmd, true)); - mycmd = strtok_r(NULL," ",&savept2); + memset(this->AllowedUserModes, (int)(true), 64); } - free(CommandList); - this->AllowedUserModes['o' - 'A'] = true; // Call me paranoid if you want. - for (unsigned char* c = (unsigned char*)iter_operclass->second.umodelist; *c; ++c) + else { - if (*c == '*') - { - memset(this->AllowedUserModes, (int)(true), 64); - } - else - { - this->AllowedUserModes[*c - 'A'] = true; - } + this->AllowedUserModes[*c - 'A'] = true; } - for (unsigned char* c = (unsigned char*)iter_operclass->second.cmodelist; *c; ++c) + } + for (unsigned char* c = (unsigned char*)iter_operclass->second.cmodelist; *c; ++c) + { + if (*c == '*') { - if (*c == '*') - { - memset(this->AllowedChanModes, (int)(true), 64); - } - else - { - this->AllowedChanModes[*c - 'A'] = true; - } + memset(this->AllowedChanModes, (int)(true), 64); + } + else + { + this->AllowedChanModes[*c - 'A'] = true; } } - myclass = strtok_r(NULL," ",&savept); } - free(Classes); + myclass = strtok_r(NULL," ",&savept); } - - FOREACH_MOD(I_OnPostOper,OnPostOper(this, opertype, opername)); + free(Classes); } - catch (...) - { - ServerInstance->Logs->Log("OPER", DEBUG,"Exception in User::Oper()"); - } + FOREACH_MOD(I_OnPostOper,OnPostOper(this, opertype, opername)); } void User::UnOper() { if (IS_OPER(this)) { + /* + * unset their oper type (what IS_OPER checks). + * note, order is important - this must come before modes as -o attempts + * to call UnOper. -- w00t + */ + this->oper.clear(); + /* Remove all oper only modes from the user when the deoper - Bug #466*/ std::string moderemove("-"); for (unsigned char letter = 'A'; letter <= 'z'; letter++) { - if (letter != 'o') - { - ModeHandler* mh = ServerInstance->Modes->FindMode(letter, MODETYPE_USER); - if (mh && mh->NeedsOper()) - moderemove += letter; - } + ModeHandler* mh = ServerInstance->Modes->FindMode(letter, MODETYPE_USER); + if (mh && mh->NeedsOper()) + moderemove += letter; } std::vector parameters; @@ -794,10 +817,6 @@ void User::UnOper() parameters.push_back(moderemove); ServerInstance->Parser->CallHandler("MODE", parameters, this); - - /* unset their oper type (what IS_OPER checks), and remove +o */ - *this->oper = 0; - this->modes[UM_OPERATOR] = 0; /* remove the user from the oper list. Will remove multiple entries as a safeguard against bug #404 */ ServerInstance->Users->all_opers.remove(this); @@ -859,11 +878,9 @@ void User::CheckClass() } this->nping = ServerInstance->Time() + a->GetPingTime() + ServerInstance->Config->dns_timeout; - this->timeout = ServerInstance->Time() + a->GetRegTimeout(); - this->MaxChans = a->GetMaxChans(); } -void User::CheckLines() +bool User::CheckLines() { const char* check[] = { "G" , "K", NULL }; @@ -876,10 +893,12 @@ void User::CheckLines() if (r) { r->Apply(this); - return; + return true; } } } + + return false; } void User::FullConnect() @@ -904,16 +923,17 @@ void User::FullConnect() return; } - CheckLines(); + if (this->CheckLines()) + return; 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 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 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()); + this->WriteNumeric(RPL_WELCOME, "%s :Welcome to the %s IRC Network %s!%s@%s",this->nick.c_str(), ServerInstance->Config->Network, this->nick.c_str(), this->ident.c_str(), this->host.c_str()); + this->WriteNumeric(RPL_YOURHOSTIS, "%s :Your host is %s, running version InspIRCd-1.2",this->nick.c_str(),ServerInstance->Config->ServerName); + this->WriteNumeric(RPL_SERVERCREATED, "%s :This server was created %s %s", this->nick.c_str(), __TIME__, __DATE__); + this->WriteNumeric(RPL_SERVERVERSION, "%s %s InspIRCd-1.2 %s %s %s", this->nick.c_str(), 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); + this->WriteNumeric(RPL_YOURUUID, "%s %s :your unique ID", this->nick.c_str(), this->uuid.c_str()); this->ShowMOTD(); @@ -924,9 +944,11 @@ void User::FullConnect() /* Trigger LUSERS output, give modules a chance too */ int MOD_RESULT = 0; - FOREACH_RESULT(I_OnPreCommand, OnPreCommand("LUSERS", std::vector(), this, true, "LUSERS")); + std::string command("LUSERS"); + std::vector parameters; + FOREACH_RESULT(I_OnPreCommand, OnPreCommand(command, parameters, this, true, "LUSERS")); if (!MOD_RESULT) - ServerInstance->CallCommandHandler("LUSERS", std::vector(), this); + ServerInstance->CallCommandHandler(command, parameters, this); /* * We don't set REG_ALL until triggering OnUserConnect, so some module events don't spew out stuff @@ -936,9 +958,11 @@ void User::FullConnect() this->registered = REG_ALL; + ServerInstance->PI->Introduce(this); + FOREACH_MOD(I_OnPostConnect,OnPostConnect(this)); - ServerInstance->SNO->WriteToSnoMask('c',"Client connecting on port %d: %s!%s@%s [%s] [%s]", this->GetPort(), this->nick, this->ident, this->host, this->GetIPString(), this->fullname); + ServerInstance->SNO->WriteToSnoMask('c',"Client connecting on port %d: %s!%s@%s [%s] [%s]", this->GetPort(), this->nick.c_str(), this->ident.c_str(), this->host.c_str(), this->GetIPString(), this->fullname.c_str()); ServerInstance->Logs->Log("BANCACHE", DEBUG, "BanCache: Adding NEGATIVE hit for %s", this->GetIPString()); ServerInstance->BanCache->AddHit(this->GetIPString(), "", ""); } @@ -952,7 +976,7 @@ User* User::UpdateNickHash(const char* New) //user_hash::iterator newnick; user_hash::iterator oldnick = ServerInstance->Users->clientlist->find(this->nick); - if (!strcasecmp(this->nick,New)) + if (!irc::string(this->nick.c_str()).compare(New)) return oldnick->second; if (oldnick == ServerInstance->Users->clientlist->end()) @@ -967,15 +991,10 @@ User* User::UpdateNickHash(const char* New) void User::InvalidateCache() { /* Invalidate cache */ - if (cached_fullhost) - free(cached_fullhost); - if (cached_hostip) - free(cached_hostip); - if (cached_makehost) - free(cached_makehost); - if (cached_fullrealhost) - free(cached_fullrealhost); - cached_fullhost = cached_hostip = cached_makehost = cached_fullrealhost = NULL; + cached_fullhost.clear(); + cached_hostip.clear(); + cached_makehost.clear(); + cached_fullrealhost.clear(); } bool User::ForceNickChange(const char* newnick) @@ -1035,7 +1054,7 @@ void User::SetSockAddr(int protocol_family, const char* sip, int port) } break; default: - ServerInstance->Logs->Log("USERS",DEBUG,"Uh oh, I dont know protocol %d to be set on '%s'!", protocol_family, this->nick); + ServerInstance->Logs->Log("USERS",DEBUG,"Uh oh, I dont know protocol %d to be set on '%s'!", protocol_family, this->nick.c_str()); break; } } @@ -1076,13 +1095,116 @@ int User::GetProtocolFamily() return sin->sin_family; } -/* - * XXX the duplication here is horrid.. - * do we really need two methods doing essentially the same thing? - */ +const char* User::GetCIDRMask(int range) +{ + static char buf[44]; + + if (this->ip == NULL) + return ""; + + if (range < 0) + throw "Negative range, sorry, no."; + + /* + * Original code written by Oliver Lupton (Om). + * Integrated by me. Thanks. :) -- w00t + */ + switch (this->GetProtocolFamily()) + { +#ifdef SUPPORT_IP6LINKS + case AF_INET6: + { + /* unsigned char s6_addr[16]; */ + struct in6_addr v6; + sockaddr_in6* sin; + int i, bytestozero, extrabits; + char buffer[40]; + + if(range > 128) + throw "CIDR mask width greater than address width (IPv6, 128 bit)"; + + /* Access the user's IP structure directly */ + sin = (sockaddr_in6*)this->ip; + + /* To create the CIDR mask we want to set all the bits after 'range' bits of the address + * to zero. This means the last (128 - range) bits of the address must be set to zero. + * Hence this number divided by 8 is the number of whole bytes from the end of the address + * which must be set to zero. + */ + bytestozero = (128 - range) / 8; + + /* Some of the least significant bits of the next most significant byte may also have to + * be zeroed. The number of bits is the remainder of the above division. + */ + extrabits = (128 - range) % 8; + + /* Populate our working struct with the parts of the user's IP which are required in the + * final CIDR mask. Set all the subsequent bytes to zero. + * (16 - bytestozero) is the number of bytes which must be populated with actual IP data. + */ + for(i = 0; i < (16 - bytestozero); i++) + { + v6.s6_addr[i] = sin->sin6_addr.s6_addr[i]; + } + + /* And zero all the remaining bytes in the IP. */ + for(; i < 16; i++) + { + v6.s6_addr[i] = 0; + } + + /* And finally, zero the extra bits required. */ + v6.s6_addr[15 - bytestozero] = (v6.s6_addr[15 - bytestozero] >> extrabits) << extrabits; + + snprintf(buf, 44, "%s/%d", inet_ntop(AF_INET6, &v6, buffer, 40), range); + return buf; + } + break; +#endif + case AF_INET: + { + struct in_addr v4; + sockaddr_in* sin; + char buffer[16]; + + if (range > 32) + throw "CIDR mask width greater than address width (IPv4, 32 bit)"; + + /* Users already have a sockaddr* pointer (User::ip) which contains either a v4 or v6 structure */ + sin = (sockaddr_in*)this->ip; + v4.s_addr = sin->sin_addr.s_addr; + + /* To create the CIDR mask we want to set all the bits after 'range' bits of the address + * to zero. This means the last (32 - range) bits of the address must be set to zero. + * This is done by shifting the value right and then back left by (32 - range) bits. + */ + if(range > 0) + { + v4.s_addr = ntohl(v4.s_addr); + v4.s_addr = (v4.s_addr >> (32 - range)) << (32 - range); + v4.s_addr = htonl(v4.s_addr); + } + else + { + /* a range of zero would cause a 32 bit value to be shifted by 32 bits. + * this has undefined behaviour, but for CIDR purposes the resulting mask + * from a.b.c.d/0 is 0.0.0.0/0 + */ + v4.s_addr = 0; + } + + snprintf(buf, 44, "%s/%d", inet_ntop(AF_INET, &v4, buffer, 16), range); + return buf; + } + break; + } + + return ""; // unused, but oh well +} + const char* User::GetIPString(bool translate4in6) { - static char buf[1024]; + static char buf[40]; if (this->ip == NULL) return ""; @@ -1157,14 +1279,14 @@ void User::Write(std::string text) return; } - if (this->io) + if (this->GetIOHook()) { /* XXX: The lack of buffering here is NOT a bug, modules implementing this interface have to * implement their own buffering mechanisms */ try { - this->io->OnRawSocketWrite(this->fd, text.data(), text.length()); + this->GetIOHook()->OnRawSocketWrite(this->fd, text.data(), text.length()); } catch (CoreException& modexcept) { @@ -1247,7 +1369,7 @@ void User::WriteFrom(User *user, const std::string &text) { char tb[MAXBUF]; - snprintf(tb,MAXBUF,":%s %s",user->GetFullHost(),text.c_str()); + snprintf(tb,MAXBUF,":%s %s",user->GetFullHost().c_str(),text.c_str()); this->Write(std::string(tb)); } @@ -1317,7 +1439,7 @@ void User::WriteCommon(const std::string &text) InitializeAlreadySent(ServerInstance->SE); /* We dont want to be doing this n times, just once */ - snprintf(tb,MAXBUF,":%s %s",this->GetFullHost(),text.c_str()); + snprintf(tb,MAXBUF,":%s %s",this->GetFullHost().c_str(),text.c_str()); std::string out = tb; for (UCListIter v = this->chans.begin(); v != this->chans.end(); v++) @@ -1374,8 +1496,8 @@ void User::WriteCommonQuit(const std::string &normal_text, const std::string &op 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()); + snprintf(tb1,MAXBUF,":%s QUIT :%s",this->GetFullHost().c_str(),normal_text.c_str()); + snprintf(tb2,MAXBUF,":%s QUIT :%s",this->GetFullHost().c_str(),oper_text.c_str()); std::string out1 = tb1; std::string out2 = tb2; @@ -1409,7 +1531,7 @@ void User::WriteCommonExcept(const std::string &text) if (!already_sent) InitializeAlreadySent(ServerInstance->SE); - snprintf(tb1,MAXBUF,":%s %s",this->GetFullHost(),text.c_str()); + snprintf(tb1,MAXBUF,":%s %s",this->GetFullHost().c_str(),text.c_str()); out1 = tb1; for (UCListIter v = this->chans.begin(); v != this->chans.end(); v++) @@ -1492,7 +1614,7 @@ bool User::SharesChannelWith(User *other) bool User::ChangeName(const char* gecos) { - if (!strcmp(gecos, this->fullname)) + if (!this->fullname.compare(gecos)) return true; if (IS_LOCAL(this)) @@ -1503,14 +1625,14 @@ bool User::ChangeName(const char* gecos) return false; FOREACH_MOD(I_OnChangeName,OnChangeName(this,gecos)); } - strlcpy(this->fullname,gecos,MAXGECOS+1); + this->fullname.assign(gecos, 0, ServerInstance->Config->Limits.MaxGecos); return true; } bool User::ChangeDisplayedHost(const char* shost) { - if (!strcmp(shost, this->dhost)) + if (dhost == shost) return true; if (IS_LOCAL(this)) @@ -1526,7 +1648,7 @@ bool User::ChangeDisplayedHost(const char* shost) this->WriteCommonExcept("QUIT :Changing hosts"); /* Fix by Om: User::dhost is 65 long, this was truncating some long hosts */ - strlcpy(this->dhost,shost,64); + this->dhost.assign(shost, 0, 64); this->InvalidateCache(); @@ -1534,28 +1656,28 @@ bool User::ChangeDisplayedHost(const char* shost) { for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++) { - i->first->WriteAllExceptSender(this, false, 0, "JOIN %s", i->first->name); + i->first->WriteAllExceptSender(this, false, 0, "JOIN %s", i->first->name.c_str()); std::string n = this->ServerInstance->Modes->ModeString(this, i->first); if (n.length() > 0) - i->first->WriteAllExceptSender(this, true, 0, "MODE %s +%s", i->first->name, n.c_str()); + i->first->WriteAllExceptSender(this, true, 0, "MODE %s +%s", i->first->name.c_str(), n.c_str()); } } if (IS_LOCAL(this)) - this->WriteNumeric(396, "%s %s :is now your displayed host",this->nick,this->dhost); + this->WriteNumeric(RPL_YOURDISPLAYEDHOST, "%s %s :is now your displayed host",this->nick.c_str(),this->dhost.c_str()); return true; } bool User::ChangeIdent(const char* newident) { - if (!strcmp(newident, this->ident)) + if (!this->ident.compare(newident)) return true; if (this->ServerInstance->Config->CycleHosts) this->WriteCommonExcept("%s","QUIT :Changing ident"); - strlcpy(this->ident, newident, IDENTMAX+1); + this->ident.assign(newident, 0, ServerInstance->Config->Limits.IdentMax + 1); this->InvalidateCache(); @@ -1563,10 +1685,10 @@ bool User::ChangeIdent(const char* newident) { for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++) { - i->first->WriteAllExceptSender(this, false, 0, "JOIN %s", i->first->name); + i->first->WriteAllExceptSender(this, false, 0, "JOIN %s", i->first->name.c_str()); std::string n = this->ServerInstance->Modes->ModeString(this, i->first); if (n.length() > 0) - i->first->WriteAllExceptSender(this, true, 0, "MODE %s +%s", i->first->name, n.c_str()); + i->first->WriteAllExceptSender(this, true, 0, "MODE %s +%s", i->first->name.c_str(), n.c_str()); } } @@ -1583,7 +1705,7 @@ void User::SendAll(const char* command, const char* text, ...) vsnprintf(textbuffer, MAXBUF, text, argsPtr); va_end(argsPtr); - snprintf(formatbuffer,MAXBUF,":%s %s $* :%s", this->GetFullHost(), command, textbuffer); + snprintf(formatbuffer,MAXBUF,":%s %s $* :%s", this->GetFullHost().c_str(), command, textbuffer); std::string fmt = formatbuffer; for (std::vector::const_iterator i = ServerInstance->Users->local_users.begin(); i != ServerInstance->Users->local_users.end(); i++) @@ -1649,12 +1771,6 @@ void User::SplitChanList(User* dest, const std::string &cl) } } -unsigned int User::GetMaxChans() -{ - return this->MaxChans; -} - - /* * Sets a user's connection class. * If the class name is provided, it will be used. Otherwise, the class will be guessed using host/ip/ident/etc. @@ -1669,14 +1785,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.c_str()); + 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; } } @@ -1687,37 +1809,65 @@ 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 (!InspIRCd::MatchCIDR(this->GetIPString(), c->GetHost(), NULL) && + !InspIRCd::MatchCIDR(this->host, c->GetHost(), NULL)) + { + 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 @@ -1786,31 +1936,31 @@ void User::ShowMOTD() { if (!ServerInstance->Config->MOTD.size()) { - this->WriteNumeric(422, "%s :Message of the day file is missing.",this->nick); + this->WriteNumeric(ERR_NOMOTD, "%s :Message of the day file is missing.",this->nick.c_str()); return; } - this->WriteNumeric(375, "%s :%s message of the day", this->nick, ServerInstance->Config->ServerName); + this->WriteNumeric(RPL_MOTDSTART, "%s :%s message of the day", this->nick.c_str(), ServerInstance->Config->ServerName); for (file_cache::iterator i = ServerInstance->Config->MOTD.begin(); i != ServerInstance->Config->MOTD.end(); i++) - this->WriteNumeric(372, "%s :- %s",this->nick,i->c_str()); + this->WriteNumeric(RPL_MOTD, "%s :- %s",this->nick.c_str(),i->c_str()); - this->WriteNumeric(376, "%s :End of message of the day.", this->nick); + this->WriteNumeric(RPL_ENDOFMOTD, "%s :End of message of the day.", this->nick.c_str()); } void User::ShowRULES() { if (!ServerInstance->Config->RULES.size()) { - this->WriteNumeric(434, "%s :RULES File is missing",this->nick); + this->WriteNumeric(ERR_NORULES, "%s :RULES File is missing",this->nick.c_str()); return; } - this->WriteNumeric(308, "%s :- %s Server Rules -",this->nick,ServerInstance->Config->ServerName); + this->WriteNumeric(RPL_RULESTART, "%s :- %s Server Rules -",this->nick.c_str(),ServerInstance->Config->ServerName); for (file_cache::iterator i = ServerInstance->Config->RULES.begin(); i != ServerInstance->Config->RULES.end(); i++) - this->WriteNumeric(232, "%s :- %s",this->nick,i->c_str()); + this->WriteNumeric(RPL_RULES, "%s :- %s",this->nick.c_str(),i->c_str()); - this->WriteNumeric(309, "%s :End of RULES command.",this->nick); + this->WriteNumeric(RPL_RULESEND, "%s :End of RULES command.",this->nick.c_str()); } void User::HandleEvent(EventType et, int errornum) @@ -1818,37 +1968,17 @@ void User::HandleEvent(EventType et, int errornum) if (this->quitting) // drop everything, user is due to be quit return; - /* WARNING: May delete this user! */ - int thisfd = this->GetFd(); - - try + switch (et) { - switch (et) - { - case EVENT_READ: - ServerInstance->ProcessUser(this); - break; - case EVENT_WRITE: - this->FlushWriteBuf(); - break; - case EVENT_ERROR: - /** This should be safe, but dont DARE do anything after it -- Brain */ - this->SetWriteError(errornum ? strerror(errornum) : "EOF from client"); - break; - } - } - catch (...) - { - ServerInstance->Logs->Log("USERS", DEBUG,"Exception in User::HandleEvent intercepted"); - } - - /* If the user has raised an error whilst being processed, quit them now we're safe to */ - if ((ServerInstance->SE->GetRef(thisfd) == this)) - { - if (!WriteError.empty()) - { - ServerInstance->Users->QuitUser(this, GetWriteError()); - } + case EVENT_READ: + ServerInstance->ProcessUser(this); + break; + case EVENT_WRITE: + this->FlushWriteBuf(); + break; + case EVENT_ERROR: + ServerInstance->Users->QuitUser(this, errornum ? strerror(errornum) : "Client closed the connection"); + break; } } @@ -1857,9 +1987,9 @@ void User::SetOperQuit(const std::string &oquit) operquitmsg = oquit; } -const char* User::GetOperQuit() +const std::string& User::GetOperQuit() { - return operquitmsg.c_str(); + return operquitmsg; } void User::IncreasePenalty(int increase)