]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_spanningtree/utils.cpp
Just comments
[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: %s",IP.c_str(), portno, strerror(errno));
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","timesync",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                 L.Bind = Conf->ReadValue("link", "bind", j);
398
399                 if ((!L.Hook.empty()) && (hooks.find(L.Hook.c_str()) ==  hooks.end()))
400                 {
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());
403                         continue;
404
405                 }
406
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)
410                 {
411                         if ((L.IPAddr != "") && (L.RecvPass != "") && (L.SendPass != "") && (L.Name != "") && (L.Port))
412                         {
413                                 ValidIPs.push_back(L.IPAddr);
414
415                                 if (Allow.length())
416                                         ValidIPs.push_back(Allow);
417
418                                 /* Needs resolving */
419                                 bool ipvalid = true;
420                                 QueryType start_type = DNS_QUERY_A;
421 #ifdef IPV6
422                                 start_type = DNS_QUERY_AAAA;
423                                 if (strchr(L.IPAddr.c_str(),':'))
424                                 {
425                                         in6_addr n;
426                                         if (inet_pton(AF_INET6, L.IPAddr.c_str(), &n) < 1)
427                                                 ipvalid = false;
428                                 }
429                                 else
430                                 {
431                                         in_addr n;
432                                         if (inet_aton(L.IPAddr.c_str(),&n) < 1)
433                                                 ipvalid = false;
434                                 }
435 #else
436                                 in_addr n;
437                                 if (inet_aton(L.IPAddr.c_str(),&n) < 1)
438                                         ipvalid = false;
439 #endif
440
441                                 if (!ipvalid)
442                                 {
443                                         try
444                                         {
445                                                 bool cached;
446                                                 SecurityIPResolver* sr = new SecurityIPResolver((Module*)this->Creator, this, ServerInstance, L.IPAddr, L, cached, start_type);
447                                                 ServerInstance->AddResolver(sr, cached);
448                                         }
449                                         catch (ModuleException& e)
450                                         {
451                                         }
452                                 }
453
454                                 LinkBlocks.push_back(L);
455                         }
456                         else
457                         {
458                                 if (L.IPAddr == "")
459                                 {
460                                         ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', IP address not defined!",L.Name.c_str());
461                                 }
462                                 else if (L.RecvPass == "")
463                                 {
464                                         ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', recvpass not defined!",L.Name.c_str());
465                                 }
466                                 else if (L.SendPass == "")
467                                 {
468                                         ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', sendpass not defined!",L.Name.c_str());
469                                 }
470                                 else if (L.Name == "")
471                                 {
472                                         ServerInstance->Log(DEFAULT,"Invalid configuration, link tag without a name!");
473                                 }
474                                 else if (!L.Port)
475                                 {
476                                         ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', no port specified!",L.Name.c_str());
477                                 }
478                         }
479                 }
480                 else
481                 {
482                         ServerInstance->Log(DEFAULT,"Invalid configuration for server '%s', link tag has the same server name as the local server!",L.Name.c_str());
483                 }
484         }
485         DELETE(Conf);
486 }
487
488 void SpanningTreeUtilities::DoFailOver(Link* x)
489 {
490         if (x->FailOver.length())
491         {
492                 if (x->FailOver == x->Name)
493                 {
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());
495                         return;
496                 }
497                 Link* TryThisOne = this->FindLink(x->FailOver.c_str());
498                 if (TryThisOne)
499                 {
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);
502                 }
503                 else
504                 {
505                         ServerInstance->SNO->WriteToSnoMask('l',"FAILOVER: Invalid failover server specified for server \002%s\002, will not follow!", x->Name.c_str());
506                 }
507         }
508 }
509
510 Link* SpanningTreeUtilities::FindLink(const std::string& name)
511 {
512         for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
513         {
514                 if (ServerInstance->MatchText(x->Name.c_str(), name.c_str()))
515                 {
516                         return &(*x);
517                 }
518         }
519         return NULL;
520 }
521