Forward port leaf and hub conf support; refactor and doxyfy m_server.c.
[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 /** @file
24  * @brief Handlers for the SERVER command.
25  * @version $Id$
26  */
27
28 #include "config.h"
29
30 #include "client.h"
31 #include "hash.h"
32 #include "ircd.h"
33 #include "ircd_log.h"
34 #include "ircd_features.h"
35 #include "ircd_reply.h"
36 #include "ircd_string.h"
37 #include "jupe.h"
38 #include "list.h"
39 #include "match.h"
40 #include "msg.h"
41 #include "numeric.h"
42 #include "numnicks.h"
43 #include "querycmds.h"
44 #include "s_bsd.h"
45 #include "s_conf.h"
46 #include "s_debug.h"
47 #include "s_misc.h"
48 #include "s_serv.h"
49 #include "send.h"
50 #include "userload.h"
51
52 #include <assert.h>
53 #include <stdlib.h>
54 #include <string.h>
55
56 /** Clean up a server name.
57  * @param[in] host Input server name.
58  * @return NULL if the name is invalid, else pointer to cleaned-up name.
59  */
60 static char *
61 clean_servername(char *host)
62 {
63   char*            ch;
64   /*
65    * Check for "FRENCH " infection ;-) (actually this should
66    * be replaced with routine to check the hostname syntax in
67    * general). [ This check is still needed, even after the parse
68    * is fixed, because someone can send "SERVER :foo bar " ].
69    * Also, changed to check other "difficult" characters, now
70    * that parse lets all through... --msa
71    */
72   if (strlen(host) > HOSTLEN)
73     host[HOSTLEN] = '\0';
74
75   for (ch = host; *ch; ch++)
76     if (*ch <= ' ' || *ch > '~')
77       break;
78   if (*ch || !strchr(host, '.') || strlen(host) > HOSTLEN)
79     return NULL;
80   return host;
81 }
82
83 /** Parse protocol version from a string.
84  * @param[in] proto String version of protocol number.
85  * @return Zero if \a proto is unrecognized, else protocol version.
86  */
87 static unsigned short
88 parse_protocol(const char *proto)
89 {
90   unsigned short prot;
91   if (strlen(proto) != 3 || (proto[0] != 'P' && proto[0] != 'J'))
92     return 0;
93   prot = atoi(proto+1);
94   if (prot > atoi(MAJOR_PROTOCOL))
95     prot = atoi(MAJOR_PROTOCOL);
96   return prot;
97 }
98
99 /** Check whether the introduction of a new server would cause a loop
100  * or be disallowed by leaf and hub configuration directives.
101  * @param[in] cptr Neighbor who sent the message.
102  * @param[in] sptr Client that originated the message.
103  * @param[in] LHcptr Who to SQUIT on error (if NULL, SQUIT new server).
104  * @param[in] lhconf Leaf or hub configuration block to use.
105  * @param[out] ghost If non-NULL, receives ghost timestamp for new server.
106  * @param[in] host Name of new server.
107  * @param[in] numnick Numnick mask of new server.
108  * @param[in] timestamp Claimed link timestamp of new server.
109  * @param[in] active_lh_line Pseudo-enum value for rejection reason.
110  * 0 means no leaf or hub restrictions.  1 means \a lhconf is a leaf
111  * rule.  2 means \a lhconf is a hub rule.  3 means #me does not have
112  * the HUB feature but already has a server link.
113  * @return CPTR_KILLED if \a cptr was SQUIT.  0 if some other server
114  * was SQUIT.  1 if the new server is allowed.
115  */
116 static int
117 check_loop_and_lh(struct Client* cptr, struct Client *sptr, struct Client *LHcptr, struct ConfItem *lhconf, time_t *ghost, const char *host, const char *numnick, time_t timestamp, int active_lh_line)
118 {
119   struct Client* acptr;
120
121   if (ghost)
122     *ghost = 0;
123
124   /*
125    *  We want to find IsConnecting() and IsHandshake() too,
126    *  use FindClient().
127    *  The second finds collisions with numeric representation of existing
128    *  servers - these shouldn't happen anymore when all upgraded to 2.10.
129    *  -- Run
130    */
131   while ((acptr = FindClient(host))
132          || (numnick && (acptr = FindNServer(numnick))))
133   {
134     /*
135      *  This link is trying feed me a server that I already have
136      *  access through another path
137      *
138      *  Do not allow Uworld to do this.
139      *  Do not allow servers that are juped.
140      *  Do not allow servers that have older link timestamps
141      *    then this try.
142      *  Do not allow servers that use the same numeric as an existing
143      *    server, but have a different name.
144      *
145      *  If my ircd.conf sucks, I can try to connect to myself:
146      */
147     if (acptr == &me)
148       return exit_client_msg(cptr, cptr, &me, "nick collision with me (%s), check server number in M:?", host);
149     /*
150      * Detect wrong numeric.
151      */
152     if (0 != ircd_strcmp(cli_name(acptr), host))
153     {
154       sendcmdto_serv_butone(&me, CMD_WALLOPS, cptr,
155                             ":SERVER Numeric Collision: %s != %s",
156                             cli_name(acptr), host);
157       return exit_client_msg(cptr, cptr, &me,
158           "NUMERIC collision between %s and %s."
159           " Is your server numeric correct ?", host, cli_name(acptr));
160     }
161     /*
162      *  Kill our try, if we had one.
163      */
164     if (IsConnecting(acptr))
165     {
166       if (!active_lh_line && exit_client(cptr, acptr, &me,
167           "Just connected via another link") == CPTR_KILLED)
168         return CPTR_KILLED;
169       /*
170        * We can have only ONE 'IsConnecting', 'IsHandshake' or
171        * 'IsServer', because new 'IsConnecting's are refused to
172        * the same server if we already had it.
173        */
174       break;
175     }
176     /*
177      * Avoid other nick collisions...
178      * This is a doubtfull test though, what else would it be
179      * when it has a server.name ?
180      */
181     else if (!IsServer(acptr) && !IsHandshake(acptr))
182       return exit_client_msg(cptr, cptr, &me,
183                              "Nickname %s already exists!", host);
184     /*
185      * Our new server might be a juped server,
186      * or someone trying abuse a second Uworld:
187      */
188     else if (IsServer(acptr) && (0 == ircd_strncmp(cli_info(acptr), "JUPE", 4) ||
189         find_conf_byhost(cli_confs(cptr), cli_name(acptr), CONF_UWORLD)))
190     {
191       if (!IsServer(sptr))
192         return exit_client(cptr, sptr, &me, cli_info(acptr));
193       sendcmdto_serv_butone(&me, CMD_WALLOPS, cptr,
194                             ":Received :%s SERVER %s from %s !?!",
195                             NumServ(cptr), host, cli_name(cptr));
196       return exit_new_server(cptr, sptr, host, timestamp, "%s", cli_info(acptr));
197     }
198     /*
199      * Of course we find the handshake this link was before :)
200      */
201     else if (IsHandshake(acptr) && acptr == cptr)
202       break;
203     /*
204      * Here we have a server nick collision...
205      * We don't want to kill the link that was last /connected,
206      * but we neither want to kill a good (old) link.
207      * Therefor we kill the second youngest link.
208      */
209     if (1)
210     {
211       struct Client* c2ptr = 0;
212       struct Client* c3ptr = acptr;
213       struct Client* ac2ptr;
214       struct Client* ac3ptr;
215
216       /* Search youngest link: */
217       for (ac3ptr = acptr; ac3ptr != &me; ac3ptr = cli_serv(ac3ptr)->up)
218         if (cli_serv(ac3ptr)->timestamp > cli_serv(c3ptr)->timestamp)
219           c3ptr = ac3ptr;
220       if (IsServer(sptr))
221       {
222         for (ac3ptr = sptr; ac3ptr != &me; ac3ptr = cli_serv(ac3ptr)->up)
223           if (cli_serv(ac3ptr)->timestamp > cli_serv(c3ptr)->timestamp)
224             c3ptr = ac3ptr;
225       }
226       if (timestamp > cli_serv(c3ptr)->timestamp)
227       {
228         c3ptr = 0;
229         c2ptr = acptr;          /* Make sure they differ */
230       }
231       /* Search second youngest link: */
232       for (ac2ptr = acptr; ac2ptr != &me; ac2ptr = cli_serv(ac2ptr)->up)
233         if (ac2ptr != c3ptr &&
234             cli_serv(ac2ptr)->timestamp >
235             (c2ptr ? cli_serv(c2ptr)->timestamp : timestamp))
236           c2ptr = ac2ptr;
237       if (IsServer(sptr))
238       {
239         for (ac2ptr = sptr; ac2ptr != &me; ac2ptr = cli_serv(ac2ptr)->up)
240           if (ac2ptr != c3ptr &&
241               cli_serv(ac2ptr)->timestamp >
242               (c2ptr ? cli_serv(c2ptr)->timestamp : timestamp))
243             c2ptr = ac2ptr;
244       }
245       if (c3ptr && timestamp > (c2ptr ? cli_serv(c2ptr)->timestamp : timestamp))
246         c2ptr = 0;
247       /* If timestamps are equal, decide which link to break
248        *  by name.
249        */
250       if ((c2ptr ? cli_serv(c2ptr)->timestamp : timestamp) ==
251           (c3ptr ? cli_serv(c3ptr)->timestamp : timestamp))
252       {
253         const char *n2, *n2up, *n3, *n3up;
254         if (c2ptr)
255         {
256           n2 = cli_name(c2ptr);
257           n2up = MyConnect(c2ptr) ? cli_name(&me) : cli_name(cli_serv(c2ptr)->up);
258         }
259         else
260         {
261           n2 = host;
262           n2up = IsServer(sptr) ? cli_name(sptr) : cli_name(&me);
263         }
264         if (c3ptr)
265         {
266           n3 = cli_name(c3ptr);
267           n3up = MyConnect(c3ptr) ? cli_name(&me) : cli_name(cli_serv(c3ptr)->up);
268         }
269         else
270         {
271           n3 = host;
272           n3up = IsServer(sptr) ? cli_name(sptr) : cli_name(&me);
273         }
274         if (strcmp(n2, n2up) > 0)
275           n2 = n2up;
276         if (strcmp(n3, n3up) > 0)
277           n3 = n3up;
278         if (strcmp(n3, n2) > 0)
279         {
280           ac2ptr = c2ptr;
281           c2ptr = c3ptr;
282           c3ptr = ac2ptr;
283         }
284       }
285       /* Now squit the second youngest link: */
286       if (!c2ptr)
287         return exit_new_server(cptr, sptr, host, timestamp,
288                                "server %s already exists and is %ld seconds younger.",
289                                host, (long)cli_serv(acptr)->timestamp - (long)timestamp);
290       else if (cli_from(c2ptr) == cptr || IsServer(sptr))
291       {
292         struct Client *killedptrfrom = cli_from(c2ptr);
293         if (active_lh_line)
294         {
295           /*
296            * If the L: or H: line also gets rid of this link,
297            * we sent just one squit.
298            */
299           if (LHcptr && a_kills_b_too(LHcptr, c2ptr))
300             break;
301           /*
302            * If breaking the loop here solves the L: or H:
303            * line problem, we don't squit that.
304            */
305           if (cli_from(c2ptr) == cptr || (LHcptr && a_kills_b_too(c2ptr, LHcptr)))
306             active_lh_line = 0;
307           else
308           {
309             /*
310              * If we still have a L: or H: line problem,
311              * we prefer to squit the new server, solving
312              * loop and L:/H: line problem with only one squit.
313              */
314             LHcptr = 0;
315             break;
316           }
317         }
318         /*
319          * If the new server was introduced by a server that caused a
320          * Ghost less then 20 seconds ago, this is probably also
321          * a Ghost... (20 seconds is more then enough because all
322          * SERVER messages are at the beginning of a net.burst). --Run
323          */
324         if (CurrentTime - cli_serv(cptr)->ghost < 20)
325         {
326           killedptrfrom = cli_from(acptr);
327           if (exit_client(cptr, acptr, &me, "Ghost loop") == CPTR_KILLED)
328             return CPTR_KILLED;
329         }
330         else if (exit_client_msg(cptr, c2ptr, &me,
331             "Loop <-- %s (new link is %ld seconds younger)", host,
332             (c3ptr ? (long)cli_serv(c3ptr)->timestamp : timestamp) -
333             (long)cli_serv(c2ptr)->timestamp) == CPTR_KILLED)
334           return CPTR_KILLED;
335         /*
336          * Did we kill the incoming server off already ?
337          */
338         if (killedptrfrom == cptr)
339           return 0;
340       }
341       else
342       {
343         if (active_lh_line)
344         {
345           if (LHcptr && a_kills_b_too(LHcptr, acptr))
346             break;
347           if (cli_from(acptr) == cptr || (LHcptr && a_kills_b_too(acptr, LHcptr)))
348             active_lh_line = 0;
349           else
350           {
351             LHcptr = 0;
352             break;
353           }
354         }
355         /*
356          * We can't believe it is a lagged server message
357          * when it directly connects to us...
358          * kill the older link at the ghost, rather then
359          * at the second youngest link, assuming it isn't
360          * a REAL loop.
361          */
362         if (ghost)
363           *ghost = CurrentTime;            /* Mark that it caused a ghost */
364         if (exit_client(cptr, acptr, &me, "Ghost") == CPTR_KILLED)
365           return CPTR_KILLED;
366         break;
367       }
368     }
369   }
370
371   if (active_lh_line)
372   {
373     if (LHcptr == 0) {
374       return exit_new_server(cptr, sptr, host, timestamp,
375           (active_lh_line == 2) ?  "Non-Hub link %s <- %s(%s), check H:" :
376                                    "Leaf-only link %s <- %s(%s), check L:",
377           cli_name(cptr), host,
378           lhconf ? (lhconf->name ? lhconf->name : "*") : "!");
379     }
380     else
381     {
382       int killed = a_kills_b_too(LHcptr, sptr);
383       if (active_lh_line < 3)
384       {
385         if (exit_client_msg(cptr, LHcptr, &me,
386             (active_lh_line == 2) ?  "Non-Hub link %s <- %s(%s), check H:" :
387                                      "Leaf-only link %s <- %s(%s), check L:",
388             cli_name(cptr), host,
389             lhconf ? (lhconf->name ? lhconf->name : "*") : "!") == CPTR_KILLED)
390           return CPTR_KILLED;
391       }
392       else
393       {
394         ServerStats->is_ref++;
395         if (exit_client(cptr, LHcptr, &me, "I'm a leaf, define HUB") == CPTR_KILLED)
396           return CPTR_KILLED;
397       }
398       /*
399        * Did we kill the incoming server off already ?
400        */
401       if (killed)
402         return 0;
403     }
404   }
405
406   return 1;
407 }
408
409 /** Update server start timestamps and TS offsets.
410  * @param[in] cptr Server that just connected.
411  * @param[in] timestamp Current time according to \a cptr.
412  * @param[in] start_timestamp Time that \a cptr started.
413  * @param[in] recv_time Current time as we know it.
414  */
415 static void
416 check_start_timestamp(struct Client *cptr, time_t timestamp, time_t start_timestamp, time_t recv_time)
417 {
418   Debug((DEBUG_DEBUG, "My start time: %Tu; other's start time: %Tu",
419          cli_serv(&me)->timestamp, start_timestamp));
420   Debug((DEBUG_DEBUG, "Receive time: %Tu; received timestamp: %Tu; "
421          "difference %ld", recv_time, timestamp, timestamp - recv_time));
422   if (feature_bool(FEAT_RELIABLE_CLOCK)) {
423     if (start_timestamp < cli_serv(&me)->timestamp)
424       cli_serv(&me)->timestamp = start_timestamp;
425     if (IsUnknown(cptr))
426       cli_serv(cptr)->timestamp = TStime();
427   } else if (start_timestamp < cli_serv(&me)->timestamp) {
428     sendto_opmask_butone(0, SNO_OLDSNO, "got earlier start time: "
429                          "%Tu < %Tu", start_timestamp,
430                          cli_serv(&me)->timestamp);
431     cli_serv(&me)->timestamp = start_timestamp;
432     TSoffset += timestamp - recv_time;
433     sendto_opmask_butone(0, SNO_OLDSNO, "clock adjusted by adding %d",
434                          (int)(timestamp - recv_time));
435   } else if ((start_timestamp > cli_serv(&me)->timestamp) &&
436              IsUnknown(cptr)) {
437     cli_serv(cptr)->timestamp = TStime();
438   } else if (timestamp != recv_time) {
439     /*
440      * Equal start times, we have a collision.  Let the connected-to
441      * server decide. This assumes leafs issue more than half of the
442      * connection attempts.
443      */
444     if (IsUnknown(cptr))
445       cli_serv(cptr)->timestamp = TStime();
446     else if (IsHandshake(cptr)) {
447       sendto_opmask_butone(0, SNO_OLDSNO, "clock adjusted by adding %d",
448                            (int)(timestamp - recv_time));
449       TSoffset += timestamp - recv_time;
450     }
451   }
452 }
453
454 /** Handle a SERVER message from an unregistered connection.
455  *
456  * \a parv has the following elements:
457  * \li \a parv[1] is the server name
458  * \li \a parv[2] is the hop count to the server
459  * \li \a parv[3] is the start timestamp for the server
460  * \li \a parv[4] is the link timestamp
461  * \li \a parv[5] is the protocol version (P10 or J10)
462  * \li \a parv[6] is the numnick mask for the server
463  * \li \a parv[7] is a string of flags like +hs to mark hubs and services
464  * \li \a parv[\a parc - 1] is the server description
465  *
466  * See @ref m_functions for discussion of the arguments.
467  * @param[in] cptr Client that sent us the message.
468  * @param[in] sptr Original source of message.
469  * @param[in] parc Number of arguments.
470  * @param[in] parv Argument vector.
471  */
472 int mr_server(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
473 {
474   char*            ch;
475   int              i;
476   char*            host;
477   const char*      encr;
478   struct ConfItem* aconf;
479   struct Jupe*     ajupe;
480   int              hop;
481   int              ret;
482   int              active_lh_line = 0;
483   unsigned short   prot;
484   time_t           start_timestamp;
485   time_t           timestamp;
486   time_t           recv_time;
487   time_t           ghost;
488
489   if (IsUserPort(cptr))
490     return exit_client_msg(cptr, cptr, &me,
491                            "Cannot connect a server to a user port");
492
493   if (parc < 8)
494   {
495     need_more_params(sptr, "SERVER");
496     return exit_client(cptr, cptr, &me, "Need more parameters");
497   }
498   host = clean_servername(parv[1]);
499   if (!host)
500   {
501     sendto_opmask_butone(0, SNO_OLDSNO, "Bogus server name (%s) from %s",
502                          host, cli_name(cptr));
503     return exit_client_msg(cptr, cptr, &me, "Bogus server name (%s)", host);
504   }
505
506   if ((ajupe = jupe_find(host)) && JupeIsActive(ajupe))
507     return exit_client_msg(cptr, sptr, &me, "Juped: %s", JupeReason(ajupe));
508
509   /* check connection rules */
510   if (0 != conf_eval_crule(host, CRULE_ALL)) {
511     ServerStats->is_ref++;
512     sendto_opmask_butone(0, SNO_OLDSNO, "Refused connection from %s.", cli_name(cptr));
513     return exit_client(cptr, cptr, &me, "Disallowed by connection rule");
514   }
515
516   log_write(LS_NETWORK, L_NOTICE, LOG_NOSNOTICE, "SERVER: %s %s[%s]", host,
517             cli_sockhost(cptr), cli_sock_ip(cptr));
518
519   /*
520    * Detect protocol
521    */
522   prot = parse_protocol(parv[5]);
523   if (!prot)
524     return exit_client_msg(cptr, sptr, &me, "Bogus protocol (%s)", parv[5]);
525   else if (prot < atoi(MINOR_PROTOCOL))
526     return exit_new_server(cptr, sptr, host, timestamp,
527                            "Incompatible protocol: %s", parv[5]);
528
529   hop = atoi(parv[2]);
530   start_timestamp = atoi(parv[3]);
531   timestamp = atoi(parv[4]);
532   Debug((DEBUG_INFO, "Got SERVER %s with timestamp [%s] age %Tu (%Tu)",
533          host, parv[4], start_timestamp, cli_serv(&me)->timestamp));
534
535   if (timestamp < OLDEST_TS || start_timestamp < OLDEST_TS)
536     return exit_client_msg(cptr, sptr, &me,
537         "Bogus timestamps (%s %s)", parv[3], parv[4]);
538
539   /* If the server had a different name before, change it. */
540   if (!EmptyString(cli_name(cptr)) &&
541       (IsUnknown(cptr) || IsHandshake(cptr)) &&
542       0 != ircd_strcmp(cli_name(cptr), host))
543     hChangeClient(cptr, host);
544   ircd_strncpy(cli_name(cptr), host, HOSTLEN);
545   ircd_strncpy(cli_info(cptr), parv[parc-1][0] ? parv[parc-1] : cli_name(&me), REALLEN);
546   cli_hopcount(cptr) = hop;
547
548   if (conf_check_server(cptr)) {
549     ++ServerStats->is_ref;
550     sendto_opmask_butone(0, SNO_OLDSNO, "Received unauthorized connection "
551                          "from %s.", cli_name(cptr));
552     log_write(LS_NETWORK, L_NOTICE, LOG_NOSNOTICE, "Received unauthorized "
553               "connection from %C [%s]", cptr,
554               ircd_ntoa(&cli_ip(cptr)));
555     return exit_client(cptr, cptr, &me, "No C:line");
556   }
557
558   host = cli_name(cptr);
559
560   update_load();
561
562   if (!(aconf = find_conf_byname(cli_confs(cptr), host, CONF_SERVER))) {
563     ++ServerStats->is_ref;
564     sendto_opmask_butone(0, SNO_OLDSNO, "Access denied. No conf line for "
565                          "server %s", cli_name(cptr));
566     return exit_client_msg(cptr, cptr, &me,
567                            "Access denied. No conf line for server %s", cli_name(cptr));
568   }
569   encr = cli_passwd(cptr);
570
571   if (*aconf->passwd && !!strcmp(aconf->passwd, encr)) {
572     ++ServerStats->is_ref;
573     sendto_opmask_butone(0, SNO_OLDSNO, "Access denied (passwd mismatch) %s",
574                          cli_name(cptr));
575     return exit_client_msg(cptr, cptr, &me,
576                            "No Access (passwd mismatch) %s", cli_name(cptr));
577   }
578
579   memset(cli_passwd(cptr), 0, sizeof(cli_passwd(cptr)));
580
581   if (!feature_bool(FEAT_HUB)) {
582     for (i = 0; i <= HighestFd; i++)
583       if (LocalClientArray[i] && IsServer(LocalClientArray[i])) {
584         active_lh_line = 3;
585         break;
586       }
587   }
588
589   ret = check_loop_and_lh(cptr, sptr, NULL, NULL, &ghost, host, (parc > 7 ? parv[6] : NULL), timestamp, active_lh_line);
590   if (ret != 1)
591     return ret;
592
593   make_server(cptr);
594   cli_serv(cptr)->timestamp = timestamp;
595   cli_serv(cptr)->prot = prot;
596   cli_serv(cptr)->ghost = ghost;
597   SetServerYXX(cptr, cptr, parv[6]);
598
599   if (*parv[7] == '+') {
600     for (ch = parv[7] + 1; *ch; ch++)
601       switch (*ch) {
602       case 'h':
603         SetHub(cptr);
604         break;
605       case 's':
606         SetService(cptr);
607         break;
608       }
609   }
610
611   check_start_timestamp(cptr, timestamp, start_timestamp, TStime());
612   ret = server_estab(cptr, aconf);
613
614   if (feature_bool(FEAT_RELIABLE_CLOCK) &&
615       abs(cli_serv(cptr)->timestamp - recv_time) > 30) {
616     sendto_opmask_butone(0, SNO_OLDSNO, "Connected to a net with a "
617                          "timestamp-clock difference of %Td seconds! "
618                          "Used SETTIME to correct this.",
619                          timestamp - recv_time);
620     sendcmdto_prio_one(&me, CMD_SETTIME, cptr, "%Tu :%s", TStime(),
621                        cli_name(&me));
622   }
623
624   return ret;
625 }
626
627 /** Handle a SERVER message from another server.
628  *
629  * \a parv has the following elements:
630  * \li \a parv[1] is the server name
631  * \li \a parv[2] is the hop count to the server
632  * \li \a parv[3] is the start timestamp for the server
633  * \li \a parv[4] is the link timestamp
634  * \li \a parv[5] is the protocol version (P10 or J10)
635  * \li \a parv[6] is the numnick mask for the server
636  * \li \a parv[7] is a string of flags like +hs to mark hubs and services
637  * \li \a parv[\a parc - 1] is the server description
638  *
639  * See @ref m_functions for discussion of the arguments.
640  * @param[in] cptr Client that sent us the message.
641  * @param[in] sptr Original source of message.
642  * @param[in] parc Number of arguments.
643  * @param[in] parv Argument vector.
644  */
645 int ms_server(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
646 {
647   char*            ch;
648   int              i;
649   char*            host;
650   struct Client*   acptr;
651   struct Client*   bcptr;
652   struct Client*   LHcptr;
653   struct ConfItem* lhconf;
654   int              hop;
655   int              ret;
656   int              active_lh_line = 0;
657   unsigned short   prot;
658   time_t           start_timestamp;
659   time_t           timestamp;
660
661   if (parc < 8)
662   {
663     return need_more_params(sptr, "SERVER");
664     return exit_client(cptr, cptr, &me, "Need more parameters");
665   }
666   host = clean_servername(parv[1]);
667   if (!host)
668   {
669     sendto_opmask_butone(0, SNO_OLDSNO, "Bogus server name (%s) from %s",
670                          host, cli_name(cptr));
671     return exit_client_msg(cptr, cptr, &me, "Bogus server name (%s)", host);
672   }
673
674   /*
675    * Detect protocol
676    */
677   prot = parse_protocol(parv[5]);
678   if (!prot)
679     return exit_client_msg(cptr, sptr, &me, "Bogus protocol (%s)", parv[5]);
680   else if (prot < atoi(MINOR_PROTOCOL))
681     return exit_new_server(cptr, sptr, host, timestamp,
682                            "Incompatible protocol: %s", parv[5]);
683
684   hop = atoi(parv[2]);
685   start_timestamp = atoi(parv[3]);
686   timestamp = atoi(parv[4]);
687   Debug((DEBUG_INFO, "Got SERVER %s with timestamp [%s] age %Tu (%Tu)",
688          host, parv[4], start_timestamp, cli_serv(&me)->timestamp));
689
690   if (timestamp < OLDEST_TS)
691     return exit_client_msg(cptr, sptr, &me,
692         "Bogus timestamps (%s %s)", parv[3], parv[4]);
693
694   /*
695    * A local server introduces a new server behind this link.
696    * Check if this is allowed according L:, H: and Q: lines.
697    */
698   if (parv[parc - 1][0] == '\0')
699     return exit_client_msg(cptr, cptr, &me,
700                            "No server info specified for %s", host);
701   /*
702    * See if the newly found server is behind a guaranteed
703    * leaf (L-line). If so, close the link.
704    */
705   if ((lhconf = find_conf_byhost(cli_confs(cptr), cli_name(cptr), CONF_LEAF)) &&
706       (!lhconf->address.port || (hop > lhconf->address.port)))
707   {
708     /*
709      * L: lines normally come in pairs, here we try to
710      * make sure that the oldest link is squitted, not
711      * both.
712      */
713     active_lh_line = 1;
714     if (timestamp <= cli_serv(cptr)->timestamp)
715       LHcptr = 0;          /* Kill incoming server */
716     else
717       LHcptr = cptr;          /* Squit ourselfs */
718   }
719   else if (!(lhconf = find_conf_byname(cli_confs(cptr), cli_name(cptr), CONF_HUB)) ||
720            (lhconf->address.port && (hop > lhconf->address.port)) ||
721            (!BadPtr(lhconf->host) && match(lhconf->host, host)))
722   {
723     struct Client *ac3ptr;
724     active_lh_line = 2;
725     /* Look for net junction causing this: */
726     LHcptr = 0;            /* incoming server */
727     if (*parv[5] != 'J') {
728       for (ac3ptr = sptr; ac3ptr != &me; ac3ptr = cli_serv(ac3ptr)->up) {
729         if (IsJunction(ac3ptr)) {
730           LHcptr = ac3ptr;
731           break;
732         }
733       }
734     }
735   }
736
737   ret = check_loop_and_lh(cptr, sptr, LHcptr, lhconf, NULL, host, (parc > 7 ? parv[6] : NULL), timestamp, active_lh_line);
738   if (ret != 1)
739     return ret;
740
741   /*
742    * Server is informing about a new server behind
743    * this link. Create REMOTE server structure,
744    * add it to list and propagate word to my other
745    * server links...
746    */
747
748   acptr = make_client(cptr, STAT_SERVER);
749   make_server(acptr);
750   cli_serv(acptr)->prot = prot;
751   cli_serv(acptr)->timestamp = timestamp;
752   cli_hopcount(acptr) = hop;
753   ircd_strncpy(cli_name(acptr), host, HOSTLEN);
754   ircd_strncpy(cli_info(acptr), parv[parc-1], REALLEN);
755   cli_serv(acptr)->up = sptr;
756   cli_serv(acptr)->updown = add_dlink(&(cli_serv(sptr))->down, acptr);
757   /* Use cptr, because we do protocol 9 -> 10 translation
758      for numeric nicks ! */
759   SetServerYXX(cptr, acptr, parv[6]);
760
761   if (*parv[7] == '+') {
762     for (ch = parv[7] + 1; *ch; ch++)
763       switch (*ch) {
764       case 'h':
765         SetHub(acptr);
766         break;
767       case 's':
768         SetService(acptr);
769         break;
770       }
771   }
772
773   Count_newremoteserver(UserStats);
774   if (Protocol(acptr) < 10)
775     SetFlag(acptr, FLAG_TS8);
776   add_client_to_list(acptr);
777   hAddClient(acptr);
778   if (*parv[5] == 'J')
779   {
780     SetBurst(acptr);
781     sendto_opmask_butone(0, SNO_NETWORK, "Net junction: %s %s",
782                          cli_name(sptr), cli_name(acptr));
783     SetJunction(acptr);
784   }
785   /*
786    * Old sendto_serv_but_one() call removed because we now need to send
787    * different names to different servers (domain name matching).
788    */
789   for (i = 0; i <= HighestFd; i++)
790   {
791     if (!(bcptr = LocalClientArray[i]) || !IsServer(bcptr) ||
792         bcptr == cptr || IsMe(bcptr))
793       continue;
794     if (0 == match(cli_name(&me), cli_name(acptr)))
795       continue;
796     sendcmdto_one(sptr, CMD_SERVER, bcptr, "%s %d 0 %s %s %s%s +%s%s :%s",
797                   cli_name(acptr), hop + 1, parv[4], parv[5],
798                   NumServCap(acptr), IsHub(acptr) ? "h" : "",
799                   IsService(acptr) ? "s" : "", cli_info(acptr));
800   }
801   return 0;
802 }