- The big forward port. I probably broke lots of stuff, so please look over any
[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   if (*parv[7] == '+') {
174     for (ch = parv[7] + 1; *ch; ch++)
175       switch (*ch) {
176       case 'h':
177         SetHub(cptr);
178         break;
179       case 's':
180         SetService(cptr);
181         break;
182       }
183   }
184
185   prot = atoi(parv[5] + 1);
186   if (prot > atoi(MAJOR_PROTOCOL))
187     prot = atoi(MAJOR_PROTOCOL);
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   if (*parv[7] == '+') {
706     for (ch = parv[7] + 1; *ch; ch++)
707       switch (*ch) {
708       case 'h':
709         SetHub(cptr);
710         break;
711       case 's':
712         SetService(cptr);
713         break;
714       }
715   }
716
717   prot = atoi(parv[5] + 1);
718   if (prot > atoi(MAJOR_PROTOCOL))
719     prot = atoi(MAJOR_PROTOCOL);
720   hop = atoi(parv[2]);
721   start_timestamp = atoi(parv[3]);
722   timestamp = atoi(parv[4]);
723
724   Debug((DEBUG_INFO, "Got SERVER %s with timestamp [%s] age %Tu (%Tu)",
725          host, parv[4], start_timestamp, cli_serv(&me)->timestamp));
726
727   if ((timestamp < OLDEST_TS || (hop == 1 && start_timestamp < OLDEST_TS)))
728   {
729     return exit_client_msg(cptr, sptr, &me,
730         "Bogus timestamps (%s %s)", parv[3], parv[4]);
731   }
732
733   ircd_strncpy(info, parv[parc - 1], REALLEN);
734   info[REALLEN] = '\0';
735
736   if (prot < atoi(MINOR_PROTOCOL)) {
737     sendto_opmask_butone(0, SNO_OLDSNO, "Got incompatible protocol version "
738                          "(%s) from %s", parv[5], cli_name(cptr));
739     return exit_new_server(cptr, sptr, host, timestamp,
740                            "Incompatible protocol: %s", parv[5]);
741   }
742
743   /*
744    * Check for "FRENCH " infection ;-) (actually this should
745    * be replaced with routine to check the hostname syntax in
746    * general). [ This check is still needed, even after the parse
747    * is fixed, because someone can send "SERVER :foo bar " ].
748    * Also, changed to check other "difficult" characters, now
749    * that parse lets all through... --msa
750    */
751   if (strlen(host) > HOSTLEN)
752     host[HOSTLEN] = '\0';
753   for (ch = host; *ch; ch++)
754     if (*ch <= ' ' || *ch > '~')
755       break;
756   if (*ch || !strchr(host, '.')) {
757     sendto_opmask_butone(0, SNO_OLDSNO, "Bogus server name (%s) from %s",
758                          host, cli_name(cptr));
759     return exit_client_msg(cptr, cptr, &me, "Bogus server name (%s)", host);
760   }
761
762   /*
763    *  We want to find IsConnecting() and IsHandshake() too,
764    *  use FindClient().
765    *  The second finds collisions with numeric representation of existing
766    *  servers - these shouldn't happen anymore when all upgraded to 2.10.
767    *  -- Run
768    */
769   while ((acptr = FindClient(host)) || 
770          (parc > 7 && (acptr = FindNServer(parv[6]))))
771   {
772     /*
773      *  This link is trying feed me a server that I already have
774      *  access through another path
775      *
776      *  Do not allow Uworld to do this.
777      *  Do not allow servers that are juped.
778      *  Do not allow servers that have older link timestamps
779      *    then this try.
780      *  Do not allow servers that use the same numeric as an existing
781      *    server, but have a different name.
782      *
783      *  If my ircd.conf sucks, I can try to connect to myself:
784      */
785     if (acptr == &me)
786       return exit_client_msg(cptr, cptr, &me,
787           "nick collision with me, check server number in M:? (%s)", host);
788     /*
789      * Detect wrong numeric.
790      */
791     if (0 != ircd_strcmp(cli_name(acptr), host))
792     {
793       sendcmdto_serv_butone(&me, CMD_WALLOPS, cptr,
794                             ":SERVER Numeric Collision: %s != %s", cli_name(acptr),
795                             host);
796       return exit_client_msg(cptr, cptr, &me,
797           "NUMERIC collision between %s and %s."
798           " Is your server numeric correct ?", host, cli_name(acptr));
799     }
800
801     /*
802      *  Kill our try, if we had one.
803      */
804     if (IsConnecting(acptr))
805     {
806       if (!active_lh_line && exit_client(cptr, acptr, &me,
807           "Just connected via another link") == CPTR_KILLED)
808         return CPTR_KILLED;
809       /*
810        * We can have only ONE 'IsConnecting', 'IsHandshake' or
811        * 'IsServer', because new 'IsConnecting's are refused to
812        * the same server if we already had it.
813        */
814       break;
815     }
816     /*
817      * Avoid other nick collisions...
818      * This is a doubtfull test though, what else would it be
819      * when it has a server.name ?
820      */
821     else if (!IsServer(acptr) && !IsHandshake(acptr))
822       return exit_client_msg(cptr, cptr, &me,
823           "Nickname %s already exists!", host);
824     /*
825      * Our new server might be a juped server,
826      * or someone trying abuse a second Uworld:
827      */
828     else if (IsServer(acptr) && (0 == ircd_strncmp(cli_info(acptr), "JUPE", 4) ||
829         find_conf_byhost(cli_confs(cptr), cli_name(acptr), CONF_UWORLD)))
830     {
831       if (!IsServer(sptr))
832         return exit_client(cptr, sptr, &me, cli_info(acptr));
833       sendcmdto_one(&me, CMD_WALLOPS, cptr, ":Received :%s SERVER %s "
834                     "from %s !?!", parv[0], parv[1], cli_name(cptr));
835       return exit_new_server(cptr, sptr, host, timestamp, "%s", cli_info(acptr));
836     }
837     /*
838      * Of course we find the handshake this link was before :)
839      */
840     else if (IsHandshake(acptr) && acptr == cptr)
841       break;
842     /*
843      * Here we have a server nick collision...
844      * We don't want to kill the link that was last /connected,
845      * but we neither want to kill a good (old) link.
846      * Therefor we kill the second youngest link.
847      */
848     if (1)
849     {
850       struct Client* c2ptr = 0;
851       struct Client* c3ptr = acptr;
852       struct Client* ac2ptr;
853       struct Client* ac3ptr;
854
855       /* Search youngest link: */
856       for (ac3ptr = acptr; ac3ptr != &me; ac3ptr = cli_serv(ac3ptr)->up)
857         if (cli_serv(ac3ptr)->timestamp > cli_serv(c3ptr)->timestamp)
858           c3ptr = ac3ptr;
859
860       if (IsServer(sptr))
861       {
862         for (ac3ptr = sptr; ac3ptr != &me; ac3ptr = cli_serv(ac3ptr)->up)
863           if (cli_serv(ac3ptr)->timestamp > cli_serv(c3ptr)->timestamp)
864             c3ptr = ac3ptr;
865       }
866
867       if (timestamp > cli_serv(c3ptr)->timestamp)
868       {
869         c3ptr = 0;
870         c2ptr = acptr;          /* Make sure they differ */
871       }
872
873       /* Search second youngest link: */
874       for (ac2ptr = acptr; ac2ptr != &me; ac2ptr = cli_serv(ac2ptr)->up)
875         if (ac2ptr != c3ptr &&
876             cli_serv(ac2ptr)->timestamp >
877             (c2ptr ? cli_serv(c2ptr)->timestamp : timestamp))
878           c2ptr = ac2ptr;
879
880       if (IsServer(sptr))
881       {
882         for (ac2ptr = sptr; ac2ptr != &me; ac2ptr = cli_serv(ac2ptr)->up)
883           if (ac2ptr != c3ptr &&
884               cli_serv(ac2ptr)->timestamp >
885               (c2ptr ? cli_serv(c2ptr)->timestamp : timestamp))
886             c2ptr = ac2ptr;
887       }
888
889       if (c3ptr && timestamp > (c2ptr ? cli_serv(c2ptr)->timestamp : timestamp))
890         c2ptr = 0;
891
892       /* If timestamps are equal, decide which link to break
893        *  by name.
894        */
895       if ((c2ptr ? cli_serv(c2ptr)->timestamp : timestamp) ==
896           (c3ptr ? cli_serv(c3ptr)->timestamp : timestamp))
897       {
898         char* n2;
899         char* n2up;
900         char* n3;
901         char* n3up;
902
903         if (c2ptr)
904         {
905           n2 = cli_name(c2ptr);
906           n2up = MyConnect(c2ptr) ? cli_name(&me) : cli_name(cli_serv(c2ptr)->up);
907         }
908         else
909         {
910           n2 = host;
911           n2up = IsServer(sptr) ? cli_name(sptr) : cli_name(&me);
912         }
913
914         if (c3ptr)
915         {
916           n3 = cli_name(c3ptr);
917           n3up = MyConnect(c3ptr) ? cli_name(&me) : cli_name(cli_serv(c3ptr)->up);
918         }
919         else
920         {
921           n3 = host;
922           n3up = IsServer(sptr) ? cli_name(sptr) : cli_name(&me);
923         }
924
925         if (strcmp(n2, n2up) > 0)
926           n2 = n2up;
927         if (strcmp(n3, n3up) > 0)
928           n3 = n3up;
929
930         if (strcmp(n3, n2) > 0)
931         {
932           ac2ptr = c2ptr;
933           c2ptr = c3ptr;
934           c3ptr = ac2ptr;
935         }
936       }
937       /* Now squit the second youngest link: */
938       if (!c2ptr)
939         return exit_new_server(cptr, sptr, host, timestamp,
940             "server %s already exists and is %ld seconds younger.",
941             host, (long)cli_serv(acptr)->timestamp - (long)timestamp);
942       else if (cli_from(c2ptr) == cptr || IsServer(sptr))
943       {
944         struct Client *killedptrfrom = cli_from(c2ptr);
945         if (active_lh_line)
946         {
947           /*
948            * If the L: or H: line also gets rid of this link,
949            * we sent just one squit.
950            */
951           if (LHcptr && a_kills_b_too(LHcptr, c2ptr))
952             break;
953           /*
954            * If breaking the loop here solves the L: or H:
955            * line problem, we don't squit that.
956            */
957           if (cli_from(c2ptr) == cptr || (LHcptr && a_kills_b_too(c2ptr, LHcptr)))
958             active_lh_line = 0;
959           else
960           {
961             /*
962              * If we still have a L: or H: line problem,
963              * we prefer to squit the new server, solving
964              * loop and L:/H: line problem with only one squit.
965              */
966             LHcptr = 0;
967             break;
968           }
969         }
970         /*
971          * If the new server was introduced by a server that caused a
972          * Ghost less then 20 seconds ago, this is probably also
973          * a Ghost... (20 seconds is more then enough because all
974          * SERVER messages are at the beginning of a net.burst). --Run
975          */
976         if (CurrentTime - cli_serv(cptr)->ghost < 20)
977         {
978           killedptrfrom = cli_from(acptr);
979           if (exit_client(cptr, acptr, &me, "Ghost loop") == CPTR_KILLED)
980             return CPTR_KILLED;
981         }
982         else if (exit_client_msg(cptr, c2ptr, &me,
983             "Loop <-- %s (new link is %ld seconds younger)", host,
984             (c3ptr ? (long)cli_serv(c3ptr)->timestamp : timestamp) -
985             (long)cli_serv(c2ptr)->timestamp) == CPTR_KILLED)
986           return CPTR_KILLED;
987         /*
988          * Did we kill the incoming server off already ?
989          */
990         if (killedptrfrom == cptr)
991           return 0;
992       }
993       else
994       {
995         if (active_lh_line)
996         {
997           if (LHcptr && a_kills_b_too(LHcptr, acptr))
998             break;
999           if (cli_from(acptr) == cptr || (LHcptr && a_kills_b_too(acptr, LHcptr)))
1000             active_lh_line = 0;
1001           else
1002           {
1003             LHcptr = 0;
1004             break;
1005           }
1006         }
1007         /*
1008          * We can't believe it is a lagged server message
1009          * when it directly connects to us...
1010          * kill the older link at the ghost, rather then
1011          * at the second youngest link, assuming it isn't
1012          * a REAL loop.
1013          */
1014         ghost = CurrentTime;            /* Mark that it caused a ghost */
1015         if (exit_client(cptr, acptr, &me, "Ghost") == CPTR_KILLED)
1016           return CPTR_KILLED;
1017         break;
1018       }
1019     }
1020   }
1021
1022   if (active_lh_line)
1023   {
1024     if (LHcptr == 0) {
1025       return exit_new_server(cptr, sptr, host, timestamp,
1026           (active_lh_line == 2) ?  "Non-Hub link %s <- %s(%s)" : "Leaf-only link %s <- %s(%s)",
1027           cli_name(cptr), host,
1028           lhconf ? (lhconf->name ? lhconf->name : "*") : "!");
1029     }
1030     else
1031     {
1032       int killed = a_kills_b_too(LHcptr, sptr);
1033       if (active_lh_line < 3)
1034       {
1035         if (exit_client_msg(cptr, LHcptr, &me,
1036             (active_lh_line == 2) ?  "Non-Hub link %s <- %s(%s)" : "Leaf-only link %s <- %s(%s)",
1037             cli_name(cptr), host,
1038             lhconf ? (lhconf->name ? lhconf->name : "*") : "!") == CPTR_KILLED)
1039           return CPTR_KILLED;
1040       }
1041       else
1042       {
1043         ServerStats->is_ref++;
1044         if (exit_client(cptr, LHcptr, &me, "I'm a leaf") == CPTR_KILLED)
1045           return CPTR_KILLED;
1046       }
1047       /*
1048        * Did we kill the incoming server off already ?
1049        */
1050       if (killed)
1051         return 0;
1052     }
1053   }
1054
1055   /*
1056    * Server is informing about a new server behind
1057    * this link. Create REMOTE server structure,
1058    * add it to list and propagate word to my other
1059    * server links...
1060    */
1061
1062   acptr = make_client(cptr, STAT_SERVER);
1063   make_server(acptr);
1064   cli_serv(acptr)->prot = prot;
1065   cli_serv(acptr)->timestamp = timestamp;
1066   cli_hopcount(acptr) = hop;
1067   ircd_strncpy(cli_name(acptr), host, HOSTLEN);
1068   ircd_strncpy(cli_info(acptr), info, REALLEN);
1069   cli_serv(acptr)->up = sptr;
1070   cli_serv(acptr)->updown = add_dlink(&(cli_serv(sptr))->down, acptr);
1071   /* Use cptr, because we do protocol 9 -> 10 translation
1072      for numeric nicks ! */
1073   SetServerYXX(cptr, acptr, parv[6]);
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 }