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