]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_spanningtree/utils.cpp
Remove the intercomm system since sqlite is synchronous.
[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 /* Create server sockets off a listener. */
27 ModResult ModuleSpanningTree::OnAcceptConnection(int newsock, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
28 {
29         if (from->bind_tag->getString("type") != "servers")
30                 return MOD_RES_PASSTHRU;
31
32         bool found = false;
33         int port;
34         std::string incomingip;
35         irc::sockets::satoap(client, incomingip, port);
36
37         found = (std::find(Utils->ValidIPs.begin(), Utils->ValidIPs.end(), incomingip) != Utils->ValidIPs.end());
38         if (!found)
39         {
40                 for (std::vector<std::string>::iterator i = Utils->ValidIPs.begin(); i != Utils->ValidIPs.end(); i++)
41                 {
42                         if (*i == "*" || irc::sockets::MatchCIDR(incomingip, *i))
43                         {
44                                 found = true;
45                                 break;
46                         }
47                 }
48
49                 if (!found)
50                 {
51                         ServerInstance->SNO->WriteToSnoMask('l', "Server connection from %s denied (no link blocks with that IP address)", incomingip.c_str());
52                         return MOD_RES_DENY;
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, from, client, server);
59         return MOD_RES_ALLOW;
60 }
61
62 /** Yay for fast searches!
63  * This is hundreds of times faster than recursion
64  * or even scanning a linked list, especially when
65  * there are more than a few servers to deal with.
66  * (read as: lots).
67  */
68 TreeServer* SpanningTreeUtilities::FindServer(const std::string &ServerName)
69 {
70         if (ServerInstance->IsSID(ServerName))
71                 return this->FindServerID(ServerName);
72
73         server_hash::iterator iter = serverlist.find(ServerName.c_str());
74         if (iter != serverlist.end())
75         {
76                 return iter->second;
77         }
78         else
79         {
80                 return NULL;
81         }
82 }
83
84 /** Returns the locally connected server we must route a
85  * message through to reach server 'ServerName'. This
86  * only applies to one-to-one and not one-to-many routing.
87  * See the comments for the constructor of TreeServer
88  * for more details.
89  */
90 TreeServer* SpanningTreeUtilities::BestRouteTo(const std::string &ServerName)
91 {
92         if (ServerName.c_str() == TreeRoot->GetName() || ServerName == ServerInstance->Config->GetSID())
93                 return NULL;
94         TreeServer* Found = FindServer(ServerName);
95         if (Found)
96         {
97                 return Found->GetRoute();
98         }
99         else
100         {
101                 // Cheat a bit. This allows for (better) working versions of routing commands with nick based prefixes, without hassle
102                 User *u = ServerInstance->FindNick(ServerName);
103                 if (u)
104                 {
105                         Found = FindServer(u->server);
106                         if (Found)
107                                 return Found->GetRoute();
108                 }
109
110                 return NULL;
111         }
112 }
113
114 /** Find the first server matching a given glob mask.
115  * Theres no find-using-glob method of hash_map [awwww :-(]
116  * so instead, we iterate over the list using an iterator
117  * and match each one until we get a hit. Yes its slow,
118  * deal with it.
119  */
120 TreeServer* SpanningTreeUtilities::FindServerMask(const std::string &ServerName)
121 {
122         for (server_hash::iterator i = serverlist.begin(); i != serverlist.end(); i++)
123         {
124                 if (InspIRCd::Match(i->first,ServerName))
125                         return i->second;
126         }
127         return NULL;
128 }
129
130 TreeServer* SpanningTreeUtilities::FindServerID(const std::string &id)
131 {
132         server_hash::iterator iter = sidlist.find(id);
133         if (iter != sidlist.end())
134                 return iter->second;
135         else
136                 return NULL;
137 }
138
139 /* A convenient wrapper that returns true if a server exists */
140 bool SpanningTreeUtilities::IsServer(const std::string &ServerName)
141 {
142         return (FindServer(ServerName) != NULL);
143 }
144
145 SpanningTreeUtilities::SpanningTreeUtilities(ModuleSpanningTree* C) : Creator(C)
146 {
147         ServerInstance->Logs->Log("m_spanningtree",DEBUG,"***** Using SID for hash: %s *****", ServerInstance->Config->GetSID().c_str());
148
149         this->TreeRoot = new TreeServer(this, ServerInstance->Config->ServerName, ServerInstance->Config->ServerDesc, ServerInstance->Config->GetSID());
150         ServerUser = new FakeUser(TreeRoot->GetID());
151
152         this->ReadConfiguration();
153 }
154
155 CullResult SpanningTreeUtilities::cull()
156 {
157         while (TreeRoot->ChildCount())
158         {
159                 TreeServer* child_server = TreeRoot->GetChild(0);
160                 if (child_server)
161                 {
162                         TreeSocket* sock = child_server->GetSocket();
163                         sock->Close();
164                         ServerInstance->GlobalCulls.AddItem(sock);
165                 }
166         }
167
168         ServerUser->uuid = TreeRoot->GetID();
169         ServerUser->cull();
170         delete ServerUser;
171         return classbase::cull();
172 }
173
174 SpanningTreeUtilities::~SpanningTreeUtilities()
175 {
176         delete TreeRoot;
177 }
178
179 void SpanningTreeUtilities::AddThisServer(TreeServer* server, TreeServerList &list)
180 {
181         if (list.find(server) == list.end())
182                 list[server] = server;
183 }
184
185 /* returns a list of DIRECT servernames for a specific channel */
186 void SpanningTreeUtilities::GetListOfServersForChannel(Channel* c, TreeServerList &list, char status, const CUList &exempt_list)
187 {
188         const UserMembList *ulist = c->GetUsers();
189
190         for (UserMembCIter i = ulist->begin(); i != ulist->end(); i++)
191         {
192                 if (IS_LOCAL(i->first))
193                         continue;
194
195                 if (status && !strchr(c->GetAllPrefixChars(i->first), status))
196                         continue;
197
198                 if (exempt_list.find(i->first) == exempt_list.end())
199                 {
200                         TreeServer* best = this->BestRouteTo(i->first->server);
201                         if (best)
202                                 AddThisServer(best,list);
203                 }
204         }
205         return;
206 }
207
208 bool SpanningTreeUtilities::DoOneToAllButSenderRaw(const std::string &data, const std::string &omit, const std::string &prefix, const irc::string &command, parameterlist &params)
209 {
210         TreeServer* omitroute = this->BestRouteTo(omit);
211         unsigned int items =this->TreeRoot->ChildCount();
212         for (unsigned int x = 0; x < items; x++)
213         {
214                 TreeServer* Route = this->TreeRoot->GetChild(x);
215                 if ((Route) && (Route->GetSocket()) && (Route->GetName() != omit) && (omitroute != Route))
216                 {
217                         TreeSocket* Sock = Route->GetSocket();
218                         if (Sock)
219                                 Sock->WriteLine(data);
220                 }
221         }
222         return true;
223 }
224
225 bool SpanningTreeUtilities::DoOneToAllButSender(const std::string &prefix, const std::string &command, parameterlist &params, std::string omit)
226 {
227         TreeServer* omitroute = this->BestRouteTo(omit);
228         std::string FullLine = ":" + prefix + " " + command;
229         unsigned int words = params.size();
230         for (unsigned int x = 0; x < words; x++)
231         {
232                 FullLine = FullLine + " " + params[x];
233         }
234         unsigned int items = this->TreeRoot->ChildCount();
235         for (unsigned int x = 0; x < items; x++)
236         {
237                 TreeServer* Route = this->TreeRoot->GetChild(x);
238                 // Send the line IF:
239                 // The route has a socket (its a direct connection)
240                 // The route isnt the one to be omitted
241                 // The route isnt the path to the one to be omitted
242                 if ((Route) && (Route->GetSocket()) && (Route->GetName() != omit) && (omitroute != Route))
243                 {
244                         TreeSocket* Sock = Route->GetSocket();
245                         if (Sock)
246                                 Sock->WriteLine(FullLine);
247                 }
248         }
249         return true;
250 }
251
252 bool SpanningTreeUtilities::DoOneToMany(const std::string &prefix, const std::string &command, parameterlist &params)
253 {
254         std::string FullLine = ":" + prefix + " " + command;
255         unsigned int words = params.size();
256         for (unsigned int x = 0; x < words; x++)
257         {
258                 FullLine = FullLine + " " + params[x];
259         }
260         unsigned int items = this->TreeRoot->ChildCount();
261         for (unsigned int x = 0; x < items; x++)
262         {
263                 TreeServer* Route = this->TreeRoot->GetChild(x);
264                 if (Route && Route->GetSocket())
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 char* prefix, const char* command, parameterlist &params)
275 {
276         std::string spfx = prefix;
277         std::string scmd = command;
278         return this->DoOneToMany(spfx, scmd, params);
279 }
280
281 bool SpanningTreeUtilities::DoOneToAllButSender(const char* prefix, const char* command, parameterlist &params, std::string omit)
282 {
283         std::string spfx = prefix;
284         std::string scmd = command;
285         return this->DoOneToAllButSender(spfx, scmd, params, omit);
286 }
287
288 bool SpanningTreeUtilities::DoOneToOne(const std::string &prefix, const std::string &command, parameterlist &params, std::string target)
289 {
290         TreeServer* Route = this->BestRouteTo(target);
291         if (Route)
292         {
293                 std::string FullLine = ":" + prefix + " " + command;
294                 unsigned int words = params.size();
295                 for (unsigned int x = 0; x < words; x++)
296                 {
297                         FullLine = FullLine + " " + params[x];
298                 }
299                 if (Route && Route->GetSocket())
300                 {
301                         TreeSocket* Sock = Route->GetSocket();
302                         if (Sock)
303                                 Sock->WriteLine(FullLine);
304                 }
305                 return true;
306         }
307         else
308         {
309                 return false;
310         }
311 }
312
313 void SpanningTreeUtilities::RefreshIPCache()
314 {
315         ValidIPs.clear();
316         for (std::vector<reference<Link> >::iterator i = LinkBlocks.begin(); i != LinkBlocks.end(); ++i)
317         {
318                 Link* L = *i;
319                 if (L->IPAddr.empty() || L->RecvPass.empty() || L->SendPass.empty() || L->Name.empty() || !L->Port)
320                 {
321                         if (L->Name.empty())
322                         {
323                                 ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"m_spanningtree: Ignoring a malformed link block (all link blocks require a name!)");
324                         }
325                         else
326                         {
327                                 ServerInstance->Logs->Log("m_spanningtree",DEFAULT,"m_spanningtree: Ignoring a link block missing recvpass, sendpass, port or ipaddr.");
328                         }
329
330                         /* Invalid link block */
331                         continue;
332                 }
333
334                 if (L->AllowMask.length())
335                         ValidIPs.push_back(L->AllowMask);
336
337                 irc::sockets::sockaddrs dummy;
338                 bool ipvalid = irc::sockets::aptosa(L->IPAddr, L->Port, &dummy);
339                 if (ipvalid)
340                         ValidIPs.push_back(L->IPAddr);
341                 else
342                 {
343                         try
344                         {
345                                 bool cached;
346                                 SecurityIPResolver* sr = new SecurityIPResolver(Creator, this, L->IPAddr, L, cached, DNS_QUERY_AAAA);
347                                 ServerInstance->AddResolver(sr, cached);
348                         }
349                         catch (...)
350                         {
351                         }
352                 }
353         }
354 }
355
356 void SpanningTreeUtilities::ReadConfiguration()
357 {
358         ConfigReader Conf;
359
360         FlatLinks = Conf.ReadFlag("security","flatlinks",0);
361         HideULines = Conf.ReadFlag("security","hideulines",0);
362         AnnounceTSChange = Conf.ReadFlag("options","announcets",0);
363         AllowOptCommon = Conf.ReadFlag("options", "allowmismatch", 0);
364         ChallengeResponse = !Conf.ReadFlag("security", "disablehmac", 0);
365         quiet_bursts = Conf.ReadFlag("performance", "quietbursts", 0);
366         PingWarnTime = Conf.ReadInteger("options", "pingwarning", 0, true);
367         PingFreq = Conf.ReadInteger("options", "serverpingfreq", 0, true);
368
369         if (PingFreq == 0)
370                 PingFreq = 60;
371
372         if (PingWarnTime < 0 || PingWarnTime > PingFreq - 1)
373                 PingWarnTime = 0;
374
375         AutoconnectBlocks.clear();
376         LinkBlocks.clear();
377         ValidIPs.clear();
378         ConfigTagList tags = ServerInstance->Config->ConfTags("link");
379         for(ConfigIter i = tags.first; i != tags.second; ++i)
380         {
381                 ConfigTag* tag = i->second;
382                 reference<Link> L = new Link(tag);
383                 L->Name = tag->getString("name").c_str();
384                 L->AllowMask = tag->getString("allowmask");
385                 L->IPAddr = tag->getString("ipaddr");
386                 L->Port = tag->getInt("port");
387                 L->SendPass = tag->getString("sendpass");
388                 L->RecvPass = tag->getString("recvpass");
389                 L->Fingerprint = tag->getString("fingerprint");
390                 L->HiddenFromStats = tag->getBool("statshidden");
391                 L->Timeout = tag->getInt("timeout");
392                 L->Hook = tag->getString("ssl");
393                 L->Bind = tag->getString("bind");
394                 L->Hidden = tag->getBool("hidden");
395
396                 if (L->Name.find('.') == std::string::npos)
397                         throw CoreException("The link name '"+assign(L->Name)+"' is invalid and must contain at least one '.' character");
398
399                 if (L->Name.length() > 64)
400                         throw CoreException("The link name '"+assign(L->Name)+"' is longer than 64 characters!");
401
402                 if ((!L->IPAddr.empty()) && (!L->RecvPass.empty()) && (!L->SendPass.empty()) && (!L->Name.empty()) && (L->Port))
403                 {
404                         ValidIPs.push_back(L->IPAddr);
405                 }
406                 else
407                 {
408                         if (L->IPAddr.empty())
409                         {
410                                 L->IPAddr = "*";
411                                 ValidIPs.push_back("*");
412                                 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.");
413                         }
414
415                         if (L->RecvPass.empty())
416                         {
417                                 throw CoreException("Invalid configuration for server '"+assign(L->Name)+"', recvpass not defined!");
418                         }
419
420                         if (L->SendPass.empty())
421                         {
422                                 throw CoreException("Invalid configuration for server '"+assign(L->Name)+"', sendpass not defined!");
423                         }
424
425                         if (L->Name.empty())
426                         {
427                                 throw CoreException("Invalid configuration, link tag without a name! IP address: "+L->IPAddr);
428                         }
429
430                         if (!L->Port)
431                         {
432                                 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.");
433                         }
434                 }
435
436                 LinkBlocks.push_back(L);
437         }
438
439         tags = ServerInstance->Config->ConfTags("autoconnect");
440         for(ConfigIter i = tags.first; i != tags.second; ++i)
441         {
442                 ConfigTag* tag = i->second;
443                 reference<Autoconnect> A = new Autoconnect(tag);
444                 A->Period = tag->getInt("period");
445                 A->NextConnectTime = ServerInstance->Time() + A->Period;
446                 A->position = -1;
447                 irc::spacesepstream ss(tag->getString("server"));
448                 std::string server;
449                 while (ss.GetToken(server))
450                 {
451                         A->servers.push_back(server);
452                 }
453
454                 if (A->Period <= 0)
455                 {
456                         throw CoreException("Invalid configuration for autoconnect, period not a positive integer!");
457                 }
458
459                 if (A->servers.empty())
460                 {
461                         throw CoreException("Invalid configuration for autoconnect, server cannot be empty!");
462                 }
463
464                 AutoconnectBlocks.push_back(A);
465         }
466
467         RefreshIPCache();
468 }
469
470 Link* SpanningTreeUtilities::FindLink(const std::string& name)
471 {
472         for (std::vector<reference<Link> >::iterator i = LinkBlocks.begin(); i != LinkBlocks.end(); ++i)
473         {
474                 Link* x = *i;
475                 if (InspIRCd::Match(x->Name.c_str(), name.c_str()))
476                 {
477                         return x;
478                 }
479         }
480         return NULL;
481 }