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