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