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