bool TreeSocket::CheckDuplicate(const std::string& sname, const std::string& sid) { /* Check for fully initialized instances of the server by name */ TreeServer* CheckDupe = Utils->FindServer(sname); if (CheckDupe) { std::string pname = CheckDupe->GetParent() ? CheckDupe->GetParent()->GetName() : "<ourself>"; SendError("Server "+sname+" already exists on server "+pname+"!"); ServerInstance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, already exists on server "+pname); return false; } /* Check for fully initialized instances of the server by id */ ServerInstance->Logs->Log("m_spanningtree",DEBUG,"Looking for dupe SID %s", sid.c_str()); CheckDupe = Utils->FindServerID(sid); if (CheckDupe) { this->SendError("Server ID "+CheckDupe->GetID()+" already exists on server "+CheckDupe->GetName()+"! You may want to specify the server ID for the server manually with <server:id> so they do not conflict."); ServerInstance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, server ID '"+CheckDupe->GetID()+ "' already exists on server "+CheckDupe->GetName()); return false; } return true; }
bool TreeSocket::CheckDuplicate(const std::string& sname, const std::string& sid) { // Check if the server name is not in use by a server that's already fully connected TreeServer* CheckDupe = Utils->FindServer(sname); if (CheckDupe) { std::string pname = CheckDupe->GetParent() ? CheckDupe->GetParent()->GetName() : "<ourself>"; SendError("Server "+sname+" already exists on server "+pname+"!"); ServerInstance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, already exists on server "+pname); return false; } // Check if the id is not in use by a server that's already fully connected ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Looking for dupe SID %s", sid.c_str()); CheckDupe = Utils->FindServerID(sid); if (CheckDupe) { this->SendError("Server ID "+CheckDupe->GetID()+" already exists on server "+CheckDupe->GetName()+"! You may want to specify the server ID for the server manually with <server:id> so they do not conflict."); ServerInstance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, server ID '"+CheckDupe->GetID()+ "' already exists on server "+CheckDupe->GetName()); return false; } return true; }
int ModuleSpanningTree::HandleStats(const char** parameters, int pcnt, userrec* user) { if (pcnt > 1) { if (match(ServerInstance->Config->ServerName, parameters[1])) return 0; /* Remote STATS, the server is within the 2nd parameter */ std::deque<std::string> params; params.push_back(parameters[0]); params.push_back(parameters[1]); /* Send it out remotely, generate no reply yet */ TreeServer* s = Utils->FindServerMask(parameters[1]); if (s) { params[1] = s->GetName(); Utils->DoOneToOne(user->nick, "STATS", params, s->GetName()); } else { user->WriteServ( "402 %s %s :No such server", user->nick, parameters[1]); } return 1; } return 0; }
/* * Some server somewhere in the network introducing another server. * -- w */ bool TreeSocket::RemoteServer(const std::string &prefix, parameterlist ¶ms) { if (params.size() < 5) { SendError("Protocol error - Not enough parameters for SERVER command"); return false; } std::string servername = params[0]; // password is not used for a remote server // hopcount is not used (ever) std::string sid = params[3]; std::string description = params[4]; TreeServer* ParentOfThis = Utils->FindServer(prefix); if (!ParentOfThis) { this->SendError("Protocol error - Introduced remote server from unknown server "+prefix); return false; } if (!ServerInstance->IsSID(sid)) { this->SendError("Invalid format server ID: "+sid+"!"); return false; } TreeServer* CheckDupe = Utils->FindServer(servername); if (CheckDupe) { this->SendError("Server "+servername+" already exists!"); ServerInstance->SNO->WriteToSnoMask('L', "Server \2"+CheckDupe->GetName()+"\2 being introduced from \2" + ParentOfThis->GetName() + "\2 denied, already exists. Closing link with " + ParentOfThis->GetName()); return false; } CheckDupe = Utils->FindServer(sid); if (CheckDupe) { this->SendError("Server ID "+sid+" already exists! You may want to specify the server ID for the server manually with <server:id> so they do not conflict."); ServerInstance->SNO->WriteToSnoMask('L', "Server \2"+servername+"\2 being introduced from \2" + ParentOfThis->GetName() + "\2 denied, server ID already exists on the network. Closing link with " + ParentOfThis->GetName()); return false; } Link* lnk = Utils->FindLink(servername); TreeServer *Node = new TreeServer(Utils, servername, description, sid, ParentOfThis,NULL, lnk ? lnk->Hidden : false); ParentOfThis->AddChild(Node); params[4] = ":" + params[4]; Utils->DoOneToAllButSender(prefix,"SERVER",params,prefix); ServerInstance->SNO->WriteToSnoMask('L', "Server \002"+ParentOfThis->GetName()+"\002 introduced server \002"+servername+"\002 ("+description+")"); return true; }
bool TreeSocket::DelLine(const std::string &prefix, parameterlist ¶ms) { if (params.size() < 2) return true; std::string setter = "<unknown>"; User* user = ServerInstance->FindNick(prefix); if (user) setter = user->nick; else { TreeServer* t = Utils->FindServer(prefix); if (t) setter = t->GetName(); } /* NOTE: No check needed on 'user', this function safely handles NULL */ if (ServerInstance->XLines->DelLine(params[1].c_str(), params[0], user)) { ServerInstance->SNO->WriteToSnoMask('X',"%s removed %s%s on %s", setter.c_str(), params[0].c_str(), params[0].length() == 1 ? "-line" : "", params[1].c_str()); Utils->DoOneToAllButSender(prefix,"DELLINE", params, prefix); } return true; }
int ModuleSpanningTree::HandleModules(const std::vector<std::string>& parameters, User* user) { if (parameters.size() > 0) { if (InspIRCd::Match(ServerInstance->Config->ServerName, parameters[0])) return 0; std::deque<std::string> params; params.push_back(parameters[0]); TreeServer* s = Utils->FindServerMask(parameters[0].c_str()); if (s) { params[0] = s->GetName(); Utils->DoOneToOne(user->uuid, "MODULES", params, s->GetName()); } else user->WriteNumeric(ERR_NOSUCHSERVER, "%s %s :No such server", user->nick.c_str(), parameters[0].c_str()); return 1; } return 0; }
void CommandMap::ShowMap(TreeServer* Current, User* user, int depth, int &line, char* names, int &maxnamew, char* stats) { ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "ShowMap depth %d on line %d", depth, line); float percent; if (ServerInstance->Users->clientlist->size() == 0) { // If there are no users, WHO THE HELL DID THE /MAP?!?!?! percent = 0; } else { percent = Current->UserCount * 100.0 / ServerInstance->Users->clientlist->size(); } const std::string operdata = user->IsOper() ? MapOperInfo(Current) : ""; char* myname = names + 100 * line; char* mystat = stats + 50 * line; memset(myname, ' ', depth); int w = depth; if (user->IsOper()) { w += snprintf(myname + depth, 99 - depth, "%s (%s)", Current->GetName().c_str(), Current->GetID().c_str()); } else { w += snprintf(myname + depth, 99 - depth, "%s", Current->GetName().c_str()); } memset(myname + w, ' ', 100 - w); if (w > maxnamew) maxnamew = w; snprintf(mystat, 49, "%5d [%5.2f%%]%s", Current->UserCount, percent, operdata.c_str()); line++; if (user->IsOper() || !Utils->FlatLinks) depth = depth + 2; const TreeServer::ChildServers& servers = Current->GetChildren(); for (TreeServer::ChildServers::const_iterator i = servers.begin(); i != servers.end(); ++i) { TreeServer* child = *i; if (!user->IsOper()) { if (child->Hidden) continue; if ((Utils->HideULines) && (ServerInstance->ULine(child->GetName()))) continue; } ShowMap(child, user, depth, line, names, maxnamew, stats); } }
int ModuleSpanningTree::HandleModules(const char** parameters, int pcnt, userrec* user) { if (pcnt > 0) { if (match(ServerInstance->Config->ServerName, parameters[0])) return 0; std::deque<std::string> params; params.push_back(parameters[0]); TreeServer* s = Utils->FindServerMask(parameters[0]); if (s) { params[0] = s->GetName(); Utils->DoOneToOne(user->nick, "MODULES", params, s->GetName()); } else user->WriteServ( "402 %s %s :No such server", user->nick, parameters[0]); return 1; } return 0; }
void ModuleSpanningTree::ShowMap(TreeServer* Current, User* user, int depth, int &line, char* names, int &maxnamew, char* stats) { ServerInstance->Logs->Log("map",LOG_DEBUG,"ShowMap depth %d on line %d", depth, line); float percent; if (ServerInstance->Users->clientlist->size() == 0) { // If there are no users, WHO THE HELL DID THE /MAP?!?!?! percent = 0; } else { percent = Current->UserCount * 100.0 / ServerInstance->Users->clientlist->size(); } const std::string operdata = user->IsOper() ? MapOperInfo(Current) : ""; char* myname = names + 100 * line; char* mystat = stats + 50 * line; memset(myname, ' ', depth); int w = depth; std::string servername = Current->GetName(); if (user->IsOper()) { w += snprintf(myname + depth, 99 - depth, "%s (%s)", servername.c_str(), Current->GetID().c_str()); } else { w += snprintf(myname + depth, 99 - depth, "%s", servername.c_str()); } memset(myname + w, ' ', 100 - w); if (w > maxnamew) maxnamew = w; snprintf(mystat, 49, "%5d [%5.2f%%]%s", Current->UserCount, percent, operdata.c_str()); line++; if (user->IsOper() || !Utils->FlatLinks) depth = depth + 2; for (unsigned int q = 0; q < Current->ChildCount(); q++) { TreeServer* child = Current->GetChild(q); if (!user->IsOper()) { if (child->Hidden) continue; if ((Utils->HideULines) && (ServerInstance->ULine(child->GetName()))) continue; } ShowMap(child, user, depth, line, names, maxnamew, stats); } }
int ModuleSpanningTree::HandleMotd(const std::vector<std::string>& parameters, User* user) { if (parameters.size() > 0) { if (InspIRCd::Match(ServerInstance->Config->ServerName, parameters[0])) return 0; /* Remote MOTD, the server is within the 1st parameter */ std::deque<std::string> params; params.push_back(parameters[0]); /* Send it out remotely, generate no reply yet */ TreeServer* s = Utils->FindServerMask(parameters[0]); if (s) { params[0] = s->GetName(); Utils->DoOneToOne(user->uuid, "MOTD", params, s->GetName()); } else user->WriteNumeric(ERR_NOSUCHSERVER, "%s %s :No such server", user->nick.c_str(), parameters[0].c_str()); return 1; } return 0; }
int ModuleSpanningTree::HandleTime(const char** parameters, int pcnt, userrec* user) { if ((IS_LOCAL(user)) && (pcnt)) { TreeServer* found = Utils->FindServerMask(parameters[0]); if (found) { // we dont' override for local server if (found == Utils->TreeRoot) return 0; std::deque<std::string> params; params.push_back(found->GetName()); params.push_back(user->nick); Utils->DoOneToOne(ServerInstance->Config->ServerName,"TIME",params,found->GetName()); } else { user->WriteServ("402 %s %s :No such server",user->nick,parameters[0]); } } return 1; }
void SpanningTreeProtocolInterface::GetServerList(ServerList& sl) { for (server_hash::iterator i = Utils->serverlist.begin(); i != Utils->serverlist.end(); i++) { ServerInfo ps; ps.servername = i->second->GetName(); TreeServer* s = i->second->GetParent(); ps.parentname = s ? s->GetName() : ""; ps.usercount = i->second->UserCount; ps.opercount = i->second->OperCount; ps.gecos = i->second->GetDesc(); ps.latencyms = i->second->rtt; sl.push_back(ps); } }
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; }
/* * Some server somewhere in the network introducing another server. * -- w */ CmdResult CommandServer::HandleServer(TreeServer* ParentOfThis, std::vector<std::string>& params) { std::string servername = params[0]; // password is not used for a remote server // hopcount is not used (ever) std::string sid = params[3]; std::string description = params[4]; TreeSocket* socket = ParentOfThis->GetSocket(); if (!InspIRCd::IsSID(sid)) { socket->SendError("Invalid format server ID: "+sid+"!"); return CMD_FAILURE; } TreeServer* CheckDupe = Utils->FindServer(servername); if (CheckDupe) { socket->SendError("Server "+servername+" already exists!"); ServerInstance->SNO->WriteToSnoMask('L', "Server \2"+CheckDupe->GetName()+"\2 being introduced from \2" + ParentOfThis->GetName() + "\2 denied, already exists. Closing link with " + ParentOfThis->GetName()); return CMD_FAILURE; } CheckDupe = Utils->FindServer(sid); if (CheckDupe) { socket->SendError("Server ID "+sid+" already exists! You may want to specify the server ID for the server manually with <server:id> so they do not conflict."); ServerInstance->SNO->WriteToSnoMask('L', "Server \2"+servername+"\2 being introduced from \2" + ParentOfThis->GetName() + "\2 denied, server ID already exists on the network. Closing link with " + ParentOfThis->GetName()); return CMD_FAILURE; } Link* lnk = Utils->FindLink(servername); TreeServer* Node = new TreeServer(servername, description, sid, ParentOfThis, ParentOfThis->GetSocket(), lnk ? lnk->Hidden : false); ParentOfThis->AddChild(Node); ServerInstance->SNO->WriteToSnoMask('L', "Server \002"+ParentOfThis->GetName()+"\002 introduced server \002"+servername+"\002 ("+description+")"); return CMD_SUCCESS; }
void ModuleSpanningTree::ShowLinks(TreeServer* Current, User* user, int hops) { std::string Parent = Utils->TreeRoot->GetName(); if (Current->GetParent()) { Parent = Current->GetParent()->GetName(); } const TreeServer::ChildServers& children = Current->GetChildren(); for (TreeServer::ChildServers::const_iterator i = children.begin(); i != children.end(); ++i) { TreeServer* server = *i; if ((server->Hidden) || ((Utils->HideULines) && (ServerInstance->ULine(server->GetName())))) { if (user->IsOper()) { ShowLinks(server, user, hops+1); } } else { ShowLinks(server, user, hops+1); } } /* Don't display the line if its a uline, hide ulines is on, and the user isnt an oper */ if ((Utils->HideULines) && (ServerInstance->ULine(Current->GetName())) && (!user->IsOper())) return; /* Or if the server is hidden and they're not an oper */ else if ((Current->Hidden) && (!user->IsOper())) return; user->WriteNumeric(364, "%s %s %s :%d %s", user->nick.c_str(), Current->GetName().c_str(), (Utils->FlatLinks && (!user->IsOper())) ? ServerInstance->Config->ServerName.c_str() : Parent.c_str(), (Utils->FlatLinks && (!user->IsOper())) ? 0 : hops, Current->GetDesc().c_str()); }
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 */ long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000); restart: for (server_hash::iterator i = Utils->serverlist.begin(); i != Utils->serverlist.end(); i++) { TreeServer *s = i->second; // Skip myself if (s->IsRoot()) continue; if (s->GetSocket()->GetLinkState() == DYING) { s->GetSocket()->Close(); goto restart; } // Do not ping servers that are not fully connected yet! // Servers which are connected to us have IsLocal() == true and if they're fully connected // then Socket->LinkState == CONNECTED. Servers that are linked to another server are always fully connected. if (s->IsLocal() && s->GetSocket()->GetLinkState() != CONNECTED) continue; // Now do PING checks on all servers // 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); s->GetSocket()->WriteLine(":" + ServerInstance->Config->GetSID() + " PING " + s->GetID()); s->LastPingMsec = ts; } else { // They didn't answer the last ping, if they are locally connected, get rid of them. if (s->IsLocal()) { TreeSocket* sock = s->GetSocket(); sock->SendError("Ping timeout"); sock->Close(); goto restart; } } } // 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; } } }
// Ok, prepare to be confused. // After much mulling over how to approach this, it struck me that // the 'usual' way of doing a /MAP isnt the best way. Instead of // keeping track of a ton of ascii characters, and line by line // under recursion working out where to place them using multiplications // and divisons, we instead render the map onto a backplane of characters // (a character matrix), then draw the branches as a series of "L" shapes // from the nodes. This is not only friendlier on CPU it uses less stack. bool ModuleSpanningTree::HandleMap(const std::vector<std::string>& parameters, User* user) { if (parameters.size() > 0) { /* Remote MAP, the server is within the 1st parameter */ TreeServer* s = Utils->FindServerMask(parameters[0]); bool ret = false; if (!s) { user->WriteNumeric(ERR_NOSUCHSERVER, "%s %s :No such server", user->nick.c_str(), parameters[0].c_str()); ret = true; } else if (s && s != Utils->TreeRoot) { parameterlist params; params.push_back(parameters[0]); params[0] = s->GetName(); Utils->DoOneToOne(user->uuid, "MAP", params, s->GetName()); ret = true; } // Don't return if s == Utils->TreeRoot (us) if (ret) return true; } // These arrays represent a virtual screen which we will // "scratch" draw to, as the console device of an irc // client does not provide for a proper terminal. int totusers = ServerInstance->Users->clientlist->size(); int totservers = this->CountServs(); int maxnamew = 0; int line = 0; char* names = new char[totservers * 100]; char* stats = new char[totservers * 50]; // The only recursive bit is called here. ShowMap(Utils->TreeRoot,user,0,line,names,maxnamew,stats); // Process each line one by one. for (int l = 1; l < line; l++) { char* myname = names + 100 * l; // scan across the line looking for the start of the // servername (the recursive part of the algorithm has placed // the servers at indented positions depending on what they // are related to) int first_nonspace = 0; while (myname[first_nonspace] == ' ') { first_nonspace++; } first_nonspace--; // Draw the `- (corner) section: this may be overwritten by // another L shape passing along the same vertical pane, becoming // a |- (branch) section instead. myname[first_nonspace] = '-'; myname[first_nonspace-1] = '`'; int l2 = l - 1; // Draw upwards until we hit the parent server, causing possibly // other corners (`-) to become branches (|-) while ((names[l2 * 100 + first_nonspace-1] == ' ') || (names[l2 * 100 + first_nonspace-1] == '`')) { names[l2 * 100 + first_nonspace-1] = '|'; l2--; } } float avg_users = totusers * 1.0 / line; ServerInstance->Logs->Log("map",LOG_DEBUG,"local"); for (int t = 0; t < line; t++) { // terminate the string at maxnamew characters names[100 * t + maxnamew] = '\0'; user->SendText(":%s %03d %s :%s %s", ServerInstance->Config->ServerName.c_str(), RPL_MAP, user->nick.c_str(), names + 100 * t, stats + 50 * t); } user->SendText(":%s %03d %s :%d server%s and %d user%s, average %.2f users per server", ServerInstance->Config->ServerName.c_str(), RPL_MAPUSERS, user->nick.c_str(), line, (line > 1 ? "s" : ""), totusers, (totusers > 1 ? "s" : ""), avg_users); user->SendText(":%s %03d %s :End of /MAP", ServerInstance->Config->ServerName.c_str(), RPL_ENDMAP, user->nick.c_str()); delete[] names; delete[] stats; 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; } } } }
CmdResult CommandUID::Handle(User* serversrc, std::vector<std::string>& params) { /** Do we have enough parameters: * 0 1 2 3 4 5 6 7 8 9 (n-1) * UID uuid age nick host dhost ident ip.string signon +modes (modepara) :gecos */ time_t age_t = ConvToInt(params[1]); time_t signon = ConvToInt(params[7]); std::string empty; const std::string& modestr = params[8]; TreeServer* remoteserver = Utils->FindServer(serversrc->server); if (!remoteserver) return CMD_INVALID; /* Is this a valid UID, and not misrouted? */ if (params[0].length() != 9 || params[0].substr(0,3) != serversrc->uuid) return CMD_INVALID; /* Check parameters for validity before introducing the client, discovered by dmb */ if (!age_t) return CMD_INVALID; if (!signon) return CMD_INVALID; if (modestr[0] != '+') return CMD_INVALID; /* check for collision */ user_hash::iterator iter = ServerInstance->Users->clientlist->find(params[2]); if (iter != ServerInstance->Users->clientlist->end()) { /* * Nick collision. */ int collide = Utils->DoCollision(iter->second, remoteserver, age_t, params[5], params[6], params[0]); ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "*** Collision on %s, collide=%d", params[2].c_str(), collide); if (collide != 1) { // Remote client lost, make sure we change their nick for the hash too params[2] = params[0]; } } /* IMPORTANT NOTE: For remote users, we pass the UUID in the constructor. This automatically * sets it up in the UUID hash for us. */ User* _new = NULL; try { _new = new RemoteUser(params[0], remoteserver->GetName()); } catch (...) { ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Duplicate UUID %s in client introduction", params[0].c_str()); return CMD_INVALID; } (*(ServerInstance->Users->clientlist))[params[2]] = _new; _new->nick = params[2]; _new->host = params[3]; _new->dhost = params[4]; _new->ident = params[5]; _new->fullname = params[params.size() - 1]; _new->registered = REG_ALL; _new->signon = signon; _new->age = age_t; unsigned int paramptr = 9; for (std::string::const_iterator v = modestr.begin(); v != modestr.end(); ++v) { // Accept more '+' chars, for now if (*v == '+') continue; /* For each mode thats set, find the mode handler and set it on the new user */ ModeHandler* mh = ServerInstance->Modes->FindMode(*v, MODETYPE_USER); if (!mh) { ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Unrecognised mode '%c' for a user in UID, dropping link", *v); return CMD_INVALID; } if (mh->GetNumParams(true)) { if (paramptr >= params.size() - 1) return CMD_INVALID; std::string mp = params[paramptr++]; /* IMPORTANT NOTE: * All modes are assumed to succeed here as they are being set by a remote server. * Modes CANNOT FAIL here. If they DO fail, then the failure is ignored. This is important * to note as all but one modules currently cannot ever fail in this situation, except for * m_servprotect which specifically works this way to prevent the mode being set ANYWHERE * but here, at client introduction. You may safely assume this behaviour is standard and * will not change in future versions if you want to make use of this protective behaviour * yourself. */ mh->OnModeChange(_new, _new, NULL, mp, true); } else mh->OnModeChange(_new, _new, NULL, empty, true); _new->SetMode(mh, true); } _new->SetClientIP(params[6].c_str()); ServerInstance->Users->AddGlobalClone(_new); remoteserver->UserCount++; bool dosend = true; if ((Utils->quiet_bursts && remoteserver->bursting) || ServerInstance->SilentULine(_new->server)) dosend = false; if (dosend) ServerInstance->SNO->WriteToSnoMask('C',"Client connecting at %s: %s (%s) [%s]", _new->server.c_str(), _new->GetFullRealHost().c_str(), _new->GetIPString().c_str(), _new->fullname.c_str()); FOREACH_MOD(OnPostConnect, (_new)); return CMD_SUCCESS; }