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