X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fmodules%2Fm_spanningtree.cpp;h=c4d335ef23e22c2525b60e10801ca593405a0964;hb=c0449726a44c9772fe60d7994500c679c743c684;hp=f14cc69deb4b913923989cc6aa31555b8d35c2f7;hpb=deb290af0f0200d530ba10e4c6c3f6a45b4971c5;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/src/modules/m_spanningtree.cpp b/src/modules/m_spanningtree.cpp index f14cc69de..c4d335ef2 100644 --- a/src/modules/m_spanningtree.cpp +++ b/src/modules/m_spanningtree.cpp @@ -111,6 +111,9 @@ static Server* Srv; typedef nspace::hash_map, irc::StrHashComp> server_hash; server_hash serverlist; +typedef nspace::hash_map uid_hash; +typedef nspace::hash_map sid_hash; + /* More forward declarations */ bool DoOneToOne(std::string prefix, std::string command, std::deque ¶ms, std::string target); bool DoOneToAllButSender(std::string prefix, std::string command, std::deque ¶ms, std::string omit); @@ -135,6 +138,53 @@ extern std::vector pzlines; extern std::vector pqlines; extern std::vector pelines; +class UserManager : public classbase +{ + uid_hash uids; + sid_hash sids; + public: + UserManager() + { + uids.clear(); + sids.clear(); + } + + std::string UserToUID(userrec* user) + { + return ""; + } + + std::string UIDToUser(const std::string &UID) + { + return ""; + } + + std::string CreateAndAdd(userrec* user) + { + return ""; + } + + std::string CreateAndAdd(const std::string &servername) + { + return ""; + } + + std::string ServerToSID(const std::string &servername) + { + return ""; + } + + std::string SIDToServer(const std::string &SID) + { + return ""; + } + + userrec* FindByID(const std::string &UID) + { + return NULL; + } +}; + /* Each server in the tree is represented by one class of * type TreeServer. A locally connected TreeServer can * have a class of type TreeSocket associated with it, for @@ -555,6 +605,7 @@ class cmd_rconnect : public command_t cmd_rconnect (Module* Callback) : command_t("RCONNECT", 'o', 2), Creator(Callback) { this->source = "m_spanningtree.so"; + syntax = " "; } void Handle (const char** parameters, int pcnt, userrec *user) @@ -836,14 +887,6 @@ class TreeSocket : public InspSocket /* Now we've whacked the kids, whack self */ num_lost_servers++; num_lost_users += Current->QuitUsers(from); - /*for (user_hash::iterator u = clientlist.begin(); u != clientlist.end(); u++) - { - if (!strcasecmp(u->second->server,Current->GetName().c_str())) - { - Goners->AddItem(u->second,from); - num_lost_users++; - } - }*/ } /* This is a wrapper function for SquitServer above, which @@ -881,83 +924,378 @@ class TreeSocket : public InspSocket } } - /* FMODE command */ + /* FMODE command - server mode with timestamp checks */ bool ForceMode(std::string source, std::deque ¶ms) { + /* Chances are this is a 1.0 FMODE without TS */ if (params.size() < 3) { this->WriteLine("ERROR :Version 1.0 FMODE sent to version 1.1 server"); return false; } - userrec* who = new userrec(); - who->fd = FD_MAGIC_NUMBER; + + bool smode = false; + std::string sourceserv; + + /* Are we dealing with an FMODE from a user, or from a server? */ + userrec* who = Srv->FindNick(source); + if (who) + { + /* FMODE from a user, set sourceserv to the users server name */ + sourceserv = who->server; + } + else + { + /* FMODE from a server, create a fake user to receive mode feedback */ + who = new userrec(); + who->fd = FD_MAGIC_NUMBER; + smode = true; /* Setting this flag tells us we should free the userrec later */ + sourceserv = source; /* Set sourceserv to the actual source string */ + } const char* modelist[64]; time_t TS = 0; int n = 0; memset(&modelist,0,sizeof(modelist)); - for (unsigned int q = 0; q < params.size(); q++, n++) + for (unsigned int q = 0; (q < params.size()) && (q < 64); q++) { if (q == 1) { - /* The timestamp */ + /* The timestamp is in this position. + * We don't want to pass that up to the + * server->client protocol! + */ TS = atoi(params[q].c_str()); } - modelist[n] = params[q].c_str(); + else + { + /* Everything else is fine to append to the modelist */ + modelist[n++] = params[q].c_str(); + } + } - // Insert the TS value of the object, either userrec or chanrec - userrec* a = Srv->FindNick(params[0]); + /* Extract the TS value of the object, either userrec or chanrec */ + userrec* dst = Srv->FindNick(params[0]); + chanrec* chan = NULL; time_t ourTS = 0; - if (a) + if (dst) { - ourTS = a->age; + ourTS = dst->age; } - //classbase* a = reinterpret_cast(Srv->FindNick(params[0])); else { - chanrec* a = Srv->FindChannel(params[0]); - if (a) + chan = Srv->FindChannel(params[0]); + if (chan) { - ourTS = a->age; + ourTS = chan->age; } } + + /* TS is equal: Merge the mode changes, use voooodoooooo on modes + * with parameters. + */ + if (TS == ourTS) + { + log(DEBUG,"Entering TS equality check"); + ModeHandler* mh = NULL; + unsigned long paramptr = 3; + std::string to_bounce = ""; + std::string to_keep = ""; + std::vector params_to_keep; + std::string params_to_bounce = ""; + bool adding = true; + char cur_change = 1; + char old_change = 0; + char old_bounce_change = 0; + /* Merge modes, basically do special stuff to mode with params */ + for (std::string::iterator x = params[2].begin(); x != params[2].end(); x++) + { + switch (*x) + { + case '-': + adding = false; + break; + case '+': + adding = true; + break; + default: + if (adding) + { + /* We only care about whats being set, + * not whats being unset + */ + mh = ServerInstance->ModeGrok->FindMode(*x, chan ? MODETYPE_CHANNEL : MODETYPE_USER); + + if ((mh->GetNumParams(adding) > 0) && (!mh->IsListMode())) + { + /* We only want to do special things to + * modes with parameters, we are going to rewrite + * those parameters + */ + ModePair ret; + adding ? cur_change = '+' : cur_change = '-'; + + ret = mh->ModeSet(smode ? NULL : who, dst, chan, params[paramptr]); + + /* The mode is set here, check which we should keep */ + if (ret.first) + { + bool which_to_keep = mh->CheckTimeStamp(TS, ourTS, params[paramptr], ret.second, chan); + + if (which_to_keep == true) + { + /* Keep ours, bounce theirs: + * Send back ours to them and + * drop their mode changs + */ + adding ? cur_change = '+' : cur_change = '-'; + if (cur_change != old_bounce_change) + to_bounce += cur_change; + to_bounce += *x; + old_bounce_change = cur_change; + + if ((mh->GetNumParams(adding) > 0) && (paramptr < params.size())) + params_to_bounce.append(" ").append(ret.second); + } + else + { + /* Keep theirs: Accept their mode change, + * do nothing else + */ + adding ? cur_change = '+' : cur_change = '-'; + if (cur_change != old_change) + to_keep += cur_change; + to_keep += *x; + old_change = cur_change; + + if ((mh->GetNumParams(adding) > 0) && (paramptr < params.size())) + params_to_keep.push_back(params[paramptr]); + } + } + else + { + /* Mode isnt set here, we want it */ + adding ? cur_change = '+' : cur_change = '-'; + if (cur_change != old_change) + to_keep += cur_change; + to_keep += *x; + old_change = cur_change; + + if ((mh->GetNumParams(adding) > 0) && (paramptr < params.size())) + params_to_keep.push_back(params[paramptr]); + } + + paramptr++; + } + else + { + mh = ServerInstance->ModeGrok->FindMode(*x, chan ? MODETYPE_CHANNEL : MODETYPE_USER); + + if (mh) + { + adding ? cur_change = '+' : cur_change = '-'; + + /* Just keep this, safe to merge with no checks + * it has no parameters + */ + + if (cur_change != old_change) + to_keep += cur_change; + to_keep += *x; + old_change = cur_change; + + if ((mh->GetNumParams(adding) > 0) && (paramptr < params.size())) + { + log(DEBUG,"Mode removal %d %d",adding, mh->GetNumParams(adding)); + params_to_keep.push_back(params[paramptr++]); + } + } + } + } + else + { + mh = ServerInstance->ModeGrok->FindMode(*x, chan ? MODETYPE_CHANNEL : MODETYPE_USER); + + if (mh) + { + /* Taking a mode away */ + adding ? cur_change = '+' : cur_change = '-'; + + if (cur_change != old_change) + to_keep += cur_change; + to_keep += *x; + old_change = cur_change; + + if ((mh->GetNumParams(adding) > 0) && (paramptr < params.size())) + params_to_keep.push_back(params[paramptr++]); + } + } + break; + } + } + + if (to_bounce.length()) + { + std::deque newparams; + newparams.push_back(params[0]); + newparams.push_back(ConvToStr(ourTS)); + newparams.push_back(to_bounce+params_to_bounce); + DoOneToOne(Srv->GetServerName(),"FMODE",newparams,sourceserv); + } + + if (to_keep.length()) + { + n = 0; + modelist[0] = params[0].c_str(); + modelist[1] = to_keep.c_str(); + + for (unsigned int q = 2; (q < params.size()) && (q < 64); q++) + modelist[q] = params_to_keep[n++].c_str(); + + if (smode) + { + Srv->SendMode(modelist, n+2, who); + } + else + { + Srv->CallCommandHandler("MODE", modelist, n+2, who); + } + + /* HOT POTATO! PASS IT ON! */ + DoOneToAllButSender(source,"FMODE",params,sourceserv); + } + } + else /* U-lined servers always win regardless of their TS */ if ((TS > ourTS) && (!Srv->IsUlined(source))) { - /* Bounce the mode back to its sender. - * We use our higher TS, so the other end + /* Bounce the mode back to its sender.* We use our lower TS, so the other end * SHOULD accept it, if its clock is right. - * If its clock is wrong well bully for you :p + * + * NOTE: We should check that we arent bouncing anything thats already set at this end. + * If we are, bounce +ourmode to 'reinforce' it. This prevents desyncs. + * e.g. They send +l 50, we have +l 10 set. rather than bounce -l 50, we bounce +l 10. + * + * Thanks to jilles for pointing out this one-hell-of-an-issue before i even finished + * writing the code. It took me a while to come up with this solution. + * + * XXX: BE SURE YOU UNDERSTAND THIS CODE FULLY BEFORE YOU MESS WITH IT. */ - params[1] = ConvToStr(ourTS); - /* Invert the mode string by changing + to -, - * and - to + in the first param. + + std::deque newparams; /* New parameter list we send back */ + newparams.push_back(params[0]); /* Target, user or channel */ + newparams.push_back(ConvToStr(ourTS)); /* Timestamp value of the target */ + newparams.push_back(""); /* This contains the mode string. For now + * it's empty, we fill it below. + */ + + /* Intelligent mode bouncing. Don't just invert, reinforce any modes which are already + * set to avoid a desync here. */ + std::string modebounce = ""; + bool adding = true; + unsigned int t = 3; + ModeHandler* mh = NULL; + char cur_change = 1; + char old_change = 0; for (std::string::iterator x = params[2].begin(); x != params[2].end(); x++) { + /* Iterate over all mode chars in the sent set */ switch (*x) { + /* Adding or subtracting modes? */ case '-': - *x = '+'; + adding = false; break; case '+': - *x = '-'; + adding = true; + break; + default: + /* Find the mode handler for this mode */ + mh = ServerInstance->ModeGrok->FindMode(*x, chan ? MODETYPE_CHANNEL : MODETYPE_USER); + + /* Got a mode handler? + * This also prevents us bouncing modes we have no handler for. + */ + if (mh) + { + ModePair ret; + std::string p = ""; + + /* Does the mode require a parameter right now? + * If it does, fetch it if we can + */ + if ((mh->GetNumParams(adding) > 0) && (t < params.size())) + p = params[t++]; + + /* Call the ModeSet method to determine if its set with the + * given parameter here or not. + */ + ret = mh->ModeSet(smode ? NULL : who, dst, chan, p); + + /* XXX: Really. Dont ask. + * Determine from if its set combined with what the current + * 'state' is (adding or not) as to wether we should 'invert' + * or 'reinforce' the mode change + */ + (!ret.first ? (adding ? cur_change = '-' : cur_change = '+') : (!adding ? cur_change = '-' : cur_change = '+')); + + /* Quickly determine if we have 'flipped' from + to -, + * or - to +, to prevent unneccessary +/- chars in the + * output string that waste bandwidth + */ + if (cur_change != old_change) + modebounce += cur_change; + old_change = cur_change; + + /* Add the mode character to the output string */ + modebounce += mh->GetModeChar(); + + /* We got a parameter back from ModeHandler::ModeSet, + * are we supposed to be sending one out right now? + */ + if (ret.second.length()) + { + if (mh->GetNumParams(cur_change == '+') > 0) + /* Yes we're supposed to be sending out + * the parameter. Make sure it goes + */ + newparams.push_back(ret.second); + } + + } break; } } - DoOneToOne(source,"FMODE",params,source); - log(DEBUG,"Mode bounced, our TS less than theirs"); + + /* Update the parameters for FMODE with the new 'bounced' string */ + newparams[2] = modebounce; + /* Only send it back the way it came, no need to send it anywhere else */ + DoOneToOne(Srv->GetServerName(),"FMODE",newparams,sourceserv); + log(DEBUG,"FMODE bounced intelligently, our TS less than theirs and the other server is NOT a uline."); } else { - if ((Srv->IsUlined(source)) && (TS > ourTS)) + log(DEBUG,"Allow modes, TS lower for sender"); + /* The server was ulined, but something iffy is up with the TS. + * Sound the alarm bells! + */ + if ((Srv->IsUlined(sourceserv)) && (TS > ourTS)) { - WriteOpers("\2WARNING!\2 U-Lined server '%s' has bad TS for '%s' (accepted change): \2SYNC YOUR CLOCKS\2 to avoid this notice",source.c_str(),params[0].c_str()); + WriteOpers("\2WARNING!\2 U-Lined server '%s' has bad TS for '%s' (accepted change): \2SYNC YOUR CLOCKS\2 to avoid this notice",sourceserv.c_str(),params[0].c_str()); } - /* Allow the mode */ - Srv->SendMode(modelist,params.size(),who); - DoOneToAllButSender(source,"FMODE",params,source); + /* Allow the mode, route it to either server or user command handling */ + if (smode) + Srv->SendMode(modelist,n,who); + else + Srv->CallCommandHandler("MODE", modelist, n, who); + + /* HOT POTATO! PASS IT ON! */ + DoOneToAllButSender(source,"FMODE",params,sourceserv); } - DELETE(who); + /* Are we supposed to free the userrec? */ + if (smode) + DELETE(who); + return true; } @@ -1214,7 +1552,7 @@ class TreeSocket : public InspSocket clientlist[tempnick]->server = FindServerNamePtr(source.c_str()); strlcpy(clientlist[tempnick]->ident, params[4].c_str(),IDENTMAX); strlcpy(clientlist[tempnick]->fullname, params[7].c_str(),MAXGECOS); - clientlist[tempnick]->registered = 7; + clientlist[tempnick]->registered = REG_ALL; clientlist[tempnick]->signon = age; for (std::string::iterator v = params[5].begin(); v != params[5].end(); v++) @@ -1426,7 +1764,7 @@ class TreeSocket : public InspSocket int iterations = 0; for (user_hash::iterator u = clientlist.begin(); u != clientlist.end(); u++, iterations++) { - if (u->second->registered == 7) + if (u->second->registered == REG_ALL) { snprintf(data,MAXBUF,":%s NICK %lu %s %s %s %s +%s %s :%s",u->second->server,(unsigned long)u->second->age,u->second->nick,u->second->host,u->second->dhost,u->second->ident,u->second->FormatModes(),inet_ntoa(u->second->ip4),u->second->fullname); this->WriteLine(data); @@ -1884,7 +2222,7 @@ class TreeSocket : public InspSocket if (params.size() == 1) { userrec* x = Srv->FindNick(params[0]); - if ((x) && (x->fd > -1)) + if ((x) && (IS_LOCAL(x))) { userrec* x = Srv->FindNick(params[0]); log(DEBUG,"Got IDLE"); @@ -1910,14 +2248,14 @@ class TreeSocket : public InspSocket { std::string who_did_the_whois = params[0]; userrec* who_to_send_to = Srv->FindNick(who_did_the_whois); - if ((who_to_send_to) && (who_to_send_to->fd > -1)) + if ((who_to_send_to) && (IS_LOCAL(who_to_send_to))) { log(DEBUG,"Got final IDLE"); // an incoming reply to a whois we sent out std::string nick_whoised = prefix; unsigned long signon = atoi(params[1].c_str()); unsigned long idle = atoi(params[2].c_str()); - if ((who_to_send_to) && (who_to_send_to->fd > -1)) + if ((who_to_send_to) && (IS_LOCAL(who_to_send_to))) do_whois(who_to_send_to,u,signon,idle,nick_whoised.c_str()); } else @@ -2671,7 +3009,7 @@ class TreeSocket : public InspSocket /* XXX: Fixme: blocks for a very short amount of time, * we should cache these on rehash/startup */ - if (CleanAndResolve(resolved_host,i->IPAddr.c_str(),true)) + if (CleanAndResolve(resolved_host,i->IPAddr.c_str(),true,1)) { if (std::string(resolved_host) == ip) { @@ -3189,7 +3527,7 @@ class ModuleSpanningTree : public Module int HandleTime(const char** parameters, int pcnt, userrec* user) { - if ((user->fd > -1) && (pcnt)) + if ((IS_LOCAL(user)) && (pcnt)) { TreeServer* found = FindServerMask(parameters[0]); if (found) @@ -3213,7 +3551,7 @@ class ModuleSpanningTree : public Module int HandleRemoteWhois(const char** parameters, int pcnt, userrec* user) { - if ((user->fd > -1) && (pcnt > 1)) + if ((IS_LOCAL(user)) && (pcnt > 1)) { userrec* remote = Srv->FindNick(parameters[1]); if ((remote) && (remote->fd < 0)) @@ -3458,7 +3796,7 @@ class ModuleSpanningTree : public Module virtual void OnUserInvite(userrec* source,userrec* dest,chanrec* channel) { - if (source->fd > -1) + if (IS_LOCAL(source)) { std::deque params; params.push_back(dest->nick); @@ -3477,7 +3815,7 @@ class ModuleSpanningTree : public Module virtual void OnWallops(userrec* user, const std::string &text) { - if (user->fd > -1) + if (IS_LOCAL(user)) { std::deque params; params.push_back(":"+text); @@ -3490,7 +3828,7 @@ class ModuleSpanningTree : public Module if (target_type == TYPE_USER) { userrec* d = (userrec*)dest; - if ((d->fd < 0) && (user->fd > -1)) + if ((d->fd < 0) && (IS_LOCAL(user))) { std::deque params; params.clear(); @@ -3501,7 +3839,7 @@ class ModuleSpanningTree : public Module } else if (target_type == TYPE_CHANNEL) { - if (user->fd > -1) + if (IS_LOCAL(user)) { chanrec *c = (chanrec*)dest; std::string cname = c->name; @@ -3520,7 +3858,7 @@ class ModuleSpanningTree : public Module } else if (target_type == TYPE_SERVER) { - if (user->fd > -1) + if (IS_LOCAL(user)) { char* target = (char*)dest; std::deque par; @@ -3538,7 +3876,7 @@ class ModuleSpanningTree : public Module // route private messages which are targetted at clients only to the server // which needs to receive them userrec* d = (userrec*)dest; - if ((d->fd < 0) && (user->fd > -1)) + if ((d->fd < 0) && (IS_LOCAL(user))) { std::deque params; params.clear(); @@ -3549,7 +3887,7 @@ class ModuleSpanningTree : public Module } else if (target_type == TYPE_CHANNEL) { - if (user->fd > -1) + if (IS_LOCAL(user)) { chanrec *c = (chanrec*)dest; std::string cname = c->name; @@ -3568,7 +3906,7 @@ class ModuleSpanningTree : public Module } else if (target_type == TYPE_SERVER) { - if (user->fd > -1) + if (IS_LOCAL(user)) { char* target = (char*)dest; std::deque par; @@ -3588,7 +3926,7 @@ class ModuleSpanningTree : public Module virtual void OnUserJoin(userrec* user, chanrec* channel) { // Only do this for local users - if (user->fd > -1) + if (IS_LOCAL(user)) { std::deque params; params.clear(); @@ -3617,7 +3955,7 @@ class ModuleSpanningTree : public Module virtual void OnChangeHost(userrec* user, const std::string &newhost) { // only occurs for local clients - if (user->registered != 7) + if (user->registered != REG_ALL) return; std::deque params; params.push_back(newhost); @@ -3627,7 +3965,7 @@ class ModuleSpanningTree : public Module virtual void OnChangeName(userrec* user, const std::string &gecos) { // only occurs for local clients - if (user->registered != 7) + if (user->registered != REG_ALL) return; std::deque params; params.push_back(gecos); @@ -3636,7 +3974,7 @@ class ModuleSpanningTree : public Module virtual void OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage) { - if (user->fd > -1) + if (IS_LOCAL(user)) { std::deque params; params.push_back(channel->name); @@ -3649,7 +3987,7 @@ class ModuleSpanningTree : public Module virtual void OnUserConnect(userrec* user) { char agestr[MAXBUF]; - if (user->fd > -1) + if (IS_LOCAL(user)) { std::deque params; snprintf(agestr,MAXBUF,"%lu",(unsigned long)user->age); @@ -3675,7 +4013,7 @@ class ModuleSpanningTree : public Module virtual void OnUserQuit(userrec* user, const std::string &reason) { - if ((user->fd > -1) && (user->registered == 7)) + if ((IS_LOCAL(user)) && (user->registered == REG_ALL)) { std::deque params; params.push_back(":"+reason); @@ -3692,7 +4030,7 @@ class ModuleSpanningTree : public Module virtual void OnUserPostNick(userrec* user, const std::string &oldnick) { - if (user->fd > -1) + if (IS_LOCAL(user)) { std::deque params; params.push_back(user->nick); @@ -3702,7 +4040,7 @@ class ModuleSpanningTree : public Module virtual void OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason) { - if ((source) && (source->fd > -1)) + if ((source) && (IS_LOCAL(source))) { std::deque params; params.push_back(chan->name); @@ -3750,7 +4088,7 @@ class ModuleSpanningTree : public Module // locally. virtual void OnOper(userrec* user, const std::string &opertype) { - if (user->fd > -1) + if (IS_LOCAL(user)) { std::deque params; params.push_back(opertype); @@ -3760,7 +4098,7 @@ class ModuleSpanningTree : public Module void OnLine(userrec* source, const std::string &host, bool adding, char linetype, long duration, const std::string &reason) { - if (source->fd > -1) + if (IS_LOCAL(source)) { char type[8]; snprintf(type,8,"%cLINE",linetype); @@ -3826,7 +4164,7 @@ class ModuleSpanningTree : public Module virtual void OnMode(userrec* user, void* dest, int target_type, const std::string &text) { - if ((user->fd > -1) && (user->registered == 7)) + if ((IS_LOCAL(user)) && (user->registered == REG_ALL)) { if (target_type == TYPE_USER) {