]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_spanningtree/treesocket2.cpp
4b5a35d498e7cc5ac0dcb090c0d5f819459f12be
[user/henk/code/inspircd.git] / src / modules / m_spanningtree / treesocket2.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2009 InspIRCd Development Team
6  * See: http://wiki.inspircd.org/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #include "inspircd.h"
15 #include "socket.h"
16 #include "xline.h"
17 #include "socketengine.h"
18
19 #include "main.h"
20 #include "utils.h"
21 #include "treeserver.h"
22 #include "link.h"
23 #include "treesocket.h"
24 #include "resolvers.h"
25
26 /* Handle ERROR command */
27 bool TreeSocket::Error(parameterlist &params)
28 {
29         if (params.size() < 1)
30                 return false;
31         ServerInstance->SNO->WriteGlobalSno('l',"ERROR from %s: %s",(!InboundServerName.empty() ? InboundServerName.c_str() : myhost.c_str()),params[0].c_str());
32         /* we will return false to cause the socket to close. */
33         return false;
34 }
35
36 void TreeSocket::Split(const std::string& line, std::string& prefix, std::string& command, parameterlist& params)
37 {
38         irc::tokenstream tokens(line);
39
40         if (!tokens.GetToken(prefix))
41                 return;
42         
43         if (prefix[0] == ':')
44         {
45                 prefix = prefix.substr(1);
46
47                 if (prefix.empty())
48                 {
49                         this->SendError("BUG (?) Empty prefix received: " + line);
50                         return;
51                 }
52                 if (!tokens.GetToken(command))
53                 {
54                         this->SendError("BUG (?) Empty command received: " + line);
55                         return;
56                 }
57         }
58         else
59         {
60                 command = prefix;
61                 prefix.clear();
62         }
63         if (command.empty())
64                 this->SendError("BUG (?) Empty command received: " + line);
65
66         std::string param;
67         while (tokens.GetToken(param))
68         {
69                 params.push_back(param);
70         }
71 }
72
73 void TreeSocket::ProcessLine(std::string &line)
74 {
75         std::string prefix;
76         std::string command;
77         parameterlist params;
78
79         ServerInstance->Logs->Log("m_spanningtree",DEBUG, "S[%d] I %s", this->GetFd(), line.c_str());
80
81         Split(line, prefix, command, params);
82
83         if (command.empty())
84                 return;
85
86         switch (this->LinkState)
87         {
88                 TreeServer* Node;
89
90                 case WAIT_AUTH_1:
91                         /*
92                          * State WAIT_AUTH_1:
93                          *  Waiting for SERVER command from remote server. Server initiating
94                          *  the connection sends the first SERVER command, listening server
95                          *  replies with theirs if its happy, then if the initiator is happy,
96                          *  it starts to send its net sync, which starts the merge, otherwise
97                          *  it sends an ERROR.
98                          */
99                         if (command == "PASS")
100                         {
101                                 /*
102                                  * Ignore this silently. Some services packages insist on sending PASS, even
103                                  * when it is not required (i.e. by us). We have to ignore this here, otherwise
104                                  * as it's an unknown command (effectively), it will cause the connection to be
105                                  * closed, which probably isn't what people want. -- w00t
106                                  */
107                         }
108                         else if (command == "SERVER")
109                         {
110                                 this->Inbound_Server(params);
111                         }
112                         else if (command == "ERROR")
113                         {
114                                 this->Error(params);
115                         }
116                         else if (command == "USER")
117                         {
118                                 this->SendError("Client connections to this port are prohibited.");
119                         }
120                         else if (command == "CAPAB")
121                         {
122                                 this->Capab(params);
123                         }
124                         else
125                         {
126                                 this->SendError(std::string("Invalid command in negotiation phase: ") + command.c_str());
127                         }
128                 break;
129                 case WAIT_AUTH_2:
130                         /*
131                          * State WAIT_AUTH_2:
132                          *  We have sent SERVER to the other side of the connection. Now we're waiting for them to start BURST.
133                          *  The other option at this stage of things, of course, is for them to close our connection thanks
134                          *  to invalid credentials.. -- w
135                          */
136                         if (command == "SERVER")
137                         {
138                                 /*
139                                  * Connection is either attempting to re-auth itself (stupid) or sending netburst without sending BURST.
140                                  * Both of these aren't allowable, so block them here. -- w
141                                  */
142                                 this->SendError("You may not re-authenticate or commence netburst without sending BURST.");
143                         }
144                         else if (command == "BURST")
145                         {
146                                 if (params.size())
147                                 {
148                                         time_t them = atoi(params[0].c_str());
149                                         time_t delta = them - ServerInstance->Time();
150                                         if ((delta < -600) || (delta > 600))
151                                         {
152                                                 ServerInstance->SNO->WriteGlobalSno('l',"\2ERROR\2: Your clocks are out by %d seconds (this is more than five minutes). Link aborted, \2PLEASE SYNC YOUR CLOCKS!\2",abs((long)delta));
153                                                 SendError("Your clocks are out by "+ConvToStr(abs((long)delta))+" seconds (this is more than five minutes). Link aborted, PLEASE SYNC YOUR CLOCKS!");
154                                                 return;
155                                         }
156                                         else if ((delta < -30) || (delta > 30))
157                                         {
158                                                 ServerInstance->SNO->WriteGlobalSno('l',"\2WARNING\2: Your clocks are out by %d seconds. Please consider synching your clocks.", abs((long)delta));
159                                         }
160                                 }
161                                 this->LinkState = CONNECTED;
162
163                                 Utils->timeoutlist.erase(this);
164                                 if (myautoconnect)
165                                 {
166                                         myautoconnect->position = -1;
167                                         myautoconnect = NULL;
168                                 }
169
170                                 Link* lnk = Utils->FindLink(InboundServerName);
171
172                                 Node = new TreeServer(this->Utils, InboundServerName, InboundDescription, InboundSID, Utils->TreeRoot, this, lnk ? lnk->Hidden : false);
173
174                                 Utils->TreeRoot->AddChild(Node);
175                                 parameterlist sparams;
176                                 sparams.push_back(InboundServerName);
177                                 sparams.push_back("*");
178                                 sparams.push_back("1");
179                                 sparams.push_back(InboundSID);
180                                 sparams.push_back(":"+InboundDescription);
181                                 Utils->DoOneToAllButSender(ServerInstance->Config->GetSID(),"SERVER",sparams,InboundServerName);
182                                 Utils->DoOneToAllButSender(prefix, "BURST", params, InboundServerName);
183                                 Node->bursting = true;
184                                 this->DoBurst(Node);
185                         }
186                         else if (command == "ERROR")
187                         {
188                                 this->Error(params);
189                         }
190                         else if (command == "CAPAB")
191                         {
192                                 this->Capab(params);
193                         }
194
195                 break;
196                 case CONNECTING:
197                         /*
198                          * State CONNECTING:
199                          *  We're connecting (OUTGOING) to another server. They are in state WAIT_AUTH_1 until they verify
200                          *  our credentials, when they proceed into WAIT_AUTH_2 and send SERVER to us. We then send BURST
201                          *  + our netburst, which will put them into CONNECTED state. -- w
202                          */
203                         if (command == "SERVER")
204                         {
205                                 // Our credentials have been accepted, send netburst. (this puts US into the CONNECTED state)
206                                 this->Outbound_Reply_Server(params);
207                         }
208                         else if (command == "ERROR")
209                         {
210                                 this->Error(params);
211                         }
212                         else if (command == "CAPAB")
213                         {
214                                 this->Capab(params);
215                         }
216                 break;
217                 case CONNECTED:
218                         /*
219                          * State CONNECTED:
220                          *  Credentials have been exchanged, we've gotten their 'BURST' (or sent ours).
221                          *  Anything from here on should be accepted a little more reasonably.
222                          */
223                         this->ProcessConnectedLine(prefix, command, params);
224                 break;
225         }
226 }
227
228 void TreeSocket::ProcessConnectedLine(std::string& prefix, std::string& command, parameterlist& params)
229 {
230         User* who = ServerInstance->FindUUID(prefix);
231         std::string direction;
232
233         if (who)
234         {
235                 direction = who->server;
236         }
237         else
238         {
239                 TreeServer* ServerSource = Utils->FindServer(prefix);
240                 if (prefix.empty())
241                         ServerSource = Utils->FindServer(GetName());
242
243                 if (ServerSource)
244                 {
245                         who = ServerSource->ServerUser;
246                         direction = prefix;
247                 }
248                 else
249                 {
250                         /* It is important that we don't close the link here, unknown prefix can occur
251                          * due to various race conditions such as the KILL message for a user somehow
252                          * crossing the users QUIT further upstream from the server. Thanks jilles!
253                          */
254                         ServerInstance->Logs->Log("m_spanningtree", DEBUG, "Command '%s' from unknown prefix '%s'! Dropping entire command.",
255                                 command.c_str(), prefix.c_str());
256                         return;
257                 }
258         }
259
260         // Make sure prefix is still good
261         prefix = who->uuid;
262
263         /*
264          * Check for fake direction here, and drop any instances that are found.
265          * What is fake direction? Imagine the following server setup:
266          *    0AA <-> 0AB <-> 0AC
267          * Fake direction would be 0AC sending a message to 0AB claiming to be from
268          * 0AA, or something similar. Basically, a message taking a path that *cannot*
269          * be correct.
270          *
271          * When would this be seen?
272          * Well, hopefully never. It could be caused by race conditions, bugs, or
273          * "miscreant" servers, though, so let's check anyway. -- w
274          *
275          * We also check here for totally invalid prefixes (prefixes that are neither
276          * a valid SID or a valid UUID, so that invalid UUID or SID never makes it
277          * to the higher level functions. -- B
278          */
279         TreeServer* route_back_again = Utils->BestRouteTo(direction);
280         if ((!route_back_again) || (route_back_again->GetSocket() != this))
281         {
282                 if (route_back_again)
283                         ServerInstance->Logs->Log("m_spanningtree",DEBUG,"Protocol violation: Fake direction '%s' from connection '%s'",
284                                 prefix.c_str(),this->GetName().c_str());
285                 return;
286         }
287
288         /*
289          * First up, check for any malformed commands (e.g. MODE without a timestamp)
290          * and rewrite commands where necessary (SVSMODE -> MODE for services). -- w
291          */
292         if (command == "SVSMODE") // This isn't in an "else if" so we still force FMODE for changes on channels.
293                 command = "MODE";
294
295         // TODO move all this into Commands
296         if (command == "UID")
297         {
298                 this->ParseUID(prefix, params);
299         }
300         else if (command == "FJOIN")
301         {
302                 this->ForceJoin(who,params);
303         }
304         else if (command == "STATS")
305         {
306                 this->Stats(prefix, params);
307         }
308         else if (command == "MOTD")
309         {
310                 this->Motd(prefix, params);
311         }
312         else if (command == "ADMIN")
313         {
314                 this->Admin(prefix, params);
315         }
316         else if (command == "MAP")
317         {
318                 Utils->Creator->HandleMap(params, who);
319         }
320         else if (command == "SERVER")
321         {
322                 this->RemoteServer(prefix,params);
323         }
324         else if (command == "ERROR")
325         {
326                 this->Error(params);
327         }
328         else if (command == "OPERTYPE")
329         {
330                 this->OperType(prefix,params);
331         }
332         else if (command == "AWAY")
333         {
334                 this->Away(prefix,params);
335         }
336         else if (command == "FMODE")
337         {
338                 this->ForceMode(who,params);
339         }
340         else if (command == "FTOPIC")
341         {
342                 this->ForceTopic(prefix,params);
343         }
344         else if (command == "METADATA")
345         {
346                 this->MetaData(prefix,params);
347         }
348         else if (command == "PING")
349         {
350                 this->LocalPing(prefix,params);
351         }
352         else if (command == "PONG")
353         {
354                 TreeServer *s = Utils->FindServer(prefix);
355                 if (s && s->bursting)
356                 {
357                         ServerInstance->SNO->WriteGlobalSno('l',"Server \002%s\002 has not finished burst, forcing end of burst (send ENDBURST!)", prefix.c_str());
358                         s->FinishBurst();
359                 }
360                 this->LocalPong(prefix,params);
361         }
362         else if (command == "VERSION")
363         {
364                 this->ServerVersion(prefix,params);
365         }
366         else if (command == "FHOST")
367         {
368                 this->ChangeHost(prefix,params);
369         }
370         else if (command == "FNAME")
371         {
372                 this->ChangeName(prefix,params);
373         }
374         else if (command == "FIDENT")
375         {
376                 this->ChangeIdent(prefix,params);
377         }
378         else if (command == "ADDLINE")
379         {
380                 this->AddLine(prefix,params);
381         }
382         else if (command == "DELLINE")
383         {
384                 this->DelLine(prefix,params);
385         }
386         else if (command == "SVSNICK")
387         {
388                 this->SVSNick(prefix,params);
389         }
390         else if (command == "SAVE")
391         {
392                 this->ForceNick(prefix,params);
393         }
394         else if (command == "OPERQUIT")
395         {
396                 this->OperQuit(prefix,params);
397         }
398         else if (command == "IDLE")
399         {
400                 this->Whois(prefix,params);
401         }
402         else if (command == "PUSH")
403         {
404                 this->Push(prefix,params);
405         }
406         else if (command == "TIME")
407         {
408                 this->Time(prefix,params);
409         }
410         else if (command == "SVSJOIN")
411         {
412                 this->ServiceJoin(prefix,params);
413         }
414         else if (command == "SVSPART")
415         {
416                 this->ServicePart(prefix,params);
417         }
418         else if (command == "SQUIT")
419         {
420                 if (params.size() == 2)
421                 {
422                         this->Squit(Utils->FindServer(params[0]),params[1]);
423                 }
424         }
425         else if (command == "MODENOTICE")
426         {
427                 if (params.size() >= 2)
428                 {
429                         ServerInstance->Users->WriteMode(params[0].c_str(), WM_AND, "*** From %s: %s",
430                                 who->nick.c_str(), params[1].c_str());
431                 }
432                 Utils->DoOneToAllButSender(prefix, command, params, prefix);
433         }
434         else if (command == "SNONOTICE")
435         {
436                 if (params.size() >= 2)
437                 {
438                         ServerInstance->SNO->WriteToSnoMask(*(params[0].c_str()), "From " + who->nick + ": "+ params[1]);
439                         Utils->DoOneToAllButSender(prefix, command, params, prefix);
440                 }
441         }
442         else if (command == "BURST")
443         {
444                 // Set prefix server as bursting
445                 TreeServer* ServerSource = Utils->FindServer(prefix);
446                 if (!ServerSource)
447                 {
448                         ServerInstance->SNO->WriteGlobalSno('l', "WTF: Got BURST from a non-server(?): %s", prefix.c_str());
449                         return;
450                 }
451
452                 ServerSource->bursting = true;
453                 Utils->DoOneToAllButSender(prefix, command, params, prefix);
454         }
455         else if (command == "ENDBURST")
456         {
457                 TreeServer* ServerSource = Utils->FindServer(prefix);
458                 if (!ServerSource)
459                 {
460                         ServerInstance->SNO->WriteGlobalSno('l', "WTF: Got ENDBURST from a non-server(?): %s", prefix.c_str());
461                         return;
462                 }
463
464                 ServerSource->FinishBurst();
465                 Utils->DoOneToAllButSender(prefix, command, params, prefix);
466         }
467         else if (command == "ENCAP")
468         {
469                 this->Encap(who, params);
470         }
471         else if (command == "NICK")
472         {
473                 if (params.size() != 2)
474                 {
475                         SendError("Protocol violation: NICK message without TS - :"+std::string(who->uuid)+" NICK "+params[0]);
476                         return;
477                 }
478
479                 if (IS_SERVER(who))
480                 {
481                         SendError("Protocol violation: Server changing nick");
482                         return;
483                 }
484
485                 /* Update timestamp on user when they change nicks */
486                 who->age = atoi(params[1].c_str());
487
488                 /*
489                  * On nick messages, check that the nick doesnt already exist here.
490                  * If it does, perform collision logic.
491                  */
492                 User* x = ServerInstance->FindNickOnly(params[0]);
493                 if ((x) && (x != who))
494                 {
495                         int collideret = 0;
496                         /* x is local, who is remote */
497                         collideret = this->DoCollision(x, who->age, who->ident, who->GetIPString(), who->uuid);
498                         if (collideret != 1)
499                         {
500                                 /*
501                                  * Remote client lost, or both lost, parsing or passing on this
502                                  * nickchange would be pointless, as the incoming client's server will
503                                  * soon recieve SVSNICK to change its nick to its UID. :) -- w00t
504                                  */
505                                 return;
506                         }
507                 }
508                 who->ForceNickChange(params[0].c_str());
509                 Utils->RouteCommand(route_back_again, command, params, who);
510         }
511         else
512         {
513                 Command* cmd = ServerInstance->Parser->GetHandler(command);
514                 CmdResult res = CMD_INVALID;
515                 if (cmd && params.size() >= cmd->min_params)
516                 {
517                         res = cmd->Handle(params, who);
518                 }
519
520                 if (res == CMD_INVALID)
521                         SendError("Unrecognised or malformed command '" + command + "' -- possibly loaded mismatched modules");
522                 if (res == CMD_SUCCESS)
523                         Utils->RouteCommand(route_back_again, command, params, who);
524         }
525 }
526
527 std::string TreeSocket::GetName()
528 {
529         std::string sourceserv = this->myhost;
530         if (!this->InboundServerName.empty())
531         {
532                 sourceserv = this->InboundServerName;
533         }
534         return sourceserv;
535 }
536
537 void TreeSocket::OnTimeout()
538 {
539         if (this->LinkState == CONNECTING)
540         {
541                 ServerInstance->SNO->WriteGlobalSno('l', "CONNECT: Connection to \002%s\002 timed out.", myhost.c_str());
542         }
543 }
544
545 void TreeSocket::Close()
546 {
547         this->BufferedSocket::Close();
548
549         // Test fix for big fuckup
550         if (this->LinkState != CONNECTED)
551                 return;
552
553         // Connection closed.
554         // If the connection is fully up (state CONNECTED)
555         // then propogate a netsplit to all peers.
556         std::string quitserver = this->myhost;
557         if (!this->InboundServerName.empty())
558         {
559                 quitserver = this->InboundServerName;
560         }
561         TreeServer* s = Utils->FindServer(quitserver);
562         if (s)
563         {
564                 Squit(s,"Remote host closed the connection");
565         }
566
567         if (!quitserver.empty())
568         {
569                 ServerInstance->SNO->WriteGlobalSno('l', "Connection to '\2%s\2' failed.",quitserver.c_str());
570
571                 time_t server_uptime = ServerInstance->Time() - this->age;
572                 if (server_uptime)
573                                 ServerInstance->SNO->WriteGlobalSno('l', "Connection to '\2%s\2' was established for %s", quitserver.c_str(), Utils->Creator->TimeToStr(server_uptime).c_str());
574         }
575 }