]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_spanningtree.cpp
Added tree walk algorithm which finds out which directly-connected server
[user/henk/code/inspircd.git] / src / modules / m_spanningtree.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  Inspire is copyright (C) 2002-2005 ChatSpike-Dev.
6  *                       E-mail:
7  *                <brain@chatspike.net>
8  *                <Craig@chatspike.net>
9  *     
10  * Written by Craig Edwards, Craig McLure, and others.
11  * This program is free but copyrighted software; see
12  *            the file COPYING for details.
13  *
14  * ---------------------------------------------------
15  */
16
17 using namespace std;
18
19 #include <stdio.h>
20 #include <vector>
21 #include <deque>
22 #include "globals.h"
23 #include "inspircd_config.h"
24 #ifdef GCC3
25 #include <ext/hash_map>
26 #else
27 #include <hash_map>
28 #endif
29 #include "users.h"
30 #include "channels.h"
31 #include "modules.h"
32 #include "socket.h"
33 #include "helperfuncs.h"
34 #include "inspircd.h"
35 #include "inspstring.h"
36 #include "hashcomp.h"
37 #include "message.h"
38
39 #ifdef GCC3
40 #define nspace __gnu_cxx
41 #else
42 #define nspace std
43 #endif
44
45 enum ServerState { LISTENER, CONNECTING, WAIT_AUTH_1, WAIT_AUTH_2, CONNECTED };
46
47 typedef nspace::hash_map<std::string, userrec*, nspace::hash<string>, irc::StrHashComp> user_hash;
48 extern user_hash clientlist;
49
50 class TreeServer;
51 class TreeSocket;
52
53 class TreeServer
54 {
55         TreeServer* Parent;
56         std::vector<TreeServer*> Children;
57         std::string ServerName;
58         std::string ServerDesc;
59         std::string VersionString;
60         int UserCount;
61         int OperCount;
62         TreeSocket* Socket;     // for directly connected servers this points at the socket object
63         
64  public:
65
66         TreeServer()
67         {
68                 Parent = NULL;
69                 ServerName = "";
70                 ServerDesc = "";
71                 VersionString = "";
72                 UserCount = OperCount = 0;
73         }
74
75         TreeServer(std::string Name, std::string Desc) : ServerName(Name), ServerDesc(Desc)
76         {
77                 Parent = NULL;
78                 VersionString = "";
79                 UserCount = OperCount = 0;
80         }
81
82         TreeServer(std::string Name, std::string Desc, TreeServer* Above, TreeSocket* Sock) : Parent(Above), ServerName(Name), ServerDesc(Desc), Socket(Sock)
83         {
84                 VersionString = "";
85                 UserCount = OperCount = 0;
86         }
87
88         std::string GetName()
89         {
90                 return this->ServerName;
91         }
92
93         std::string GetDesc()
94         {
95                 return this->ServerDesc;
96         }
97
98         std::string GetVersion()
99         {
100                 return this->VersionString;
101         }
102
103         int GetUserCount()
104         {
105                 return this->UserCount;
106         }
107
108         int GetOperCount()
109         {
110                 return this->OperCount;
111         }
112
113         TreeSocket* GetSocket()
114         {
115                 return this->Socket;
116         }
117
118         TreeServer* GetParent()
119         {
120                 return this->Parent;
121         }
122
123         unsigned int ChildCount()
124         {
125                 return Children.size();
126         }
127
128         TreeServer* GetChild(unsigned int n)
129         {
130                 if (n < Children.size())
131                 {
132                         return Children[n];
133                 }
134                 else
135                 {
136                         return NULL;
137                 }
138         }
139
140         void AddChild(TreeServer* Child)
141         {
142                 Children.push_back(Child);
143         }
144
145         bool DelChild(TreeServer* Child)
146         {
147                 for (std::vector<TreeServer*>::iterator a = Children.begin(); a < Children.end(); a++)
148                 {
149                         if (*a == Child)
150                         {
151                                 Children.erase(a);
152                                 return true;
153                         }
154                 }
155                 return false;
156         }
157 };
158
159 class Link
160 {
161  public:
162          std::string Name;
163          std::string IPAddr;
164          int Port;
165          std::string SendPass;
166          std::string RecvPass;
167 };
168
169 /* $ModDesc: Povides a spanning tree server link protocol */
170
171 Server *Srv;
172 ConfigReader *Conf;
173 TreeServer *TreeRoot;
174 std::vector<Link> LinkBlocks;
175
176 TreeServer* RouteEnumerate(TreeServer* Current, std::string ServerName)
177 {
178         if (Current->GetName() == ServerName)
179                 return Current;
180         for (unsigned int q = 0; q < Current->ChildCount(); q++)
181         {
182                 TreeServer* found = RouteEnumerate(Current->GetChild(),ServerName);
183                 if (found)
184                 {
185                         return found;
186                 }
187         }
188         return NULL;
189 }
190
191 // Returns the locally connected server we must route a
192 // message through to reach server 'ServerName'. This
193 // only applies to one-to-one and not one-to-many routing.
194 TreeServer* BestRouteTo(std::string ServerName)
195 {
196         log(DEBUG,"Finding best route to %s",ServerName.c_str());
197         // first, find the server by recursively walking the tree
198         TreeServer* Found = RouteEnumerate(TreeRoot,ServerName);
199         // did we find it? If not, they did something wrong, abort.
200         if (!Found)
201         {
202                 log(DEBUG,"Failed to find %s by walking tree!",ServerName.c_str());
203                 return NULL;
204         }
205         else
206         {
207                 // The server exists, follow its parent nodes until
208                 // the parent of the current is 'TreeRoot', we know
209                 // then that this is a directly-connected server.
210                 while ((Found) && (Found->GetParent() != TreeRoot))
211                 {
212                         Found = Found->GetParent();
213                 }
214                 log(DEBUG,"Route to %s is via %s",ServerName.c_str(),Found->GetName().c_str());
215                 return Found;
216         }
217 }
218
219 class TreeSocket : public InspSocket
220 {
221         std::string myhost;
222         std::string in_buffer;
223         ServerState LinkState;
224         std::string InboundServerName;
225         std::string InboundDescription;
226         
227  public:
228
229         TreeSocket(std::string host, int port, bool listening, unsigned long maxtime)
230                 : InspSocket(host, port, listening, maxtime)
231         {
232                 Srv->Log(DEBUG,"Create new listening");
233                 myhost = host;
234                 this->LinkState = LISTENER;
235         }
236
237         TreeSocket(std::string host, int port, bool listening, unsigned long maxtime, std::string ServerName)
238                 : InspSocket(host, port, listening, maxtime)
239         {
240                 Srv->Log(DEBUG,"Create new outbound");
241                 myhost = ServerName;
242                 this->LinkState = CONNECTING;
243         }
244
245         TreeSocket(int newfd, char* ip)
246                 : InspSocket(newfd, ip)
247         {
248                 Srv->Log(DEBUG,"Associate new inbound");
249                 this->LinkState = WAIT_AUTH_1;
250         }
251         
252         virtual bool OnConnected()
253         {
254                 if (this->LinkState == CONNECTING)
255                 {
256                         Srv->SendOpers("*** Connection to "+myhost+"["+this->GetIP()+"] established.");
257                         // we should send our details here.
258                         // if the other side is satisfied, they send theirs.
259                         // we do not need to change state here.
260                         for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
261                         {
262                                 if (x->Name == this->myhost)
263                                 {
264                                         // found who we're supposed to be connecting to, send the neccessary gubbins.
265                                         this->WriteLine("SERVER "+Srv->GetServerName()+" "+x->SendPass+" 0 :"+Srv->GetServerDescription());
266                                         return true;
267                                 }
268                         }
269                 }
270                 log(DEBUG,"Outbound connection ERROR: Could not find the right link block!");
271                 return true;
272         }
273         
274         virtual void OnError(InspSocketError e)
275         {
276         }
277
278         virtual int OnDisconnect()
279         {
280                 return true;
281         }
282
283         // recursively send the server tree with distances as hops
284         void SendServers(TreeServer* Current, TreeServer* s, int hops)
285         {
286                 char command[1024];
287                 for (unsigned int q = 0; q < Current->ChildCount(); q++)
288                 {
289                         TreeServer* recursive_server = Current->GetChild(q);
290                         if (recursive_server != s)
291                         {
292                                 // :source.server SERVER server.name hops :Description
293                                 snprintf(command,1024,":%s SERVER %s * %d :%s",Current->GetName().c_str(),recursive_server->GetName().c_str(),hops,recursive_server->GetDesc().c_str());
294                                 this->WriteLine(command);
295                                 // down to next level
296                                 this->SendServers(recursive_server, s, hops+1);
297                         }
298                 }
299         }
300
301         // send all users and their channels
302         void SendUsers(TreeServer* Current)
303         {
304                 char data[MAXBUF];
305                 for (user_hash::iterator u = clientlist.begin(); u != clientlist.end(); u++)
306                 {
307                         snprintf(data,MAXBUF,":%s NICK %lu %s %s %s %s +%s %s :%s",u->second->server,(unsigned long)u->second->age,u->second->nick,u->second->host,u->second->dhost,u->second->ident,u->second->modes,u->second->ip,u->second->fullname);
308                         this->WriteLine(data);
309                         if (strchr(u->second->modes,'o'))
310                         {
311                                 this->WriteLine(":"+std::string(u->second->nick)+" OPERTYPE "+std::string(u->second->oper));
312                         }
313                         char* chl = chlist(u->second,u->second);
314                         if (*chl)
315                         {
316                                 this->WriteLine(":"+std::string(u->second->nick)+" JOIN "+std::string(chl));
317                         }
318                 }
319         }
320
321         void DoBurst(TreeServer* s)
322         {
323                 log(DEBUG,"Beginning network burst");
324                 Srv->SendOpers("*** Bursting to "+s->GetName()+".");
325                 this->WriteLine("BURST");
326                 // Send server tree
327                 this->SendServers(TreeRoot,s,1);
328                 // Send users and their channels
329                 this->SendUsers(s);
330                 // TODO: Send everything else
331                 this->WriteLine("ENDBURST");
332         }
333
334         virtual bool OnDataReady()
335         {
336                 char* data = this->Read();
337                 if (data)
338                 {
339                         this->in_buffer += data;
340                         while (in_buffer.find("\n") != std::string::npos)
341                         {
342                                 char* line = (char*)in_buffer.c_str();
343                                 std::string ret = "";
344                                 while ((*line != '\n') && (strlen(line)))
345                                 {
346                                         ret = ret + *line;
347                                         line++;
348                                 }
349                                 if ((*line == '\n') || (*line == '\r'))
350                                         line++;
351                                 in_buffer = line;
352                                 if (!this->ProcessLine(ret))
353                                 {
354                                         return false;
355                                 }
356                         }
357                 }
358                 return (data != NULL);
359         }
360
361         int WriteLine(std::string line)
362         {
363                 return this->Write(line + "\r\n");
364         }
365
366         bool Error(std::deque<std::string> params)
367         {
368                 if (params.size() < 1)
369                         return false;
370                 std::string Errmsg = params[0];
371                 std::string SName = myhost;
372                 if (InboundServerName != "")
373                 {
374                         SName = InboundServerName;
375                 }
376                 Srv->SendOpers("*** ERROR from "+SName+": "+Errmsg);
377                 // we will return false to cause the socket to close.
378                 return false;
379         }
380
381         bool Outbound_Reply_Server(std::deque<std::string> params)
382         {
383                 if (params.size() < 4)
384                         return false;
385                 std::string servername = params[0];
386                 std::string password = params[1];
387                 int hops = atoi(params[2].c_str());
388                 if (hops)
389                 {
390                         this->WriteLine("ERROR :Server too far away for authentication");
391                         return false;
392                 }
393                 std::string description = params[3];
394                 Srv->SendToModeMask("o",WM_AND,"outbound-server-replied: name='"+servername+"' pass='"+password+"' description='"+description+"'");
395                 for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
396                 {
397                         if ((x->Name == servername) && (x->RecvPass == password))
398                         {
399                                 // Begin the sync here. this kickstarts the
400                                 // other side, waiting in WAIT_AUTH_2 state,
401                                 // into starting their burst, as it shows
402                                 // that we're happy.
403                                 this->LinkState = CONNECTED;
404                                 // we should add the details of this server now
405                                 // to the servers tree, as a child of the root
406                                 // node.
407                                 TreeServer* Node = new TreeServer(servername,description,TreeRoot,this);
408                                 TreeRoot->AddChild(Node);
409                                 this->DoBurst(Node);
410                                 return true;
411                         }
412                 }
413                 this->WriteLine("ERROR :Invalid credentials");
414                 return false;
415         }
416
417         bool Inbound_Server(std::deque<std::string> params)
418         {
419                 if (params.size() < 4)
420                         return false;
421                 std::string servername = params[0];
422                 std::string password = params[1];
423                 int hops = atoi(params[2].c_str());
424                 if (hops)
425                 {
426                         this->WriteLine("ERROR :Server too far away for authentication");
427                         return false;
428                 }
429                 std::string description = params[3];
430                 for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
431                 {
432                         if ((x->Name == servername) && (x->RecvPass == password))
433                         {
434                                 Srv->SendOpers("*** Verified incoming server connection from "+servername+"["+this->GetIP()+"] ("+description+")");
435                                 this->InboundServerName = servername;
436                                 this->InboundDescription = description;
437                                 // this is good. Send our details: Our server name and description and hopcount of 0,
438                                 // along with the sendpass from this block.
439                                 this->WriteLine("SERVER "+Srv->GetServerName()+" "+x->SendPass+" 0 :"+Srv->GetServerDescription());
440                                 // move to the next state, we are now waiting for THEM.
441                                 this->LinkState = WAIT_AUTH_2;
442                                 return true;
443                         }
444                 }
445                 this->WriteLine("ERROR :Invalid credentials");
446                 return false;
447         }
448
449         std::deque<std::string> Split(std::string line)
450         {
451                 std::deque<std::string> n;
452                 std::stringstream s(line);
453                 std::string param = "";
454                 n.clear();
455                 int item = 0;
456                 while (!s.eof())
457                 {
458                         s >> param;
459                         if ((param.c_str()[0] == ':') && (item))
460                         {
461                                 char* str = (char*)param.c_str();
462                                 str++;
463                                 param = str;
464                                 std::string append;
465                                 while (!s.eof())
466                                 {
467                                         append = "";
468                                         s >> append;
469                                         if (append != "")
470                                         {
471                                                 param = param + " " + append;
472                                         }
473                                 }
474                         }
475                         item++;
476                         n.push_back(param);
477                 }
478                 return n;
479         }
480
481         bool ProcessLine(std::string line)
482         {
483                 Srv->SendToModeMask("o",WM_AND,"inbound-line: '"+line+"'");
484
485                 std::deque<std::string> params = this->Split(line);
486                 std::string command = "";
487                 std::string prefix = "";
488                 if ((params[0].c_str())[0] == ':')
489                 {
490                         prefix = params[0];
491                         command = params[1];
492                         params.pop_front();
493                         params.pop_front();
494                 }
495                 else
496                 {
497                         prefix = "";
498                         command = params[0];
499                         params.pop_front();
500                 }
501                 
502                 switch (this->LinkState)
503                 {
504                         TreeServer* Node;
505                         
506                         case WAIT_AUTH_1:
507                                 // Waiting for SERVER command from remote server. Server initiating
508                                 // the connection sends the first SERVER command, listening server
509                                 // replies with theirs if its happy, then if the initiator is happy,
510                                 // it starts to send its net sync, which starts the merge, otherwise
511                                 // it sends an ERROR.
512                                 if (command == "SERVER")
513                                 {
514                                         return this->Inbound_Server(params);
515                                 }
516                                 else if (command == "ERROR")
517                                 {
518                                         return this->Error(params);
519                                 }
520                         break;
521                         case WAIT_AUTH_2:
522                                 // Waiting for start of other side's netmerge to say they liked our
523                                 // password.
524                                 if (command == "SERVER")
525                                 {
526                                         // cant do this, they sent it to us in the WAIT_AUTH_1 state!
527                                         // silently ignore.
528                                         return true;
529                                 }
530                                 else if (command == "BURST")
531                                 {
532                                         this->LinkState = CONNECTED;
533                                         Node = new TreeServer(InboundServerName,InboundDescription,TreeRoot,this);
534                                         TreeRoot->AddChild(Node);
535                                         this->DoBurst(Node);
536                                 }
537                                 else if (command == "ERROR")
538                                 {
539                                         return this->Error(params);
540                                 }
541                                 
542                         break;
543                         case LISTENER:
544                                 this->WriteLine("ERROR :Internal error -- listening socket accepted its own descriptor!!!");
545                                 return false;
546                         break;
547                         case CONNECTING:
548                                 if (command == "SERVER")
549                                 {
550                                         // another server we connected to, which was in WAIT_AUTH_1 state,
551                                         // has just sent us their credentials. If we get this far, theyre
552                                         // happy with OUR credentials, and they are now in WAIT_AUTH_2 state.
553                                         // if we're happy with this, we should send our netburst which
554                                         // kickstarts the merge.
555                                         return this->Outbound_Reply_Server(params);
556                                 }
557                         break;
558                         case CONNECTED:
559                                 // This is the 'authenticated' state, when all passwords
560                                 // have been exchanged and anything past this point is taken
561                                 // as gospel.
562                                 return true;
563                         break;  
564                 }
565                 return true;
566         }
567
568         virtual void OnTimeout()
569         {
570                 if (this->LinkState = CONNECTING)
571                 {
572                         Srv->SendOpers("*** CONNECT: Connection to "+myhost+" timed out.");
573                 }
574         }
575
576         virtual void OnClose()
577         {
578         }
579
580         virtual int OnIncomingConnection(int newsock, char* ip)
581         {
582                 TreeSocket* s = new TreeSocket(newsock, ip);
583                 Srv->AddSocket(s);
584                 return true;
585         }
586 };
587
588 class ModuleSpanningTree : public Module
589 {
590         std::vector<TreeSocket*> Bindings;
591
592  public:
593
594         void ReadConfiguration(bool rebind)
595         {
596                 if (rebind)
597                 {
598                         for (int j =0; j < Conf->Enumerate("bind"); j++)
599                         {
600                                 std::string Type = Conf->ReadValue("bind","type",j);
601                                 std::string IP = Conf->ReadValue("bind","address",j);
602                                 long Port = Conf->ReadInteger("bind","port",j,true);
603                                 if (Type == "servers")
604                                 {
605                                         if (IP == "*")
606                                         {
607                                                 IP = "";
608                                         }
609                                         TreeSocket* listener = new TreeSocket(IP.c_str(),Port,true,10);
610                                         if (listener->GetState() == I_LISTENING)
611                                         {
612                                                 Srv->AddSocket(listener);
613                                                 Bindings.push_back(listener);
614                                         }
615                                         else
616                                         {
617                                                 log(DEFAULT,"m_spanningtree: Warning: Failed to bind server port %d",Port);
618                                                 listener->Close();
619                                                 delete listener;
620                                         }
621                                 }
622                         }
623                 }
624                 LinkBlocks.clear();
625                 for (int j =0; j < Conf->Enumerate("link"); j++)
626                 {
627                         Link L;
628                         L.Name = Conf->ReadValue("link","name",j);
629                         L.IPAddr = Conf->ReadValue("link","ipaddr",j);
630                         L.Port = Conf->ReadInteger("link","port",j,true);
631                         L.SendPass = Conf->ReadValue("link","sendpass",j);
632                         L.RecvPass = Conf->ReadValue("link","recvpass",j);
633                         LinkBlocks.push_back(L);
634                         log(DEBUG,"m_spanningtree: Read server %s with host %s:%d",L.Name.c_str(),L.IPAddr.c_str(),L.Port);
635                 }
636         }
637
638         ModuleSpanningTree()
639         {
640                 Srv = new Server;
641                 Conf = new ConfigReader;
642                 Bindings.clear();
643
644                 // Create the root of the tree
645                 TreeRoot = new TreeServer(Srv->GetServerName(),Srv->GetServerDescription());
646
647                 ReadConfiguration(true);
648         }
649
650         void HandleLinks(char** parameters, int pcnt, userrec* user)
651         {
652                 return;
653         }
654
655         void HandleLusers(char** parameters, int pcnt, userrec* user)
656         {
657                 return;
658         }
659
660         void HandleMap(char** parameters, int pcnt, userrec* user)
661         {
662                 return;
663         }
664
665         int HandleSquit(char** parameters, int pcnt, userrec* user)
666         {
667                 return 1;
668         }
669
670         int HandleConnect(char** parameters, int pcnt, userrec* user)
671         {
672                 for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
673                 {
674                         if (Srv->MatchText(x->Name.c_str(),parameters[0]))
675                         {
676                                 WriteServ(user->fd,"NOTICE %s :*** CONNECT: Connecting to server: %s (%s:%d)",user->nick,x->Name.c_str(),x->IPAddr.c_str(),x->Port);
677                                 TreeSocket* newsocket = new TreeSocket(x->IPAddr,x->Port,false,10,x->Name);
678                                 Srv->AddSocket(newsocket);
679                                 return 1;
680                         }
681                 }
682                 WriteServ(user->fd,"NOTICE %s :*** CONNECT: No matching server could be found in the config file.",user->nick);
683                 return 1;
684         }
685
686         virtual int OnPreCommand(std::string command, char **parameters, int pcnt, userrec *user)
687         {
688                 if (command == "CONNECT")
689                 {
690                         return this->HandleConnect(parameters,pcnt,user);
691                 }
692                 else if (command == "SQUIT")
693                 {
694                         return this->HandleSquit(parameters,pcnt,user);
695                 }
696                 else if (command == "MAP")
697                 {
698                         this->HandleMap(parameters,pcnt,user);
699                         return 1;
700                 }
701                 else if (command == "LUSERS")
702                 {
703                         this->HandleLusers(parameters,pcnt,user);
704                         return 1;
705                 }
706                 else if (command == "LINKS")
707                 {
708                         this->HandleLinks(parameters,pcnt,user);
709                         return 1;
710                 }
711                 return 0;
712         }
713
714         virtual ~ModuleSpanningTree()
715         {
716                 delete Srv;
717         }
718
719         virtual Version GetVersion()
720         {
721                 return Version(1,0,0,0,VF_STATIC|VF_VENDOR);
722         }
723 };
724
725
726 class ModuleSpanningTreeFactory : public ModuleFactory
727 {
728  public:
729         ModuleSpanningTreeFactory()
730         {
731         }
732         
733         ~ModuleSpanningTreeFactory()
734         {
735         }
736         
737         virtual Module * CreateModule()
738         {
739                 return new ModuleSpanningTree;
740         }
741         
742 };
743
744
745 extern "C" void * init_module( void )
746 {
747         return new ModuleSpanningTreeFactory;
748 }
749