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