]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_spanningtree/utils.cpp
Remove VF_SERVICEPROVIDER, prevent heap allocation of ConfigReader
[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 CullResult SpanningTreeUtilities::cull()
155 {
156         for (unsigned int i = 0; i < ServerInstance->ports.size(); i++)
157         {
158                 if (ServerInstance->ports[i]->type == "servers")
159                         ServerInstance->ports[i]->cull();
160         }
161
162         while (TreeRoot->ChildCount())
163         {
164                 TreeServer* child_server = TreeRoot->GetChild(0);
165                 if (child_server)
166                 {
167                         TreeSocket* sock = child_server->GetSocket();
168                         sock->Close();
169                         ServerInstance->GlobalCulls.AddItem(sock);
170                 }
171         }
172
173         ServerUser->uuid = TreeRoot->GetID();
174         ServerUser->cull();
175         delete ServerUser;
176         return classbase::cull();
177 }
178
179 SpanningTreeUtilities::~SpanningTreeUtilities()
180 {
181         for (unsigned int i = 0; i < ServerInstance->ports.size(); i++)
182         {
183                 if (ServerInstance->ports[i]->type == "servers")
184                         delete ServerInstance->ports[i];
185         }
186
187         delete TreeRoot;
188 }
189
190 void SpanningTreeUtilities::AddThisServer(TreeServer* server, TreeServerList &list)
191 {
192         if (list.find(server) == list.end())
193                 list[server] = server;
194 }
195
196 /* returns a list of DIRECT servernames for a specific channel */
197 void SpanningTreeUtilities::GetListOfServersForChannel(Channel* c, TreeServerList &list, char status, const CUList &exempt_list)
198 {
199         const UserMembList *ulist = c->GetUsers();
200
201         for (UserMembCIter i = ulist->begin(); i != ulist->end(); i++)
202         {
203                 if (IS_LOCAL(i->first))
204                         continue;
205
206                 if (status && !strchr(c->GetAllPrefixChars(i->first), status))
207                         continue;
208
209                 if (exempt_list.find(i->first) == exempt_list.end())
210                 {
211                         TreeServer* best = this->BestRouteTo(i->first->server);
212                         if (best)
213                                 AddThisServer(best,list);
214                 }
215         }
216         return;
217 }
218
219 bool SpanningTreeUtilities::DoOneToAllButSenderRaw(const std::string &data, const std::string &omit, const std::string &prefix, const irc::string &command, parameterlist &params)
220 {
221         TreeServer* omitroute = this->BestRouteTo(omit);
222         unsigned int items =this->TreeRoot->ChildCount();
223         for (unsigned int x = 0; x < items; x++)
224         {
225                 TreeServer* Route = this->TreeRoot->GetChild(x);
226                 if ((Route) && (Route->GetSocket()) && (Route->GetName() != omit) && (omitroute != Route))
227                 {
228                         TreeSocket* Sock = Route->GetSocket();
229                         if (Sock)
230                                 Sock->WriteLine(data);
231                 }
232         }
233         return true;
234 }
235
236 bool SpanningTreeUtilities::DoOneToAllButSender(const std::string &prefix, const std::string &command, parameterlist &params, std::string omit)
237 {
238         TreeServer* omitroute = this->BestRouteTo(omit);
239         std::string FullLine = ":" + prefix + " " + command;
240         unsigned int words = params.size();
241         for (unsigned int x = 0; x < words; x++)
242         {
243                 FullLine = FullLine + " " + params[x];
244         }
245         unsigned int items = this->TreeRoot->ChildCount();
246         for (unsigned int x = 0; x < items; x++)
247         {
248                 TreeServer* Route = this->TreeRoot->GetChild(x);
249                 // Send the line IF:
250                 // The route has a socket (its a direct connection)
251                 // The route isnt the one to be omitted
252                 // The route isnt the path to the one to be omitted
253                 if ((Route) && (Route->GetSocket()) && (Route->GetName() != omit) && (omitroute != Route))
254                 {
255                         TreeSocket* Sock = Route->GetSocket();
256                         if (Sock)
257                                 Sock->WriteLine(FullLine);
258                 }
259         }
260         return true;
261 }
262
263 bool SpanningTreeUtilities::DoOneToMany(const std::string &prefix, const std::string &command, parameterlist &params)
264 {
265         std::string FullLine = ":" + prefix + " " + command;
266         unsigned int words = params.size();
267         for (unsigned int x = 0; x < words; x++)
268         {
269                 FullLine = FullLine + " " + params[x];
270         }
271         unsigned int items = this->TreeRoot->ChildCount();
272         for (unsigned int x = 0; x < items; x++)
273         {
274                 TreeServer* Route = this->TreeRoot->GetChild(x);
275                 if (Route && Route->GetSocket())
276                 {
277                         TreeSocket* Sock = Route->GetSocket();
278                         if (Sock)
279                                 Sock->WriteLine(FullLine);
280                 }
281         }
282         return true;
283 }
284
285 bool SpanningTreeUtilities::DoOneToMany(const char* prefix, const char* command, parameterlist &params)
286 {
287         std::string spfx = prefix;
288         std::string scmd = command;
289         return this->DoOneToMany(spfx, scmd, params);
290 }
291
292 bool SpanningTreeUtilities::DoOneToAllButSender(const char* prefix, const char* command, parameterlist &params, std::string omit)
293 {
294         std::string spfx = prefix;
295         std::string scmd = command;
296         return this->DoOneToAllButSender(spfx, scmd, params, omit);
297 }
298
299 bool SpanningTreeUtilities::DoOneToOne(const std::string &prefix, const std::string &command, parameterlist &params, std::string target)
300 {
301         TreeServer* Route = this->BestRouteTo(target);
302         if (Route)
303         {
304                 std::string FullLine = ":" + prefix + " " + command;
305                 unsigned int words = params.size();
306                 for (unsigned int x = 0; x < words; x++)
307                 {
308                         FullLine = FullLine + " " + params[x];
309                 }
310                 if (Route && Route->GetSocket())
311                 {
312                         TreeSocket* Sock = Route->GetSocket();
313                         if (Sock)
314                                 Sock->WriteLine(FullLine);
315                 }
316                 return true;
317         }
318         else
319         {
320                 return false;
321         }
322 }
323
324 void SpanningTreeUtilities::RefreshIPCache()
325 {
326         ValidIPs.clear();
327         for (std::vector<reference<Link> >::iterator i = LinkBlocks.begin(); i != LinkBlocks.end(); ++i)
328         {
329                 Link* L = *i;
330                 if (L->IPAddr.empty() || L->RecvPass.empty() || L->SendPass.empty() || L->Name.empty() || !L->Port)
331                 {
332                         if (L->Name.empty())
333                         {
334                                 ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"m_spanningtree: Ignoring a malformed link block (all link blocks require a name!)");
335                         }
336                         else
337                         {
338                                 ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"m_spanningtree: Ignoring a link block missing recvpass, sendpass, port or ipaddr.");
339                         }
340
341                         /* Invalid link block */
342                         continue;
343                 }
344
345                 ValidIPs.push_back(L->IPAddr);
346
347                 if (L->AllowMask.length())
348                         ValidIPs.push_back(L->AllowMask);
349
350                 /* Needs resolving */
351                 bool ipvalid = true;
352                 QueryType start_type = DNS_QUERY_A;
353                 start_type = DNS_QUERY_AAAA;
354                 if (strchr(L->IPAddr.c_str(),':'))
355                 {
356                         in6_addr n;
357                         if (inet_pton(AF_INET6, L->IPAddr.c_str(), &n) < 1)
358                                 ipvalid = false;
359                 }
360                 else
361                 {
362                         in_addr n;
363                         if (inet_aton(L->IPAddr.c_str(),&n) < 1)
364                                 ipvalid = false;
365                 }
366
367                 if (!ipvalid)
368                 {
369                         try
370                         {
371                                 bool cached;
372                                 SecurityIPResolver* sr = new SecurityIPResolver(Creator, this, L->IPAddr, L, cached, start_type);
373                                 ServerInstance->AddResolver(sr, cached);
374                         }
375                         catch (...)
376                         {
377                         }
378                 }
379         }
380 }
381
382 void SpanningTreeUtilities::ReadConfiguration(bool rebind)
383 {
384         ConfigReader Conf;
385
386         if (rebind)
387         {
388                 for (int j = 0; j < Conf.Enumerate("bind"); j++)
389                 {
390                         std::string Type = Conf.ReadValue("bind","type",j);
391                         std::string IP = Conf.ReadValue("bind","address",j);
392                         std::string Port = Conf.ReadValue("bind","port",j);
393                         std::string ssl = Conf.ReadValue("bind","ssl",j);
394                         if (Type == "servers")
395                         {
396                                 irc::portparser portrange(Port, false);
397                                 int portno = -1;
398
399                                 if (IP == "*")
400                                         IP.clear();
401
402                                 while ((portno = portrange.GetToken()))
403                                 {
404                                         ServerSocketListener *listener = new ServerSocketListener(this, portno, IP, ssl);
405                                         if (listener->GetFd() == -1)
406                                         {
407                                                 delete listener;
408                                                 continue;
409                                         }
410
411                                         ServerInstance->ports.push_back(listener);
412                                 }
413                         }
414                 }
415         }
416         FlatLinks = Conf.ReadFlag("security","flatlinks",0);
417         HideULines = Conf.ReadFlag("security","hideulines",0);
418         AnnounceTSChange = Conf.ReadFlag("options","announcets",0);
419         AllowOptCommon = Conf.ReadFlag("options", "allowmismatch", 0);
420         ChallengeResponse = !Conf.ReadFlag("security", "disablehmac", 0);
421         quiet_bursts = Conf.ReadFlag("performance", "quietbursts", 0);
422         PingWarnTime = Conf.ReadInteger("options", "pingwarning", 0, true);
423         PingFreq = Conf.ReadInteger("options", "serverpingfreq", 0, true);
424
425         if (PingFreq == 0)
426                 PingFreq = 60;
427
428         if (PingWarnTime < 0 || PingWarnTime > PingFreq - 1)
429                 PingWarnTime = 0;
430
431         AutoconnectBlocks.clear();
432         LinkBlocks.clear();
433         ValidIPs.clear();
434         for (int j = 0; j < Conf.Enumerate("link"); ++j)
435         {
436                 reference<Link> L = new Link;
437                 std::string Allow = Conf.ReadValue("link", "allowmask", j);
438                 L->Name = (Conf.ReadValue("link", "name", j)).c_str();
439                 L->AllowMask = Allow;
440                 L->IPAddr = Conf.ReadValue("link", "ipaddr", j);
441                 L->Port = Conf.ReadInteger("link", "port", j, true);
442                 L->SendPass = Conf.ReadValue("link", "sendpass", j);
443                 L->RecvPass = Conf.ReadValue("link", "recvpass", j);
444                 L->Fingerprint = Conf.ReadValue("link", "fingerprint", j);
445                 L->HiddenFromStats = Conf.ReadFlag("link", "statshidden", j);
446                 L->Timeout = Conf.ReadInteger("link", "timeout", j, true);
447                 L->Hook = Conf.ReadValue("link", "ssl", j);
448                 L->Bind = Conf.ReadValue("link", "bind", j);
449                 L->Hidden = Conf.ReadFlag("link", "hidden", j);
450
451                 if (L->Name.find('.') == std::string::npos)
452                         throw CoreException("The link name '"+assign(L->Name)+"' is invalid and must contain at least one '.' character");
453
454                 if (L->Name.length() > 64)
455                         throw CoreException("The link name '"+assign(L->Name)+"' is longer than 64 characters!");
456
457                 if ((!L->IPAddr.empty()) && (!L->RecvPass.empty()) && (!L->SendPass.empty()) && (!L->Name.empty()) && (L->Port))
458                 {
459                         if (Allow.length())
460                                 ValidIPs.push_back(Allow);
461
462                         ValidIPs.push_back(L->IPAddr);
463
464                         /* Needs resolving */
465                         bool ipvalid = true;
466                         QueryType start_type = DNS_QUERY_A;
467                         start_type = DNS_QUERY_AAAA;
468                         if (strchr(L->IPAddr.c_str(),':'))
469                         {
470                                 in6_addr n;
471                                 if (inet_pton(AF_INET6, L->IPAddr.c_str(), &n) < 1)
472                                         ipvalid = false;
473                         }
474                         else
475                         {
476                                 in_addr n;
477                                 if (inet_aton(L->IPAddr.c_str(),&n) < 1)
478                                         ipvalid = false;
479                         }
480
481                         if (!ipvalid)
482                         {
483                                 try
484                                 {
485                                         bool cached;
486                                         SecurityIPResolver* sr = new SecurityIPResolver(Creator, this, L->IPAddr, L, cached, start_type);
487                                         ServerInstance->AddResolver(sr, cached);
488                                 }
489                                 catch (...)
490                                 {
491                                 }
492                         }
493                 }
494                 else
495                 {
496                         if (L->IPAddr.empty())
497                         {
498                                 L->IPAddr = "*";
499                                 ValidIPs.push_back("*");
500                                 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.");
501                         }
502
503                         if (L->RecvPass.empty())
504                         {
505                                 throw CoreException("Invalid configuration for server '"+assign(L->Name)+"', recvpass not defined!");
506                         }
507
508                         if (L->SendPass.empty())
509                         {
510                                 throw CoreException("Invalid configuration for server '"+assign(L->Name)+"', sendpass not defined!");
511                         }
512
513                         if (L->Name.empty())
514                         {
515                                 throw CoreException("Invalid configuration, link tag without a name! IP address: "+L->IPAddr);
516                         }
517
518                         if (!L->Port)
519                         {
520                                 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.");
521                         }
522                 }
523
524                 LinkBlocks.push_back(L);
525         }
526
527         for (int j = 0; j < Conf.Enumerate("autoconnect"); ++j)
528         {
529                 reference<Autoconnect> A = new Autoconnect;
530                 A->Period = Conf.ReadInteger("autoconnect", "period", j, true);
531                 A->NextConnectTime = ServerInstance->Time() + A->Period;
532                 A->position = -1;
533                 std::string servers = Conf.ReadValue("autoconnect", "server", j);
534                 irc::spacesepstream ss(servers);
535                 std::string server;
536                 while (ss.GetToken(server))
537                 {
538                         A->servers.push_back(server);
539                 }
540
541                 if (A->Period <= 0)
542                 {
543                         throw CoreException("Invalid configuration for autoconnect, period not a positive integer!");
544                 }
545
546                 if (A->servers.empty())
547                 {
548                         throw CoreException("Invalid configuration for autoconnect, server cannot be empty!");
549                 }
550
551                 AutoconnectBlocks.push_back(A);
552         }
553 }
554
555 Link* SpanningTreeUtilities::FindLink(const std::string& name)
556 {
557         for (std::vector<reference<Link> >::iterator i = LinkBlocks.begin(); i != LinkBlocks.end(); ++i)
558         {
559                 Link* x = *i;
560                 if (InspIRCd::Match(x->Name.c_str(), name.c_str()))
561                 {
562                         return x;
563                 }
564         }
565         return NULL;
566 }