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