]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_spanningtree/utils.cpp
3b70fce312fbe0b7abb92bb406f54d36608cf04d
[user/henk/code/inspircd.git] / src / modules / m_spanningtree / utils.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2009 InspIRCd Development Team
6  * See: http://wiki.inspircd.org/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #include "inspircd.h"
15 #include "socket.h"
16 #include "xline.h"
17 #include "socketengine.h"
18
19 #include "main.h"
20 #include "utils.h"
21 #include "treeserver.h"
22 #include "link.h"
23 #include "treesocket.h"
24 #include "resolvers.h"
25
26 /* $ModDep: m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h */
27
28 /* Create server sockets off a listener. */
29 void ServerSocketListener::OnAcceptReady(int newsock)
30 {
31         bool found = false;
32         int port;
33         std::string incomingip;
34         irc::sockets::satoap(&client, incomingip, port);
35
36         found = (std::find(Utils->ValidIPs.begin(), Utils->ValidIPs.end(), incomingip) != Utils->ValidIPs.end());
37         if (!found)
38         {
39                 for (std::vector<std::string>::iterator i = Utils->ValidIPs.begin(); i != Utils->ValidIPs.end(); i++)
40                 {
41                         if (*i == "*" || irc::sockets::MatchCIDR(incomingip, *i))
42                         {
43                                 found = true;
44                                 break;
45                         }
46                 }
47
48                 if (!found)
49                 {
50                         ServerInstance->SNO->WriteToSnoMask('l', "Server connection from %s denied (no link blocks with that IP address)", incomingip.c_str());
51                         ServerInstance->SE->Close(newsock);
52                         return;
53                 }
54         }
55
56         /* we don't need to do anything with the pointer, creating it stores it in the necessary places */
57
58         new TreeSocket(Utils, newsock, this, &client, &server);
59 }
60
61 /** Yay for fast searches!
62  * This is hundreds of times faster than recursion
63  * or even scanning a linked list, especially when
64  * there are more than a few servers to deal with.
65  * (read as: lots).
66  */
67 TreeServer* SpanningTreeUtilities::FindServer(const std::string &ServerName)
68 {
69         if (ServerInstance->IsSID(ServerName))
70                 return this->FindServerID(ServerName);
71
72         server_hash::iterator iter = serverlist.find(ServerName.c_str());
73         if (iter != serverlist.end())
74         {
75                 return iter->second;
76         }
77         else
78         {
79                 return NULL;
80         }
81 }
82
83 /** Returns the locally connected server we must route a
84  * message through to reach server 'ServerName'. This
85  * only applies to one-to-one and not one-to-many routing.
86  * See the comments for the constructor of TreeServer
87  * for more details.
88  */
89 TreeServer* SpanningTreeUtilities::BestRouteTo(const std::string &ServerName)
90 {
91         if (ServerName.c_str() == TreeRoot->GetName() || ServerName == ServerInstance->Config->GetSID())
92                 return NULL;
93         TreeServer* Found = FindServer(ServerName);
94         if (Found)
95         {
96                 return Found->GetRoute();
97         }
98         else
99         {
100                 // Cheat a bit. This allows for (better) working versions of routing commands with nick based prefixes, without hassle
101                 User *u = ServerInstance->FindNick(ServerName);
102                 if (u)
103                 {
104                         Found = FindServer(u->server);
105                         if (Found)
106                                 return Found->GetRoute();
107                 }
108
109                 return NULL;
110         }
111 }
112
113 /** Find the first server matching a given glob mask.
114  * Theres no find-using-glob method of hash_map [awwww :-(]
115  * so instead, we iterate over the list using an iterator
116  * and match each one until we get a hit. Yes its slow,
117  * deal with it.
118  */
119 TreeServer* SpanningTreeUtilities::FindServerMask(const std::string &ServerName)
120 {
121         for (server_hash::iterator i = serverlist.begin(); i != serverlist.end(); i++)
122         {
123                 if (InspIRCd::Match(i->first,ServerName))
124                         return i->second;
125         }
126         return NULL;
127 }
128
129 TreeServer* SpanningTreeUtilities::FindServerID(const std::string &id)
130 {
131         server_hash::iterator iter = sidlist.find(id);
132         if (iter != sidlist.end())
133                 return iter->second;
134         else
135                 return NULL;
136 }
137
138 /* A convenient wrapper that returns true if a server exists */
139 bool SpanningTreeUtilities::IsServer(const std::string &ServerName)
140 {
141         return (FindServer(ServerName) != NULL);
142 }
143
144 SpanningTreeUtilities::SpanningTreeUtilities(ModuleSpanningTree* C) : Creator(C)
145 {
146         ServerInstance->Logs->Log("m_spanningtree",DEBUG,"***** Using SID for hash: %s *****", ServerInstance->Config->GetSID().c_str());
147
148         this->TreeRoot = new TreeServer(this, ServerInstance->Config->ServerName, ServerInstance->Config->ServerDesc, ServerInstance->Config->GetSID());
149         ServerUser = new FakeUser(TreeRoot->GetID());
150
151         this->ReadConfiguration(true);
152 }
153
154 bool SpanningTreeUtilities::cull()
155 {
156         for (unsigned int i = 0; i < Bindings.size(); i++)
157         {
158                 Bindings[i]->cull();
159         }
160
161         while (TreeRoot->ChildCount())
162         {
163                 TreeServer* child_server = TreeRoot->GetChild(0);
164                 if (child_server)
165                 {
166                         TreeSocket* sock = child_server->GetSocket();
167                         sock->Close();
168                         ServerInstance->GlobalCulls.AddItem(sock);
169                 }
170         }
171
172         ServerUser->uuid = TreeRoot->GetID();
173         if (ServerUser->cull())
174                 delete ServerUser;
175         return true;
176 }
177
178 SpanningTreeUtilities::~SpanningTreeUtilities()
179 {
180         for (unsigned int i = 0; i < Bindings.size(); i++)
181         {
182                 delete Bindings[i];
183         }
184
185         delete TreeRoot;
186 }
187
188 void SpanningTreeUtilities::AddThisServer(TreeServer* server, TreeServerList &list)
189 {
190         if (list.find(server) == list.end())
191                 list[server] = server;
192 }
193
194 /* returns a list of DIRECT servernames for a specific channel */
195 void SpanningTreeUtilities::GetListOfServersForChannel(Channel* c, TreeServerList &list, char status, const CUList &exempt_list)
196 {
197         const UserMembList *ulist = c->GetUsers();
198
199         for (UserMembCIter i = ulist->begin(); i != ulist->end(); i++)
200         {
201                 if (IS_LOCAL(i->first))
202                         continue;
203
204                 if (status && !strchr(c->GetAllPrefixChars(i->first), status))
205                         continue;
206
207                 if (exempt_list.find(i->first) == exempt_list.end())
208                 {
209                         TreeServer* best = this->BestRouteTo(i->first->server);
210                         if (best)
211                                 AddThisServer(best,list);
212                 }
213         }
214         return;
215 }
216
217 bool SpanningTreeUtilities::DoOneToAllButSenderRaw(const std::string &data, const std::string &omit, const std::string &prefix, const irc::string &command, parameterlist &params)
218 {
219         TreeServer* omitroute = this->BestRouteTo(omit);
220         unsigned int items =this->TreeRoot->ChildCount();
221         for (unsigned int x = 0; x < items; x++)
222         {
223                 TreeServer* Route = this->TreeRoot->GetChild(x);
224                 if ((Route) && (Route->GetSocket()) && (Route->GetName() != omit) && (omitroute != Route))
225                 {
226                         TreeSocket* Sock = Route->GetSocket();
227                         if (Sock)
228                                 Sock->WriteLine(data);
229                 }
230         }
231         return true;
232 }
233
234 bool SpanningTreeUtilities::DoOneToAllButSender(const std::string &prefix, const std::string &command, parameterlist &params, std::string omit)
235 {
236         TreeServer* omitroute = this->BestRouteTo(omit);
237         std::string FullLine = ":" + prefix + " " + command;
238         unsigned int words = params.size();
239         for (unsigned int x = 0; x < words; x++)
240         {
241                 FullLine = FullLine + " " + params[x];
242         }
243         unsigned int items = this->TreeRoot->ChildCount();
244         for (unsigned int x = 0; x < items; x++)
245         {
246                 TreeServer* Route = this->TreeRoot->GetChild(x);
247                 // Send the line IF:
248                 // The route has a socket (its a direct connection)
249                 // The route isnt the one to be omitted
250                 // The route isnt the path to the one to be omitted
251                 if ((Route) && (Route->GetSocket()) && (Route->GetName() != omit) && (omitroute != Route))
252                 {
253                         TreeSocket* Sock = Route->GetSocket();
254                         if (Sock)
255                                 Sock->WriteLine(FullLine);
256                 }
257         }
258         return true;
259 }
260
261 bool SpanningTreeUtilities::DoOneToMany(const std::string &prefix, const std::string &command, parameterlist &params)
262 {
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                 if (Route && Route->GetSocket())
274                 {
275                         TreeSocket* Sock = Route->GetSocket();
276                         if (Sock)
277                                 Sock->WriteLine(FullLine);
278                 }
279         }
280         return true;
281 }
282
283 bool SpanningTreeUtilities::DoOneToMany(const char* prefix, const char* command, parameterlist &params)
284 {
285         std::string spfx = prefix;
286         std::string scmd = command;
287         return this->DoOneToMany(spfx, scmd, params);
288 }
289
290 bool SpanningTreeUtilities::DoOneToAllButSender(const char* prefix, const char* command, parameterlist &params, std::string omit)
291 {
292         std::string spfx = prefix;
293         std::string scmd = command;
294         return this->DoOneToAllButSender(spfx, scmd, params, omit);
295 }
296
297 bool SpanningTreeUtilities::DoOneToOne(const std::string &prefix, const std::string &command, parameterlist &params, std::string target)
298 {
299         TreeServer* Route = this->BestRouteTo(target);
300         if (Route)
301         {
302                 std::string FullLine = ":" + prefix + " " + command;
303                 unsigned int words = params.size();
304                 for (unsigned int x = 0; x < words; x++)
305                 {
306                         FullLine = FullLine + " " + params[x];
307                 }
308                 if (Route && Route->GetSocket())
309                 {
310                         TreeSocket* Sock = Route->GetSocket();
311                         if (Sock)
312                                 Sock->WriteLine(FullLine);
313                 }
314                 return true;
315         }
316         else
317         {
318                 return false;
319         }
320 }
321
322 void SpanningTreeUtilities::RefreshIPCache()
323 {
324         ValidIPs.clear();
325         for (std::vector<reference<Link> >::iterator i = LinkBlocks.begin(); i != LinkBlocks.end(); ++i)
326         {
327                 Link* L = *i;
328                 if (L->IPAddr.empty() || L->RecvPass.empty() || L->SendPass.empty() || L->Name.empty() || !L->Port)
329                 {
330                         if (L->Name.empty())
331                         {
332                                 ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"m_spanningtree: Ignoring a malformed link block (all link blocks require a name!)");
333                         }
334                         else
335                         {
336                                 ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"m_spanningtree: Ignoring a link block missing recvpass, sendpass, port or ipaddr.");
337                         }
338
339                         /* Invalid link block */
340                         continue;
341                 }
342
343                 ValidIPs.push_back(L->IPAddr);
344
345                 if (L->AllowMask.length())
346                         ValidIPs.push_back(L->AllowMask);
347
348                 /* Needs resolving */
349                 bool ipvalid = true;
350                 QueryType start_type = DNS_QUERY_A;
351                 start_type = DNS_QUERY_AAAA;
352                 if (strchr(L->IPAddr.c_str(),':'))
353                 {
354                         in6_addr n;
355                         if (inet_pton(AF_INET6, L->IPAddr.c_str(), &n) < 1)
356                                 ipvalid = false;
357                 }
358                 else
359                 {
360                         in_addr n;
361                         if (inet_aton(L->IPAddr.c_str(),&n) < 1)
362                                 ipvalid = false;
363                 }
364
365                 if (!ipvalid)
366                 {
367                         try
368                         {
369                                 bool cached;
370                                 SecurityIPResolver* sr = new SecurityIPResolver(Creator, this, L->IPAddr, L, cached, start_type);
371                                 ServerInstance->AddResolver(sr, cached);
372                         }
373                         catch (...)
374                         {
375                         }
376                 }
377         }
378 }
379
380 void SpanningTreeUtilities::ReadConfiguration(bool rebind)
381 {
382         ConfigReader* Conf = new ConfigReader;
383
384         if (rebind)
385         {
386                 for (unsigned int i = 0; i < Bindings.size(); i++)
387                 {
388                         delete Bindings[i];
389                 }
390                 Bindings.clear();
391
392                 for (int j = 0; j < Conf->Enumerate("bind"); j++)
393                 {
394                         std::string Type = Conf->ReadValue("bind","type",j);
395                         std::string IP = Conf->ReadValue("bind","address",j);
396                         std::string Port = Conf->ReadValue("bind","port",j);
397                         std::string ssl = Conf->ReadValue("bind","ssl",j);
398                         if (Type == "servers")
399                         {
400                                 irc::portparser portrange(Port, false);
401                                 int portno = -1;
402
403                                 if (IP == "*")
404                                         IP.clear();
405
406                                 while ((portno = portrange.GetToken()))
407                                 {
408                                         ServerSocketListener *listener = new ServerSocketListener(this, portno, IP, ssl);
409                                         if (listener->GetFd() == -1)
410                                         {
411                                                 delete listener;
412                                                 continue;
413                                         }
414
415                                         Bindings.push_back(listener);
416                                 }
417                         }
418                 }
419         }
420         FlatLinks = Conf->ReadFlag("security","flatlinks",0);
421         HideULines = Conf->ReadFlag("security","hideulines",0);
422         AnnounceTSChange = Conf->ReadFlag("options","announcets",0);
423         AllowOptCommon = Conf->ReadFlag("options", "allowmismatch", 0);
424         ChallengeResponse = !Conf->ReadFlag("security", "disablehmac", 0);
425         quiet_bursts = Conf->ReadFlag("performance", "quietbursts", 0);
426         PingWarnTime = Conf->ReadInteger("options", "pingwarning", 0, true);
427         PingFreq = Conf->ReadInteger("options", "serverpingfreq", 0, true);
428
429         if (PingFreq == 0)
430                 PingFreq = 60;
431
432         if (PingWarnTime < 0 || PingWarnTime > PingFreq - 1)
433                 PingWarnTime = 0;
434
435         AutoconnectBlocks.clear();
436         LinkBlocks.clear();
437         ValidIPs.clear();
438         for (int j = 0; j < Conf->Enumerate("link"); ++j)
439         {
440                 reference<Link> L = new Link;
441                 std::string Allow = Conf->ReadValue("link", "allowmask", j);
442                 L->Name = (Conf->ReadValue("link", "name", j)).c_str();
443                 L->AllowMask = Allow;
444                 L->IPAddr = Conf->ReadValue("link", "ipaddr", j);
445                 L->Port = Conf->ReadInteger("link", "port", j, true);
446                 L->SendPass = Conf->ReadValue("link", "sendpass", j);
447                 L->RecvPass = Conf->ReadValue("link", "recvpass", j);
448                 L->Fingerprint = Conf->ReadValue("link", "fingerprint", j);
449                 L->HiddenFromStats = Conf->ReadFlag("link", "statshidden", j);
450                 L->Timeout = Conf->ReadInteger("link", "timeout", j, true);
451                 L->Hook = Conf->ReadValue("link", "transport", j);
452                 L->Bind = Conf->ReadValue("link", "bind", j);
453                 L->Hidden = Conf->ReadFlag("link", "hidden", j);
454
455                 if (L->Name.find('.') == std::string::npos)
456                         throw CoreException("The link name '"+assign(L->Name)+"' is invalid and must contain at least one '.' character");
457
458                 if (L->Name.length() > 64)
459                         throw CoreException("The link name '"+assign(L->Name)+"' is longer than 64 characters!");
460
461                 if ((!L->IPAddr.empty()) && (!L->RecvPass.empty()) && (!L->SendPass.empty()) && (!L->Name.empty()) && (L->Port))
462                 {
463                         if (Allow.length())
464                                 ValidIPs.push_back(Allow);
465
466                         ValidIPs.push_back(L->IPAddr);
467
468                         /* Needs resolving */
469                         bool ipvalid = true;
470                         QueryType start_type = DNS_QUERY_A;
471                         start_type = DNS_QUERY_AAAA;
472                         if (strchr(L->IPAddr.c_str(),':'))
473                         {
474                                 in6_addr n;
475                                 if (inet_pton(AF_INET6, L->IPAddr.c_str(), &n) < 1)
476                                         ipvalid = false;
477                         }
478                         else
479                         {
480                                 in_addr n;
481                                 if (inet_aton(L->IPAddr.c_str(),&n) < 1)
482                                         ipvalid = false;
483                         }
484
485                         if (!ipvalid)
486                         {
487                                 try
488                                 {
489                                         bool cached;
490                                         SecurityIPResolver* sr = new SecurityIPResolver(Creator, this, L->IPAddr, L, cached, start_type);
491                                         ServerInstance->AddResolver(sr, cached);
492                                 }
493                                 catch (...)
494                                 {
495                                 }
496                         }
497                 }
498                 else
499                 {
500                         if (L->IPAddr.empty())
501                         {
502                                 L->IPAddr = "*";
503                                 ValidIPs.push_back("*");
504                                 ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"Configuration warning: Link block " + assign(L->Name) + " has no IP defined! This will allow any IP to connect as this server, and MAY not be what you want.");
505                         }
506
507                         if (L->RecvPass.empty())
508                         {
509                                 throw CoreException("Invalid configuration for server '"+assign(L->Name)+"', recvpass not defined!");
510                         }
511
512                         if (L->SendPass.empty())
513                         {
514                                 throw CoreException("Invalid configuration for server '"+assign(L->Name)+"', sendpass not defined!");
515                         }
516
517                         if (L->Name.empty())
518                         {
519                                 throw CoreException("Invalid configuration, link tag without a name! IP address: "+L->IPAddr);
520                         }
521
522                         if (!L->Port)
523                         {
524                                 ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"Configuration warning: Link block " + assign(L->Name) + " has no port defined, you will not be able to /connect it.");
525                         }
526                 }
527
528                 LinkBlocks.push_back(L);
529         }
530
531         for (int j = 0; j < Conf->Enumerate("autoconnect"); ++j)
532         {
533                 reference<Autoconnect> A = new Autoconnect;
534                 A->Period = Conf->ReadInteger("autoconnect", "period", j, true);
535                 A->NextConnectTime = ServerInstance->Time() + A->Period;
536                 A->position = -1;
537                 std::string servers = Conf->ReadValue("autoconnect", "server", j);
538                 irc::spacesepstream ss(servers);
539                 std::string server;
540                 while (ss.GetToken(server))
541                 {
542                         A->servers.push_back(server);
543                 }
544
545                 if (A->Period <= 0)
546                 {
547                         throw CoreException("Invalid configuration for autoconnect, period not a positive integer!");
548                 }
549
550                 if (A->servers.empty())
551                 {
552                         throw CoreException("Invalid configuration for autoconnect, server cannot be empty!");
553                 }
554
555                 AutoconnectBlocks.push_back(A);
556         }
557
558         delete Conf;
559 }
560
561 Link* SpanningTreeUtilities::FindLink(const std::string& name)
562 {
563         for (std::vector<reference<Link> >::iterator i = LinkBlocks.begin(); i != LinkBlocks.end(); ++i)
564         {
565                 Link* x = *i;
566                 if (InspIRCd::Match(x->Name.c_str(), name.c_str()))
567                 {
568                         return x;
569                 }
570         }
571         return NULL;
572 }