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