1 #include "configreader.h"
5 #include "commands/cmd_whois.h"
6 #include "commands/cmd_stats.h"
11 #include "transport.h"
12 #include "socketengine.h"
14 #include "m_spanningtree/main.h"
15 #include "m_spanningtree/utils.h"
16 #include "m_spanningtree/treeserver.h"
17 #include "m_spanningtree/link.h"
18 #include "m_spanningtree/treesocket.h"
19 #include "m_spanningtree/resolvers.h"
21 /* $ModDep: m_spanningtree/timesynctimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h */
23 /** Yay for fast searches!
24 * This is hundreds of times faster than recursion
25 * or even scanning a linked list, especially when
26 * there are more than a few servers to deal with.
29 TreeServer* SpanningTreeUtilities::FindServer(const std::string &ServerName)
31 server_hash::iterator iter;
32 iter = serverlist.find(ServerName.c_str());
33 if (iter != serverlist.end())
43 /** Returns the locally connected server we must route a
44 * message through to reach server 'ServerName'. This
45 * only applies to one-to-one and not one-to-many routing.
46 * See the comments for the constructor of TreeServer
49 TreeServer* SpanningTreeUtilities::BestRouteTo(const std::string &ServerName)
51 if (ServerName.c_str() == TreeRoot->GetName())
53 TreeServer* Found = FindServer(ServerName);
56 return Found->GetRoute();
64 /** Find the first server matching a given glob mask.
65 * Theres no find-using-glob method of hash_map [awwww :-(]
66 * so instead, we iterate over the list using an iterator
67 * and match each one until we get a hit. Yes its slow,
70 TreeServer* SpanningTreeUtilities::FindServerMask(const std::string &ServerName)
72 for (server_hash::iterator i = serverlist.begin(); i != serverlist.end(); i++)
74 if (match(i->first.c_str(),ServerName.c_str()))
80 /* A convenient wrapper that returns true if a server exists */
81 bool SpanningTreeUtilities::IsServer(const std::string &ServerName)
83 return (FindServer(ServerName) != NULL);
86 SpanningTreeUtilities::SpanningTreeUtilities(InspIRCd* Instance, ModuleSpanningTree* C) : ServerInstance(Instance), Creator(C)
92 this->TreeRoot = new TreeServer(this, ServerInstance, ServerInstance->Config->ServerName, ServerInstance->Config->ServerDesc);
94 modulelist* ml = ServerInstance->FindInterface("InspSocketHook");
96 /* Did we find any modules? */
99 /* Yes, enumerate them all to find out the hook name */
100 for (modulelist::iterator m = ml->begin(); m != ml->end(); m++)
102 /* Make a request to it for its name, its implementing
103 * InspSocketHook so we know its safe to do this
105 std::string name = InspSocketNameRequest((Module*)Creator, *m).Send();
106 /* Build a map of them */
107 hooks[name.c_str()] = *m;
108 hooknames.push_back(name);
112 this->ReadConfiguration(true);
115 SpanningTreeUtilities::~SpanningTreeUtilities()
117 for (unsigned int i = 0; i < Bindings.size(); i++)
119 ServerInstance->SE->DelFd(Bindings[i]);
120 Bindings[i]->Close();
123 while (TreeRoot->ChildCount())
125 TreeServer* child_server = TreeRoot->GetChild(0);
128 TreeSocket* sock = child_server->GetSocket();
129 ServerInstance->SE->DelFd(sock);
137 void SpanningTreeUtilities::AddThisServer(TreeServer* server, TreeServerList &list)
139 if (list.find(server) == list.end())
140 list[server] = server;
143 /* returns a list of DIRECT servernames for a specific channel */
144 void SpanningTreeUtilities::GetListOfServersForChannel(chanrec* c, TreeServerList &list, char status, const CUList &exempt_list)
150 ulist = c->GetOppedUsers();
153 ulist = c->GetHalfoppedUsers();
156 ulist = c->GetVoicedUsers();
159 ulist = c->GetUsers();
162 for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
164 if ((i->second->GetFd() < 0) && (exempt_list.find(i->second) == exempt_list.end()))
166 TreeServer* best = this->BestRouteTo(i->second->server);
168 AddThisServer(best,list);
174 bool SpanningTreeUtilities::DoOneToAllButSenderRaw(const std::string &data, const std::string &omit, const std::string &prefix, const irc::string &command, std::deque<std::string> ¶ms)
177 TreeServer* omitroute = this->BestRouteTo(omit);
178 if ((command == "NOTICE") || (command == "PRIVMSG"))
180 if (params.size() >= 2)
183 if ((*(params[0].c_str()) == '@') || (*(params[0].c_str()) == '%') || (*(params[0].c_str()) == '+'))
186 params[0] = params[0].substr(1, params[0].length()-1);
188 if ((*(params[0].c_str()) != '#') && (*(params[0].c_str()) != '$'))
190 // special routing for private messages/notices
191 userrec* d = ServerInstance->FindNick(params[0]);
194 std::deque<std::string> par;
195 par.push_back(params[0]);
196 par.push_back(":"+params[1]);
197 this->DoOneToOne(prefix,command.c_str(),par,d->server);
201 else if (*(params[0].c_str()) == '$')
203 std::deque<std::string> par;
204 par.push_back(params[0]);
205 par.push_back(":"+params[1]);
206 this->DoOneToAllButSender(prefix,command.c_str(),par,omitroute->GetName());
211 chanrec* c = ServerInstance->FindChan(params[0]);
212 userrec* u = ServerInstance->FindNick(prefix);
217 FOREACH_MOD(I_OnBuildExemptList, OnBuildExemptList((command == "PRIVMSG" ? MSG_PRIVMSG : MSG_NOTICE), c, u, pfx, elist));
218 GetListOfServersForChannel(c,list,pfx,elist);
220 for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
222 TreeSocket* Sock = i->second->GetSocket();
223 if ((Sock) && (i->second->GetName() != omit) && (omitroute != i->second))
225 Sock->WriteLine(data);
233 unsigned int items =this->TreeRoot->ChildCount();
234 for (unsigned int x = 0; x < items; x++)
236 TreeServer* Route = this->TreeRoot->GetChild(x);
237 if ((Route) && (Route->GetSocket()) && (Route->GetName() != omit) && (omitroute != Route))
239 TreeSocket* Sock = Route->GetSocket();
241 Sock->WriteLine(data);
247 bool SpanningTreeUtilities::DoOneToAllButSender(const std::string &prefix, const std::string &command, std::deque<std::string> ¶ms, std::string omit)
249 TreeServer* omitroute = this->BestRouteTo(omit);
250 std::string FullLine = ":" + prefix + " " + command;
251 unsigned int words = params.size();
252 for (unsigned int x = 0; x < words; x++)
254 FullLine = FullLine + " " + params[x];
256 unsigned int items = this->TreeRoot->ChildCount();
257 for (unsigned int x = 0; x < items; x++)
259 TreeServer* Route = this->TreeRoot->GetChild(x);
261 // The route has a socket (its a direct connection)
262 // The route isnt the one to be omitted
263 // The route isnt the path to the one to be omitted
264 if ((Route) && (Route->GetSocket()) && (Route->GetName() != omit) && (omitroute != Route))
266 TreeSocket* Sock = Route->GetSocket();
268 Sock->WriteLine(FullLine);
274 bool SpanningTreeUtilities::DoOneToMany(const std::string &prefix, const std::string &command, std::deque<std::string> ¶ms)
276 std::string FullLine = ":" + prefix + " " + command;
277 unsigned int words = params.size();
278 for (unsigned int x = 0; x < words; x++)
280 FullLine = FullLine + " " + params[x];
282 unsigned int items = this->TreeRoot->ChildCount();
283 for (unsigned int x = 0; x < items; x++)
285 TreeServer* Route = this->TreeRoot->GetChild(x);
286 if (Route && Route->GetSocket())
288 TreeSocket* Sock = Route->GetSocket();
290 Sock->WriteLine(FullLine);
296 bool SpanningTreeUtilities::DoOneToMany(const char* prefix, const char* command, std::deque<std::string> ¶ms)
298 std::string spfx = prefix;
299 std::string scmd = command;
300 return this->DoOneToMany(spfx, scmd, params);
303 bool SpanningTreeUtilities::DoOneToAllButSender(const char* prefix, const char* command, std::deque<std::string> ¶ms, std::string omit)
305 std::string spfx = prefix;
306 std::string scmd = command;
307 return this->DoOneToAllButSender(spfx, scmd, params, omit);
310 bool SpanningTreeUtilities::DoOneToOne(const std::string &prefix, const std::string &command, std::deque<std::string> ¶ms, std::string target)
312 TreeServer* Route = this->BestRouteTo(target);
315 std::string FullLine = ":" + prefix + " " + command;
316 unsigned int words = params.size();
317 for (unsigned int x = 0; x < words; x++)
319 FullLine = FullLine + " " + params[x];
321 if (Route && Route->GetSocket())
323 TreeSocket* Sock = Route->GetSocket();
325 Sock->WriteLine(FullLine);
335 void SpanningTreeUtilities::ReadConfiguration(bool rebind)
337 ConfigReader* Conf = new ConfigReader(ServerInstance);
340 for (int j =0; j < Conf->Enumerate("bind"); j++)
342 std::string Type = Conf->ReadValue("bind","type",j);
343 std::string IP = Conf->ReadValue("bind","address",j);
344 std::string Port = Conf->ReadValue("bind","port",j);
345 std::string transport = Conf->ReadValue("bind","transport",j);
346 if (Type == "servers")
348 irc::portparser portrange(Port, false);
350 while ((portno = portrange.GetToken()))
355 if ((!transport.empty()) && (hooks.find(transport.c_str()) == hooks.end()))
357 ServerInstance->Log(DEFAULT,"m_spanningtree: WARNING: Can't find transport type '%s' for port %s:%s - maybe you forgot to load it BEFORE m_spanningtree in your config file? - Skipping this port binding", transport.c_str(), IP.c_str(), Port.c_str());
361 TreeSocket* listener = new TreeSocket(this, ServerInstance, IP.c_str(), portno, true, 10, transport.empty() ? NULL : hooks[transport.c_str()]);
362 if (listener->GetState() == I_LISTENING)
364 ServerInstance->Log(DEFAULT,"m_spanningtree: Binding server port %s:%d successful!", IP.c_str(), portno);
365 Bindings.push_back(listener);
369 ServerInstance->Log(DEFAULT,"m_spanningtree: Warning: Failed to bind server port: %s:%d: %s",IP.c_str(), portno, strerror(errno));
377 FlatLinks = Conf->ReadFlag("options","flatlinks",0);
378 HideULines = Conf->ReadFlag("options","hideulines",0);
379 AnnounceTSChange = Conf->ReadFlag("options","announcets",0);
380 EnableTimeSync = Conf->ReadFlag("options","timesync",0);
383 for (int j =0; j < Conf->Enumerate("link"); j++)
386 std::string Allow = Conf->ReadValue("link","allowmask",j);
387 L.Name = (Conf->ReadValue("link","name",j)).c_str();
388 L.IPAddr = Conf->ReadValue("link","ipaddr",j);
389 L.FailOver = Conf->ReadValue("link","failover",j).c_str();
390 L.Port = Conf->ReadInteger("link","port",j,true);
391 L.SendPass = Conf->ReadValue("link","sendpass",j);
392 L.RecvPass = Conf->ReadValue("link","recvpass",j);
393 L.AutoConnect = Conf->ReadInteger("link","autoconnect",j,true);
394 L.HiddenFromStats = Conf->ReadFlag("link","hidden",j);
395 L.Timeout = Conf->ReadInteger("link","timeout",j,true);
396 L.Hook = Conf->ReadValue("link", "transport", j);
397 L.Bind = Conf->ReadValue("link", "bind", j);
399 if ((!L.Hook.empty()) && (hooks.find(L.Hook.c_str()) == hooks.end()))
401 ServerInstance->Log(DEFAULT,"m_spanningtree: WARNING: Can't find transport type '%s' for link '%s' - maybe you forgot to load it BEFORE m_spanningtree in your config file? Skipping <link> tag completely.",
402 L.Hook.c_str(), L.Name.c_str());
407 L.NextConnectTime = time(NULL) + L.AutoConnect;
408 /* Bugfix by brain, do not allow people to enter bad configurations */
409 if (L.Name != ServerInstance->Config->ServerName)
411 if ((L.IPAddr != "") && (L.RecvPass != "") && (L.SendPass != "") && (L.Name != "") && (L.Port))
413 ValidIPs.push_back(L.IPAddr);
416 ValidIPs.push_back(Allow);
418 /* Needs resolving */
420 QueryType start_type = DNS_QUERY_A;
422 start_type = DNS_QUERY_AAAA;
423 if (strchr(L.IPAddr.c_str(),':'))
426 if (inet_pton(AF_INET6, L.IPAddr.c_str(), &n) < 1)
432 if (inet_aton(L.IPAddr.c_str(),&n) < 1)
437 if (inet_aton(L.IPAddr.c_str(),&n) < 1)
446 SecurityIPResolver* sr = new SecurityIPResolver((Module*)this->Creator, this, ServerInstance, L.IPAddr, L, cached, start_type);
447 ServerInstance->AddResolver(sr, cached);
449 catch (ModuleException& e)
454 LinkBlocks.push_back(L);
460 ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', IP address not defined!",L.Name.c_str());
462 else if (L.RecvPass == "")
464 ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', recvpass not defined!",L.Name.c_str());
466 else if (L.SendPass == "")
468 ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', sendpass not defined!",L.Name.c_str());
470 else if (L.Name == "")
472 ServerInstance->Log(DEFAULT,"Invalid configuration, link tag without a name!");
476 ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', no port specified!",L.Name.c_str());
482 ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', link tag has the same server name as the local server!",L.Name.c_str());
488 void SpanningTreeUtilities::DoFailOver(Link* x)
490 if (x->FailOver.length())
492 if (x->FailOver == x->Name)
494 ServerInstance->SNO->WriteToSnoMask('l',"FAILOVER: Some muppet configured the failover for server \002%s\002 to point at itself. Not following it!", x->Name.c_str());
497 Link* TryThisOne = this->FindLink(x->FailOver.c_str());
500 ServerInstance->SNO->WriteToSnoMask('l',"FAILOVER: Trying failover link for \002%s\002: \002%s\002...", x->Name.c_str(), TryThisOne->Name.c_str());
501 Creator->ConnectServer(TryThisOne);
505 ServerInstance->SNO->WriteToSnoMask('l',"FAILOVER: Invalid failover server specified for server \002%s\002, will not follow!", x->Name.c_str());
510 Link* SpanningTreeUtilities::FindLink(const std::string& name)
512 for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
514 if (ServerInstance->MatchText(x->Name.c_str(), name.c_str()))