Author: Isomer <isomer@coders.net>
[ircu2.10.12-pk.git] / ircd / m_server.c
1 /*
2  * IRC - Internet Relay Chat, ircd/m_server.c
3  * Copyright (C) 1990 Jarkko Oikarinen and
4  *                    University of Oulu, Computing Center
5  *
6  * See file AUTHORS in IRC package for additional names of
7  * the programmers.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 1, or (at your option)
12  * any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  *
23  * $Id$
24  */
25
26 /*
27  * m_functions execute protocol messages on this server:
28  *
29  *    cptr    is always NON-NULL, pointing to a *LOCAL* client
30  *            structure (with an open socket connected!). This
31  *            identifies the physical socket where the message
32  *            originated (or which caused the m_function to be
33  *            executed--some m_functions may call others...).
34  *
35  *    sptr    is the source of the message, defined by the
36  *            prefix part of the message if present. If not
37  *            or prefix not found, then sptr==cptr.
38  *
39  *            (!IsServer(cptr)) => (cptr == sptr), because
40  *            prefixes are taken *only* from servers...
41  *
42  *            (IsServer(cptr))
43  *                    (sptr == cptr) => the message didn't
44  *                    have the prefix.
45  *
46  *                    (sptr != cptr && IsServer(sptr) means
47  *                    the prefix specified servername. (?)
48  *
49  *                    (sptr != cptr && !IsServer(sptr) means
50  *                    that message originated from a remote
51  *                    user (not local).
52  *
53  *            combining
54  *
55  *            (!IsServer(sptr)) means that, sptr can safely
56  *            taken as defining the target structure of the
57  *            message in this server.
58  *
59  *    *Always* true (if 'parse' and others are working correct):
60  *
61  *    1)      sptr->from == cptr  (note: cptr->from == cptr)
62  *
63  *    2)      MyConnect(sptr) <=> sptr == cptr (e.g. sptr
64  *            *cannot* be a local connection, unless it's
65  *            actually cptr!). [MyConnect(x) should probably
66  *            be defined as (x == x->from) --msa ]
67  *
68  *    parc    number of variable parameter strings (if zero,
69  *            parv is allowed to be NULL)
70  *
71  *    parv    a NULL terminated list of parameter pointers,
72  *
73  *                    parv[0], sender (prefix string), if not present
74  *                            this points to an empty string.
75  *                    parv[1]...parv[parc-1]
76  *                            pointers to additional parameters
77  *                    parv[parc] == NULL, *always*
78  *
79  *            note:   it is guaranteed that parv[0]..parv[parc-1] are all
80  *                    non-NULL pointers.
81  */
82 #include "config.h"
83
84 #include "client.h"
85 #include "hash.h"
86 #include "ircd.h"
87 #include "ircd_log.h"
88 #include "ircd_features.h"
89 #include "ircd_reply.h"
90 #include "ircd_string.h"
91 #include "jupe.h"
92 #include "list.h"
93 #include "match.h"
94 #include "msg.h"
95 #include "numeric.h"
96 #include "numnicks.h"
97 #include "querycmds.h"
98 #include "s_bsd.h"
99 #include "s_conf.h"
100 #include "s_debug.h"
101 #include "s_misc.h"
102 #include "s_serv.h"
103 #include "send.h"
104 #include "userload.h"
105
106 #include <assert.h>
107 #include <stdlib.h>
108 #include <string.h>
109
110 /*
111  * mr_server - registration message handler
112  *
113  *    parv[0] = sender prefix
114  *    parv[1] = servername
115  *    parv[2] = hopcount
116  *    parv[3] = start timestamp
117  *    parv[4] = link timestamp
118  *    parv[5] = major protocol version: P09/P10
119  *    parv[parc-1] = serverinfo
120  *  If cptr is P10:
121  *    parv[6] = "YMM", where 'Y' is the server numeric and "MM" is the
122  *              numeric nick mask of this server.
123  *    parv[7] = +hs (h == hub, s == service)
124  */
125 int mr_server(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
126 {
127   char*            ch;
128   int              i;
129   char             info[REALLEN + 1];
130   char*            host;
131   struct Client*   acptr;
132   struct Client*   bcptr;
133   struct Client*   LHcptr = 0;
134   struct ConfItem* aconf = 0;
135   struct ConfItem* lhconf = 0;
136   struct Jupe*     ajupe = 0;
137   int              hop;
138   int              ret;
139   int              active_lh_line = 0;
140   unsigned short   prot;
141   time_t           start_timestamp;
142   time_t           timestamp = 0;
143   time_t           recv_time;
144   time_t           ghost = 0;
145
146   if (IsUserPort(cptr))
147     return exit_client_msg(cptr, cptr, &me, 
148                            "Cannot connect a server to a user port");
149
150   recv_time = TStime();
151   info[0] = '\0';
152
153   if (parc < 7)
154   {
155     need_more_params(sptr, "SERVER");
156     return exit_client(cptr, cptr, &me, "Need more parameters");
157   }
158   host = parv[1];
159
160   if ((ajupe = jupe_find(host)) && JupeIsActive(ajupe))
161     return exit_client_msg(cptr, sptr, &me, "Juped: %s", JupeReason(ajupe));
162
163   log_write(LS_NETWORK, L_NOTICE, LOG_NOSNOTICE, "SERVER: %s %s[%s]", parv[1],
164             cli_sockhost(cptr), cli_sock_ip(cptr));
165
166   /*
167    * Detect protocol
168    */
169   if (strlen(parv[5]) != 3 || (parv[5][0] != 'P' && parv[5][0] != 'J'))
170     return exit_client_msg(cptr, sptr, &me, "Bogus protocol (%s)", parv[5]);
171
172   *parv[5] = 'J';
173
174   if (*parv[7] == '+') {
175     for (ch = parv[7] + 1; *ch; ch++)
176       switch (*ch) {
177       case 'h':
178         SetHub(cptr);
179         break;
180       case 's':
181         SetService(cptr);
182         break;
183       }
184   }
185
186   prot = atoi(parv[5] + 1);
187   if (prot > atoi(MAJOR_PROTOCOL))
188     prot = atoi(MAJOR_PROTOCOL);
189
190   hop = atoi(parv[2]);
191   start_timestamp = atoi(parv[3]);
192   timestamp = atoi(parv[4]);
193   Debug((DEBUG_INFO, "Got SERVER %s with timestamp [%s] age %Tu (%Tu)",
194          host, parv[4], start_timestamp, cli_serv(&me)->timestamp));
195
196   if ((timestamp < OLDEST_TS || (hop == 1 && start_timestamp < OLDEST_TS)))
197   {
198     return exit_client_msg(cptr, sptr, &me,
199         "Bogus timestamps (%s %s)", parv[3], parv[4]);
200   }
201   ircd_strncpy(info, parv[parc - 1], REALLEN);
202   info[REALLEN] = '\0';
203   if (prot < atoi(MINOR_PROTOCOL)) {
204     sendto_opmask_butone(0, SNO_OLDSNO, "Got incompatible protocol version "
205                          "(%s) from %s", parv[5], cli_name(cptr));
206     return exit_new_server(cptr, sptr, host, timestamp,
207                            "Incompatible protocol: %s", parv[5]);
208   }
209   /*
210    * Check for "FRENCH " infection ;-) (actually this should
211    * be replaced with routine to check the hostname syntax in
212    * general). [ This check is still needed, even after the parse
213    * is fixed, because someone can send "SERVER :foo bar " ].
214    * Also, changed to check other "difficult" characters, now
215    * that parse lets all through... --msa
216    */
217   if (strlen(host) > HOSTLEN)
218     host[HOSTLEN] = '\0';
219
220   for (ch = host; *ch; ch++) {
221     if (*ch <= ' ' || *ch > '~')
222       break;
223   }
224   if (*ch || !strchr(host, '.')) {
225     sendto_opmask_butone(0, SNO_OLDSNO, "Bogus server name (%s) from %s",
226                          host, cli_name(cptr));
227     return exit_client_msg(cptr, cptr, &me, "Bogus server name (%s)", host);
228   }
229
230   if (IsUnknown(cptr) || IsHandshake(cptr))
231   {
232     const char* encr;
233
234     /*
235      * A local link that is still in undefined state wants
236      * to be a SERVER. Check if this is allowed and change
237      * status accordingly...
238      */
239     /*
240      * If there is more then one server on the same machine
241      * that we try to connect to, it could be that the /CONNECT
242      * <mask> caused this connect to be put at the wrong place
243      * in the hashtable.        --Run
244      * Same thing for Unknown connections that first send NICK.
245      *                          --Xorath
246      * Better check if the two strings are (caseless) identical 
247      * and not mess with hash internals. 
248      *                          --Nemesi
249      */
250     if (!EmptyString(cli_name(cptr)) && 
251         (IsUnknown(cptr) || IsHandshake(cptr)) &&
252         0 != ircd_strcmp(cli_name(cptr), host))
253       hChangeClient(cptr, host);
254     ircd_strncpy(cli_name(cptr), host, HOSTLEN);
255     ircd_strncpy(cli_info(cptr), info[0] ? info : cli_name(&me), REALLEN);
256     cli_hopcount(cptr) = hop;
257
258     /* check connection rules */
259     if (0 != conf_eval_crule(host, CRULE_ALL)) {
260       ServerStats->is_ref++;
261       sendto_opmask_butone(0, SNO_OLDSNO, "Refused connection from %s.", cli_name(cptr));
262       return exit_client(cptr, cptr, &me, "Disallowed by connection rule");
263     }
264
265     if (conf_check_server(cptr)) {
266       ++ServerStats->is_ref;
267       sendto_opmask_butone(0, SNO_OLDSNO, "Received unauthorized connection "
268                            "from %s.", cli_name(cptr));
269       return exit_client(cptr, cptr, &me, "No C:line");
270     }
271
272     host = cli_name(cptr);
273
274     update_load();
275
276     if (!(aconf = find_conf_byname(cli_confs(cptr), host, CONF_SERVER))) {
277       ++ServerStats->is_ref;
278       sendto_opmask_butone(0, SNO_OLDSNO, "Access denied. No conf line for "
279                            "server %s", cli_name(cptr));
280       return exit_client_msg(cptr, cptr, &me,
281           "Access denied. No conf line for server %s", cli_name(cptr));
282     }
283     encr = cli_passwd(cptr);
284
285     if (*aconf->passwd && !!strcmp(aconf->passwd, encr)) {
286       ++ServerStats->is_ref;
287       sendto_opmask_butone(0, SNO_OLDSNO, "Access denied (passwd mismatch) %s",
288                            cli_name(cptr));
289       return exit_client_msg(cptr, cptr, &me,
290           "No Access (passwd mismatch) %s", cli_name(cptr));
291     }
292
293     memset(cli_passwd(cptr), 0, sizeof(cli_passwd(cptr)));
294
295     if (!feature_bool(FEAT_HUB)) {
296       for (i = 0; i <= HighestFd; i++)
297         if (LocalClientArray[i] && IsServer(LocalClientArray[i])) {
298           active_lh_line = 3;
299           LHcptr = 0;
300           break;
301         }
302     }
303   }
304
305   /*
306    *  We want to find IsConnecting() and IsHandshake() too,
307    *  use FindClient().
308    *  The second finds collisions with numeric representation of existing
309    *  servers - these shouldn't happen anymore when all upgraded to 2.10.
310    *  -- Run
311    */
312   while ((acptr = FindClient(host)) || 
313          (parc > 7 && (acptr = FindNServer(parv[6]))))
314   {
315     /*
316      *  This link is trying feed me a server that I already have
317      *  access through another path
318      *
319      *  Do not allow Uworld to do this.
320      *  Do not allow servers that are juped.
321      *  Do not allow servers that have older link timestamps
322      *    then this try.
323      *  Do not allow servers that use the same numeric as an existing
324      *    server, but have a different name.
325      *
326      *  If my ircd.conf sucks, I can try to connect to myself:
327      */
328     if (acptr == &me)
329       return exit_client_msg(cptr, cptr, &me, "nick collision with me (%s), check server number in M:?", host);
330     /*
331      * Detect wrong numeric.
332      */
333     if (0 != ircd_strcmp(cli_name(acptr), host)) {
334       sendcmdto_serv_butone(&me, CMD_WALLOPS, cptr,
335                             ":SERVER Numeric Collision: %s != %s",
336                             cli_name(acptr), host);
337       return exit_client_msg(cptr, cptr, &me,
338           "NUMERIC collision between %s and %s."
339           " Is your server numeric correct ?", host, cli_name(acptr));
340     }
341     /*
342      *  Kill our try, if we had one.
343      */
344     if (IsConnecting(acptr))
345     {
346       if (!active_lh_line && exit_client(cptr, acptr, &me,
347           "Just connected via another link") == CPTR_KILLED)
348         return CPTR_KILLED;
349       /*
350        * We can have only ONE 'IsConnecting', 'IsHandshake' or
351        * 'IsServer', because new 'IsConnecting's are refused to
352        * the same server if we already had it.
353        */
354       break;
355     }
356     /*
357      * Avoid other nick collisions...
358      * This is a doubtfull test though, what else would it be
359      * when it has a server.name ?
360      */
361     else if (!IsServer(acptr) && !IsHandshake(acptr))
362       return exit_client_msg(cptr, cptr, &me,
363                              "Nickname %s already exists!", host);
364     /*
365      * Our new server might be a juped server,
366      * or someone trying abuse a second Uworld:
367      */
368     else if (IsServer(acptr) && (0 == ircd_strncmp(cli_info(acptr), "JUPE", 4) ||
369         find_conf_byhost(cli_confs(cptr), cli_name(acptr), CONF_UWORLD)))
370     {
371       if (!IsServer(sptr))
372         return exit_client(cptr, sptr, &me, cli_info(acptr));
373       sendcmdto_serv_butone(&me, CMD_WALLOPS, cptr,
374                             ":Received :%s SERVER %s from %s !?!", parv[0],
375                             parv[1], cli_name(cptr));
376       return exit_new_server(cptr, sptr, host, timestamp, "%s", cli_info(acptr));
377     }
378     /*
379      * Of course we find the handshake this link was before :)
380      */
381     else if (IsHandshake(acptr) && acptr == cptr)
382       break;
383     /*
384      * Here we have a server nick collision...
385      * We don't want to kill the link that was last /connected,
386      * but we neither want to kill a good (old) link.
387      * Therefor we kill the second youngest link.
388      */
389     if (1)
390     {
391       struct Client* c2ptr = 0;
392       struct Client* c3ptr = acptr;
393       struct Client* ac2ptr;
394       struct Client* ac3ptr;
395
396       /* Search youngest link: */
397       for (ac3ptr = acptr; ac3ptr != &me; ac3ptr = cli_serv(ac3ptr)->up)
398         if (cli_serv(ac3ptr)->timestamp > cli_serv(c3ptr)->timestamp)
399           c3ptr = ac3ptr;
400       if (IsServer(sptr))
401       {
402         for (ac3ptr = sptr; ac3ptr != &me; ac3ptr = cli_serv(ac3ptr)->up)
403           if (cli_serv(ac3ptr)->timestamp > cli_serv(c3ptr)->timestamp)
404             c3ptr = ac3ptr;
405       }
406       if (timestamp > cli_serv(c3ptr)->timestamp)
407       {
408         c3ptr = 0;
409         c2ptr = acptr;          /* Make sure they differ */
410       }
411       /* Search second youngest link: */
412       for (ac2ptr = acptr; ac2ptr != &me; ac2ptr = cli_serv(ac2ptr)->up)
413         if (ac2ptr != c3ptr &&
414             cli_serv(ac2ptr)->timestamp >
415             (c2ptr ? cli_serv(c2ptr)->timestamp : timestamp))
416           c2ptr = ac2ptr;
417       if (IsServer(sptr))
418       {
419         for (ac2ptr = sptr; ac2ptr != &me; ac2ptr = cli_serv(ac2ptr)->up)
420           if (ac2ptr != c3ptr &&
421               cli_serv(ac2ptr)->timestamp >
422               (c2ptr ? cli_serv(c2ptr)->timestamp : timestamp))
423             c2ptr = ac2ptr;
424       }
425       if (c3ptr && timestamp > (c2ptr ? cli_serv(c2ptr)->timestamp : timestamp))
426         c2ptr = 0;
427       /* If timestamps are equal, decide which link to break
428        *  by name.
429        */
430       if ((c2ptr ? cli_serv(c2ptr)->timestamp : timestamp) ==
431           (c3ptr ? cli_serv(c3ptr)->timestamp : timestamp))
432       {
433         char* n2;
434         char* n2up;
435         char* n3;
436         char* n3up;
437         if (c2ptr)
438         {
439           n2 = cli_name(c2ptr);
440           n2up = MyConnect(c2ptr) ? cli_name(&me) : cli_name(cli_serv(c2ptr)->up);
441         }
442         else
443         {
444           n2 = host;
445           n2up = IsServer(sptr) ? cli_name(sptr) : cli_name(&me);
446         }
447         if (c3ptr)
448         {
449           n3 = cli_name(c3ptr);
450           n3up = MyConnect(c3ptr) ? cli_name(&me) : cli_name(cli_serv(c3ptr)->up);
451         }
452         else
453         {
454           n3 = host;
455           n3up = IsServer(sptr) ? cli_name(sptr) : cli_name(&me);
456         }
457         if (strcmp(n2, n2up) > 0)
458           n2 = n2up;
459         if (strcmp(n3, n3up) > 0)
460           n3 = n3up;
461         if (strcmp(n3, n2) > 0)
462         {
463           ac2ptr = c2ptr;
464           c2ptr = c3ptr;
465           c3ptr = ac2ptr;
466         }
467       }
468       /* Now squit the second youngest link: */
469       if (!c2ptr)
470         return exit_new_server(cptr, sptr, host, timestamp,
471                                "server %s already exists and is %ld seconds younger.",
472                                host, (long)cli_serv(acptr)->timestamp - (long)timestamp);
473       else if (cli_from(c2ptr) == cptr || IsServer(sptr))
474       {
475         struct Client *killedptrfrom = cli_from(c2ptr);
476         if (active_lh_line)
477         {
478           /*
479            * If the L: or H: line also gets rid of this link,
480            * we sent just one squit.
481            */
482           if (LHcptr && a_kills_b_too(LHcptr, c2ptr))
483             break;
484           /*
485            * If breaking the loop here solves the L: or H:
486            * line problem, we don't squit that.
487            */
488           if (cli_from(c2ptr) == cptr || (LHcptr && a_kills_b_too(c2ptr, LHcptr)))
489             active_lh_line = 0;
490           else
491           {
492             /*
493              * If we still have a L: or H: line problem,
494              * we prefer to squit the new server, solving
495              * loop and L:/H: line problem with only one squit.
496              */
497             LHcptr = 0;
498             break;
499           }
500         }
501         /*
502          * If the new server was introduced by a server that caused a
503          * Ghost less then 20 seconds ago, this is probably also
504          * a Ghost... (20 seconds is more then enough because all
505          * SERVER messages are at the beginning of a net.burst). --Run
506          */
507         if (CurrentTime - cli_serv(cptr)->ghost < 20)
508         {
509           killedptrfrom = cli_from(acptr);
510           if (exit_client(cptr, acptr, &me, "Ghost loop") == CPTR_KILLED)
511             return CPTR_KILLED;
512         }
513         else if (exit_client_msg(cptr, c2ptr, &me,
514             "Loop <-- %s (new link is %ld seconds younger)", host,
515             (c3ptr ? (long)cli_serv(c3ptr)->timestamp : timestamp) -
516             (long)cli_serv(c2ptr)->timestamp) == CPTR_KILLED)
517           return CPTR_KILLED;
518         /*
519          * Did we kill the incoming server off already ?
520          */
521         if (killedptrfrom == cptr)
522           return 0;
523       }
524       else
525       {
526         if (active_lh_line)
527         {
528           if (LHcptr && a_kills_b_too(LHcptr, acptr))
529             break;
530           if (cli_from(acptr) == cptr || (LHcptr && a_kills_b_too(acptr, LHcptr)))
531             active_lh_line = 0;
532           else
533           {
534             LHcptr = 0;
535             break;
536           }
537         }
538         /*
539          * We can't believe it is a lagged server message
540          * when it directly connects to us...
541          * kill the older link at the ghost, rather then
542          * at the second youngest link, assuming it isn't
543          * a REAL loop.
544          */
545         ghost = CurrentTime;            /* Mark that it caused a ghost */
546         if (exit_client(cptr, acptr, &me, "Ghost") == CPTR_KILLED)
547           return CPTR_KILLED;
548         break;
549       }
550     }
551   }
552
553   if (active_lh_line)
554   {
555     if (LHcptr == 0) {
556       return exit_new_server(cptr, sptr, host, timestamp,
557           (active_lh_line == 2) ?  "Non-Hub link %s <- %s(%s), check H:" : 
558                                    "Leaf-only link %s <- %s(%s), check L:",
559           cli_name(cptr), host, 
560           lhconf ? (lhconf->name ? lhconf->name : "*") : "!");
561     }
562     else
563     {
564       int killed = a_kills_b_too(LHcptr, sptr);
565       if (active_lh_line < 3)
566       {
567         if (exit_client_msg(cptr, LHcptr, &me,
568             (active_lh_line == 2) ?  "Non-Hub link %s <- %s(%s), check H:" : 
569                                      "Leaf-only link %s <- %s(%s), check L:",
570             cli_name(cptr), host,
571             lhconf ? (lhconf->name ? lhconf->name : "*") : "!") == CPTR_KILLED)
572           return CPTR_KILLED;
573       }
574       else
575       {
576         ServerStats->is_ref++;
577         if (exit_client(cptr, LHcptr, &me, "I'm a leaf, define HUB") == CPTR_KILLED)
578           return CPTR_KILLED;
579       }
580       /*
581        * Did we kill the incoming server off already ?
582        */
583       if (killed)
584         return 0;
585     }
586   }
587
588   if (IsUnknown(cptr) || IsHandshake(cptr))
589   {
590     make_server(cptr);
591     cli_serv(cptr)->timestamp = timestamp;
592     cli_serv(cptr)->prot = prot;
593     cli_serv(cptr)->ghost = ghost;
594     SetServerYXX(cptr, cptr, parv[6]);
595     if (start_timestamp > OLDEST_TS)
596     {
597       Debug((DEBUG_DEBUG, "My start time: %Tu; other's start time: %Tu",
598              cli_serv(&me)->timestamp, start_timestamp));
599       Debug((DEBUG_DEBUG, "Receive time: %Tu; received timestamp: %Tu; "
600              "difference %ld", recv_time, timestamp, timestamp - recv_time));
601       if (feature_bool(FEAT_RELIABLE_CLOCK)) {
602         if (start_timestamp < cli_serv(&me)->timestamp)
603           cli_serv(&me)->timestamp = start_timestamp;
604         if (IsUnknown(cptr))
605           cli_serv(cptr)->timestamp = TStime();
606       } else {
607         if (start_timestamp < cli_serv(&me)->timestamp) {
608           sendto_opmask_butone(0, SNO_OLDSNO, "got earlier start time: "
609                                "%Tu < %Tu", start_timestamp,
610                                cli_serv(&me)->timestamp);
611           cli_serv(&me)->timestamp = start_timestamp;
612           TSoffset += timestamp - recv_time;
613           sendto_opmask_butone(0, SNO_OLDSNO, "clock adjusted by adding %d",
614                                (int)(timestamp - recv_time));
615         } else if ((start_timestamp > cli_serv(&me)->timestamp) &&
616                  IsUnknown(cptr))
617           cli_serv(cptr)->timestamp = TStime();
618
619         else if (timestamp != recv_time) {
620           /*
621            * Equal start times, we have a collision.  Let the connected-to
622            * server decide. This assumes leafs issue more than half of the
623            * connection attempts.
624            */
625           if (IsUnknown(cptr))
626             cli_serv(cptr)->timestamp = TStime();
627           else if (IsHandshake(cptr)) {
628             sendto_opmask_butone(0, SNO_OLDSNO, "clock adjusted by adding %d",
629                                  (int)(timestamp - recv_time));
630             TSoffset += timestamp - recv_time;
631           }
632         }
633       }
634     }
635
636     ret = server_estab(cptr, aconf);
637   }
638   else
639     ret = 0;
640
641   if (feature_bool(FEAT_RELIABLE_CLOCK) &&
642       abs(cli_serv(cptr)->timestamp - recv_time) > 30) {
643     sendto_opmask_butone(0, SNO_OLDSNO, "Connected to a net with a "
644                          "timestamp-clock difference of %Td seconds! "
645                          "Used SETTIME to correct this.",
646                          timestamp - recv_time);
647     sendcmdto_prio_one(&me, CMD_SETTIME, cptr, "%Tu :%s", TStime(),
648                        cli_name(&me));
649   }
650
651   return ret;
652 }
653
654 /*
655  * ms_server - server message handler
656  *
657  *    parv[0] = sender prefix
658  *    parv[1] = servername
659  *    parv[2] = hopcount
660  *    parv[3] = start timestamp
661  *    parv[4] = link timestamp
662  *    parv[5] = major protocol version: P09/P10
663  *    parv[parc-1] = serverinfo
664  *  If cptr is P10:
665  *    parv[6] = "YMM", where 'Y' is the server numeric and "MM" is the
666  *              numeric nick mask of this server.
667  *    parv[7] = +hs (h == hub, s == service)
668  */
669 int ms_server(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
670 {
671   char*            ch;
672   int              i;
673   char             info[REALLEN + 1];
674   char*            host;
675   struct Client*   acptr;
676   struct Client*   bcptr;
677   struct Client*   LHcptr = 0;
678   struct ConfItem* aconf = 0;
679   struct ConfItem* lhconf = 0;
680   int              hop;
681   int              ret;
682   int              active_lh_line = 0;
683   unsigned short   prot;
684   time_t           start_timestamp;
685   time_t           timestamp = 0;
686   time_t           recv_time;
687   time_t           ghost = 0;
688
689   recv_time = TStime();
690   info[0] = '\0';
691   if (parc < 7)
692   {
693     return need_more_params(sptr, "SERVER");
694     return exit_client(cptr, cptr, &me, "Need more parameters");
695   }
696   host = parv[1];
697
698   /*
699    * Detect protocol
700    */
701   if (strlen(parv[5]) != 3 || (parv[5][0] != 'P' && parv[5][0] != 'J'))
702     return exit_client_msg(cptr, sptr, &me, "Bogus protocol (%s)", parv[5]);
703
704   *parv[5] = 'J';
705
706   if (*parv[7] == '+') {
707     for (ch = parv[7] + 1; *ch; ch++)
708       switch (*ch) {
709       case 'h':
710         SetHub(cptr);
711         break;
712       case 's':
713         SetService(cptr);
714         break;
715       }
716   }
717
718   prot = atoi(parv[5] + 1);
719   if (prot > atoi(MAJOR_PROTOCOL))
720     prot = atoi(MAJOR_PROTOCOL);
721   hop = atoi(parv[2]);
722   start_timestamp = atoi(parv[3]);
723   timestamp = atoi(parv[4]);
724
725   Debug((DEBUG_INFO, "Got SERVER %s with timestamp [%s] age %Tu (%Tu)",
726          host, parv[4], start_timestamp, cli_serv(&me)->timestamp));
727
728   if ((timestamp < OLDEST_TS || (hop == 1 && start_timestamp < OLDEST_TS)))
729   {
730     return exit_client_msg(cptr, sptr, &me,
731         "Bogus timestamps (%s %s)", parv[3], parv[4]);
732   }
733
734   ircd_strncpy(info, parv[parc - 1], REALLEN);
735   info[REALLEN] = '\0';
736
737   if (prot < atoi(MINOR_PROTOCOL)) {
738     sendto_opmask_butone(0, SNO_OLDSNO, "Got incompatible protocol version "
739                          "(%s) from %s", parv[5], cli_name(cptr));
740     return exit_new_server(cptr, sptr, host, timestamp,
741                            "Incompatible protocol: %s", parv[5]);
742   }
743
744   /*
745    * Check for "FRENCH " infection ;-) (actually this should
746    * be replaced with routine to check the hostname syntax in
747    * general). [ This check is still needed, even after the parse
748    * is fixed, because someone can send "SERVER :foo bar " ].
749    * Also, changed to check other "difficult" characters, now
750    * that parse lets all through... --msa
751    */
752   if (strlen(host) > HOSTLEN)
753     host[HOSTLEN] = '\0';
754   for (ch = host; *ch; ch++)
755     if (*ch <= ' ' || *ch > '~')
756       break;
757   if (*ch || !strchr(host, '.')) {
758     sendto_opmask_butone(0, SNO_OLDSNO, "Bogus server name (%s) from %s",
759                          host, cli_name(cptr));
760     return exit_client_msg(cptr, cptr, &me, "Bogus server name (%s)", host);
761   }
762
763   /*
764    *  We want to find IsConnecting() and IsHandshake() too,
765    *  use FindClient().
766    *  The second finds collisions with numeric representation of existing
767    *  servers - these shouldn't happen anymore when all upgraded to 2.10.
768    *  -- Run
769    */
770   while ((acptr = FindClient(host)) || 
771          (parc > 7 && (acptr = FindNServer(parv[6]))))
772   {
773     /*
774      *  This link is trying feed me a server that I already have
775      *  access through another path
776      *
777      *  Do not allow Uworld to do this.
778      *  Do not allow servers that are juped.
779      *  Do not allow servers that have older link timestamps
780      *    then this try.
781      *  Do not allow servers that use the same numeric as an existing
782      *    server, but have a different name.
783      *
784      *  If my ircd.conf sucks, I can try to connect to myself:
785      */
786     if (acptr == &me)
787       return exit_client_msg(cptr, cptr, &me,
788           "nick collision with me, check server number in M:? (%s)", host);
789     /*
790      * Detect wrong numeric.
791      */
792     if (0 != ircd_strcmp(cli_name(acptr), host))
793     {
794       sendcmdto_serv_butone(&me, CMD_WALLOPS, cptr,
795                             ":SERVER Numeric Collision: %s != %s", cli_name(acptr),
796                             host);
797       return exit_client_msg(cptr, cptr, &me,
798           "NUMERIC collision between %s and %s."
799           " Is your server numeric correct ?", host, cli_name(acptr));
800     }
801
802     /*
803      *  Kill our try, if we had one.
804      */
805     if (IsConnecting(acptr))
806     {
807       if (!active_lh_line && exit_client(cptr, acptr, &me,
808           "Just connected via another link") == CPTR_KILLED)
809         return CPTR_KILLED;
810       /*
811        * We can have only ONE 'IsConnecting', 'IsHandshake' or
812        * 'IsServer', because new 'IsConnecting's are refused to
813        * the same server if we already had it.
814        */
815       break;
816     }
817     /*
818      * Avoid other nick collisions...
819      * This is a doubtfull test though, what else would it be
820      * when it has a server.name ?
821      */
822     else if (!IsServer(acptr) && !IsHandshake(acptr))
823       return exit_client_msg(cptr, cptr, &me,
824           "Nickname %s already exists!", host);
825     /*
826      * Our new server might be a juped server,
827      * or someone trying abuse a second Uworld:
828      */
829     else if (IsServer(acptr) && (0 == ircd_strncmp(cli_info(acptr), "JUPE", 4) ||
830         find_conf_byhost(cli_confs(cptr), cli_name(acptr), CONF_UWORLD)))
831     {
832       if (!IsServer(sptr))
833         return exit_client(cptr, sptr, &me, cli_info(acptr));
834       sendcmdto_one(&me, CMD_WALLOPS, cptr, ":Received :%s SERVER %s "
835                     "from %s !?!", parv[0], parv[1], cli_name(cptr));
836       return exit_new_server(cptr, sptr, host, timestamp, "%s", cli_info(acptr));
837     }
838     /*
839      * Of course we find the handshake this link was before :)
840      */
841     else if (IsHandshake(acptr) && acptr == cptr)
842       break;
843     /*
844      * Here we have a server nick collision...
845      * We don't want to kill the link that was last /connected,
846      * but we neither want to kill a good (old) link.
847      * Therefor we kill the second youngest link.
848      */
849     if (1)
850     {
851       struct Client* c2ptr = 0;
852       struct Client* c3ptr = acptr;
853       struct Client* ac2ptr;
854       struct Client* ac3ptr;
855
856       /* Search youngest link: */
857       for (ac3ptr = acptr; ac3ptr != &me; ac3ptr = cli_serv(ac3ptr)->up)
858         if (cli_serv(ac3ptr)->timestamp > cli_serv(c3ptr)->timestamp)
859           c3ptr = ac3ptr;
860
861       if (IsServer(sptr))
862       {
863         for (ac3ptr = sptr; ac3ptr != &me; ac3ptr = cli_serv(ac3ptr)->up)
864           if (cli_serv(ac3ptr)->timestamp > cli_serv(c3ptr)->timestamp)
865             c3ptr = ac3ptr;
866       }
867
868       if (timestamp > cli_serv(c3ptr)->timestamp)
869       {
870         c3ptr = 0;
871         c2ptr = acptr;          /* Make sure they differ */
872       }
873
874       /* Search second youngest link: */
875       for (ac2ptr = acptr; ac2ptr != &me; ac2ptr = cli_serv(ac2ptr)->up)
876         if (ac2ptr != c3ptr &&
877             cli_serv(ac2ptr)->timestamp >
878             (c2ptr ? cli_serv(c2ptr)->timestamp : timestamp))
879           c2ptr = ac2ptr;
880
881       if (IsServer(sptr))
882       {
883         for (ac2ptr = sptr; ac2ptr != &me; ac2ptr = cli_serv(ac2ptr)->up)
884           if (ac2ptr != c3ptr &&
885               cli_serv(ac2ptr)->timestamp >
886               (c2ptr ? cli_serv(c2ptr)->timestamp : timestamp))
887             c2ptr = ac2ptr;
888       }
889
890       if (c3ptr && timestamp > (c2ptr ? cli_serv(c2ptr)->timestamp : timestamp))
891         c2ptr = 0;
892
893       /* If timestamps are equal, decide which link to break
894        *  by name.
895        */
896       if ((c2ptr ? cli_serv(c2ptr)->timestamp : timestamp) ==
897           (c3ptr ? cli_serv(c3ptr)->timestamp : timestamp))
898       {
899         char* n2;
900         char* n2up;
901         char* n3;
902         char* n3up;
903
904         if (c2ptr)
905         {
906           n2 = cli_name(c2ptr);
907           n2up = MyConnect(c2ptr) ? cli_name(&me) : cli_name(cli_serv(c2ptr)->up);
908         }
909         else
910         {
911           n2 = host;
912           n2up = IsServer(sptr) ? cli_name(sptr) : cli_name(&me);
913         }
914
915         if (c3ptr)
916         {
917           n3 = cli_name(c3ptr);
918           n3up = MyConnect(c3ptr) ? cli_name(&me) : cli_name(cli_serv(c3ptr)->up);
919         }
920         else
921         {
922           n3 = host;
923           n3up = IsServer(sptr) ? cli_name(sptr) : cli_name(&me);
924         }
925
926         if (strcmp(n2, n2up) > 0)
927           n2 = n2up;
928         if (strcmp(n3, n3up) > 0)
929           n3 = n3up;
930
931         if (strcmp(n3, n2) > 0)
932         {
933           ac2ptr = c2ptr;
934           c2ptr = c3ptr;
935           c3ptr = ac2ptr;
936         }
937       }
938       /* Now squit the second youngest link: */
939       if (!c2ptr)
940         return exit_new_server(cptr, sptr, host, timestamp,
941             "server %s already exists and is %ld seconds younger.",
942             host, (long)cli_serv(acptr)->timestamp - (long)timestamp);
943       else if (cli_from(c2ptr) == cptr || IsServer(sptr))
944       {
945         struct Client *killedptrfrom = cli_from(c2ptr);
946         if (active_lh_line)
947         {
948           /*
949            * If the L: or H: line also gets rid of this link,
950            * we sent just one squit.
951            */
952           if (LHcptr && a_kills_b_too(LHcptr, c2ptr))
953             break;
954           /*
955            * If breaking the loop here solves the L: or H:
956            * line problem, we don't squit that.
957            */
958           if (cli_from(c2ptr) == cptr || (LHcptr && a_kills_b_too(c2ptr, LHcptr)))
959             active_lh_line = 0;
960           else
961           {
962             /*
963              * If we still have a L: or H: line problem,
964              * we prefer to squit the new server, solving
965              * loop and L:/H: line problem with only one squit.
966              */
967             LHcptr = 0;
968             break;
969           }
970         }
971         /*
972          * If the new server was introduced by a server that caused a
973          * Ghost less then 20 seconds ago, this is probably also
974          * a Ghost... (20 seconds is more then enough because all
975          * SERVER messages are at the beginning of a net.burst). --Run
976          */
977         if (CurrentTime - cli_serv(cptr)->ghost < 20)
978         {
979           killedptrfrom = cli_from(acptr);
980           if (exit_client(cptr, acptr, &me, "Ghost loop") == CPTR_KILLED)
981             return CPTR_KILLED;
982         }
983         else if (exit_client_msg(cptr, c2ptr, &me,
984             "Loop <-- %s (new link is %ld seconds younger)", host,
985             (c3ptr ? (long)cli_serv(c3ptr)->timestamp : timestamp) -
986             (long)cli_serv(c2ptr)->timestamp) == CPTR_KILLED)
987           return CPTR_KILLED;
988         /*
989          * Did we kill the incoming server off already ?
990          */
991         if (killedptrfrom == cptr)
992           return 0;
993       }
994       else
995       {
996         if (active_lh_line)
997         {
998           if (LHcptr && a_kills_b_too(LHcptr, acptr))
999             break;
1000           if (cli_from(acptr) == cptr || (LHcptr && a_kills_b_too(acptr, LHcptr)))
1001             active_lh_line = 0;
1002           else
1003           {
1004             LHcptr = 0;
1005             break;
1006           }
1007         }
1008         /*
1009          * We can't believe it is a lagged server message
1010          * when it directly connects to us...
1011          * kill the older link at the ghost, rather then
1012          * at the second youngest link, assuming it isn't
1013          * a REAL loop.
1014          */
1015         ghost = CurrentTime;            /* Mark that it caused a ghost */
1016         if (exit_client(cptr, acptr, &me, "Ghost") == CPTR_KILLED)
1017           return CPTR_KILLED;
1018         break;
1019       }
1020     }
1021   }
1022
1023   if (active_lh_line)
1024   {
1025     if (LHcptr == 0) {
1026       return exit_new_server(cptr, sptr, host, timestamp,
1027           (active_lh_line == 2) ?  "Non-Hub link %s <- %s(%s)" : "Leaf-only link %s <- %s(%s)",
1028           cli_name(cptr), host,
1029           lhconf ? (lhconf->name ? lhconf->name : "*") : "!");
1030     }
1031     else
1032     {
1033       int killed = a_kills_b_too(LHcptr, sptr);
1034       if (active_lh_line < 3)
1035       {
1036         if (exit_client_msg(cptr, LHcptr, &me,
1037             (active_lh_line == 2) ?  "Non-Hub link %s <- %s(%s)" : "Leaf-only link %s <- %s(%s)",
1038             cli_name(cptr), host,
1039             lhconf ? (lhconf->name ? lhconf->name : "*") : "!") == CPTR_KILLED)
1040           return CPTR_KILLED;
1041       }
1042       else
1043       {
1044         ServerStats->is_ref++;
1045         if (exit_client(cptr, LHcptr, &me, "I'm a leaf") == CPTR_KILLED)
1046           return CPTR_KILLED;
1047       }
1048       /*
1049        * Did we kill the incoming server off already ?
1050        */
1051       if (killed)
1052         return 0;
1053     }
1054   }
1055
1056   /*
1057    * Server is informing about a new server behind
1058    * this link. Create REMOTE server structure,
1059    * add it to list and propagate word to my other
1060    * server links...
1061    */
1062
1063   acptr = make_client(cptr, STAT_SERVER);
1064   make_server(acptr);
1065   cli_serv(acptr)->prot = prot;
1066   cli_serv(acptr)->timestamp = timestamp;
1067   cli_hopcount(acptr) = hop;
1068   ircd_strncpy(cli_name(acptr), host, HOSTLEN);
1069   ircd_strncpy(cli_info(acptr), info, REALLEN);
1070   cli_serv(acptr)->up = sptr;
1071   cli_serv(acptr)->updown = add_dlink(&(cli_serv(sptr))->down, acptr);
1072   /* Use cptr, because we do protocol 9 -> 10 translation
1073      for numeric nicks ! */
1074   SetServerYXX(cptr, acptr, parv[6]);
1075
1076   Count_newremoteserver(UserStats);
1077   add_client_to_list(acptr);
1078   hAddClient(acptr);
1079
1080   if (*parv[5] == 'J')
1081   {
1082     SetBurst(acptr);
1083     sendto_opmask_butone(0, SNO_NETWORK, "Net junction: %s %s",
1084            cli_name(sptr), cli_name(acptr));
1085     SetJunction(acptr);
1086   }
1087
1088   /*
1089    * Old sendto_serv_but_one() call removed because we now need to send
1090    * different names to different servers (domain name matching).
1091    */
1092   for (i = 0; i <= HighestFd; i++)
1093   {
1094     if (!(bcptr = LocalClientArray[i]) || !IsServer(bcptr) ||
1095         bcptr == cptr || IsMe(bcptr))
1096       continue;
1097     if (0 == match(cli_name(&me), cli_name(acptr)))
1098       continue;
1099     sendcmdto_one(sptr, CMD_SERVER, bcptr, "%s %d 0 %s %s %s%s +%s%s :%s",
1100                   cli_name(acptr), hop + 1, parv[4], parv[5],
1101                   NumServCap(acptr), IsHub(acptr) ? "h" : "",
1102                   IsService(acptr) ? "s" : "", cli_info(acptr));
1103   }
1104   return 0;
1105
1106 }