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