1 |
/* |
2 |
* ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd) |
3 |
* |
4 |
* Copyright (c) 1997-2018 ircd-hybrid development team |
5 |
* |
6 |
* This program is free software; you can redistribute it and/or modify |
7 |
* it under the terms of the GNU General Public License as published by |
8 |
* the Free Software Foundation; either version 2 of the License, or |
9 |
* (at your option) any later version. |
10 |
* |
11 |
* This program is distributed in the hope that it will be useful, |
12 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 |
* GNU General Public License for more details. |
15 |
* |
16 |
* You should have received a copy of the GNU General Public License |
17 |
* along with this program; if not, write to the Free Software |
18 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 |
19 |
* USA |
20 |
*/ |
21 |
|
22 |
/*! \file m_server.c |
23 |
* \brief Includes required functions for processing the SERVER/SID command. |
24 |
* \version $Id$ |
25 |
*/ |
26 |
|
27 |
#include "stdinc.h" |
28 |
#include "list.h" |
29 |
#include "client.h" |
30 |
#include "client_svstag.h" |
31 |
#include "event.h" |
32 |
#include "hash.h" |
33 |
#include "id.h" |
34 |
#include "irc_string.h" |
35 |
#include "ircd.h" |
36 |
#include "numeric.h" |
37 |
#include "conf.h" |
38 |
#include "conf_service.h" |
39 |
#include "log.h" |
40 |
#include "misc.h" |
41 |
#include "server.h" |
42 |
#include "server_capab.h" |
43 |
#include "user.h" |
44 |
#include "send.h" |
45 |
#include "parse.h" |
46 |
#include "memory.h" |
47 |
#include "modules.h" |
48 |
#include "fdlist.h" |
49 |
|
50 |
|
51 |
/*! Parses server flags to be potentially set |
52 |
* \param client_p Pointer to server's Client structure |
53 |
* \param flags Pointer to the flag string to be parsed |
54 |
*/ |
55 |
static void |
56 |
server_set_flags(struct Client *client_p, const char *flags) |
57 |
{ |
58 |
const unsigned char *p = (const unsigned char *)flags; |
59 |
|
60 |
if (*p != '+') |
61 |
return; |
62 |
|
63 |
while (*++p) |
64 |
{ |
65 |
switch (*p) |
66 |
{ |
67 |
case 'h': |
68 |
AddFlag(client_p, FLAGS_HIDDEN); |
69 |
break; |
70 |
default: |
71 |
break; |
72 |
} |
73 |
} |
74 |
} |
75 |
|
76 |
/* |
77 |
* send_tb |
78 |
* |
79 |
* inputs - pointer to Client |
80 |
* - pointer to channel |
81 |
* output - NONE |
82 |
* side effects - Called on a server burst when |
83 |
* server is CAPAB_TBURST capable |
84 |
*/ |
85 |
static void |
86 |
server_send_tburst(struct Client *client_p, const struct Channel *chptr) |
87 |
{ |
88 |
/* |
89 |
* We may also send an empty topic here, but only if topic_time isn't 0, |
90 |
* i.e. if we had a topic that got unset. This is required for syncing |
91 |
* topics properly. |
92 |
* |
93 |
* Imagine the following scenario: Our downlink introduces a channel |
94 |
* to us with a TS that is equal to ours, but the channel topic on |
95 |
* their side got unset while the servers were in splitmode, which means |
96 |
* their 'topic' is newer. They simply wanted to unset it, so we have to |
97 |
* deal with it in a more sophisticated fashion instead of just resetting |
98 |
* it to their old topic they had before. Read m_tburst.c:ms_tburst |
99 |
* for further information -Michael |
100 |
*/ |
101 |
if (chptr->topic_time) |
102 |
sendto_one(client_p, ":%s TBURST %ju %s %ju %s :%s", me.id, |
103 |
chptr->creationtime, chptr->name, |
104 |
chptr->topic_time, |
105 |
chptr->topic_info, |
106 |
chptr->topic); |
107 |
} |
108 |
|
109 |
/* sendnick_TS() |
110 |
* |
111 |
* inputs - client (server) to send nick towards |
112 |
* - client to send nick for |
113 |
* output - NONE |
114 |
* side effects - NICK message is sent towards given client_p |
115 |
*/ |
116 |
static void |
117 |
server_send_client(struct Client *client_p, struct Client *target_p) |
118 |
{ |
119 |
dlink_node *node; |
120 |
char buf[UMODE_MAX_STR] = ""; |
121 |
|
122 |
assert(IsClient(target_p)); |
123 |
|
124 |
send_umode(target_p, 0, 0, buf); |
125 |
|
126 |
if (buf[0] == '\0') |
127 |
{ |
128 |
buf[0] = '+'; |
129 |
buf[1] = '\0'; |
130 |
} |
131 |
|
132 |
/* TBR: compatibility mode */ |
133 |
if (IsCapable(client_p, CAPAB_RHOST)) |
134 |
sendto_one(client_p, ":%s UID %s %u %ju %s %s %s %s %s %s %s :%s", |
135 |
target_p->servptr->id, |
136 |
target_p->name, target_p->hopcount + 1, |
137 |
target_p->tsinfo, |
138 |
buf, target_p->username, target_p->host, target_p->realhost, |
139 |
target_p->sockhost, target_p->id, |
140 |
target_p->account, target_p->info); |
141 |
else |
142 |
sendto_one(client_p, ":%s UID %s %u %ju %s %s %s %s %s %s :%s", |
143 |
target_p->servptr->id, |
144 |
target_p->name, target_p->hopcount + 1, |
145 |
target_p->tsinfo, |
146 |
buf, target_p->username, target_p->host, |
147 |
target_p->sockhost, target_p->id, |
148 |
target_p->account, target_p->info); |
149 |
|
150 |
if (!EmptyString(target_p->certfp)) |
151 |
sendto_one(client_p, ":%s CERTFP %s", target_p->id, target_p->certfp); |
152 |
|
153 |
if (target_p->away[0]) |
154 |
sendto_one(client_p, ":%s AWAY :%s", target_p->id, target_p->away); |
155 |
|
156 |
|
157 |
DLINK_FOREACH(node, target_p->svstags.head) |
158 |
{ |
159 |
const struct ServicesTag *svstag = node->data; |
160 |
char *m = buf; |
161 |
|
162 |
for (const struct user_modes *tab = umode_tab; tab->c; ++tab) |
163 |
if (svstag->umodes & tab->flag) |
164 |
*m++ = tab->c; |
165 |
*m = '\0'; |
166 |
|
167 |
sendto_one(client_p, ":%s SVSTAG %s %ju %u +%s :%s", me.id, target_p->id, |
168 |
target_p->tsinfo, svstag->numeric, buf, svstag->tag); |
169 |
} |
170 |
} |
171 |
|
172 |
/* burst_all() |
173 |
* |
174 |
* inputs - pointer to server to send burst to |
175 |
* output - NONE |
176 |
* side effects - complete burst of channels/nicks is sent to client_p |
177 |
*/ |
178 |
static void |
179 |
server_burst(struct Client *client_p) |
180 |
{ |
181 |
dlink_node *node = NULL; |
182 |
|
183 |
DLINK_FOREACH(node, global_client_list.head) |
184 |
{ |
185 |
struct Client *target_p = node->data; |
186 |
|
187 |
if (target_p->from != client_p) |
188 |
server_send_client(client_p, target_p); |
189 |
} |
190 |
|
191 |
DLINK_FOREACH(node, channel_get_list()->head) |
192 |
{ |
193 |
const struct Channel *chptr = node->data; |
194 |
|
195 |
if (dlink_list_length(&chptr->members)) |
196 |
{ |
197 |
channel_send_modes(client_p, chptr); |
198 |
|
199 |
if (IsCapable(client_p, CAPAB_TBURST)) |
200 |
server_send_tburst(client_p, chptr); |
201 |
} |
202 |
} |
203 |
|
204 |
/* Always send a PING after connect burst is done */ |
205 |
sendto_one(client_p, "PING :%s", me.id); |
206 |
} |
207 |
|
208 |
/* server_estab() |
209 |
* |
210 |
* inputs - pointer to a struct Client |
211 |
* output - |
212 |
* side effects - |
213 |
*/ |
214 |
static void |
215 |
server_estab(struct Client *client_p) |
216 |
{ |
217 |
struct MaskItem *conf = NULL; |
218 |
dlink_node *node = NULL; |
219 |
|
220 |
if ((conf = find_conf_name(&client_p->connection->confs, client_p->name, CONF_SERVER)) == NULL) |
221 |
{ |
222 |
/* This shouldn't happen, better tell the ops... -A1kmm */ |
223 |
sendto_realops_flags(UMODE_SERVNOTICE, L_ADMIN, SEND_NOTICE, |
224 |
"Warning: lost connect{} block for %s", |
225 |
client_get_name(client_p, SHOW_IP)); |
226 |
sendto_realops_flags(UMODE_SERVNOTICE, L_OPER, SEND_NOTICE, |
227 |
"Warning: lost connect{} block for %s", |
228 |
client_get_name(client_p, MASK_IP)); |
229 |
exit_client(client_p, "Lost connect{} block!"); |
230 |
return; |
231 |
} |
232 |
|
233 |
xfree(client_p->connection->password); |
234 |
client_p->connection->password = NULL; |
235 |
|
236 |
if (!ConfigServerInfo.hub && dlink_list_length(&local_server_list)) |
237 |
{ |
238 |
++ServerStats.is_ref; |
239 |
exit_client(client_p, "I'm a leaf not a hub"); |
240 |
return; |
241 |
} |
242 |
|
243 |
if (IsUnknown(client_p)) |
244 |
{ |
245 |
sendto_one(client_p, "PASS %s TS %u %s", conf->spasswd, TS_CURRENT, me.id); |
246 |
|
247 |
sendto_one(client_p, "CAPAB :%s", capab_get(NULL)); |
248 |
|
249 |
sendto_one(client_p, "SERVER %s 1 :%s%s", |
250 |
me.name, ConfigServerHide.hidden ? "(H) " : "", me.info); |
251 |
} |
252 |
|
253 |
sendto_one(client_p, ":%s SVINFO %u %u 0 :%ju", me.id, TS_CURRENT, TS_MIN, |
254 |
CurrentTime); |
255 |
|
256 |
client_p->servptr = &me; |
257 |
|
258 |
if (HasFlag(client_p, FLAGS_CLOSING)) |
259 |
return; |
260 |
|
261 |
SetServer(client_p); |
262 |
|
263 |
dlinkAdd(client_p, &client_p->lnode, &me.serv->server_list); |
264 |
|
265 |
assert(dlinkFind(&unknown_list, client_p)); |
266 |
dlink_move_node(&client_p->connection->lclient_node, |
267 |
&unknown_list, &local_server_list); |
268 |
|
269 |
dlinkAdd(client_p, &client_p->node, &global_server_list); |
270 |
|
271 |
if ((dlink_list_length(&local_client_list) + |
272 |
dlink_list_length(&local_server_list)) > Count.max_loc_con) |
273 |
Count.max_loc_con = dlink_list_length(&local_client_list) + |
274 |
dlink_list_length(&local_server_list); |
275 |
|
276 |
hash_add_client(client_p); |
277 |
hash_add_id(client_p); |
278 |
|
279 |
/* Doesn't duplicate client_p->serv if allocated this struct already */ |
280 |
server_make(client_p); |
281 |
|
282 |
/* Fixing eob timings.. -gnp */ |
283 |
client_p->connection->firsttime = CurrentTime; |
284 |
|
285 |
if (service_find(client_p->name, irccmp)) |
286 |
AddFlag(client_p, FLAGS_SERVICE); |
287 |
|
288 |
/* Show the real host/IP to admins */ |
289 |
if (tls_isusing(&client_p->connection->fd->ssl)) |
290 |
{ |
291 |
/* Show the real host/IP to admins */ |
292 |
sendto_realops_flags(UMODE_SERVNOTICE, L_ADMIN, SEND_NOTICE, |
293 |
"Link with %s established: [TLS: %s] (Capabilities: %s)", |
294 |
client_get_name(client_p, SHOW_IP), tls_get_cipher(&client_p->connection->fd->ssl), |
295 |
capab_get(client_p)); |
296 |
|
297 |
/* Now show the masked hostname/IP to opers */ |
298 |
sendto_realops_flags(UMODE_SERVNOTICE, L_OPER, SEND_NOTICE, |
299 |
"Link with %s established: [TLS: %s] (Capabilities: %s)", |
300 |
client_get_name(client_p, MASK_IP), tls_get_cipher(&client_p->connection->fd->ssl), |
301 |
capab_get(client_p)); |
302 |
ilog(LOG_TYPE_IRCD, "Link with %s established: [TLS: %s] (Capabilities: %s)", |
303 |
client_get_name(client_p, SHOW_IP), tls_get_cipher(&client_p->connection->fd->ssl), |
304 |
capab_get(client_p)); |
305 |
} |
306 |
else |
307 |
{ |
308 |
/* Show the real host/IP to admins */ |
309 |
sendto_realops_flags(UMODE_SERVNOTICE, L_ADMIN, SEND_NOTICE, |
310 |
"Link with %s established: (Capabilities: %s)", |
311 |
client_get_name(client_p, SHOW_IP), capab_get(client_p)); |
312 |
/* Now show the masked hostname/IP to opers */ |
313 |
sendto_realops_flags(UMODE_SERVNOTICE, L_OPER, SEND_NOTICE, |
314 |
"Link with %s established: (Capabilities: %s)", |
315 |
client_get_name(client_p, MASK_IP), capab_get(client_p)); |
316 |
ilog(LOG_TYPE_IRCD, "Link with %s established: (Capabilities: %s)", |
317 |
client_get_name(client_p, SHOW_IP), capab_get(client_p)); |
318 |
} |
319 |
|
320 |
fd_note(client_p->connection->fd, "Server: %s", client_p->name); |
321 |
|
322 |
sendto_server(client_p, 0, 0, ":%s SID %s 2 %s :%s%s", |
323 |
me.id, client_p->name, client_p->id, |
324 |
IsHidden(client_p) ? "(H) " : "", client_p->info); |
325 |
|
326 |
/* |
327 |
* Pass on my client information to the new server |
328 |
* |
329 |
* First, pass only servers (idea is that if the link gets |
330 |
* cancelled beacause the server was already there, |
331 |
* there are no NICK's to be cancelled...). Of course, |
332 |
* if cancellation occurs, all this info is sent anyway, |
333 |
* and I guess the link dies when a read is attempted...? --msa |
334 |
* |
335 |
* Note: Link cancellation to occur at this point means |
336 |
* that at least two servers from my fragment are building |
337 |
* up connection this other fragment at the same time, it's |
338 |
* a race condition, not the normal way of operation... |
339 |
*/ |
340 |
DLINK_FOREACH_PREV(node, global_server_list.tail) |
341 |
{ |
342 |
struct Client *target_p = node->data; |
343 |
|
344 |
/* target_p->from == target_p for target_p == client_p */ |
345 |
if (IsMe(target_p) || target_p->from == client_p) |
346 |
continue; |
347 |
|
348 |
sendto_one(client_p, ":%s SID %s %u %s :%s%s", |
349 |
target_p->servptr->id, target_p->name, target_p->hopcount+1, |
350 |
target_p->id, IsHidden(target_p) ? "(H) " : "", |
351 |
target_p->info); |
352 |
} |
353 |
|
354 |
server_burst(client_p); |
355 |
|
356 |
if (IsCapable(client_p, CAPAB_EOB)) |
357 |
{ |
358 |
DLINK_FOREACH_PREV(node, global_server_list.tail) |
359 |
{ |
360 |
struct Client *target_p = node->data; |
361 |
|
362 |
if (target_p->from == client_p) |
363 |
continue; |
364 |
|
365 |
if (IsMe(target_p) || HasFlag(target_p, FLAGS_EOB)) |
366 |
sendto_one(client_p, ":%s EOB", target_p->id); |
367 |
} |
368 |
} |
369 |
} |
370 |
|
371 |
/* set_server_gecos() |
372 |
* |
373 |
* input - pointer to client |
374 |
* output - NONE |
375 |
* side effects - servers gecos field is set |
376 |
*/ |
377 |
static void |
378 |
server_set_gecos(struct Client *client_p, const char *info) |
379 |
{ |
380 |
const char *s = info; |
381 |
|
382 |
/* check for (H) which is a hidden server */ |
383 |
if (!strncmp(s, "(H) ", 4)) |
384 |
{ |
385 |
AddFlag(client_p, FLAGS_HIDDEN); |
386 |
s = s + 4; |
387 |
} |
388 |
|
389 |
if (!EmptyString(s)) |
390 |
strlcpy(client_p->info, s, sizeof(client_p->info)); |
391 |
else |
392 |
strlcpy(client_p->info, "(Unknown Location)", sizeof(client_p->info)); |
393 |
} |
394 |
|
395 |
static int |
396 |
server_check(const char *name, struct Client *client_p) |
397 |
{ |
398 |
dlink_node *node = NULL; |
399 |
struct MaskItem *server_conf = NULL; |
400 |
int error = -1; |
401 |
|
402 |
assert(client_p); |
403 |
|
404 |
/* Loop through looking for all possible connect items that might work */ |
405 |
DLINK_FOREACH(node, connect_items.head) |
406 |
{ |
407 |
struct MaskItem *conf = node->data; |
408 |
|
409 |
if (irccmp(name, conf->name)) |
410 |
continue; |
411 |
|
412 |
error = -3; |
413 |
|
414 |
if (!irccmp(conf->host, client_p->host) || |
415 |
!irccmp(conf->host, client_p->sockhost)) |
416 |
{ |
417 |
error = -2; |
418 |
|
419 |
if (!match_conf_password(client_p->connection->password, conf)) |
420 |
return -2; |
421 |
|
422 |
if (!EmptyString(conf->certfp)) |
423 |
if (EmptyString(client_p->certfp) || strcasecmp(client_p->certfp, conf->certfp)) |
424 |
return -4; |
425 |
|
426 |
server_conf = conf; |
427 |
} |
428 |
} |
429 |
|
430 |
if (server_conf == NULL) |
431 |
return error; |
432 |
|
433 |
conf_attach(client_p, server_conf); |
434 |
|
435 |
switch (server_conf->aftype) |
436 |
{ |
437 |
case AF_INET6: |
438 |
{ |
439 |
const struct sockaddr_in6 *v6 = (struct sockaddr_in6 *)&server_conf->addr; |
440 |
|
441 |
if (IN6_IS_ADDR_UNSPECIFIED(&v6->sin6_addr)) |
442 |
memcpy(&server_conf->addr, &client_p->ip, sizeof(server_conf->addr)); |
443 |
break; |
444 |
} |
445 |
case AF_INET: |
446 |
{ |
447 |
const struct sockaddr_in *v4 = (struct sockaddr_in *)&server_conf->addr; |
448 |
|
449 |
if (v4->sin_addr.s_addr == INADDR_NONE) |
450 |
memcpy(&server_conf->addr, &client_p->ip, sizeof(server_conf->addr)); |
451 |
break; |
452 |
} |
453 |
} |
454 |
|
455 |
return 0; |
456 |
} |
457 |
|
458 |
/* mr_server() |
459 |
* parv[0] = command |
460 |
* parv[1] = servername |
461 |
* parv[2] = hopcount |
462 |
* parv[3] = serverinfo |
463 |
* |
464 |
* 8.3.x+: |
465 |
* parv[0] = command |
466 |
* parv[1] = servername |
467 |
* parv[2] = hopcount |
468 |
* parv[3] = sid |
469 |
* parv[4] = string of flags starting with '+' |
470 |
* parv[5] = serverinfo |
471 |
*/ |
472 |
static int |
473 |
mr_server(struct Client *source_p, int parc, char *parv[]) |
474 |
{ |
475 |
const char *name = parv[1]; |
476 |
const char *sid = parc == 6 ? parv[3] : source_p->id; /* TBR: compatibility 'mode' */ |
477 |
struct Client *target_p = NULL; |
478 |
|
479 |
if (EmptyString(parv[parc - 1])) |
480 |
{ |
481 |
exit_client(source_p, "No server description supplied"); |
482 |
return 0; |
483 |
} |
484 |
|
485 |
if (!server_valid_name(name)) |
486 |
{ |
487 |
sendto_realops_flags(UMODE_SERVNOTICE, L_ADMIN, SEND_NOTICE, |
488 |
"Unauthorized server connection attempt from %s: Bogus server name " |
489 |
"for server %s", client_get_name(source_p, SHOW_IP), name); |
490 |
sendto_realops_flags(UMODE_SERVNOTICE, L_OPER, SEND_NOTICE, |
491 |
"Unauthorized server connection attempt from %s: Bogus server name " |
492 |
"for server %s", client_get_name(source_p, MASK_IP), name); |
493 |
exit_client(source_p, "Bogus server name"); |
494 |
return 0; |
495 |
} |
496 |
|
497 |
if (!valid_sid(sid)) |
498 |
{ |
499 |
sendto_realops_flags(UMODE_SERVNOTICE, L_ADMIN, SEND_NOTICE, |
500 |
"Link %s introduced server with bogus server ID %s", |
501 |
client_get_name(source_p, SHOW_IP), sid); |
502 |
sendto_realops_flags(UMODE_SERVNOTICE, L_OPER, SEND_NOTICE, |
503 |
"Link %s introduced server with bogus server ID %s", |
504 |
client_get_name(source_p, MASK_IP), sid); |
505 |
exit_client(source_p, "Bogus server ID introduced"); |
506 |
return 0; |
507 |
} |
508 |
|
509 |
/* Now we just have to call server_check() and everything should |
510 |
* be check for us... -A1kmm. |
511 |
*/ |
512 |
switch (server_check(name, source_p)) |
513 |
{ |
514 |
case -1: |
515 |
if (ConfigGeneral.warn_no_connect_block) |
516 |
{ |
517 |
sendto_realops_flags(UMODE_SERVNOTICE, L_ADMIN, SEND_NOTICE, |
518 |
"Unauthorized server connection attempt from %s: No entry for " |
519 |
"servername %s", client_get_name(source_p, SHOW_IP), name); |
520 |
|
521 |
sendto_realops_flags(UMODE_SERVNOTICE, L_OPER, SEND_NOTICE, |
522 |
"Unauthorized server connection attempt from %s: No entry for " |
523 |
"servername %s", client_get_name(source_p, MASK_IP), name); |
524 |
} |
525 |
|
526 |
exit_client(source_p, "No connect {} block."); |
527 |
return 0; |
528 |
/* NOT REACHED */ |
529 |
break; |
530 |
|
531 |
case -2: |
532 |
sendto_realops_flags(UMODE_SERVNOTICE, L_ADMIN, SEND_NOTICE, |
533 |
"Unauthorized server connection attempt from %s: Bad password " |
534 |
"for server %s", client_get_name(source_p, SHOW_IP), name); |
535 |
|
536 |
sendto_realops_flags(UMODE_SERVNOTICE, L_OPER, SEND_NOTICE, |
537 |
"Unauthorized server connection attempt from %s: Bad password " |
538 |
"for server %s", client_get_name(source_p, MASK_IP), name); |
539 |
|
540 |
exit_client(source_p, "Invalid password."); |
541 |
return 0; |
542 |
/* NOT REACHED */ |
543 |
break; |
544 |
|
545 |
case -3: |
546 |
sendto_realops_flags(UMODE_SERVNOTICE, L_ADMIN, SEND_NOTICE, |
547 |
"Unauthorized server connection attempt from %s: Invalid host " |
548 |
"for server %s", client_get_name(source_p, SHOW_IP), name); |
549 |
|
550 |
sendto_realops_flags(UMODE_SERVNOTICE, L_OPER, SEND_NOTICE, |
551 |
"Unauthorized server connection attempt from %s: Invalid host " |
552 |
"for server %s", client_get_name(source_p, MASK_IP), name); |
553 |
|
554 |
exit_client(source_p, "Invalid host."); |
555 |
return 0; |
556 |
case -4: |
557 |
sendto_realops_flags(UMODE_SERVNOTICE, L_ADMIN, SEND_NOTICE, |
558 |
"Unauthorized server connection attempt from %s: Invalid certificate fingerprint " |
559 |
"for server %s", client_get_name(source_p, SHOW_IP), name); |
560 |
|
561 |
sendto_realops_flags(UMODE_SERVNOTICE, L_OPER, SEND_NOTICE, |
562 |
"Unauthorized server connection attempt from %s: Invalid certificate fingerprint " |
563 |
"for server %s", client_get_name(source_p, MASK_IP), name); |
564 |
|
565 |
exit_client(source_p, "Invalid certificate fingerprint."); |
566 |
return 0; |
567 |
/* NOT REACHED */ |
568 |
break; |
569 |
} |
570 |
|
571 |
if ((target_p = hash_find_server(name))) |
572 |
{ |
573 |
/* This link is trying feed me a server that I already have |
574 |
* access through another path -- multiple paths not accepted |
575 |
* currently, kill this link immediately!! |
576 |
* |
577 |
* Rather than KILL the link which introduced it, KILL the |
578 |
* youngest of the two links. -avalon |
579 |
* |
580 |
* Definitely don't do that here. This is from an unregistered |
581 |
* connect - A1kmm. |
582 |
*/ |
583 |
sendto_realops_flags(UMODE_SERVNOTICE, L_ADMIN, SEND_NOTICE, |
584 |
"Attempt to re-introduce server %s from %s", |
585 |
name, client_get_name(source_p, SHOW_IP)); |
586 |
sendto_realops_flags(UMODE_SERVNOTICE, L_OPER, SEND_NOTICE, |
587 |
"Attempt to re-introduce server %s from %s", |
588 |
name, client_get_name(source_p, MASK_IP)); |
589 |
exit_client(source_p, "Server already exists"); |
590 |
return 0; |
591 |
} |
592 |
|
593 |
if ((target_p = hash_find_id(source_p->id))) |
594 |
{ |
595 |
sendto_realops_flags(UMODE_SERVNOTICE, L_ADMIN, SEND_NOTICE, |
596 |
"Attempt to re-introduce server %s SID %s from %s", |
597 |
name, source_p->id, |
598 |
client_get_name(source_p, SHOW_IP)); |
599 |
sendto_realops_flags(UMODE_SERVNOTICE, L_OPER, SEND_NOTICE, |
600 |
"Attempt to re-introduce server %s SID %s from %s", |
601 |
name, source_p->id, |
602 |
client_get_name(source_p, MASK_IP)); |
603 |
exit_client(source_p, "Server ID already exists"); |
604 |
return 0; |
605 |
} |
606 |
|
607 |
/* XXX If somehow there is a connect in progress and |
608 |
* a connect comes in with same name toss the pending one, |
609 |
* but only if it's not the same client! - Dianora |
610 |
*/ |
611 |
if ((target_p = find_servconn_in_progress(name))) |
612 |
if (target_p != source_p) |
613 |
exit_client(target_p, "Overridden"); |
614 |
|
615 |
/* if we are connecting (Handshake), we already have the name from the |
616 |
* connect{} block in source_p->name |
617 |
*/ |
618 |
strlcpy(source_p->name, name, sizeof(source_p->name)); |
619 |
|
620 |
if (parc == 6) /* TBR: compatibility 'mode' */ |
621 |
{ |
622 |
strlcpy(source_p->id, sid, sizeof(source_p->id)); |
623 |
strlcpy(source_p->info, parv[parc - 1], sizeof(source_p->info)); |
624 |
server_set_flags(source_p, parv[4]); |
625 |
} |
626 |
else |
627 |
server_set_gecos(source_p, parv[parc - 1]); |
628 |
|
629 |
source_p->hopcount = atoi(parv[2]); |
630 |
server_estab(source_p); |
631 |
return 0; |
632 |
} |
633 |
|
634 |
/* ms_sid() |
635 |
* parv[0] = command |
636 |
* parv[1] = servername |
637 |
* parv[2] = hopcount |
638 |
* parv[3] = sid of new server |
639 |
* parv[4] = serverinfo |
640 |
* |
641 |
* 8.3.x+: |
642 |
* parv[0] = command |
643 |
* parv[1] = servername |
644 |
* parv[2] = hopcount |
645 |
* parv[3] = sid of new server |
646 |
* parv[4] = string of flags starting with '+' |
647 |
* parv[5] = serverinfo |
648 |
*/ |
649 |
static int |
650 |
ms_sid(struct Client *source_p, int parc, char *parv[]) |
651 |
{ |
652 |
struct Client *target_p = NULL; |
653 |
|
654 |
/* Just to be sure -A1kmm. */ |
655 |
if (!IsServer(source_p)) |
656 |
return 0; |
657 |
|
658 |
if (EmptyString(parv[parc - 1])) |
659 |
{ |
660 |
exit_client(source_p->from, "No server description supplied"); |
661 |
return 0; |
662 |
} |
663 |
|
664 |
if (!server_valid_name(parv[1])) |
665 |
{ |
666 |
sendto_realops_flags(UMODE_SERVNOTICE, L_ADMIN, SEND_NOTICE, |
667 |
"Link %s introduced server with bogus server name %s", |
668 |
client_get_name(source_p->from, SHOW_IP), parv[1]); |
669 |
sendto_realops_flags(UMODE_SERVNOTICE, L_OPER, SEND_NOTICE, |
670 |
"Link %s introduced server with bogus server name %s", |
671 |
client_get_name(source_p->from, MASK_IP), parv[1]); |
672 |
exit_client(source_p->from, "Bogus server name introduced"); |
673 |
return 0; |
674 |
} |
675 |
|
676 |
if (!valid_sid(parv[3])) |
677 |
{ |
678 |
sendto_realops_flags(UMODE_SERVNOTICE, L_ADMIN, SEND_NOTICE, |
679 |
"Link %s introduced server with bogus server ID %s", |
680 |
client_get_name(source_p->from, SHOW_IP), parv[3]); |
681 |
sendto_realops_flags(UMODE_SERVNOTICE, L_OPER, SEND_NOTICE, |
682 |
"Link %s introduced server with bogus server ID %s", |
683 |
client_get_name(source_p->from, MASK_IP), parv[3]); |
684 |
exit_client(source_p->from, "Bogus server ID introduced"); |
685 |
return 0; |
686 |
} |
687 |
|
688 |
/* collision on SID? */ |
689 |
if ((target_p = hash_find_id(parv[3]))) |
690 |
{ |
691 |
sendto_realops_flags(UMODE_SERVNOTICE, L_ADMIN, SEND_NOTICE, |
692 |
"Link %s cancelled, server ID %s already exists", |
693 |
client_get_name(source_p->from, SHOW_IP), parv[3]); |
694 |
sendto_realops_flags(UMODE_SERVNOTICE, L_OPER, SEND_NOTICE, |
695 |
"Link %s cancelled, server ID %s already exists", |
696 |
client_get_name(source_p->from, MASK_IP), parv[3]); |
697 |
exit_client(source_p->from, "Link cancelled, server ID already exists"); |
698 |
return 0; |
699 |
} |
700 |
|
701 |
/* collision on name? */ |
702 |
if ((target_p = hash_find_server(parv[1]))) |
703 |
{ |
704 |
sendto_realops_flags(UMODE_SERVNOTICE, L_ADMIN, SEND_NOTICE, |
705 |
"Link %s cancelled, server %s already exists", |
706 |
client_get_name(source_p->from, SHOW_IP), parv[1]); |
707 |
sendto_realops_flags(UMODE_SERVNOTICE, L_OPER, SEND_NOTICE, |
708 |
"Link %s cancelled, server %s already exists", |
709 |
client_get_name(source_p->from, MASK_IP), parv[1]); |
710 |
exit_client(source_p->from, "Server exists"); |
711 |
return 0; |
712 |
} |
713 |
|
714 |
/* XXX If somehow there is a connect in progress and |
715 |
* a connect comes in with same name toss the pending one, |
716 |
* but only if it's not the same client! - Dianora |
717 |
*/ |
718 |
if ((target_p = find_servconn_in_progress(parv[1]))) |
719 |
if (target_p != source_p->from) |
720 |
exit_client(target_p, "Overridden"); |
721 |
|
722 |
/* |
723 |
* See if the newly found server is behind a guaranteed |
724 |
* leaf. If so, close the link. |
725 |
*/ |
726 |
dlink_node *node; |
727 |
unsigned int hlined = 0; |
728 |
unsigned int llined = 0; |
729 |
const struct MaskItem *conf = source_p->from->connection->confs.head->data; |
730 |
|
731 |
DLINK_FOREACH(node, conf->leaf_list.head) |
732 |
{ |
733 |
if (!match(node->data, parv[1])) |
734 |
{ |
735 |
llined = 1; |
736 |
break; |
737 |
} |
738 |
} |
739 |
|
740 |
DLINK_FOREACH(node, conf->hub_list.head) |
741 |
{ |
742 |
if (!match(node->data, parv[1])) |
743 |
{ |
744 |
hlined = 1; |
745 |
break; |
746 |
} |
747 |
} |
748 |
|
749 |
/* Ok, this way this works is |
750 |
* |
751 |
* A server can have a CONF_HUB allowing it to introduce servers |
752 |
* behind it. |
753 |
* |
754 |
* connect { |
755 |
* name = "irc.bighub.net"; |
756 |
* hub_mask="*"; |
757 |
* ... |
758 |
* |
759 |
* That would allow "irc.bighub.net" to introduce anything it wanted.. |
760 |
* |
761 |
* However |
762 |
* |
763 |
* connect { |
764 |
* name = "irc.somehub.fi"; |
765 |
* hub_mask="*"; |
766 |
* leaf_mask="*.edu"; |
767 |
*... |
768 |
* Would allow this server in finland to hub anything but |
769 |
* .edu's |
770 |
*/ |
771 |
|
772 |
/* Ok, check source_p->from can hub the new server */ |
773 |
if (!hlined) |
774 |
{ |
775 |
/* OOOPs nope can't HUB */ |
776 |
sendto_realops_flags(UMODE_SERVNOTICE, L_ADMIN, SEND_NOTICE, |
777 |
"Non-Hub link %s introduced %s.", |
778 |
client_get_name(source_p->from, SHOW_IP), parv[1]); |
779 |
sendto_realops_flags(UMODE_SERVNOTICE, L_OPER, SEND_NOTICE, |
780 |
"Non-Hub link %s introduced %s.", |
781 |
client_get_name(source_p->from, MASK_IP), parv[1]); |
782 |
exit_client(source_p, "No matching hub_mask."); |
783 |
return 0; |
784 |
} |
785 |
|
786 |
/* Check for the new server being leafed behind this HUB */ |
787 |
if (llined) |
788 |
{ |
789 |
/* OOOPs nope can't HUB this leaf */ |
790 |
sendto_realops_flags(UMODE_SERVNOTICE, L_ADMIN, SEND_NOTICE, |
791 |
"Link %s introduced leafed server %s.", |
792 |
client_get_name(source_p->from, SHOW_IP), parv[1]); |
793 |
sendto_realops_flags(UMODE_SERVNOTICE, L_OPER, SEND_NOTICE, |
794 |
"Link %s introduced leafed server %s.", |
795 |
client_get_name(source_p->from, MASK_IP), parv[1]); |
796 |
exit_client(source_p->from, "Leafed server."); |
797 |
return 0; |
798 |
} |
799 |
|
800 |
target_p = client_make(source_p->from); |
801 |
server_make(target_p); |
802 |
target_p->hopcount = atoi(parv[2]); |
803 |
target_p->servptr = source_p; |
804 |
|
805 |
strlcpy(target_p->name, parv[1], sizeof(target_p->name)); |
806 |
strlcpy(target_p->id, parv[3], sizeof(target_p->id)); |
807 |
|
808 |
if (parc == 6) /* TBR: compatibility 'mode' */ |
809 |
{ |
810 |
strlcpy(target_p->info, parv[parc - 1], sizeof(target_p->info)); |
811 |
server_set_flags(target_p, parv[4]); |
812 |
} |
813 |
else |
814 |
server_set_gecos(target_p, parv[parc - 1]); |
815 |
|
816 |
SetServer(target_p); |
817 |
|
818 |
if (service_find(target_p->name, irccmp)) |
819 |
AddFlag(target_p, FLAGS_SERVICE); |
820 |
|
821 |
dlinkAdd(target_p, &target_p->node, &global_server_list); |
822 |
dlinkAdd(target_p, &target_p->lnode, &target_p->servptr->serv->server_list); |
823 |
|
824 |
hash_add_client(target_p); |
825 |
hash_add_id(target_p); |
826 |
|
827 |
sendto_server(source_p->from, 0, 0, ":%s SID %s %u %s :%s%s", |
828 |
source_p->id, target_p->name, target_p->hopcount + 1, |
829 |
target_p->id, IsHidden(target_p) ? "(H) " : "", target_p->info); |
830 |
sendto_realops_flags(UMODE_EXTERNAL, L_ALL, SEND_NOTICE, |
831 |
"Server %s being introduced by %s", |
832 |
target_p->name, source_p->name); |
833 |
return 0; |
834 |
} |
835 |
|
836 |
static struct Message server_msgtab = |
837 |
{ |
838 |
.cmd = "SERVER", |
839 |
.args_min = 4, |
840 |
.args_max = MAXPARA, |
841 |
.handlers[UNREGISTERED_HANDLER] = mr_server, |
842 |
.handlers[CLIENT_HANDLER] = m_registered, |
843 |
.handlers[SERVER_HANDLER] = m_ignore, |
844 |
.handlers[ENCAP_HANDLER] = m_ignore, |
845 |
.handlers[OPER_HANDLER] = m_registered |
846 |
}; |
847 |
|
848 |
static struct Message sid_msgtab = |
849 |
{ |
850 |
.cmd = "SID", |
851 |
.args_min = 5, |
852 |
.args_max = MAXPARA, |
853 |
.handlers[UNREGISTERED_HANDLER] = m_ignore, |
854 |
.handlers[CLIENT_HANDLER] = m_ignore, |
855 |
.handlers[SERVER_HANDLER] = ms_sid, |
856 |
.handlers[ENCAP_HANDLER] = m_ignore, |
857 |
.handlers[OPER_HANDLER] = m_ignore |
858 |
}; |
859 |
|
860 |
static void |
861 |
module_init(void) |
862 |
{ |
863 |
mod_add_cmd(&sid_msgtab); |
864 |
mod_add_cmd(&server_msgtab); |
865 |
} |
866 |
|
867 |
static void |
868 |
module_exit(void) |
869 |
{ |
870 |
mod_del_cmd(&sid_msgtab); |
871 |
mod_del_cmd(&server_msgtab); |
872 |
} |
873 |
|
874 |
struct module module_entry = |
875 |
{ |
876 |
.version = "$Revision$", |
877 |
.modinit = module_init, |
878 |
.modexit = module_exit, |
879 |
.flags = MODULE_FLAG_CORE |
880 |
}; |