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