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