51ed2c881b45c3dd00b141a6d6ac1fad4465ec50
[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                           "Leaf-only link %s <- %s, check L:",
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                           "Non-Hub link %s <- %s, check H:",
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     /*
428      * Did we kill the incoming server off already ?
429      */
430     if (killed)
431       return 0;
432   }
433
434   return 1;
435 }
436
437 /** Update server start timestamps and TS offsets.
438  * @param[in] cptr Server that just connected.
439  * @param[in] timestamp Current time according to \a cptr.
440  * @param[in] start_timestamp Time that \a cptr started.
441  * @param[in] recv_time Current time as we know it.
442  */
443 static void
444 check_start_timestamp(struct Client *cptr, time_t timestamp, time_t start_timestamp, time_t recv_time)
445 {
446   Debug((DEBUG_DEBUG, "My start time: %Tu; other's start time: %Tu",
447          cli_serv(&me)->timestamp, start_timestamp));
448   Debug((DEBUG_DEBUG, "Receive time: %Tu; received timestamp: %Tu; "
449          "difference %ld", recv_time, timestamp, timestamp - recv_time));
450   if (feature_bool(FEAT_RELIABLE_CLOCK)) {
451     if (start_timestamp < cli_serv(&me)->timestamp)
452       cli_serv(&me)->timestamp = start_timestamp;
453     if (IsUnknown(cptr))
454       cli_serv(cptr)->timestamp = TStime();
455   } else if (start_timestamp < cli_serv(&me)->timestamp) {
456     sendto_opmask_butone(0, SNO_OLDSNO, "got earlier start time: "
457                          "%Tu < %Tu", start_timestamp,
458                          cli_serv(&me)->timestamp);
459     cli_serv(&me)->timestamp = start_timestamp;
460     TSoffset += timestamp - recv_time;
461     sendto_opmask_butone(0, SNO_OLDSNO, "clock adjusted by adding %d",
462                          (int)(timestamp - recv_time));
463   } else if ((start_timestamp > cli_serv(&me)->timestamp) &&
464              IsUnknown(cptr)) {
465     cli_serv(cptr)->timestamp = TStime();
466   } else if (timestamp != recv_time) {
467     /*
468      * Equal start times, we have a collision.  Let the connected-to
469      * server decide. This assumes leafs issue more than half of the
470      * connection attempts.
471      */
472     if (IsUnknown(cptr))
473       cli_serv(cptr)->timestamp = TStime();
474     else if (IsHandshake(cptr)) {
475       sendto_opmask_butone(0, SNO_OLDSNO, "clock adjusted by adding %d",
476                            (int)(timestamp - recv_time));
477       TSoffset += timestamp - recv_time;
478     }
479   }
480 }
481
482 /** Interpret a server's flags.
483  *
484  * @param[in] cptr New server structure.
485  * @param[in] flags String listing server's P10 flags.
486  */
487 void set_server_flags(struct Client *cptr, const char *flags)
488 {
489     while (*flags) switch (*flags++) {
490     case 'h': SetHub(cptr); break;
491     case 's': SetService(cptr); break;
492     case '6': SetIPv6(cptr); break;
493     }
494 }
495
496 /** Handle a SERVER message from an unregistered connection.
497  *
498  * \a parv has the following elements:
499  * \li \a parv[1] is the server name
500  * \li \a parv[2] is the hop count to the server
501  * \li \a parv[3] is the start timestamp for the server
502  * \li \a parv[4] is the link timestamp
503  * \li \a parv[5] is the protocol version (P10 or J10)
504  * \li \a parv[6] is the numnick mask for the server
505  * \li \a parv[7] is a string of flags like +hs to mark hubs and services
506  * \li \a parv[\a parc - 1] is the server description
507  *
508  * See @ref m_functions for discussion of the arguments.
509  * @param[in] cptr Client that sent us the message.
510  * @param[in] sptr Original source of message.
511  * @param[in] parc Number of arguments.
512  * @param[in] parv Argument vector.
513  */
514 int mr_server(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
515 {
516   char*            host;
517   struct ConfItem* aconf;
518   struct Jupe*     ajupe;
519   int              hop;
520   int              ret;
521   unsigned short   prot;
522   time_t           start_timestamp;
523   time_t           timestamp;
524   time_t           recv_time;
525   time_t           ghost;
526
527   if (IsUserPort(cptr))
528     return exit_client_msg(cptr, cptr, &me,
529                            "Cannot connect a server to a user port");
530
531   if (parc < 8)
532   {
533     need_more_params(sptr, "SERVER");
534     return exit_client(cptr, cptr, &me, "Need more parameters");
535   }
536   host = clean_servername(parv[1]);
537   if (!host)
538   {
539     sendto_opmask_butone(0, SNO_OLDSNO, "Bogus server name (%s) from %s",
540                          host, cli_name(cptr));
541     return exit_client_msg(cptr, cptr, &me, "Bogus server name (%s)", host);
542   }
543
544   if ((ajupe = jupe_find(host)) && JupeIsActive(ajupe))
545     return exit_client_msg(cptr, sptr, &me, "Juped: %s", JupeReason(ajupe));
546
547   /* check connection rules */
548   if (0 != conf_eval_crule(host, CRULE_ALL)) {
549     ServerStats->is_ref++;
550     sendto_opmask_butone(0, SNO_OLDSNO, "Refused connection from %s.", cli_name(cptr));
551     return exit_client(cptr, cptr, &me, "Disallowed by connection rule");
552   }
553
554   log_write(LS_NETWORK, L_NOTICE, LOG_NOSNOTICE, "SERVER: %s %s[%s]", host,
555             cli_sockhost(cptr), cli_sock_ip(cptr));
556
557   /*
558    * Detect protocol
559    */
560   hop = atoi(parv[2]);
561   start_timestamp = atoi(parv[3]);
562   timestamp = atoi(parv[4]);
563   prot = parse_protocol(parv[5]);
564   if (!prot)
565     return exit_client_msg(cptr, sptr, &me, "Bogus protocol (%s)", parv[5]);
566   else if (prot < atoi(MINOR_PROTOCOL))
567     return exit_new_server(cptr, sptr, host, timestamp,
568                            "Incompatible protocol: %s", parv[5]);
569
570   Debug((DEBUG_INFO, "Got SERVER %s with timestamp [%s] age %Tu (%Tu)",
571          host, parv[4], start_timestamp, cli_serv(&me)->timestamp));
572
573   if (timestamp < OLDEST_TS || start_timestamp < OLDEST_TS)
574     return exit_client_msg(cptr, sptr, &me,
575         "Bogus timestamps (%s %s)", parv[3], parv[4]);
576
577   /* If the server had a different name before, change it. */
578   if (!EmptyString(cli_name(cptr)) &&
579       (IsUnknown(cptr) || IsHandshake(cptr)) &&
580       0 != ircd_strcmp(cli_name(cptr), host))
581     hChangeClient(cptr, host);
582   ircd_strncpy(cli_name(cptr), host, HOSTLEN);
583   ircd_strncpy(cli_info(cptr), parv[parc-1][0] ? parv[parc-1] : cli_name(&me), REALLEN);
584   cli_hopcount(cptr) = hop;
585
586   if (conf_check_server(cptr)) {
587     ++ServerStats->is_ref;
588     sendto_opmask_butone(0, SNO_OLDSNO, "Received unauthorized connection "
589                          "from %s.", cli_name(cptr));
590     log_write(LS_NETWORK, L_NOTICE, LOG_NOSNOTICE, "Received unauthorized "
591               "connection from %C [%s]", cptr,
592               ircd_ntoa(&cli_ip(cptr)));
593     return exit_client(cptr, cptr, &me, "No Connect block");
594   }
595
596   host = cli_name(cptr);
597
598   update_load();
599
600   if (!(aconf = find_conf_byname(cli_confs(cptr), host, CONF_SERVER))) {
601     ++ServerStats->is_ref;
602     sendto_opmask_butone(0, SNO_OLDSNO, "Access denied. No conf line for "
603                          "server %s", cli_name(cptr));
604     return exit_client_msg(cptr, cptr, &me,
605                            "Access denied. No conf line for server %s", cli_name(cptr));
606   }
607
608   if (*aconf->passwd && !!strcmp(aconf->passwd, cli_passwd(cptr))) {
609     ++ServerStats->is_ref;
610     sendto_opmask_butone(0, SNO_OLDSNO, "Access denied (passwd mismatch) %s",
611                          cli_name(cptr));
612     return exit_client_msg(cptr, cptr, &me,
613                            "No Access (passwd mismatch) %s", cli_name(cptr));
614   }
615
616   memset(cli_passwd(cptr), 0, sizeof(cli_passwd(cptr)));
617
618   ret = check_loop_and_lh(cptr, sptr, &ghost, host, (parc > 7 ? parv[6] : NULL), timestamp, hop, 1);
619   if (ret != 1)
620     return ret;
621
622   make_server(cptr);
623   cli_serv(cptr)->timestamp = timestamp;
624   cli_serv(cptr)->prot = prot;
625   cli_serv(cptr)->ghost = ghost;
626   memset(cli_privs(cptr), 255, sizeof(struct Privs));
627   ClrPriv(cptr, PRIV_SET);
628   SetServerYXX(cptr, cptr, parv[6]);
629
630   /* Attach any necessary UWorld config items. */
631   attach_confs_byhost(cptr, host, CONF_UWORLD);
632
633   if (*parv[7] == '+')
634     set_server_flags(cptr, parv[7] + 1);
635
636   recv_time = TStime();
637   check_start_timestamp(cptr, timestamp, start_timestamp, recv_time);
638   ret = server_estab(cptr, aconf);
639
640   if (feature_bool(FEAT_RELIABLE_CLOCK) &&
641       abs(cli_serv(cptr)->timestamp - recv_time) > 30) {
642     sendto_opmask_butone(0, SNO_OLDSNO, "Connected to a net with a "
643                          "timestamp-clock difference of %Td seconds! "
644                          "Used SETTIME to correct this.",
645                          timestamp - recv_time);
646     sendcmdto_prio_one(&me, CMD_SETTIME, cptr, "%Tu :%s", TStime(),
647                        cli_name(&me));
648   }
649
650   return ret;
651 }
652
653 /** Handle a SERVER message from another server.
654  *
655  * \a parv has the following elements:
656  * \li \a parv[1] is the server name
657  * \li \a parv[2] is the hop count to the server
658  * \li \a parv[3] is the start timestamp for the server
659  * \li \a parv[4] is the link timestamp
660  * \li \a parv[5] is the protocol version (P10 or J10)
661  * \li \a parv[6] is the numnick mask for the server
662  * \li \a parv[7] is a string of flags like +hs to mark hubs and services
663  * \li \a parv[\a parc - 1] is the server description
664  *
665  * See @ref m_functions for discussion of the arguments.
666  * @param[in] cptr Client that sent us the message.
667  * @param[in] sptr Original source of message.
668  * @param[in] parc Number of arguments.
669  * @param[in] parv Argument vector.
670  */
671 int ms_server(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
672 {
673   int              i;
674   char*            host;
675   struct Client*   acptr;
676   struct Client*   bcptr;
677   int              hop;
678   int              ret;
679   unsigned short   prot;
680   time_t           start_timestamp;
681   time_t           timestamp;
682
683   if (parc < 8)
684   {
685     return need_more_params(sptr, "SERVER");
686     return exit_client(cptr, cptr, &me, "Need more parameters");
687   }
688   host = clean_servername(parv[1]);
689   if (!host)
690   {
691     sendto_opmask_butone(0, SNO_OLDSNO, "Bogus server name (%s) from %s",
692                          host, cli_name(cptr));
693     return exit_client_msg(cptr, cptr, &me, "Bogus server name (%s)", host);
694   }
695
696   /*
697    * Detect protocol
698    */
699   hop = atoi(parv[2]);
700   start_timestamp = atoi(parv[3]);
701   timestamp = atoi(parv[4]);
702   prot = parse_protocol(parv[5]);
703   if (!prot)
704     return exit_client_msg(cptr, sptr, &me, "Bogus protocol (%s)", parv[5]);
705   else if (prot < atoi(MINOR_PROTOCOL))
706     return exit_new_server(cptr, sptr, host, timestamp,
707                            "Incompatible protocol: %s", parv[5]);
708
709   Debug((DEBUG_INFO, "Got SERVER %s with timestamp [%s] age %Tu (%Tu)",
710          host, parv[4], start_timestamp, cli_serv(&me)->timestamp));
711
712   if (timestamp < OLDEST_TS)
713     return exit_client_msg(cptr, sptr, &me,
714         "Bogus timestamps (%s %s)", parv[3], parv[4]);
715
716   if (parv[parc - 1][0] == '\0')
717     return exit_client_msg(cptr, cptr, &me,
718                            "No server info specified for %s", host);
719
720   ret = check_loop_and_lh(cptr, sptr, NULL, host, (parc > 7 ? parv[6] : NULL), timestamp, hop, parv[5][0] == 'J');
721   if (ret != 1)
722     return ret;
723
724   /*
725    * Server is informing about a new server behind
726    * this link. Create REMOTE server structure,
727    * add it to list and propagate word to my other
728    * server links...
729    */
730
731   acptr = make_client(cptr, STAT_SERVER);
732   make_server(acptr);
733   cli_serv(acptr)->prot = prot;
734   cli_serv(acptr)->timestamp = timestamp;
735   cli_hopcount(acptr) = hop;
736   ircd_strncpy(cli_name(acptr), host, HOSTLEN);
737   ircd_strncpy(cli_info(acptr), parv[parc-1], REALLEN);
738   cli_serv(acptr)->up = sptr;
739   cli_serv(acptr)->updown = add_dlink(&(cli_serv(sptr))->down, acptr);
740   /* Use cptr, because we do protocol 9 -> 10 translation
741      for numeric nicks ! */
742   SetServerYXX(cptr, acptr, parv[6]);
743
744   /* Attach any necessary UWorld config items. */
745   attach_confs_byhost(cptr, host, CONF_UWORLD);
746
747   if (*parv[7] == '+')
748     set_server_flags(acptr, parv[7] + 1);
749
750   Count_newremoteserver(UserStats);
751   if (Protocol(acptr) < 10)
752     SetFlag(acptr, FLAG_TS8);
753   add_client_to_list(acptr);
754   hAddClient(acptr);
755   if (*parv[5] == 'J')
756   {
757     SetBurst(acptr);
758     SetJunction(acptr);
759     for (bcptr = cli_serv(acptr)->up; !IsMe(bcptr); bcptr = cli_serv(bcptr)->up)
760       if (IsBurstOrBurstAck(bcptr))
761           break;
762     if (IsMe(bcptr))
763       sendto_opmask_butone(0, SNO_NETWORK, "Net junction: %s %s",
764                            cli_name(sptr), cli_name(acptr));
765   }
766   /*
767    * Old sendto_serv_but_one() call removed because we now need to send
768    * different names to different servers (domain name matching).
769    */
770   for (i = 0; i <= HighestFd; i++)
771   {
772     if (!(bcptr = LocalClientArray[i]) || !IsServer(bcptr) ||
773         bcptr == cptr || IsMe(bcptr))
774       continue;
775     if (0 == match(cli_name(&me), cli_name(acptr)))
776       continue;
777     sendcmdto_one(sptr, CMD_SERVER, bcptr, "%s %d 0 %s %s %s%s +%s%s%s :%s",
778                   cli_name(acptr), hop + 1, parv[4], parv[5],
779                   NumServCap(acptr), IsHub(acptr) ? "h" : "",
780                   IsService(acptr) ? "s" : "", IsIPv6(acptr) ? "6" : "",
781                   cli_info(acptr));
782   }
783   return 0;
784 }