]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_spanningtree/treesocket2.cpp
some WriteGlobalSno changes
[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 = Utils->ServerUser;
246                         Utils->ServerUser->SetFakeServer(ServerSource->GetName());
247                         Utils->ServerUser->uuid = ServerSource->GetID();
248                         direction = prefix;
249                 }
250                 else
251                 {
252                         /* It is important that we don't close the link here, unknown prefix can occur
253                          * due to various race conditions such as the KILL message for a user somehow
254                          * crossing the users QUIT further upstream from the server. Thanks jilles!
255                          */
256                         ServerInstance->Logs->Log("m_spanningtree", DEBUG, "Command '%s' from unknown prefix '%s'! Dropping entire command.",
257                                 command.c_str(), prefix.c_str());
258                         return;
259                 }
260         }
261
262         // Make sure prefix is still good
263         prefix = who->uuid;
264
265         /*
266          * Check for fake direction here, and drop any instances that are found.
267          * What is fake direction? Imagine the following server setup:
268          *    0AA <-> 0AB <-> 0AC
269          * Fake direction would be 0AC sending a message to 0AB claiming to be from
270          * 0AA, or something similar. Basically, a message taking a path that *cannot*
271          * be correct.
272          *
273          * When would this be seen?
274          * Well, hopefully never. It could be caused by race conditions, bugs, or
275          * "miscreant" servers, though, so let's check anyway. -- w
276          *
277          * We also check here for totally invalid prefixes (prefixes that are neither
278          * a valid SID or a valid UUID, so that invalid UUID or SID never makes it
279          * to the higher level functions. -- B
280          */
281         TreeServer* route_back_again = Utils->BestRouteTo(direction);
282         if ((!route_back_again) || (route_back_again->GetSocket() != this))
283         {
284                 if (route_back_again)
285                         ServerInstance->Logs->Log("m_spanningtree",DEBUG,"Protocol violation: Fake direction '%s' from connection '%s'",
286                                 prefix.c_str(),this->GetName().c_str());
287                 return;
288         }
289
290         /*
291          * First up, check for any malformed commands (e.g. MODE without a timestamp)
292          * and rewrite commands where necessary (SVSMODE -> MODE for services). -- w
293          */
294         if (command == "SVSMODE") // This isn't in an "else if" so we still force FMODE for changes on channels.
295                 command = "MODE";
296
297         // TODO move all this into Commands
298         if (command == "UID")
299         {
300                 this->ParseUID(prefix, params);
301         }
302         else if (command == "FJOIN")
303         {
304                 this->ForceJoin(prefix,params);
305         }
306         else if (command == "STATS")
307         {
308                 this->Stats(prefix, params);
309         }
310         else if (command == "MOTD")
311         {
312                 this->Motd(prefix, params);
313         }
314         else if (command == "ADMIN")
315         {
316                 this->Admin(prefix, params);
317         }
318         else if (command == "MAP")
319         {
320                 Utils->Creator->HandleMap(params, who);
321         }
322         else if (command == "SERVER")
323         {
324                 this->RemoteServer(prefix,params);
325         }
326         else if (command == "ERROR")
327         {
328                 this->Error(params);
329         }
330         else if (command == "OPERTYPE")
331         {
332                 this->OperType(prefix,params);
333         }
334         else if (command == "AWAY")
335         {
336                 this->Away(prefix,params);
337         }
338         else if (command == "FMODE")
339         {
340                 this->ForceMode(prefix,params);
341         }
342         else if (command == "FTOPIC")
343         {
344                 this->ForceTopic(prefix,params);
345         }
346         else if (command == "METADATA")
347         {
348                 this->MetaData(prefix,params);
349         }
350         else if (command == "PING")
351         {
352                 this->LocalPing(prefix,params);
353         }
354         else if (command == "PONG")
355         {
356                 TreeServer *s = Utils->FindServer(prefix);
357                 if (s && s->bursting)
358                 {
359                         ServerInstance->SNO->WriteGlobalSno('l',"Server \002%s\002 has not finished burst, forcing end of burst (send ENDBURST!)", prefix.c_str());
360                         s->FinishBurst();
361                 }
362                 this->LocalPong(prefix,params);
363         }
364         else if (command == "VERSION")
365         {
366                 this->ServerVersion(prefix,params);
367         }
368         else if (command == "FHOST")
369         {
370                 this->ChangeHost(prefix,params);
371         }
372         else if (command == "FNAME")
373         {
374                 this->ChangeName(prefix,params);
375         }
376         else if (command == "FIDENT")
377         {
378                 this->ChangeIdent(prefix,params);
379         }
380         else if (command == "ADDLINE")
381         {
382                 this->AddLine(prefix,params);
383         }
384         else if (command == "DELLINE")
385         {
386                 this->DelLine(prefix,params);
387         }
388         else if (command == "SVSNICK")
389         {
390                 this->SVSNick(prefix,params);
391         }
392         else if (command == "SAVE")
393         {
394                 this->ForceNick(prefix,params);
395         }
396         else if (command == "OPERQUIT")
397         {
398                 this->OperQuit(prefix,params);
399         }
400         else if (command == "IDLE")
401         {
402                 this->Whois(prefix,params);
403         }
404         else if (command == "PUSH")
405         {
406                 this->Push(prefix,params);
407         }
408         else if (command == "TIME")
409         {
410                 this->Time(prefix,params);
411         }
412         else if (command == "SVSJOIN")
413         {
414                 this->ServiceJoin(prefix,params);
415         }
416         else if (command == "SVSPART")
417         {
418                 this->ServicePart(prefix,params);
419         }
420         else if (command == "SQUIT")
421         {
422                 if (params.size() == 2)
423                 {
424                         this->Squit(Utils->FindServer(params[0]),params[1]);
425                 }
426         }
427         else if (command == "MODENOTICE")
428         {
429                 if (params.size() >= 2)
430                 {
431                         ServerInstance->Users->WriteMode(params[0].c_str(), WM_AND, "*** From %s: %s",
432                                 who->nick.c_str(), params[1].c_str());
433                 }
434                 Utils->DoOneToAllButSender(prefix, command, params, prefix);
435         }
436         else if (command == "SNONOTICE")
437         {
438                 if (params.size() >= 2)
439                 {
440                         ServerInstance->SNO->WriteGlobalSno(*(params[0].c_str()), "From " + who->nick + ": "+ params[1]);
441                         Utils->DoOneToAllButSender(prefix, command, params, prefix);
442                 }
443         }
444         else if (command == "BURST")
445         {
446                 // Set prefix server as bursting
447                 if (!IS_SERVER(who))
448                 {
449                         ServerInstance->SNO->WriteGlobalSno('l', "WTF: Got BURST from a non-server(?): %s", prefix.c_str());
450                         return;
451                 }
452
453                 route_back_again->bursting = true;
454                 Utils->DoOneToAllButSender(prefix, command, params, prefix);
455         }
456         else if (command == "ENDBURST")
457         {
458                 TreeServer* ServerSource = Utils->FindServer(prefix);
459                 if (!ServerSource)
460                 {
461                         ServerInstance->SNO->WriteGlobalSno('l', "WTF: Got ENDBURST from a non-server(?): %s", prefix.c_str());
462                         return;
463                 }
464
465                 ServerSource->FinishBurst();
466                 Utils->DoOneToAllButSender(prefix, command, params, prefix);
467         }
468         else if (command == "ENCAP")
469         {
470                 this->Encap(prefix, params);
471         }
472         else if (command == "NICK")
473         {
474                 if (params.size() != 2)
475                 {
476                         SendError("Protocol violation: NICK message without TS - :"+std::string(who->uuid)+" NICK "+params[0]);
477                         return;
478                 }
479
480                 if (IS_SERVER(who))
481                 {
482                         SendError("Protocol violation: Server changing nick");
483                         return;
484                 }
485
486                 /* Update timestamp on user when they change nicks */
487                 who->age = atoi(params[1].c_str());
488
489                 /*
490                  * On nick messages, check that the nick doesnt already exist here.
491                  * If it does, perform collision logic.
492                  */
493                 User* x = ServerInstance->FindNickOnly(params[0]);
494                 if ((x) && (x != who))
495                 {
496                         int collideret = 0;
497                         /* x is local, who is remote */
498                         collideret = this->DoCollision(x, who->age, who->ident, who->GetIPString(), who->uuid);
499                         if (collideret != 1)
500                         {
501                                 /*
502                                  * Remote client lost, or both lost, parsing or passing on this
503                                  * nickchange would be pointless, as the incoming client's server will
504                                  * soon recieve SVSNICK to change its nick to its UID. :) -- w00t
505                                  */
506                                 return;
507                         }
508                 }
509                 who->ForceNickChange(params[0].c_str());
510                 Utils->RouteCommand(route_back_again, command, params, who);
511         }
512         else
513         {
514                 Command* cmd = ServerInstance->Parser->GetHandler(command);
515                 CmdResult res = CMD_INVALID;
516                 if (cmd && params.size() >= cmd->min_params)
517                 {
518                         res = cmd->Handle(params, who);
519                 }
520
521                 if (res == CMD_INVALID)
522                         SendError("Unrecognised or malformed command '" + command + "' -- possibly loaded mismatched modules");
523                 if (res == CMD_SUCCESS)
524                         Utils->RouteCommand(route_back_again, command, params, who);
525         }
526 }
527
528 std::string TreeSocket::GetName()
529 {
530         std::string sourceserv = this->myhost;
531         if (!this->InboundServerName.empty())
532         {
533                 sourceserv = this->InboundServerName;
534         }
535         return sourceserv;
536 }
537
538 void TreeSocket::OnTimeout()
539 {
540         if (this->LinkState == CONNECTING)
541         {
542                 ServerInstance->SNO->WriteGlobalSno('l', "CONNECT: Connection to \002%s\002 timed out.", myhost.c_str());
543         }
544 }
545
546 void TreeSocket::Close()
547 {
548         this->BufferedSocket::Close();
549
550         // Test fix for big fuckup
551         if (this->LinkState != CONNECTED)
552                 return;
553
554         // Connection closed.
555         // If the connection is fully up (state CONNECTED)
556         // then propogate a netsplit to all peers.
557         std::string quitserver = this->myhost;
558         if (!this->InboundServerName.empty())
559         {
560                 quitserver = this->InboundServerName;
561         }
562         TreeServer* s = Utils->FindServer(quitserver);
563         if (s)
564         {
565                 Squit(s,"Remote host closed the connection");
566         }
567
568         if (!quitserver.empty())
569         {
570                 ServerInstance->SNO->WriteGlobalSno('l', "Connection to '\2%s\2' failed.",quitserver.c_str());
571
572                 time_t server_uptime = ServerInstance->Time() - this->age;
573                 if (server_uptime)
574                                 ServerInstance->SNO->WriteGlobalSno('l', "Connection to '\2%s\2' was established for %s", quitserver.c_str(), Utils->Creator->TimeToStr(server_uptime).c_str());
575         }
576 }