]>
Commit | Line | Data |
---|---|---|
189935b1 | 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,v 1.41 2005/05/30 16:51:05 entrope Exp $ | |
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 | } |