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