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