]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_spanningtree/treesocket2.cpp
Move a piece of HMAC to hmac.cpp
[user/henk/code/inspircd.git] / src / modules / m_spanningtree / treesocket2.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2008 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/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 "commands/cmd_whois.h"
16 #include "commands/cmd_stats.h"
17 #include "socket.h"
18 #include "wildcard.h"
19 #include "xline.h"
20 #include "transport.h"
21 #include "socketengine.h"
22
23 #include "m_spanningtree/main.h"
24 #include "m_spanningtree/utils.h"
25 #include "m_spanningtree/treeserver.h"
26 #include "m_spanningtree/link.h"
27 #include "m_spanningtree/treesocket.h"
28 #include "m_spanningtree/resolvers.h"
29 #include "m_spanningtree/handshaketimer.h"
30
31 /* $ModDep: m_spanningtree/timesynctimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h */
32
33 static std::map<std::string, std::string> warned;       /* Server names that have had protocol violation warnings displayed for them */
34
35 int TreeSocket::WriteLine(std::string line)
36 {
37         Instance->Log(DEBUG, "S[%d] O %s", this->GetFd(), line.c_str());
38         line.append("\r\n");
39         return this->Write(line);
40 }
41
42
43 /* Handle ERROR command */
44 bool TreeSocket::Error(std::deque<std::string> &params)
45 {
46         if (params.size() < 1)
47                 return false;
48         this->Instance->SNO->WriteToSnoMask('l',"ERROR from %s: %s",(!InboundServerName.empty() ? InboundServerName.c_str() : myhost.c_str()),params[0].c_str());
49         /* we will return false to cause the socket to close. */
50         return false;
51 }
52
53 /** TODO: This creates a total mess of output and needs to really use irc::modestacker.
54  */
55 bool TreeSocket::RemoveStatus(const std::string &prefix, std::deque<std::string> &params)
56 {
57         if (params.size() < 1)
58                 return true;
59         Channel* c = Instance->FindChan(params[0]);
60         if (c)
61         {
62                 for (char modeletter = 'A'; modeletter <= 'z'; modeletter++)
63                 {
64                         ModeHandler* mh = Instance->Modes->FindMode(modeletter, MODETYPE_CHANNEL);
65                         if (mh)
66                                 mh->RemoveMode(c);
67                 }
68         }
69         return true;
70 }
71
72 bool TreeSocket::RemoteServer(const std::string &prefix, std::deque<std::string> &params)
73 {
74         if (params.size() < 5)
75         {
76                 SendError("Protocol error - Missing SID");
77                 return false;
78         }
79
80         std::string servername = params[0];
81         std::string password = params[1];
82         // hopcount is not used for a remote server, we calculate this ourselves
83         std::string sid = params[3];
84         std::string description = params[4];
85         TreeServer* ParentOfThis = Utils->FindServer(prefix);
86         if (!ParentOfThis)
87         {
88                 this->SendError("Protocol error - Introduced remote server from unknown server "+prefix);
89                 return false;
90         }
91         if (!this->Instance->IsSID(sid))
92         {
93                 this->SendError("Invalid format server ID: "+sid+"!");
94                 return false;
95         }
96         TreeServer* CheckDupe = Utils->FindServer(servername);
97         if (CheckDupe)
98         {
99                 this->SendError("Server "+servername+" already exists!");
100                 this->Instance->SNO->WriteToSnoMask('l',"Server \2"+servername+"\2 being introduced from \2" + prefix + "\2 denied, already exists. Closing link with " + prefix);
101                 return false;
102         }
103
104         Link* lnk = Utils->FindLink(servername);
105
106         TreeServer *Node = new TreeServer(this->Utils, this->Instance, servername, description, sid, ParentOfThis,NULL, lnk ? lnk->Hidden : false);
107
108         if (Node->DuplicateID())
109         {
110                 this->SendError("Server ID "+sid+" already exists on the network!");
111                 this->Instance->SNO->WriteToSnoMask('l',"Server \2"+servername+"\2 being introduced from \2" + prefix + "\2 denied, server ID already exists on the network. Closing link with " + prefix);
112                 return false;
113         }
114
115         ParentOfThis->AddChild(Node);
116         params[4] = ":" + params[4];
117         Utils->DoOneToAllButSender(prefix,"SERVER",params,prefix);
118         this->Instance->SNO->WriteToSnoMask('l',"Server \002"+prefix+"\002 introduced server \002"+servername+"\002 ("+description+")");
119         return true;
120 }
121
122 bool TreeSocket::Outbound_Reply_Server(std::deque<std::string> &params)
123 {
124         if (params.size() < 5)
125         {
126                 SendError("Protocol error - Missing SID");
127                 return false;
128         }
129
130         irc::string servername = params[0].c_str();
131         std::string sname = params[0];
132         std::string password = params[1];
133         std::string sid = params[3];
134         std::string description = params[4];
135         int hops = atoi(params[2].c_str());
136
137         this->InboundServerName = sname;
138         this->InboundDescription = description;
139         this->InboundSID = sid;
140
141         if (!sentcapab)
142                 this->SendCapabilities();
143
144         if (hops)
145         {
146                 this->SendError("Server too far away for authentication");
147                 this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, server is too far away for authentication");
148                 return false;
149         }
150
151         if (!this->Instance->IsSID(sid))
152         {
153                 this->SendError("Invalid format server ID: "+sid+"!");
154                 return false;
155         }
156
157         for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)
158         {
159                 if ((x->Name == servername) && ((ComparePass(this->MakePass(x->RecvPass,this->GetOurChallenge()),password)) || (x->RecvPass == password && (this->GetTheirChallenge().empty()))))
160                 {
161                         TreeServer* CheckDupe = Utils->FindServer(sname);
162                         if (CheckDupe)
163                         {
164                                 this->SendError("Server "+sname+" already exists on server "+CheckDupe->GetParent()->GetName()+"!");
165                                 this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, already exists on server "+CheckDupe->GetParent()->GetName());
166                                 return false;
167                         }
168                         // Begin the sync here. this kickstarts the
169                         // other side, waiting in WAIT_AUTH_2 state,
170                         // into starting their burst, as it shows
171                         // that we're happy.
172                         this->LinkState = CONNECTED;
173                         // we should add the details of this server now
174                         // to the servers tree, as a child of the root
175                         // node.
176
177                         TreeServer *Node = new TreeServer(this->Utils, this->Instance, sname, description, sid, Utils->TreeRoot, this, x->Hidden);
178
179                         if (Node->DuplicateID())
180                         {
181                                 this->SendError("Server ID "+sid+" already exists on the network!");
182                                 this->Instance->SNO->WriteToSnoMask('l',"Server \2"+assign(servername)+"\2 being introduced denied, server ID already exists on the network. Closing link.");
183                                 return false;
184                         }
185
186                         Utils->TreeRoot->AddChild(Node);
187                         params[4] = ":" + params[4];
188                         Utils->DoOneToAllButSender(Instance->Config->GetSID(),"SERVER",params,sname);
189                         Node->bursting = true;
190                         this->DoBurst(Node);
191                         return true;
192                 }
193         }
194         this->SendError("Invalid credentials");
195         this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, invalid link credentials");
196         return false;
197 }
198
199 bool TreeSocket::Inbound_Server(std::deque<std::string> &params)
200 {
201         if (params.size() < 5)
202         {
203                 SendError("Protocol error - Missing SID");
204                 return false;
205         }
206
207         irc::string servername = params[0].c_str();
208         std::string sname = params[0];
209         std::string password = params[1];
210         std::string sid = params[3];
211         std::string description = params[4];
212         int hops = atoi(params[2].c_str());
213
214         this->InboundServerName = sname;
215         this->InboundDescription = description;
216         this->InboundSID = sid;
217
218         if (!sentcapab)
219                 this->SendCapabilities();
220
221         if (hops)
222         {
223                 this->SendError("Server too far away for authentication");
224                 this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, server is too far away for authentication");
225                 return false;
226         }
227
228         if (!this->Instance->IsSID(sid))
229         {
230                 this->SendError("Invalid format server ID: "+sid+"!");
231                 return false;
232         }
233
234         for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)
235         {
236                 if ((x->Name == servername) && ((ComparePass(this->MakePass(x->RecvPass,this->GetOurChallenge()),password) || x->RecvPass == password && (this->GetTheirChallenge().empty()))))
237                 {
238                         /* Check for fully initialized instances of the server by id */
239                         Instance->Log(DEBUG,"Looking for dupe SID %s", sid.c_str());
240                         TreeServer* CheckDupeSID = Utils->FindServerID(sid);
241                         if (CheckDupeSID)
242                         {
243                                 this->SendError("Server ID "+CheckDupeSID->GetID()+" already exists on server "+CheckDupeSID->GetName()+"!");
244                                 this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, server ID '"+CheckDupeSID->GetID()+
245                                                 "' already exists on server "+CheckDupeSID->GetName());
246                                 return false;
247                         }
248                         /* Now check for fully initialized instances of the server by name */
249                         TreeServer* CheckDupe = Utils->FindServer(sname);
250                         if (CheckDupe)
251                         {
252                                 this->SendError("Server "+sname+" already exists on server "+CheckDupe->GetParent()->GetName()+"!");
253                                 this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, already exists on server "+CheckDupe->GetParent()->GetName());
254                                 return false;
255                         }
256                         this->Instance->SNO->WriteToSnoMask('l',"Verified incoming server connection from \002"+sname+"\002["+(x->HiddenFromStats ? "<hidden>" : this->GetIP())+"] ("+description+")");
257                         if (this->Hook)
258                         {
259                                 std::string name = BufferedSocketNameRequest((Module*)Utils->Creator, this->Hook).Send();
260                                 this->Instance->SNO->WriteToSnoMask('l',"Connection from \2"+sname+"\2["+(x->HiddenFromStats ? "<hidden>" : this->GetIP())+"] using transport \2"+name+"\2");
261                         }
262
263                         // this is good. Send our details: Our server name and description and hopcount of 0,
264                         // along with the sendpass from this block.
265                         this->WriteLine(std::string("SERVER ")+this->Instance->Config->ServerName+" "+this->MakePass(x->SendPass, this->GetTheirChallenge())+" 0 "+Instance->Config->GetSID()+" :"+this->Instance->Config->ServerDesc);
266                         // move to the next state, we are now waiting for THEM.
267                         this->LinkState = WAIT_AUTH_2;
268                         return true;
269                 }
270         }
271         this->SendError("Invalid credentials");
272         this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, invalid link credentials");
273         return false;
274 }
275
276 void TreeSocket::Split(const std::string &line, std::deque<std::string> &n)
277 {
278         n.clear();
279         irc::tokenstream tokens(line);
280         std::string param;
281         while (tokens.GetToken(param))
282         {
283                 n.push_back(param);
284         }
285         return;
286 }
287
288 bool TreeSocket::ProcessLine(std::string &line)
289 {
290         std::deque<std::string> params;
291         irc::string command;
292         std::string prefix;
293
294         line = line.substr(0, line.find_first_of("\r\n"));
295
296         if (line.empty())
297                 return true;
298
299         Instance->Log(DEBUG, "S[%d] I %s", this->GetFd(), line.c_str());
300
301         this->Split(line.c_str(),params);
302         
303         if (params.empty())
304                 return true;
305         
306         if ((params[0][0] == ':') && (params.size() > 1))
307         {
308                 prefix = params[0].substr(1);
309                 params.pop_front();
310                 
311                 if (prefix.empty())
312                 {
313                         this->SendError("BUG (?) Empty prefix recieved.");
314                         return false;
315                 }
316         }
317         
318         command = params[0].c_str();
319         params.pop_front();
320
321         switch (this->LinkState)
322         {
323                 TreeServer* Node;
324
325                 case WAIT_AUTH_1:
326                         /*
327                          * State WAIT_AUTH_1:
328                          *  Waiting for SERVER command from remote server. Server initiating
329                          *  the connection sends the first SERVER command, listening server
330                          *  replies with theirs if its happy, then if the initiator is happy,
331                          *  it starts to send its net sync, which starts the merge, otherwise
332                          *  it sends an ERROR.
333                          */
334                         if (command == "PASS")
335                         {
336                                 /*
337                                  * Ignore this silently. Some services packages insist on sending PASS, even
338                                  * when it is not required (i.e. by us). We have to ignore this here, otherwise
339                                  * as it's an unknown command (effectively), it will cause the connection to be
340                                  * closed, which probably isn't what people want. -- w00t
341                                  */
342                         }
343                         else if (command == "SERVER")
344                         {
345                                 return this->Inbound_Server(params);
346                         }
347                         else if (command == "ERROR")
348                         {
349                                 return this->Error(params);
350                         }
351                         else if (command == "USER")
352                         {
353                                 this->SendError("Client connections to this port are prohibited.");
354                                 return false;
355                         }
356                         else if (command == "CAPAB")
357                         {
358                                 return this->Capab(params);
359                         }
360                         else
361                         {
362                                 // XXX ...wtf.
363                                 irc::string error = "Invalid command in negotiation phase: " + command;
364                                 this->SendError(assign(error));
365                                 return false;
366                         }
367                 break;
368                 case WAIT_AUTH_2:
369                         /*
370                          * State WAIT_AUTH_2:
371                          *  We have sent SERVER to the other side of the connection. Now we're waiting for them to start BURST.
372                          *  The other option at this stage of things, of course, is for them to close our connection thanks
373                          *  to invalid credentials.. -- w
374                          */
375                         if (command == "SERVER")
376                         {
377                                 /*
378                                  * Connection is either attempting to re-auth itself (stupid) or sending netburst without sending BURST.
379                                  * Both of these aren't allowable, so block them here. -- w
380                                  */
381                                 this->SendError("You may not re-authenticate or commence netburst without sending BURST.");
382                                 return true;
383                         }
384                         else if (command == "BURST")
385                         {
386                                 if (params.size() && Utils->EnableTimeSync)
387                                 {
388                                         bool we_have_delta = (Instance->Time(false) != Instance->Time(true));
389                                         time_t them = atoi(params[0].c_str());
390                                         time_t delta = them - Instance->Time(false);
391                                         if ((delta < -600) || (delta > 600))
392                                         {
393                                                 Instance->SNO->WriteToSnoMask('l',"\2ERROR\2: Your clocks are out by %d seconds (this is more than five minutes). Link aborted, \2PLEASE SYNC YOUR CLOCKS!\2",abs(delta));
394                                                 SendError("Your clocks are out by "+ConvToStr(abs(delta))+" seconds (this is more than five minutes). Link aborted, PLEASE SYNC YOUR CLOCKS!");
395                                                 return false;
396                                         }
397                                         else if ((delta < -30) || (delta > 30))
398                                         {
399                                                 Instance->SNO->WriteToSnoMask('l',"\2WARNING\2: Your clocks are out by %d seconds. Please consider synching your clocks.", abs(delta));
400                                         }
401
402                                         if (!Utils->MasterTime && !we_have_delta)
403                                         {
404                                                 this->Instance->SetTimeDelta(delta);
405                                                 // Send this new timestamp to any other servers
406                                                 Utils->DoOneToMany(Instance->Config->GetSID(), "TIMESET", params);
407                                         }
408                                 }
409                                 this->LinkState = CONNECTED;
410                                 Link* lnk = Utils->FindLink(InboundServerName);
411
412                                 Node = new TreeServer(this->Utils, this->Instance, InboundServerName, InboundDescription, InboundSID, Utils->TreeRoot, this, lnk ? lnk->Hidden : false);
413
414                                 if (Node->DuplicateID())
415                                 {
416                                         this->SendError("Server ID "+InboundSID+" already exists on the network!");
417                                         this->Instance->SNO->WriteToSnoMask('l',"Server \2"+InboundServerName+"\2 being introduced from \2" + prefix + "\2 denied, server ID already exists on the network. Closing link.");
418                                         return false;
419                                 }
420
421                                 Utils->TreeRoot->AddChild(Node);
422                                 params.clear();
423                                 params.push_back(InboundServerName);
424                                 params.push_back("*");
425                                 params.push_back("1");
426                                 params.push_back(InboundSID);
427                                 params.push_back(":"+InboundDescription);
428                                 Utils->DoOneToAllButSender(Instance->Config->GetSID(),"SERVER",params,InboundServerName);
429                                 Node->bursting = true;
430                                 this->DoBurst(Node);
431                         }
432                         else if (command == "ERROR")
433                         {
434                                 return this->Error(params);
435                         }
436                         else if (command == "CAPAB")
437                         {
438                                 return this->Capab(params);
439                         }
440
441                 break;
442                 case LISTENER:
443                         /*
444                          * This really shouldn't happen.
445                          */
446                         this->SendError("Internal error -- listening socket accepted its own descriptor!!!");
447                         return false;
448                 break;
449                 case CONNECTING:
450                         /*
451                          * State CONNECTING:
452                          *  We're connecting (OUTGOING) to another server. They are in state WAIT_AUTH_1 until they verify
453                          *  our credentials, when they proceed into WAIT_AUTH_2 and send SERVER to us. We then send BURST
454                          *  + our netburst, which will put them into CONNECTED state. -- w
455                          */
456                         if (command == "SERVER")
457                         {
458                                 // Our credentials have been accepted, send netburst. (this puts US into the CONNECTED state)
459                                 return this->Outbound_Reply_Server(params);
460                         }
461                         else if (command == "ERROR")
462                         {
463                                 return this->Error(params);
464                         }
465                         else if (command == "CAPAB")
466                         {
467                                 return this->Capab(params);
468                         }
469                 break;
470                 case CONNECTED:
471                         /*
472                         * State CONNECTED:
473                          *  Credentials have been exchanged, we've gotten their 'BURST' (or sent ours).
474                          *  Anything from here on should be accepted a little more reasonably.
475                          */
476                         if (!prefix.empty())
477                         {
478                                 /*
479                                  * Check for fake direction here, and drop any instances that are found.
480                                  * What is fake direction? Imagine the following server setup:
481                                  *    0AA <-> 0AB <-> 0AC
482                                  * Fake direction would be 0AC sending a message to 0AB claiming to be from
483                                  * 0AA, or something similar. Basically, a message taking a path that *cannot*
484                                  * be correct.
485                                  *
486                                  * When would this be seen?
487                                  * Well, hopefully never. It could be caused by race conditions, bugs, or
488                                  * "miscreant" servers, though, so let's check anyway. -- w
489                                  */
490                                 std::string direction = prefix;
491
492                                 User *t = this->Instance->FindUUID(prefix);
493                                 if (t)
494                                 {
495                                         direction = t->server;
496                                 }
497
498                                 TreeServer* route_back_again = Utils->BestRouteTo(direction);
499                                 if ((!route_back_again) || (route_back_again->GetSocket() != this))
500                                 {
501                                         if (route_back_again)
502                                                 Instance->Log(DEBUG,"Protocol violation: Fake direction in command '%s' from connection '%s'",line.c_str(),this->GetName().c_str());
503                                         return true;
504                                 }
505                                 /* Fix by brain:
506                                  * When there is activity on the socket, reset the ping counter so
507                                  * that we're not wasting bandwidth pinging an active server.
508                                  */
509                                 route_back_again->SetNextPingTime(time(NULL) + Utils->PingFreq);
510                                 route_back_again->SetPingFlag();
511                         }
512                         else
513                         {
514                                 /*
515                                  * Empty prefix from a server to server link:
516                                  *  This is somewhat bad/naughty, so let's set the prefix
517                                  *  to be the link that we got it from, so we don't break anything. -- w
518                                  */
519                                 TreeServer* n = Utils->FindServer(GetName());
520                                 if (n)
521                                         prefix = n->GetID();
522                                 else
523                                         prefix = GetName();
524                         }
525
526                         /*
527                          * First up, check for any malformed commands (e.g. MODE without a timestamp)
528                          * and rewrite commands where necessary (SVSMODE -> MODE for services). -- w
529                          */
530                         if (command == "MODE")
531                         {
532                                 if (params.size() >= 2)
533                                 {
534                                         Channel* channel = Instance->FindChan(params[0]);
535                                         if (channel)
536                                         {
537                                                 User* x = Instance->FindNick(prefix);
538                                                 if (x)
539                                                 {
540                                                         if (warned.find(x->server) == warned.end())
541                                                         {
542                                                                 Instance->Log(DEFAULT,"WARNING: I revceived modes '%s' from another server '%s'. This is not compliant with InspIRCd. Please check that server for bugs.", params[1].c_str(), x->server);
543                                                                 Instance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending nonstandard modes: '%s MODE %s' where FMODE should be used, and may cause desyncs.", x->server, x->nick, params[1].c_str());
544                                                                 warned[x->server] = x->nick;
545                                                         }
546                                                 }
547                                         }
548                                 }
549                         }
550                         else if (command == "SVSMODE")
551                         {
552                                 command = "MODE";
553                         }
554
555
556                         /*
557                          * Now, check for (and parse) commands as appropriate. -- w
558                          */     
559                 
560                         /* Find the server that this command originated from, used in the handlers below */
561                         TreeServer *ServerSource = Utils->FindServer(prefix);
562
563                         /* Find the link we just got this from so we don't bounce it back incorrectly */
564                         std::string sourceserv = this->myhost;
565                         if (!this->InboundServerName.empty())
566                         {
567                                 sourceserv = this->InboundServerName;
568                         }
569
570                         /*
571                          * XXX one of these days, this needs to be moved into class Commands.
572                          */
573                         if (command == "UID")
574                         {
575                                 return this->ParseUID(prefix, params);
576                         }
577                         else if (command == "FJOIN")
578                         {
579                                 return this->ForceJoin(prefix,params);
580                         }
581                         else if (command == "STATS")
582                         {
583                                 return this->Stats(prefix, params);
584                         }
585                         else if (command == "MOTD")
586                         {
587                                 return this->Motd(prefix, params);
588                         }
589                         else if (command == "KILL" && ServerSource)
590                         {
591                                 // Kill from a server
592                                 return this->RemoteKill(prefix,params);
593                         }
594                         else if (command == "MODULES")
595                         {
596                                 return this->Modules(prefix, params);
597                         }
598                         else if (command == "ADMIN")
599                         {
600                                 return this->Admin(prefix, params);
601                         }
602                         else if (command == "SERVER")
603                         {
604                                 return this->RemoteServer(prefix,params);
605                         }
606                         else if (command == "ERROR")
607                         {
608                                 return this->Error(params);
609                         }
610                         else if (command == "OPERTYPE")
611                         {
612                                 return this->OperType(prefix,params);
613                         }
614                         else if (command == "FMODE")
615                         {
616                                 return this->ForceMode(prefix,params);
617                         }
618                         else if (command == "FTOPIC")
619                         {
620                                 return this->ForceTopic(prefix,params);
621                         }
622                         else if (command == "REHASH")
623                         {
624                                 return this->RemoteRehash(prefix,params);
625                         }
626                         else if (command == "METADATA")
627                         {
628                                 return this->MetaData(prefix,params);
629                         }
630                         else if (command == "REMSTATUS")
631                         {
632                                 return this->RemoveStatus(prefix,params);
633                         }
634                         else if (command == "PING")
635                         {
636                                 return this->LocalPing(prefix,params);
637                         }
638                         else if (command == "PONG")
639                         {
640                                 return this->LocalPong(prefix,params);
641                         }
642                         else if (command == "VERSION")
643                         {
644                                 return this->ServerVersion(prefix,params);
645                         }
646                         else if (command == "FHOST")
647                         {
648                                 return this->ChangeHost(prefix,params);
649                         }
650                         else if (command == "FNAME")
651                         {
652                                 return this->ChangeName(prefix,params);
653                         }
654                         else if (command == "ADDLINE")
655                         {
656                                 return this->AddLine(prefix,params);
657                         }
658                         else if (command == "DELLINE")
659                         {
660                                 return this->DelLine(prefix,params);
661                         }
662                         else if (command == "SVSNICK")
663                         {
664                                 return this->ForceNick(prefix,params);
665                         }
666                         else if (command == "OPERQUIT")
667                         {
668                                 return this->OperQuit(prefix,params);
669                         }
670                         else if (command == "IDLE")
671                         {
672                                 return this->Whois(prefix,params);
673                         }
674                         else if (command == "PUSH")
675                         {
676                                 return this->Push(prefix,params);
677                         }
678                         else if (command == "TIMESET")
679                         {
680                                 return this->HandleSetTime(prefix, params);
681                         }
682                         else if (command == "TIME")
683                         {
684                                 return this->Time(prefix,params);
685                         }
686                         else if ((command == "KICK") && (Utils->IsServer(prefix)))
687                         {
688                                 if (params.size() == 3)
689                                 {
690                                         User* user = this->Instance->FindNick(params[1]);
691                                         Channel* chan = this->Instance->FindChan(params[0]);
692                                         if (user && chan)
693                                         {
694                                                 if (!chan->ServerKickUser(user, params[2].c_str(), false))
695                                                         /* Yikes, the channels gone! */
696                                                         delete chan;
697                                         }
698                                 }
699
700                                 return Utils->DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params);
701                         }
702                         else if (command == "SVSJOIN")
703                         {
704                                 return this->ServiceJoin(prefix,params);
705                         }
706                         else if (command == "SVSPART")
707                         {
708                                 return this->ServicePart(prefix,params);
709                         }
710                         else if (command == "SQUIT")
711                         {
712                                 if (params.size() == 2)
713                                 {
714                                         this->Squit(Utils->FindServer(params[0]),params[1]);
715                                 }
716                                 return true;
717                         }
718                         else if (command == "OPERNOTICE")
719                         {
720                                 if (params.size() >= 1)
721                                         Instance->SNO->WriteToSnoMask('A', "From " + prefix + ": " + params[0]);
722                                 return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params);
723                         }
724                         else if (command == "MODENOTICE")
725                         {
726                                 if (params.size() >= 2)
727                                 {
728                                         Instance->Users->WriteMode(params[0].c_str(), WM_AND, "*** From %s: %s", prefix.c_str(), params[1].c_str());
729                                 }
730                                 return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params);
731                         }
732                         else if (command == "SNONOTICE")
733                         {
734                                 if (params.size() >= 2)
735                                 {
736                                         Instance->SNO->WriteToSnoMask(*(params[0].c_str()), "From " + prefix + ": "+ params[1]);
737                                 }
738                                 return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params);
739                         }
740                         else if (command == "BURST")
741                         {
742                                 // Set prefix server as bursting
743                                 if (!ServerSource)
744                                 {
745                                         this->Instance->SNO->WriteToSnoMask('l', "WTF: Got BURST from a nonexistant server(?): %s", prefix.c_str());
746                                         return false;
747                                 }
748                                 
749                                 ServerSource->bursting = true;
750                                 return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params);
751                         }
752                         else if (command == "ENDBURST")
753                         {
754                                 if (!ServerSource)
755                                 {
756                                         this->Instance->SNO->WriteToSnoMask('l', "WTF: Got ENDBURST from a nonexistant server(?): %s", prefix.c_str());
757                                         return false;
758                                 }
759                                 
760                                 ServerSource->FinishBurst();
761                                 return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params);
762                         }
763                         else if (command == "MODE")
764                         {
765                                 // Server-prefix MODE.
766                                 const char* modelist[MAXPARAMETERS];
767                                 for (size_t i = 0; i < params.size(); i++)
768                                         modelist[i] = params[i].c_str();
769                                         
770                                 // Insert into the parser
771                                 this->Instance->SendMode(modelist, params.size(), this->Instance->FakeClient);
772                                 
773                                 // Pass out to the network
774                                 return Utils->DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params);
775                         }
776                         else
777                         {
778                                 /*
779                                  * Not a special s2s command. Emulate the user doing it.
780                                  * This saves us having a huge ugly command parser again.
781                                  */
782                                 User *who = this->Instance->FindUUID(prefix);
783
784                                 if (!who)
785                                 {
786                                         // this looks ugly because command is an irc::string
787                                         this->SendError("Command (" + std::string(command.c_str()) + ") from unknown prefix (" + prefix + ")! Dropping link.");
788                                         return false;
789                                 }
790
791                                 if (command == "NICK")
792                                 {
793                                         if (params.size() != 2)
794                                         {
795                                                 SendError("Protocol violation: NICK message without TS - :"+std::string(who->uuid)+" NICK "+params[0]);
796                                                 return false;
797                                         }
798                                         /* Update timestamp on user when they change nicks */
799                                         who->age = atoi(params[1].c_str());
800
801                                         /*
802                                          * On nick messages, check that the nick doesnt already exist here.
803                                          * If it does, perform collision logic.
804                                          */
805                                         User* x = this->Instance->FindNickOnly(params[0]);
806                                         if ((x) && (x != who))
807                                         {
808                                                 int collideret = 0;
809                                                 /* x is local, who is remote */
810                                                 collideret = this->DoCollision(x, who->age, who->ident, who->GetIPString(), who->uuid);
811                                                 if (collideret != 1)
812                                                 {
813                                                         /*
814                                                          * Remote client lost, or both lost, parsing this nickchange would be
815                                                          * pointless, as the incoming client's server will soon recieve SVSNICK to
816                                                          * change its nick to its UID. :) -- w00t
817                                                          */
818                                                         return true;
819                                                 }
820                                         }
821                                 }
822                                         
823                                 // its a user
824                                 const char* strparams[127];
825                                 for (unsigned int q = 0; q < params.size(); q++)
826                                 {
827                                         strparams[q] = params[q].c_str();
828                                 }
829
830                                 switch (this->Instance->CallCommandHandler(command.c_str(), strparams, params.size(), who))
831                                 {
832                                         case CMD_INVALID:
833                                                 // command is irc::string, hence ugliness
834                                                 this->SendError("Unrecognised or malformed command '" + std::string(command.c_str()) + "' -- possibly loaded mismatched modules");
835                                                 return false;
836                                                 break;
837                                         /*
838                                          * CMD_LOCALONLY is aliased to CMD_FAILURE, so this won't go out onto the network.
839                                          */
840                                         case CMD_FAILURE:
841                                                 return true;
842                                                 break;
843                                         default:
844                                                 /* CMD_SUCCESS and CMD_USER_DELETED fall through here */
845                                                 break;
846                                 }
847
848                                 return Utils->DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params);
849
850                         }
851                         return true;
852                         break; // end of state CONNECTED (phew).
853         }
854         return true;
855 }
856
857 std::string TreeSocket::GetName()
858 {
859         std::string sourceserv = this->myhost;
860         if (!this->InboundServerName.empty())
861         {
862                 sourceserv = this->InboundServerName;
863         }
864         return sourceserv;
865 }
866
867 void TreeSocket::OnTimeout()
868 {
869         if (this->LinkState == CONNECTING)
870         {
871                 Utils->Creator->RemoteMessage(NULL, "CONNECT: Connection to \002%s\002 timed out.", myhost.c_str());
872                 Link* MyLink = Utils->FindLink(myhost);
873                 if (MyLink)
874                         Utils->DoFailOver(MyLink);
875         }
876 }
877
878 void TreeSocket::OnClose()
879 {
880         // Test fix for big fuckup
881         if (this->LinkState != CONNECTED)
882                 return;
883
884         // Connection closed.
885         // If the connection is fully up (state CONNECTED)
886         // then propogate a netsplit to all peers.
887         std::string quitserver = this->myhost;
888         if (!this->InboundServerName.empty())
889         {
890                 quitserver = this->InboundServerName;
891         }
892         TreeServer* s = Utils->FindServer(quitserver);
893         if (s)
894         {
895                 Squit(s,"Remote host closed the connection");
896         }
897
898         if (!quitserver.empty())
899         {
900                 Utils->Creator->RemoteMessage(NULL,"Connection to '\2%s\2' failed.",quitserver.c_str());
901                 time_t server_uptime = Instance->Time() - this->age;    
902                 if (server_uptime)
903                         Utils->Creator->RemoteMessage(NULL,"Connection to '\2%s\2' was established for %s", quitserver.c_str(), Utils->Creator->TimeToStr(server_uptime).c_str());
904         }
905 }
906
907 int TreeSocket::OnIncomingConnection(int newsock, char* ip)
908 {
909         /* To prevent anyone from attempting to flood opers/DDoS by connecting to the server port,
910          * or discovering if this port is the server port, we don't allow connections from any
911          * IPs for which we don't have a link block.
912          */
913         bool found = false;
914
915         found = (std::find(Utils->ValidIPs.begin(), Utils->ValidIPs.end(), ip) != Utils->ValidIPs.end());
916         if (!found)
917         {
918                 for (std::vector<std::string>::iterator i = Utils->ValidIPs.begin(); i != Utils->ValidIPs.end(); i++)
919                         if (irc::sockets::MatchCIDR(ip, (*i).c_str()))
920                                 found = true;
921
922                 if (!found)
923                 {
924                         Utils->Creator->RemoteMessage(NULL,"Server connection from %s denied (no link blocks with that IP address)", ip);
925                         Instance->SE->Close(newsock);
926                         return false;
927                 }
928         }
929
930         TreeSocket* s = new TreeSocket(this->Utils, this->Instance, newsock, ip, this->Hook);
931         s = s; /* Whinge whinge whinge, thats all GCC ever does. */
932         return true;
933 }