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