]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_spanningtree.cpp
Added most of the connection/authentication code, need to add CONNECT and syncs
[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         
111  public:
112
113         TreeSocket(std::string host, int port, bool listening, unsigned long maxtime)
114                 : InspSocket(host, port, listening, maxtime)
115         {
116                 Srv->Log(DEBUG,"Create new");
117                 myhost = host;
118                 this->LinkState = LISTENER;
119         }
120
121         TreeSocket(int newfd)
122                 : InspSocket(newfd)
123         {
124                 this->LinkState = WAIT_AUTH_1;
125         }
126         
127         virtual bool OnConnected()
128         {
129                 return true;
130         }
131         
132         virtual void OnError(InspSocketError e)
133         {
134         }
135
136         virtual int OnDisconnect()
137         {
138                 return true;
139         }
140
141         virtual bool OnDataReady()
142         {
143                 char* data = this->Read();
144                 if (data)
145                 {
146                         this->in_buffer += data;
147                         while (in_buffer.find("\n") != std::string::npos)
148                         {
149                                 char* line = (char*)in_buffer.c_str();
150                                 std::string ret = "";
151                                 while ((*line != '\n') && (strlen(line)))
152                                 {
153                                         ret = ret + *line;
154                                         line++;
155                                 }
156                                 if ((*line == '\n') || (*line == '\r'))
157                                         line++;
158                                 in_buffer = line;
159                                 if (!this->ProcessLine(ret))
160                                 {
161                                         return false;
162                                 }
163                         }
164                 }
165                 return (data != NULL);
166         }
167
168         int WriteLine(std::string line)
169         {
170                 return this->Write(line + "\r\n");
171         }
172
173         bool Outbound_Reply_Server(std::deque<std::string> params)
174         {
175                 if (params.size() < 4)
176                         return false;
177                 std::string servername = params[0];
178                 std::string password = params[1];
179                 int hops = atoi(params[2].c_str());
180                 if (hops)
181                 {
182                         this->WriteLine("ERROR :Server too far away for authentication");
183                         return false;
184                 }
185                 std::string description = params[3];
186                 Srv->SendToModeMask("o",WM_AND,"outbound-server-replied: name='"+servername+"' pass='"+password+"' description='"+description+"'");
187                 for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
188                 {
189                         if ((x->Name == servername) && (x->RecvPass == password))
190                         {
191                                 // Begin the sync here. this kickstarts the
192                                 // other side, waiting in WAIT_AUTH_2 state,
193                                 // into starting their burst, as it shows
194                                 // that we're happy.
195                                 this->LinkState = CONNECTED;
196                                 // we should add the details of this server now
197                                 // to the servers tree, as a child of the root
198                                 // node.
199                                 TreeServer* Node = new TreeServer(servername,description,TreeRoot,this);
200                                 TreeRoot->AddChild(Node);
201                                 return true;
202                         }
203                 }
204                 this->WriteLine("ERROR :Invalid credentials");
205                 return false;
206         }
207
208         bool Inbound_Server(std::deque<std::string> params)
209         {
210                 if (params.size() < 4)
211                         return false;
212                 std::string servername = params[0];
213                 std::string password = params[1];
214                 int hops = atoi(params[2].c_str());
215                 if (hops)
216                 {
217                         this->WriteLine("ERROR :Server too far away for authentication");
218                         return false;
219                 }
220                 std::string description = params[3];
221                 Srv->SendToModeMask("o",WM_AND,"inbound-server: name='"+servername+"' pass='"+password+"' description='"+description+"'");
222                 for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
223                 {
224                         if ((x->Name == servername) && (x->RecvPass == password))
225                         {
226                                 // this is good. Send our details: Our server name and description and hopcount of 0,
227                                 // along with the sendpass from this block.
228                                 this->WriteLine("SERVER "+Srv->GetServerName()+" "+x->SendPass+" 0 :"+Srv->GetServerDescription());
229                                 // move to the next state, we are now waiting for THEM.
230                                 this->LinkState = WAIT_AUTH_2;
231                                 return true;
232                         }
233                 }
234                 this->WriteLine("ERROR :Invalid credentials");
235                 return false;
236         }
237
238         std::deque<std::string> Split(std::string line)
239         {
240                 std::deque<std::string> n;
241                 std::stringstream s(line);
242                 std::string param = "";
243                 n.clear();
244                 int item = 0;
245                 while (!s.eof())
246                 {
247                         s >> param;
248                         if ((param.c_str()[0] == ':') && (item))
249                         {
250                                 char* str = (char*)param.c_str();
251                                 str++;
252                                 param = str;
253                                 std::string append;
254                                 while (!s.eof())
255                                 {
256                                         s >> append;
257                                         if (append != "")
258                                         {
259                                                 param = param + " " + append;
260                                         }
261                                 }
262                         }
263                         item++;
264                         n.push_back(param);
265                 }
266                 return n;
267         }
268
269         bool ProcessLine(std::string line)
270         {
271                 Srv->SendToModeMask("o",WM_AND,"inbound-line: '"+line+"'");
272
273                 std::deque<std::string> params = this->Split(line);
274                 std::string command = "";
275                 std::string prefix = "";
276                 if ((params[0].c_str())[0] == ':')
277                 {
278                         prefix = params.pop_front();
279                         command = params.pop_front();
280                 }
281                 else
282                 {
283                         prefix = "";
284                         command = params.pop_front();
285                 }
286                 
287                 switch (this->LinkState)
288                 {
289                         case WAIT_AUTH_1:
290                                 // Waiting for SERVER command from remote server. Server initiating
291                                 // the connection sends the first SERVER command, listening server
292                                 // replies with theirs if its happy, then if the initiator is happy,
293                                 // it starts to send its net sync, which starts the merge, otherwise
294                                 // it sends an ERROR.
295                                 if (command == "SERVER")
296                                 {
297                                         return this->Inbound_Server(params);
298                                 }
299                         break;
300                         case WAIT_AUTH_2:
301                                 // Waiting for start of other side's netmerge to say they liked our
302                                 // password.
303                                 if (command == "SERVER")
304                                 {
305                                         // cant do this, they sent it to us in the WAIT_AUTH_1 state!
306                                         // silently ignore.
307                                         return true;
308                                 }
309                                 
310                         break;
311                         case LISTENER:
312                                 this->WriteLine("ERROR :Internal error -- listening socket accepted its own descriptor!!!");
313                                 return false;
314                         break;
315                         case CONNECTING:
316                                 if (command == "SERVER")
317                                 {
318                                         // another server we connected to, which was in WAIT_AUTH_1 state,
319                                         // has just sent us their credentials. If we get this far, theyre
320                                         // happy with OUR credentials, and they are now in WAIT_AUTH_2 state.
321                                         // if we're happy with this, we should send our netburst which
322                                         // kickstarts the merge.
323                                         return this->Outbound_Reply_Server(params);
324                                 }
325                         break;
326                         case CONNECTED:
327                                 // This is the 'authenticated' state, when all passwords
328                                 // have been exchanged and anything past this point is taken
329                                 // as gospel.
330                                 return true;
331                         break;  
332                 }
333                 return true;
334         }
335
336         virtual void OnTimeout()
337         {
338         }
339
340         virtual void OnClose()
341         {
342         }
343
344         virtual int OnIncomingConnection(int newsock, char* ip)
345         {
346                 TreeSocket* s = new TreeSocket(newsock);
347                 Srv->AddSocket(s);
348                 return true;
349         }
350 };
351
352 void tree_handle_connect(char **parameters, int pcnt, userrec *user)
353 {
354         std::string addr = parameters[0];
355         TreeSocket* sock = new TreeSocket(addr,80,false,10);
356         Srv->AddSocket(sock);
357 }
358
359 class ModuleSpanningTree : public Module
360 {
361         std::vector<TreeSocket*> Bindings;
362
363  public:
364
365         void ReadConfiguration(bool rebind)
366         {
367                 if (rebind)
368                 {
369                         for (int j =0; j < Conf->Enumerate("bind"); j++)
370                         {
371                                 std::string Type = Conf->ReadValue("bind","type",j);
372                                 std::string IP = Conf->ReadValue("bind","address",j);
373                                 long Port = Conf->ReadInteger("bind","port",j,true);
374                                 if (Type == "servers")
375                                 {
376                                         if (IP == "*")
377                                         {
378                                                 IP = "";
379                                         }
380                                         TreeSocket* listener = new TreeSocket(IP.c_str(),Port,true,10);
381                                         if (listener->GetState() == I_LISTENING)
382                                         {
383                                                 Srv->AddSocket(listener);
384                                                 Bindings.push_back(listener);
385                                         }
386                                         else
387                                         {
388                                                 log(DEFAULT,"m_spanningtree: Warning: Failed to bind server port %d",Port);
389                                                 listener->Close();
390                                                 delete listener;
391                                         }
392                                 }
393                         }
394                 }
395                 LinkBlocks.clear();
396                 for (int j =0; j < Conf->Enumerate("link"); j++)
397                 {
398                         Link L;
399                         L.Name = Conf->ReadValue("link","name",j);
400                         L.IPAddr = Conf->ReadValue("link","ipaddr",j);
401                         L.Port = Conf->ReadInteger("link","port",j,true);
402                         L.SendPass = Conf->ReadValue("link","sendpass",j);
403                         L.RecvPass = Conf->ReadValue("link","recvpass",j);
404                         LinkBlocks.push_back(L);
405                         log(DEBUG,"m_spanningtree: Read server %s with host %s:%d",L.Name.c_str(),L.IPAddr.c_str(),L.Port);
406                 }
407         }
408
409         ModuleSpanningTree()
410         {
411                 Srv = new Server;
412                 Conf = new ConfigReader;
413                 Bindings.clear();
414
415                 // Create the root of the tree
416                 TreeRoot = new TreeServer(Srv->GetServerName(),Srv->GetServerDescription());
417
418                 ReadConfiguration(true);
419         }
420
421         void HandleLinks(char** parameters, int pcnt, userrec* user)
422         {
423                 return;
424         }
425
426         void HandleLusers(char** parameters, int pcnt, userrec* user)
427         {
428                 return;
429         }
430
431         void HandleMap(char** parameters, int pcnt, userrec* user)
432         {
433                 return;
434         }
435
436         int HandleSquit(char** parameters, int pcnt, userrec* user)
437         {
438                 return 1;
439         }
440
441         int HandleConnect(char** parameters, int pcnt, userrec* user)
442         {
443                 return 1;
444         }
445
446         virtual int OnPreCommand(std::string command, char **parameters, int pcnt, userrec *user)
447         {
448                 if (command == "CONNECT")
449                 {
450                         return this->HandleConnect(parameters,pcnt,user);
451                 }
452                 else if (command == "SQUIT")
453                 {
454                         return this->HandleSquit(parameters,pcnt,user);
455                 }
456                 else if (command == "MAP")
457                 {
458                         this->HandleMap(parameters,pcnt,user);
459                         return 1;
460                 }
461                 else if (command == "LUSERS")
462                 {
463                         this->HandleLusers(parameters,pcnt,user);
464                         return 1;
465                 }
466                 else if (command == "LINKS")
467                 {
468                         this->HandleLinks(parameters,pcnt,user);
469                         return 1;
470                 }
471                 return 0;
472         }
473
474         virtual ~ModuleSpanningTree()
475         {
476                 delete Srv;
477         }
478
479         virtual Version GetVersion()
480         {
481                 return Version(1,0,0,0,VF_STATIC|VF_VENDOR);
482         }
483 };
484
485
486 class ModuleSpanningTreeFactory : public ModuleFactory
487 {
488  public:
489         ModuleSpanningTreeFactory()
490         {
491         }
492         
493         ~ModuleSpanningTreeFactory()
494         {
495         }
496         
497         virtual Module * CreateModule()
498         {
499                 return new ModuleSpanningTree;
500         }
501         
502 };
503
504
505 extern "C" void * init_module( void )
506 {
507         return new ModuleSpanningTreeFactory;
508 }
509