]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_spanningtree/utils.cpp
25ea2190f8a2786343173d1153bac2c09fb3f5f3
[user/henk/code/inspircd.git] / src / modules / m_spanningtree / utils.cpp
1 #include "configreader.h"
2 #include "users.h"
3 #include "channels.h"
4 #include "modules.h"
5 #include "commands/cmd_whois.h"
6 #include "commands/cmd_stats.h"
7 #include "socket.h"
8 #include "inspircd.h"
9 #include "wildcard.h"
10 #include "xline.h"
11 #include "transport.h"
12 #include "socketengine.h"
13
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"
20
21 /** Yay for fast searches!
22  * This is hundreds of times faster than recursion
23  * or even scanning a linked list, especially when
24  * there are more than a few servers to deal with.
25  * (read as: lots).
26  */
27 TreeServer* SpanningTreeUtilities::FindServer(const std::string &ServerName)
28 {
29         server_hash::iterator iter;
30         iter = serverlist.find(ServerName.c_str());
31         if (iter != serverlist.end())
32         {
33                 return iter->second;
34         }
35         else
36         {
37                 return NULL;
38         }
39 }
40
41 /** Returns the locally connected server we must route a
42  * message through to reach server 'ServerName'. This
43  * only applies to one-to-one and not one-to-many routing.
44  * See the comments for the constructor of TreeServer
45  * for more details.
46  */
47 TreeServer* SpanningTreeUtilities::BestRouteTo(const std::string &ServerName)
48 {
49         if (ServerName.c_str() == TreeRoot->GetName())
50                 return NULL;
51         TreeServer* Found = FindServer(ServerName);
52         if (Found)
53         {
54                 return Found->GetRoute();
55         }
56         else
57         {
58                 return NULL;
59         }
60 }
61
62 /** Find the first server matching a given glob mask.
63  * Theres no find-using-glob method of hash_map [awwww :-(]
64  * so instead, we iterate over the list using an iterator
65  * and match each one until we get a hit. Yes its slow,
66  * deal with it.
67  */
68 TreeServer* SpanningTreeUtilities::FindServerMask(const std::string &ServerName)
69 {
70         for (server_hash::iterator i = serverlist.begin(); i != serverlist.end(); i++)
71         {
72                 if (match(i->first.c_str(),ServerName.c_str()))
73                         return i->second;
74         }
75         return NULL;
76 }
77
78 /* A convenient wrapper that returns true if a server exists */
79 bool SpanningTreeUtilities::IsServer(const std::string &ServerName)
80 {
81         return (FindServer(ServerName) != NULL);
82 }
83
84 SpanningTreeUtilities::SpanningTreeUtilities(InspIRCd* Instance, ModuleSpanningTree* C) : ServerInstance(Instance), Creator(C)
85 {
86         Bindings.clear();
87
88         lines_to_apply = 0;
89
90         this->TreeRoot = new TreeServer(this, ServerInstance, ServerInstance->Config->ServerName, ServerInstance->Config->ServerDesc);
91
92         modulelist* ml = ServerInstance->FindInterface("InspSocketHook");
93
94         /* Did we find any modules? */
95         if (ml)
96         {
97                 /* Yes, enumerate them all to find out the hook name */
98                 for (modulelist::iterator m = ml->begin(); m != ml->end(); m++)
99                 {
100                         /* Make a request to it for its name, its implementing
101                          * InspSocketHook so we know its safe to do this
102                          */
103                         std::string name = InspSocketNameRequest((Module*)Creator, *m).Send();
104                         /* Build a map of them */
105                         hooks[name.c_str()] = *m;
106                         hooknames.push_back(name);
107                 }
108         }
109
110         this->ReadConfiguration(true);
111 }
112
113 SpanningTreeUtilities::~SpanningTreeUtilities()
114 {
115         for (unsigned int i = 0; i < Bindings.size(); i++)
116         {
117                 ServerInstance->SE->DelFd(Bindings[i]);
118                 Bindings[i]->Close();
119                 DELETE(Bindings[i]);
120         }
121         while (TreeRoot->ChildCount())
122         {
123                 TreeServer* child_server = TreeRoot->GetChild(0);
124                 if (child_server)
125                 {
126                         TreeSocket* sock = child_server->GetSocket();
127                         ServerInstance->SE->DelFd(sock);
128                         sock->Close();
129                         DELETE(sock);
130                 }
131         }
132         delete TreeRoot;
133 }
134
135 void SpanningTreeUtilities::AddThisServer(TreeServer* server, TreeServerList &list)
136 {
137         if (list.find(server) == list.end())
138                 list[server] = server;
139 }
140
141 /* returns a list of DIRECT servernames for a specific channel */
142 void SpanningTreeUtilities::GetListOfServersForChannel(chanrec* c, TreeServerList &list, char status, const CUList &exempt_list)
143 {
144         CUList *ulist;
145         switch (status)
146         {
147                 case '@':
148                         ulist = c->GetOppedUsers();
149                 break;
150                 case '%':
151                         ulist = c->GetHalfoppedUsers();
152                 break;
153                 case '+':
154                         ulist = c->GetVoicedUsers();
155                 break;
156                 default:
157                         ulist = c->GetUsers();
158                 break;
159         }
160         for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
161         {
162                 if ((i->second->GetFd() < 0) && (exempt_list.find(i->second) == exempt_list.end()))
163                 {
164                         TreeServer* best = this->BestRouteTo(i->second->server);
165                         if (best)
166                                 AddThisServer(best,list);
167                 }
168         }
169         return;
170 }
171
172 bool SpanningTreeUtilities::DoOneToAllButSenderRaw(const std::string &data, const std::string &omit, const std::string &prefix, const irc::string &command, std::deque<std::string> &params)
173 {
174         char pfx = 0;
175         TreeServer* omitroute = this->BestRouteTo(omit);
176         if ((command == "NOTICE") || (command == "PRIVMSG"))
177         {
178                 if (params.size() >= 2)
179                 {
180                         /* Prefixes */
181                         if ((*(params[0].c_str()) == '@') || (*(params[0].c_str()) == '%') || (*(params[0].c_str()) == '+'))
182                         {
183                                 pfx = params[0][0];
184                                 params[0] = params[0].substr(1, params[0].length()-1);
185                         }
186                         if ((*(params[0].c_str()) != '#') && (*(params[0].c_str()) != '$'))
187                         {
188                                 // special routing for private messages/notices
189                                 userrec* d = ServerInstance->FindNick(params[0]);
190                                 if (d)
191                                 {
192                                         std::deque<std::string> par;
193                                         par.push_back(params[0]);
194                                         par.push_back(":"+params[1]);
195                                         this->DoOneToOne(prefix,command.c_str(),par,d->server);
196                                         return true;
197                                 }
198                         }
199                         else if (*(params[0].c_str()) == '$')
200                         {
201                                 std::deque<std::string> par;
202                                 par.push_back(params[0]);
203                                 par.push_back(":"+params[1]);
204                                 this->DoOneToAllButSender(prefix,command.c_str(),par,omitroute->GetName());
205                                 return true;
206                         }
207                         else
208                         {
209                                 chanrec* c = ServerInstance->FindChan(params[0]);
210                                 userrec* u = ServerInstance->FindNick(prefix);
211                                 if (c && u)
212                                 {
213                                         CUList elist;
214                                         TreeServerList list;
215                                         FOREACH_MOD(I_OnBuildExemptList, OnBuildExemptList((command == "PRIVMSG" ? MSG_PRIVMSG : MSG_NOTICE), c, u, pfx, elist));
216                                         GetListOfServersForChannel(c,list,pfx,elist);
217
218                                         for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
219                                         {
220                                                 TreeSocket* Sock = i->second->GetSocket();
221                                                 if ((Sock) && (i->second->GetName() != omit) && (omitroute != i->second))
222                                                 {
223                                                         Sock->WriteLine(data);
224                                                 }
225                                         }
226                                         return true;
227                                 }
228                         }
229                 }
230         }
231         unsigned int items =this->TreeRoot->ChildCount();
232         for (unsigned int x = 0; x < items; x++)
233         {
234                 TreeServer* Route = this->TreeRoot->GetChild(x);
235                 if ((Route) && (Route->GetSocket()) && (Route->GetName() != omit) && (omitroute != Route))
236                 {
237                         TreeSocket* Sock = Route->GetSocket();
238                         if (Sock)
239                                 Sock->WriteLine(data);
240                 }
241         }
242         return true;
243 }
244
245 bool SpanningTreeUtilities::DoOneToAllButSender(const std::string &prefix, const std::string &command, std::deque<std::string> &params, std::string omit)
246 {
247         TreeServer* omitroute = this->BestRouteTo(omit);
248         std::string FullLine = ":" + prefix + " " + command;
249         unsigned int words = params.size();
250         for (unsigned int x = 0; x < words; x++)
251         {
252                 FullLine = FullLine + " " + params[x];
253         }
254         unsigned int items = this->TreeRoot->ChildCount();
255         for (unsigned int x = 0; x < items; x++)
256         {
257                 TreeServer* Route = this->TreeRoot->GetChild(x);
258                 // Send the line IF:
259                 // The route has a socket (its a direct connection)
260                 // The route isnt the one to be omitted
261                 // The route isnt the path to the one to be omitted
262                 if ((Route) && (Route->GetSocket()) && (Route->GetName() != omit) && (omitroute != Route))
263                 {
264                         TreeSocket* Sock = Route->GetSocket();
265                         if (Sock)
266                                 Sock->WriteLine(FullLine);
267                 }
268         }
269         return true;
270 }
271
272 bool SpanningTreeUtilities::DoOneToMany(const std::string &prefix, const std::string &command, std::deque<std::string> &params)
273 {
274         std::string FullLine = ":" + prefix + " " + command;
275         unsigned int words = params.size();
276         for (unsigned int x = 0; x < words; x++)
277         {
278                 FullLine = FullLine + " " + params[x];
279         }
280         unsigned int items = this->TreeRoot->ChildCount();
281         for (unsigned int x = 0; x < items; x++)
282         {
283                 TreeServer* Route = this->TreeRoot->GetChild(x);
284                 if (Route && Route->GetSocket())
285                 {
286                         TreeSocket* Sock = Route->GetSocket();
287                         if (Sock)
288                                 Sock->WriteLine(FullLine);
289                 }
290         }
291         return true;
292 }
293
294 bool SpanningTreeUtilities::DoOneToMany(const char* prefix, const char* command, std::deque<std::string> &params)
295 {
296         std::string spfx = prefix;
297         std::string scmd = command;
298         return this->DoOneToMany(spfx, scmd, params);
299 }
300
301 bool SpanningTreeUtilities::DoOneToAllButSender(const char* prefix, const char* command, std::deque<std::string> &params, std::string omit)
302 {
303         std::string spfx = prefix;
304         std::string scmd = command;
305         return this->DoOneToAllButSender(spfx, scmd, params, omit);
306 }
307
308 bool SpanningTreeUtilities::DoOneToOne(const std::string &prefix, const std::string &command, std::deque<std::string> &params, std::string target)
309 {
310         TreeServer* Route = this->BestRouteTo(target);
311         if (Route)
312         {
313                 std::string FullLine = ":" + prefix + " " + command;
314                 unsigned int words = params.size();
315                 for (unsigned int x = 0; x < words; x++)
316                 {
317                         FullLine = FullLine + " " + params[x];
318                 }
319                 if (Route && Route->GetSocket())
320                 {
321                         TreeSocket* Sock = Route->GetSocket();
322                         if (Sock)
323                                 Sock->WriteLine(FullLine);
324                 }
325                 return true;
326         }
327         else
328         {
329                 return false;
330         }
331 }
332
333 void SpanningTreeUtilities::ReadConfiguration(bool rebind)
334 {
335         ConfigReader* Conf = new ConfigReader(ServerInstance);
336         if (rebind)
337         {
338                 for (int j =0; j < Conf->Enumerate("bind"); j++)
339                 {
340                         std::string Type = Conf->ReadValue("bind","type",j);
341                         std::string IP = Conf->ReadValue("bind","address",j);
342                         std::string Port = Conf->ReadValue("bind","port",j);
343                         std::string transport = Conf->ReadValue("bind","transport",j);
344                         if (Type == "servers")
345                         {
346                                 irc::portparser portrange(Port, false);
347                                 int portno = -1;
348                                 while ((portno = portrange.GetToken()))
349                                 {
350                                         if (IP == "*")
351                                                 IP = "";
352
353                                         if ((!transport.empty()) && (hooks.find(transport.c_str()) ==  hooks.end()))
354                                         {
355                                                 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());
356                                                 break;
357                                         }
358
359                                         TreeSocket* listener = new TreeSocket(this, ServerInstance, IP.c_str(), portno, true, 10, transport.empty() ? NULL : hooks[transport.c_str()]);
360                                         if (listener->GetState() == I_LISTENING)
361                                         {
362                                                 ServerInstance->Log(DEFAULT,"m_spanningtree: Binding server port %s:%d successful!", IP.c_str(), portno);
363                                                 Bindings.push_back(listener);
364                                         }
365                                         else
366                                         {
367                                                 ServerInstance->Log(DEFAULT,"m_spanningtree: Warning: Failed to bind server port %s:%d",IP.c_str(), portno);
368                                                 listener->Close();
369                                                 DELETE(listener);
370                                         }
371                                 }
372                         }
373                 }
374         }
375         FlatLinks = Conf->ReadFlag("options","flatlinks",0);
376         HideULines = Conf->ReadFlag("options","hideulines",0);
377         AnnounceTSChange = Conf->ReadFlag("options","announcets",0);
378         EnableTimeSync = !(Conf->ReadFlag("options","notimesync",0));
379         LinkBlocks.clear();
380         ValidIPs.clear();
381         for (int j =0; j < Conf->Enumerate("link"); j++)
382         {
383                 Link L;
384                 std::string Allow = Conf->ReadValue("link","allowmask",j);
385                 L.Name = (Conf->ReadValue("link","name",j)).c_str();
386                 L.IPAddr = Conf->ReadValue("link","ipaddr",j);
387                 L.FailOver = Conf->ReadValue("link","failover",j).c_str();
388                 L.Port = Conf->ReadInteger("link","port",j,true);
389                 L.SendPass = Conf->ReadValue("link","sendpass",j);
390                 L.RecvPass = Conf->ReadValue("link","recvpass",j);
391                 L.AutoConnect = Conf->ReadInteger("link","autoconnect",j,true);
392                 L.HiddenFromStats = Conf->ReadFlag("link","hidden",j);
393                 L.Timeout = Conf->ReadInteger("link","timeout",j,true);
394                 L.Hook = Conf->ReadValue("link", "transport", j);
395
396                 if ((!L.Hook.empty()) && (hooks.find(L.Hook.c_str()) ==  hooks.end()))
397                 {
398                         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.",
399                         L.Hook.c_str(), L.Name.c_str());
400                         continue;
401
402                 }
403
404                 L.NextConnectTime = time(NULL) + L.AutoConnect;
405                 /* Bugfix by brain, do not allow people to enter bad configurations */
406                 if (L.Name != ServerInstance->Config->ServerName)
407                 {
408                         if ((L.IPAddr != "") && (L.RecvPass != "") && (L.SendPass != "") && (L.Name != "") && (L.Port))
409                         {
410                                 ValidIPs.push_back(L.IPAddr);
411
412                                 if (Allow.length())
413                                         ValidIPs.push_back(Allow);
414
415                                 /* Needs resolving */
416                                 insp_inaddr binip;
417                                 if (insp_aton(L.IPAddr.c_str(), &binip) < 1)
418                                 {
419                                         try
420                                         {
421                                                 bool cached;
422                                                 SecurityIPResolver* sr = new SecurityIPResolver((Module*)this->Creator, this, ServerInstance, L.IPAddr, L, cached);
423                                                 ServerInstance->AddResolver(sr, cached);
424                                         }
425                                         catch (ModuleException& e)
426                                         {
427                                         }
428                                 }
429
430                                 LinkBlocks.push_back(L);
431                         }
432                         else
433                         {
434                                 if (L.IPAddr == "")
435                                 {
436                                         ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', IP address not defined!",L.Name.c_str());
437                                 }
438                                 else if (L.RecvPass == "")
439                                 {
440                                         ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', recvpass not defined!",L.Name.c_str());
441                                 }
442                                 else if (L.SendPass == "")
443                                 {
444                                         ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', sendpass not defined!",L.Name.c_str());
445                                 }
446                                 else if (L.Name == "")
447                                 {
448                                         ServerInstance->Log(DEFAULT,"Invalid configuration, link tag without a name!");
449                                 }
450                                 else if (!L.Port)
451                                 {
452                                         ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', no port specified!",L.Name.c_str());
453                                 }
454                         }
455                 }
456                 else
457                 {
458                         ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', link tag has the same server name as the local server!",L.Name.c_str());
459                 }
460         }
461         DELETE(Conf);
462 }
463
464 void SpanningTreeUtilities::DoFailOver(Link* x)
465 {
466         if (x->FailOver.length())
467         {
468                 if (x->FailOver == x->Name)
469                 {
470                         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());
471                         return;
472                 }
473                 Link* TryThisOne = this->FindLink(x->FailOver.c_str());
474                 if (TryThisOne)
475                 {
476                         ServerInstance->SNO->WriteToSnoMask('l',"FAILOVER: Trying failover link for \002%s\002: \002%s\002...", x->Name.c_str(), TryThisOne->Name.c_str());
477                         Creator->ConnectServer(TryThisOne);
478                 }
479                 else
480                 {
481                         ServerInstance->SNO->WriteToSnoMask('l',"FAILOVER: Invalid failover server specified for server \002%s\002, will not follow!", x->Name.c_str());
482                 }
483         }
484 }
485
486 Link* SpanningTreeUtilities::FindLink(const std::string& name)
487 {
488         for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
489         {
490                 if (ServerInstance->MatchText(x->Name.c_str(), name.c_str()))
491                 {
492                         return &(*x);
493                 }
494         }
495         return NULL;
496 }
497