void ModuleSpanningTree::ProtoSendMetaData(void* opaque, Extensible* target, const std::string &extname, const std::string &extdata) { TreeSocket* s = static_cast<TreeSocket*>(opaque); User* u = dynamic_cast<User*>(target); Channel* c = dynamic_cast<Channel*>(target); if (u) s->WriteLine(":"+ServerInstance->Config->GetSID()+" METADATA "+u->uuid+" "+extname+" :"+extdata); else if (c) s->WriteLine(":"+ServerInstance->Config->GetSID()+" METADATA "+c->name+" "+ConvToStr(c->age)+" "+extname+" :"+extdata); else if (!target) s->WriteLine(":"+ServerInstance->Config->GetSID()+" METADATA * "+extname+" :"+extdata); }
void SpanningTreeUtilities::DoOneToOne(const CmdBuilder& params, Server* server) { TreeServer* ts = static_cast<TreeServer*>(server); TreeSocket* sock = ts->GetSocket(); if (sock) sock->WriteLine(params); }
void ModuleSpanningTree::ProtoSendMode(void* opaque, int target_type, void* target, const std::string &modeline) { TreeSocket* s = (TreeSocket*)opaque; if (target) { if (target_type == TYPE_USER) { userrec* u = (userrec*)target; s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" FMODE "+u->nick+" "+ConvToStr(u->age)+" "+modeline); } else { chanrec* c = (chanrec*)target; s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" FMODE "+c->name+" "+ConvToStr(c->age)+" "+modeline); } } }
void ModuleSpanningTree::ProtoSendMode(void* opaque, TargetTypeFlags target_type, void* target, const parameterlist &modeline, const std::vector<TranslateType> &translate) { TreeSocket* s = (TreeSocket*)opaque; std::string output_text = CommandParser::TranslateUIDs(translate, modeline); if (target) { if (target_type == TYPE_USER) { User* u = (User*)target; s->WriteLine(":"+ServerInstance->Config->GetSID()+" MODE "+u->uuid+" "+output_text); } else if (target_type == TYPE_CHANNEL) { Channel* c = (Channel*)target; s->WriteLine(":"+ServerInstance->Config->GetSID()+" FMODE "+c->name+" "+ConvToStr(c->age)+" "+output_text); } } }
void ModuleSpanningTree::ProtoSendMetaData(void* opaque, int target_type, void* target, const std::string &extname, const std::string &extdata) { TreeSocket* s = (TreeSocket*)opaque; if (target) { if (target_type == TYPE_USER) { userrec* u = (userrec*)target; s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" METADATA "+u->nick+" "+extname+" :"+extdata); } else if (target_type == TYPE_CHANNEL) { chanrec* c = (chanrec*)target; s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" METADATA "+c->name+" "+extname+" :"+extdata); } } if (target_type == TYPE_OTHER) { s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" METADATA * "+extname+" :"+extdata); } }
void ModuleSpanningTree::ProtoSendMetaData(void* opaque, TargetTypeFlags target_type, void* target, const std::string &extname, const std::string &extdata) { TreeSocket* s = (TreeSocket*)opaque; if (target) { if (target_type == TYPE_USER) { User* u = (User*)target; s->WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" METADATA "+u->uuid+" "+extname+" :"+extdata); } else if (target_type == TYPE_CHANNEL) { Channel* c = (Channel*)target; s->WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" METADATA "+c->name+" "+extname+" :"+extdata); } } if (target_type == TYPE_OTHER) { s->WriteLine(std::string(":")+ServerInstance->Config->GetSID()+" METADATA * "+extname+" :"+extdata); } }
void ModuleSpanningTree::OnUserMessage(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list) { /* Server origin */ if (user == NULL) return; if (target_type == TYPE_USER) { // route private messages which are targetted at clients only to the server // which needs to receive them User* d = (User*)dest; if ((d->GetFd() < 0) && (IS_LOCAL(user))) { std::deque<std::string> params; params.clear(); params.push_back(d->uuid); params.push_back(":"+text); Utils->DoOneToOne(user->uuid,"PRIVMSG",params,d->server); } } else if (target_type == TYPE_CHANNEL) { if (IS_LOCAL(user)) { Channel *c = (Channel*)dest; if (c) { std::string cname = c->name; if (status) cname = status + cname; TreeServerList list; Utils->GetListOfServersForChannel(c,list,status,exempt_list); for (TreeServerList::iterator i = list.begin(); i != list.end(); i++) { TreeSocket* Sock = i->second->GetSocket(); if (Sock) Sock->WriteLine(":"+std::string(user->uuid)+" PRIVMSG "+cname+" :"+text); } } } } else if (target_type == TYPE_SERVER) { if (IS_LOCAL(user)) { char* target = (char*)dest; std::deque<std::string> par; par.push_back(target); par.push_back(":"+text); Utils->DoOneToMany(user->uuid,"PRIVMSG",par); } } }
void SpanningTreeUtilities::SendChannelMessage(const std::string& prefix, Channel* target, const std::string& text, char status, const CUList& exempt_list, const char* message_type, TreeSocket* omit) { CmdBuilder msg(prefix, message_type); if (status == 0) status = ' '; msg.push(status).push_raw(target->name).push_last(text); TreeSocketSet list; this->GetListOfServersForChannel(target, list, status, exempt_list); for (TreeSocketSet::iterator i = list.begin(); i != list.end(); ++i) { TreeSocket* Sock = *i; if (Sock != omit) Sock->WriteLine(msg); } }
void ModuleSpanningTree::OnUserNotice(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list) { if (target_type == TYPE_USER) { userrec* d = (userrec*)dest; if ((d->GetFd() < 0) && (IS_LOCAL(user))) { std::deque<std::string> params; params.clear(); params.push_back(d->nick); params.push_back(":"+text); Utils->DoOneToOne(user->nick,"NOTICE",params,d->server); } } else if (target_type == TYPE_CHANNEL) { if (IS_LOCAL(user)) { chanrec *c = (chanrec*)dest; if (c) { std::string cname = c->name; if (status) cname = status + cname; TreeServerList list; Utils->GetListOfServersForChannel(c,list,status,exempt_list); for (TreeServerList::iterator i = list.begin(); i != list.end(); i++) { TreeSocket* Sock = i->second->GetSocket(); if (Sock) Sock->WriteLine(":"+std::string(user->nick)+" NOTICE "+cname+" :"+text); } } } } else if (target_type == TYPE_SERVER) { if (IS_LOCAL(user)) { char* target = (char*)dest; std::deque<std::string> par; par.push_back(target); par.push_back(":"+text); Utils->DoOneToMany(user->nick,"NOTICE",par); } } }
void SpanningTreeUtilities::SendChannelMessage(const std::string& prefix, Channel* target, const std::string& text, char status, const CUList& exempt_list, const char* message_type, TreeSocket* omit) { std::string raw(":"); raw.append(prefix).append(1, ' ').append(message_type).push_back(' '); if (status) raw.push_back(status); raw.append(target->name).append(" :").append(text); TreeSocketSet list; this->GetListOfServersForChannel(target, list, status, exempt_list); for (TreeSocketSet::iterator i = list.begin(); i != list.end(); ++i) { TreeSocket* Sock = *i; if (Sock != omit) Sock->WriteLine(raw); } }
bool SpanningTreeUtilities::DoOneToMany(const std::string &prefix, const std::string &command, const parameterlist ¶ms) { std::string FullLine = ConstructLine(prefix, command, params); unsigned int items = this->TreeRoot->ChildCount(); for (unsigned int x = 0; x < items; x++) { TreeServer* Route = this->TreeRoot->GetChild(x); if (Route && Route->GetSocket()) { TreeSocket* Sock = Route->GetSocket(); if (Sock) Sock->WriteLine(FullLine); } } return true; }
bool SpanningTreeUtilities::DoOneToOne(const std::string& prefix, const std::string& command, const parameterlist& params, const std::string& target) { TreeServer* Route = this->BestRouteTo(target); if (Route) { if (Route && Route->GetSocket()) { TreeSocket* Sock = Route->GetSocket(); if (Sock) Sock->WriteLine(ConstructLine(prefix, command, params)); } return true; } else { return false; } }
void ModuleSpanningTree::DoPingChecks(time_t curtime) { for (unsigned int j = 0; j < Utils->TreeRoot->ChildCount(); j++) { TreeServer* serv = Utils->TreeRoot->GetChild(j); TreeSocket* sock = serv->GetSocket(); if (sock) { if (curtime >= serv->NextPingTime()) { if (serv->AnsweredLastPing()) { sock->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" PING "+serv->GetName()); serv->SetNextPingTime(curtime + 60); serv->LastPing = curtime; serv->Warned = false; } else { /* they didnt answer, boot them */ sock->SendError("Ping timeout"); sock->Squit(serv,"Ping timeout"); ServerInstance->SE->DelFd(sock); sock->Close(); return; } } else if ((Utils->PingWarnTime) && (!serv->Warned) && (curtime >= serv->NextPingTime() - (60 - Utils->PingWarnTime)) && (!serv->AnsweredLastPing())) { /* The server hasnt responded, send a warning to opers */ ServerInstance->SNO->WriteToSnoMask('l',"Server \002%s\002 has not responded to PING for %d seconds, high latency.", serv->GetName().c_str(), Utils->PingWarnTime); serv->Warned = true; } } } /* Cancel remote burst mode on any servers which still have it enabled due to latency/lack of data. * This prevents lost REMOTECONNECT notices */ for (server_hash::iterator i = Utils->serverlist.begin(); i != Utils->serverlist.end(); i++) Utils->SetRemoteBursting(i->second, false); }
bool SpanningTreeUtilities::DoOneToAllButSender(const std::string& prefix, const std::string& command, const parameterlist& params, const std::string& omit) { TreeServer* omitroute = this->BestRouteTo(omit); std::string FullLine = ConstructLine(prefix, command, params); unsigned int items = this->TreeRoot->ChildCount(); for (unsigned int x = 0; x < items; x++) { TreeServer* Route = this->TreeRoot->GetChild(x); // Send the line IF: // The route has a socket (its a direct connection) // The route isnt the one to be omitted // The route isnt the path to the one to be omitted if ((Route) && (Route->GetSocket()) && (Route->GetName() != omit) && (omitroute != Route)) { TreeSocket* Sock = Route->GetSocket(); if (Sock) Sock->WriteLine(FullLine); } } return true; }
void ModuleSpanningTree::DoPingChecks(time_t curtime) { /* * Cancel remote burst mode on any servers which still have it enabled due to latency/lack of data. * This prevents lost REMOTECONNECT notices */ timeval t; gettimeofday(&t, NULL); long ts = (t.tv_sec * 1000) + (t.tv_usec / 1000); for (server_hash::iterator i = Utils->serverlist.begin(); i != Utils->serverlist.end(); i++) { TreeServer *s = i->second; // Fix for bug #792, do not ping servers that are not connected yet! // Remote servers have Socket == NULL and local connected servers have // Socket->LinkState == CONNECTED if (s->GetSocket() && s->GetSocket()->GetLinkState() != CONNECTED) continue; // Now do PING checks on all servers TreeServer *mts = Utils->BestRouteTo(s->GetID()); if (mts) { // Only ping if this server needs one if (curtime >= s->NextPingTime()) { // And if they answered the last if (s->AnsweredLastPing()) { // They did, send a ping to them s->SetNextPingTime(curtime + Utils->PingFreq); TreeSocket *tsock = mts->GetSocket(); // ... if we can find a proper route to them if (tsock) { tsock->WriteLine(std::string(":") + ServerInstance->Config->GetSID() + " PING " + ServerInstance->Config->GetSID() + " " + s->GetID()); s->LastPingMsec = ts; } } else { // They didn't answer the last ping, if they are locally connected, get rid of them. TreeSocket *sock = s->GetSocket(); if (sock) { sock->SendError("Ping timeout"); sock->Squit(s,"Ping timeout"); ServerInstance->SE->DelFd(sock); sock->Close(); return; } } } // If warn on ping enabled and not warned and the difference is sufficient and they didn't answer the last ping... if ((Utils->PingWarnTime) && (!s->Warned) && (curtime >= s->NextPingTime() - (Utils->PingFreq - Utils->PingWarnTime)) && (!s->AnsweredLastPing())) { /* The server hasnt responded, send a warning to opers */ ServerInstance->SNO->WriteToSnoMask('l',"Server \002%s\002 has not responded to PING for %d seconds, high latency.", s->GetName().c_str(), Utils->PingWarnTime); s->Warned = true; } } } }
void SpanningTreeUtilities::RouteCommand(TreeServer* origin, Command* thiscmd, const parameterlist& parameters, User* user) { const std::string& command = thiscmd->name; RouteDescriptor routing = thiscmd->GetRouting(user, parameters); std::string sent_cmd = command; parameterlist params; if (routing.type == ROUTE_TYPE_LOCALONLY) { return; } else if (routing.type == ROUTE_TYPE_OPT_BCAST) { params.push_back("*"); params.push_back(command); sent_cmd = "ENCAP"; } else if (routing.type == ROUTE_TYPE_OPT_UCAST) { TreeServer* sdest = FindServer(routing.serverdest); if (!sdest) { ServerInstance->Logs->Log("m_spanningtree", LOG_DEFAULT, "Trying to route ENCAP to nonexistant server %s", routing.serverdest.c_str()); return; } params.push_back(sdest->GetID()); params.push_back(command); sent_cmd = "ENCAP"; } else { Module* srcmodule = thiscmd->creator; Version ver = srcmodule->GetVersion(); if (!(ver.Flags & (VF_COMMON | VF_CORE)) && srcmodule != Creator) { ServerInstance->Logs->Log("m_spanningtree", LOG_DEFAULT, "Routed command %s from non-VF_COMMON module %s", command.c_str(), srcmodule->ModuleSourceFile.c_str()); return; } } std::string output_text = CommandParser::TranslateUIDs(thiscmd->translation, parameters, true, thiscmd); params.push_back(output_text); if (routing.type == ROUTE_TYPE_MESSAGE) { char pfx = 0; std::string dest = routing.serverdest; if (ServerInstance->Modes->FindPrefix(dest[0])) { pfx = dest[0]; dest = dest.substr(1); } if (dest[0] == '#') { Channel* c = ServerInstance->FindChan(dest); if (!c) return; TreeServerList list; // TODO OnBuildExemptList hook was here GetListOfServersForChannel(c,list,pfx, CUList()); std::string data = ":" + user->uuid + " " + sent_cmd; for (unsigned int x = 0; x < params.size(); x++) data += " " + params[x]; for (TreeServerList::iterator i = list.begin(); i != list.end(); i++) { TreeSocket* Sock = (*i)->GetSocket(); if (origin && origin->GetSocket() == Sock) continue; if (Sock) Sock->WriteLine(data); } } else if (dest[0] == '$') { if (origin) DoOneToAllButSender(user->uuid, sent_cmd, params, origin->GetName()); else DoOneToMany(user->uuid, sent_cmd, params); } else { // user target? User* d = ServerInstance->FindNick(dest); if (!d) return; TreeServer* tsd = BestRouteTo(d->server); if (tsd == origin) // huh? no routing stuff around in a circle, please. return; DoOneToOne(user->uuid, sent_cmd, params, d->server); } } else if (routing.type == ROUTE_TYPE_BROADCAST || routing.type == ROUTE_TYPE_OPT_BCAST) { if (origin) DoOneToAllButSender(user->uuid, sent_cmd, params, origin->GetName()); else DoOneToMany(user->uuid, sent_cmd, params); } else if (routing.type == ROUTE_TYPE_UNICAST || routing.type == ROUTE_TYPE_OPT_UCAST) { if (origin && routing.serverdest == origin->GetName()) return; DoOneToOne(user->uuid, sent_cmd, params, routing.serverdest); } }
/* * Yes, this function looks a little ugly. * However, in some circumstances we may not have a User, so we need to do things this way. * Returns 1 if colliding local client, 2 if colliding remote, 3 if colliding both. * Sends SAVEs as appropriate and forces nickchanges too. */ int SpanningTreeUtilities::DoCollision(User* u, TreeServer* server, time_t remotets, const std::string& remoteident, const std::string& remoteip, const std::string& remoteuid) { /* * Under old protocol rules, we would have had to kill both clients. * Really, this sucks. * These days, we have UID. And, so what we do is, force nick change client(s) * involved according to timestamp rules. * * RULES: * user@ip equal: * Force nick change on OLDER timestamped client * user@ip differ: * Force nick change on NEWER timestamped client * TS EQUAL: * FNC both. * * This stops abusive use of collisions, simplifies problems with loops, and so on. * -- w00t */ bool bChangeLocal = true; bool bChangeRemote = true; /* for brevity, don't use the User - use defines to avoid any copy */ #define localts u->age #define localident u->ident #define localip u->GetIPString() /* mmk. let's do this again. */ if (remotets == localts) { /* equal. f**k them both! do nada, let the handler at the bottom figure this out. */ } else { /* f**k. now it gets complex. */ /* first, let's see if ident@host matches. */ bool SamePerson = (localident == remoteident) && (localip == remoteip); /* * if ident@ip is equal, and theirs is newer, or * ident@ip differ, and ours is newer */ if((SamePerson && remotets < localts) || (!SamePerson && remotets > localts)) { /* remote needs to change */ bChangeLocal = false; } else { /* ours needs to change */ bChangeRemote = false; } } /* * Cheat a little here. Instead of a dedicated command to change UID, * use SAVE and accept the losing client with its UID (as we know the SAVE will * not fail under any circumstances -- UIDs are netwide exclusive). * * This means that each side of a collide will generate one extra NICK back to where * they have just linked (and where it got the SAVE from), however, it will * be dropped harmlessly as it will come in as :928AAAB NICK 928AAAB, and we already * have 928AAAB's nick set to that. * -- w00t */ if (bChangeLocal) { /* * Local-side nick needs to change. Just in case we are hub, and * this "local" nick is actually behind us, send an SAVE out. */ CmdBuilder params("SAVE"); params.push_back(u->uuid); params.push_back(ConvToStr(u->age)); params.Broadcast(); u->ForceNickChange(u->uuid); if (!bChangeRemote) return 1; } if (bChangeRemote) { User *remote = ServerInstance->FindUUID(remoteuid); /* * remote side needs to change. If this happens, we will modify * the UID or halt the propagation of the nick change command, * so other servers don't need to see the SAVE */ TreeSocket* sock = server->GetSocket(); sock->WriteLine(":"+ServerInstance->Config->GetSID()+" SAVE "+remoteuid+" "+ ConvToStr(remotets)); if (remote) { /* nick change collide. Force change their nick. */ remote->ForceNickChange(remoteuid); } if (!bChangeLocal) return 2; } return 3; }