]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_spanningtree.cpp
71f3af876b66dd273413c974910fd3f39c36c107
[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 "users.h"
23 #include "channels.h"
24 #include "modules.h"
25 #include "socket.h"
26 #include "helperfuncs.h"
27 #include "inspircd.h"
28
29 enum ServerState { LISTENER, CONNECTING, WAIT_AUTH_1, WAIT_AUTH_2, CONNECTED };
30
31 class TreeServer;
32 class TreeSocket;
33
34 class TreeServer
35 {
36         TreeServer* Parent;
37         std::vector<TreeServer*> Children;
38         std::string ServerName;
39         std::string ServerDesc;
40         std::string VersionString;
41         int UserCount;
42         int OperCount;
43         TreeSocket* Socket;     // for directly connected servers this points at the socket object
44         
45  public:
46
47         TreeServer()
48         {
49                 Parent = NULL;
50                 ServerName = "";
51                 ServerDesc = "";
52                 VersionString = "";
53                 UserCount = OperCount = 0;
54         }
55
56         TreeServer(std::string Name, std::string Desc) : ServerName(Name), ServerDesc(Desc)
57         {
58                 Parent = NULL;
59                 VersionString = "";
60                 UserCount = OperCount = 0;
61         }
62
63         TreeServer(std::string Name, std::string Desc, TreeServer* Above, TreeSocket* Sock) : Parent(Above), ServerName(Name), ServerDesc(Desc), Socket(Sock)
64         {
65                 VersionString = "";
66                 UserCount = OperCount = 0;
67         }
68
69         void AddChild(TreeServer* Child)
70         {
71                 Children.push_back(Child);
72         }
73
74         bool DelChild(TreeServer* Child)
75         {
76                 for (std::vector<TreeServer*>::iterator a = Children.begin(); a < Children.end(); a++)
77                 {
78                         if (*a == Child)
79                         {
80                                 Children.erase(a);
81                                 return true;
82                         }
83                 }
84                 return false;
85         }
86 };
87
88 class Link
89 {
90  public:
91          std::string Name;
92          std::string IPAddr;
93          int Port;
94          std::string SendPass;
95          std::string RecvPass;
96 };
97
98 /* $ModDesc: Povides a spanning tree server link protocol */
99
100 Server *Srv;
101 ConfigReader *Conf;
102 TreeServer *TreeRoot;
103 std::vector<Link> LinkBlocks;
104
105 class TreeSocket : public InspSocket
106 {
107         std::string myhost;
108         std::string in_buffer;
109         ServerState LinkState;
110         std::string InboundServerName;
111         std::string InboundDescription;
112         
113  public:
114
115         TreeSocket(std::string host, int port, bool listening, unsigned long maxtime)
116                 : InspSocket(host, port, listening, maxtime)
117         {
118                 Srv->Log(DEBUG,"Create new listening");
119                 myhost = host;
120                 this->LinkState = LISTENER;
121         }
122
123         TreeSocket(std::string host, int port, bool listening, unsigned long maxtime, std::string ServerName)
124                 : InspSocket(host, port, listening, maxtime)
125         {
126                 Srv->Log(DEBUG,"Create new outbound");
127                 myhost = ServerName;
128                 this->LinkState = CONNECTING;
129         }
130
131         TreeSocket(int newfd)
132                 : InspSocket(newfd)
133         {
134                 Srv->Log(DEBUG,"Associate new inbound");
135                 this->LinkState = WAIT_AUTH_1;
136         }
137         
138         virtual bool OnConnected()
139         {
140                 if (this->LinkState == CONNECTING)
141                 {
142                         // we should send our details here.
143                         // if the other side is satisfied, they send theirs.
144                         // we do not need to change state here.
145                         for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
146                         {
147                                 if (x->Name == this->myhost)
148                                 {
149                                         // found who we're supposed to be connecting to, send the neccessary gubbins.
150                                         this->WriteLine("SERVER "+Srv->GetServerName()+" "+x->SendPass+" 0 :"+Srv->GetServerDescription());
151                                         return true;
152                                 }
153                         }
154                 }
155                 log(DEBUG,"Outbound connection ERROR: Could not find the right link block!");
156                 return true;
157         }
158         
159         virtual void OnError(InspSocketError e)
160         {
161         }
162
163         virtual int OnDisconnect()
164         {
165                 return true;
166         }
167
168         void DoBurst(TreeServer* s)
169         {
170                 log(DEBUG,"Beginning network burst");
171                 this->WriteLine("BURST");
172                 this->WriteLine("ENDBURST");
173         }
174
175         virtual bool OnDataReady()
176         {
177                 char* data = this->Read();
178                 if (data)
179                 {
180                         this->in_buffer += data;
181                         while (in_buffer.find("\n") != std::string::npos)
182                         {
183                                 char* line = (char*)in_buffer.c_str();
184                                 std::string ret = "";
185                                 while ((*line != '\n') && (strlen(line)))
186                                 {
187                                         ret = ret + *line;
188                                         line++;
189                                 }
190                                 if ((*line == '\n') || (*line == '\r'))
191                                         line++;
192                                 in_buffer = line;
193                                 if (!this->ProcessLine(ret))
194                                 {
195                                         return false;
196                                 }
197                         }
198                 }
199                 return (data != NULL);
200         }
201
202         int WriteLine(std::string line)
203         {
204                 return this->Write(line + "\r\n");
205         }
206
207         bool Error(std::deque<std::string> params)
208         {
209                 if (params.size() < 1)
210                         return false;
211                 std::string Errmsg = params[0];
212                 std::string SName = myhost;
213                 if (InboundServerName != "")
214                 {
215                         SName = InboundServerName;
216                 }
217                 Srv->SendOpers("*** ERROR from "+SName+": "+Errmsg);
218                 // we will return false to cause the socket to close.
219                 return false;
220         }
221
222         bool Outbound_Reply_Server(std::deque<std::string> params)
223         {
224                 if (params.size() < 4)
225                         return false;
226                 std::string servername = params[0];
227                 std::string password = params[1];
228                 int hops = atoi(params[2].c_str());
229                 if (hops)
230                 {
231                         this->WriteLine("ERROR :Server too far away for authentication");
232                         return false;
233                 }
234                 std::string description = params[3];
235                 Srv->SendToModeMask("o",WM_AND,"outbound-server-replied: name='"+servername+"' pass='"+password+"' description='"+description+"'");
236                 for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
237                 {
238                         if ((x->Name == servername) && (x->RecvPass == password))
239                         {
240                                 // Begin the sync here. this kickstarts the
241                                 // other side, waiting in WAIT_AUTH_2 state,
242                                 // into starting their burst, as it shows
243                                 // that we're happy.
244                                 this->LinkState = CONNECTED;
245                                 // we should add the details of this server now
246                                 // to the servers tree, as a child of the root
247                                 // node.
248                                 TreeServer* Node = new TreeServer(servername,description,TreeRoot,this);
249                                 TreeRoot->AddChild(Node);
250                                 this->DoBurst(Node);
251                                 return true;
252                         }
253                 }
254                 this->WriteLine("ERROR :Invalid credentials");
255                 return false;
256         }
257
258         bool Inbound_Server(std::deque<std::string> params)
259         {
260                 if (params.size() < 4)
261                         return false;
262                 std::string servername = params[0];
263                 std::string password = params[1];
264                 int hops = atoi(params[2].c_str());
265                 if (hops)
266                 {
267                         this->WriteLine("ERROR :Server too far away for authentication");
268                         return false;
269                 }
270                 std::string description = params[3];
271                 for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
272                 {
273                         if ((x->Name == servername) && (x->RecvPass == password))
274                         {
275                                 Srv->SendOpers("*** Verified incoming server connection from "+servername+"["+this->GetIP()+"] ("+description+")");
276                                 this->InboundServerName = servername;
277                                 this->InboundDescription = description;
278                                 // this is good. Send our details: Our server name and description and hopcount of 0,
279                                 // along with the sendpass from this block.
280                                 this->WriteLine("SERVER "+Srv->GetServerName()+" "+x->SendPass+" 0 :"+Srv->GetServerDescription());
281                                 // move to the next state, we are now waiting for THEM.
282                                 this->LinkState = WAIT_AUTH_2;
283                                 return true;
284                         }
285                 }
286                 this->WriteLine("ERROR :Invalid credentials");
287                 return false;
288         }
289
290         std::deque<std::string> Split(std::string line)
291         {
292                 std::deque<std::string> n;
293                 std::stringstream s(line);
294                 std::string param = "";
295                 n.clear();
296                 int item = 0;
297                 while (!s.eof())
298                 {
299                         s >> param;
300                         if ((param.c_str()[0] == ':') && (item))
301                         {
302                                 char* str = (char*)param.c_str();
303                                 str++;
304                                 param = str;
305                                 std::string append;
306                                 while (!s.eof())
307                                 {
308                                         append = "";
309                                         s >> append;
310                                         if (append != "")
311                                         {
312                                                 param = param + " " + append;
313                                         }
314                                 }
315                         }
316                         item++;
317                         n.push_back(param);
318                 }
319                 return n;
320         }
321
322         bool ProcessLine(std::string line)
323         {
324                 Srv->SendToModeMask("o",WM_AND,"inbound-line: '"+line+"'");
325
326                 std::deque<std::string> params = this->Split(line);
327                 std::string command = "";
328                 std::string prefix = "";
329                 if ((params[0].c_str())[0] == ':')
330                 {
331                         prefix = params[0];
332                         command = params[1];
333                         params.pop_front();
334                         params.pop_front();
335                 }
336                 else
337                 {
338                         prefix = "";
339                         command = params[0];
340                         params.pop_front();
341                 }
342                 
343                 switch (this->LinkState)
344                 {
345                         TreeServer* Node;
346                         
347                         case WAIT_AUTH_1:
348                                 // Waiting for SERVER command from remote server. Server initiating
349                                 // the connection sends the first SERVER command, listening server
350                                 // replies with theirs if its happy, then if the initiator is happy,
351                                 // it starts to send its net sync, which starts the merge, otherwise
352                                 // it sends an ERROR.
353                                 if (command == "SERVER")
354                                 {
355                                         return this->Inbound_Server(params);
356                                 }
357                                 else if (command == "ERROR")
358                                 {
359                                         return this->Error(params);
360                                 }
361                         break;
362                         case WAIT_AUTH_2:
363                                 // Waiting for start of other side's netmerge to say they liked our
364                                 // password.
365                                 if (command == "SERVER")
366                                 {
367                                         // cant do this, they sent it to us in the WAIT_AUTH_1 state!
368                                         // silently ignore.
369                                         return true;
370                                 }
371                                 else if (command == "BURST")
372                                 {
373                                         this->LinkState = CONNECTED;
374                                         Node = new TreeServer(InboundServerName,InboundDescription,TreeRoot,this);
375                                         TreeRoot->AddChild(Node);
376                                         this->DoBurst(Node);
377                                 }
378                                 else if (command == "ERROR")
379                                 {
380                                         return this->Error(params);
381                                 }
382                                 
383                         break;
384                         case LISTENER:
385                                 this->WriteLine("ERROR :Internal error -- listening socket accepted its own descriptor!!!");
386                                 return false;
387                         break;
388                         case CONNECTING:
389                                 if (command == "SERVER")
390                                 {
391                                         // another server we connected to, which was in WAIT_AUTH_1 state,
392                                         // has just sent us their credentials. If we get this far, theyre
393                                         // happy with OUR credentials, and they are now in WAIT_AUTH_2 state.
394                                         // if we're happy with this, we should send our netburst which
395                                         // kickstarts the merge.
396                                         return this->Outbound_Reply_Server(params);
397                                 }
398                         break;
399                         case CONNECTED:
400                                 // This is the 'authenticated' state, when all passwords
401                                 // have been exchanged and anything past this point is taken
402                                 // as gospel.
403                                 return true;
404                         break;  
405                 }
406                 return true;
407         }
408
409         virtual void OnTimeout()
410         {
411                 if (this->LinkState = CONNECTING)
412                 {
413                         Srv->SendOpers("*** CONNECT: Connection to "+myhost+" timed out.");
414                 }
415         }
416
417         virtual void OnClose()
418         {
419         }
420
421         virtual int OnIncomingConnection(int newsock, char* ip)
422         {
423                 TreeSocket* s = new TreeSocket(newsock);
424                 Srv->AddSocket(s);
425                 return true;
426         }
427 };
428
429 class ModuleSpanningTree : public Module
430 {
431         std::vector<TreeSocket*> Bindings;
432
433  public:
434
435         void ReadConfiguration(bool rebind)
436         {
437                 if (rebind)
438                 {
439                         for (int j =0; j < Conf->Enumerate("bind"); j++)
440                         {
441                                 std::string Type = Conf->ReadValue("bind","type",j);
442                                 std::string IP = Conf->ReadValue("bind","address",j);
443                                 long Port = Conf->ReadInteger("bind","port",j,true);
444                                 if (Type == "servers")
445                                 {
446                                         if (IP == "*")
447                                         {
448                                                 IP = "";
449                                         }
450                                         TreeSocket* listener = new TreeSocket(IP.c_str(),Port,true,10);
451                                         if (listener->GetState() == I_LISTENING)
452                                         {
453                                                 Srv->AddSocket(listener);
454                                                 Bindings.push_back(listener);
455                                         }
456                                         else
457                                         {
458                                                 log(DEFAULT,"m_spanningtree: Warning: Failed to bind server port %d",Port);
459                                                 listener->Close();
460                                                 delete listener;
461                                         }
462                                 }
463                         }
464                 }
465                 LinkBlocks.clear();
466                 for (int j =0; j < Conf->Enumerate("link"); j++)
467                 {
468                         Link L;
469                         L.Name = Conf->ReadValue("link","name",j);
470                         L.IPAddr = Conf->ReadValue("link","ipaddr",j);
471                         L.Port = Conf->ReadInteger("link","port",j,true);
472                         L.SendPass = Conf->ReadValue("link","sendpass",j);
473                         L.RecvPass = Conf->ReadValue("link","recvpass",j);
474                         LinkBlocks.push_back(L);
475                         log(DEBUG,"m_spanningtree: Read server %s with host %s:%d",L.Name.c_str(),L.IPAddr.c_str(),L.Port);
476                 }
477         }
478
479         ModuleSpanningTree()
480         {
481                 Srv = new Server;
482                 Conf = new ConfigReader;
483                 Bindings.clear();
484
485                 // Create the root of the tree
486                 TreeRoot = new TreeServer(Srv->GetServerName(),Srv->GetServerDescription());
487
488                 ReadConfiguration(true);
489         }
490
491         void HandleLinks(char** parameters, int pcnt, userrec* user)
492         {
493                 return;
494         }
495
496         void HandleLusers(char** parameters, int pcnt, userrec* user)
497         {
498                 return;
499         }
500
501         void HandleMap(char** parameters, int pcnt, userrec* user)
502         {
503                 return;
504         }
505
506         int HandleSquit(char** parameters, int pcnt, userrec* user)
507         {
508                 return 1;
509         }
510
511         int HandleConnect(char** parameters, int pcnt, userrec* user)
512         {
513                 for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
514                 {
515                         if (Srv->MatchText(x->Name.c_str(),parameters[0]))
516                         {
517                                 WriteServ(user->fd,"NOTICE %s :*** CONNECT: Connecting to server: %s (%s:%d)",user->nick,x->Name.c_str(),x->IPAddr.c_str(),x->Port);
518                                 TreeSocket* newsocket = new TreeSocket(x->IPAddr,x->Port,false,10,x->Name);
519                                 Srv->AddSocket(newsocket);
520                                 return 1;
521                         }
522                 }
523                 WriteServ(user->fd,"NOTICE %s :*** CONNECT: No matching server could be found in the config file.",user->nick);
524                 return 1;
525         }
526
527         virtual int OnPreCommand(std::string command, char **parameters, int pcnt, userrec *user)
528         {
529                 if (command == "CONNECT")
530                 {
531                         return this->HandleConnect(parameters,pcnt,user);
532                 }
533                 else if (command == "SQUIT")
534                 {
535                         return this->HandleSquit(parameters,pcnt,user);
536                 }
537                 else if (command == "MAP")
538                 {
539                         this->HandleMap(parameters,pcnt,user);
540                         return 1;
541                 }
542                 else if (command == "LUSERS")
543                 {
544                         this->HandleLusers(parameters,pcnt,user);
545                         return 1;
546                 }
547                 else if (command == "LINKS")
548                 {
549                         this->HandleLinks(parameters,pcnt,user);
550                         return 1;
551                 }
552                 return 0;
553         }
554
555         virtual ~ModuleSpanningTree()
556         {
557                 delete Srv;
558         }
559
560         virtual Version GetVersion()
561         {
562                 return Version(1,0,0,0,VF_STATIC|VF_VENDOR);
563         }
564 };
565
566
567 class ModuleSpanningTreeFactory : public ModuleFactory
568 {
569  public:
570         ModuleSpanningTreeFactory()
571         {
572         }
573         
574         ~ModuleSpanningTreeFactory()
575         {
576         }
577         
578         virtual Module * CreateModule()
579         {
580                 return new ModuleSpanningTree;
581         }
582         
583 };
584
585
586 extern "C" void * init_module( void )
587 {
588         return new ModuleSpanningTreeFactory;
589 }
590