1 |
/* |
2 |
* dccutil.c -- handles: |
3 |
* lots of little functions to send formatted text to |
4 |
* varying types of connections |
5 |
* '.who', '.whom', and '.dccstat' code |
6 |
* memory management for dcc structures |
7 |
* timeout checking for dcc connections |
8 |
* |
9 |
* $Id: dccutil.c,v 1.42 2002/01/16 22:09:43 ite Exp $ |
10 |
*/ |
11 |
/* |
12 |
* Copyright (C) 1997 Robey Pointer |
13 |
* Copyright (C) 1999, 2000, 2001 Eggheads Development Team |
14 |
* |
15 |
* This program is free software; you can redistribute it and/or |
16 |
* modify it under the terms of the GNU General Public License |
17 |
* as published by the Free Software Foundation; either version 2 |
18 |
* of the License, or (at your option) any later version. |
19 |
* |
20 |
* This program is distributed in the hope that it will be useful, |
21 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
22 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
23 |
* GNU General Public License for more details. |
24 |
* |
25 |
* You should have received a copy of the GNU General Public License |
26 |
* along with this program; if not, write to the Free Software |
27 |
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
28 |
*/ |
29 |
|
30 |
#include <sys/stat.h> |
31 |
#include "main.h" |
32 |
#include <errno.h> |
33 |
#include "chan.h" |
34 |
#include "modules.h" |
35 |
#include "tandem.h" |
36 |
#include "logfile.h" |
37 |
#include "misc.h" |
38 |
|
39 |
extern struct dcc_t *dcc; |
40 |
extern int dcc_total, max_dcc, dcc_flood_thr, backgrd, MAXSOCKS; |
41 |
extern char botnetnick[], spaces[], version[]; |
42 |
extern time_t now; |
43 |
extern sock_list *socklist; |
44 |
|
45 |
char motdfile[121] = "text/motd"; /* File where the motd is stored */ |
46 |
int connect_timeout = 15; /* How long to wait before a telnet |
47 |
connection times out */ |
48 |
|
49 |
int reserved_port_min = 0; |
50 |
int reserved_port_max = 0; |
51 |
|
52 |
void init_dcc_max() |
53 |
{ |
54 |
int osock = MAXSOCKS; |
55 |
|
56 |
if (max_dcc < 1) |
57 |
max_dcc = 1; |
58 |
if (dcc) |
59 |
dcc = realloc(dcc, sizeof(struct dcc_t) * max_dcc); |
60 |
else |
61 |
dcc = malloc(sizeof(struct dcc_t) * max_dcc); |
62 |
|
63 |
MAXSOCKS = max_dcc + 10; |
64 |
if (socklist) |
65 |
socklist = (sock_list *) realloc((void *) socklist, |
66 |
sizeof(sock_list) * MAXSOCKS); |
67 |
else |
68 |
socklist = (sock_list *) malloc(sizeof(sock_list) * MAXSOCKS); |
69 |
for (; osock < MAXSOCKS; osock++) |
70 |
socklist[osock].flags = SOCK_UNUSED; |
71 |
} |
72 |
|
73 |
|
74 |
/* Replace \n with \r\n */ |
75 |
char *add_cr(char *buf) |
76 |
{ |
77 |
static char WBUF[1024]; |
78 |
char *p, *q; |
79 |
|
80 |
for (p = buf, q = WBUF; *p; p++, q++) { |
81 |
if (*p == '\n') |
82 |
*q++ = '\r'; |
83 |
*q = *p; |
84 |
} |
85 |
*q = *p; |
86 |
return WBUF; |
87 |
} |
88 |
|
89 |
extern void (*qserver) (int, char *, int); |
90 |
|
91 |
void dprintf EGG_VARARGS_DEF(int, arg1) |
92 |
{ |
93 |
static char buf[1024]; |
94 |
char *format; |
95 |
int idx, len; |
96 |
va_list va; |
97 |
|
98 |
idx = EGG_VARARGS_START(int, arg1, va); |
99 |
format = va_arg(va, char *); |
100 |
vsnprintf(buf, 1023, format, va); |
101 |
va_end(va); |
102 |
/* We can not use the return value vsnprintf() to determine where |
103 |
* to null terminate. The C99 standard specifies that vsnprintf() |
104 |
* shall return the number of bytes that would be written if the |
105 |
* buffer had been large enough, rather then -1. |
106 |
*/ |
107 |
/* We actually can, since if it's < 0 or >= sizeof(buf), we know it wrote |
108 |
* sizeof(buf) bytes. But we're not doing that anyway. |
109 |
*/ |
110 |
buf[sizeof(buf)-1] = 0; |
111 |
|
112 |
#if (TCL_MAJOR_VERSION >= 8 && TCL_MINOR_VERSION >= 1) || (TCL_MAJOR_VERSION >= 9) |
113 |
str_nutf8tounicode(buf, sizeof buf); |
114 |
#endif |
115 |
len = strlen(buf); |
116 |
|
117 |
if (idx < 0) { |
118 |
tputs(-idx, buf, len); |
119 |
} else if (idx > 0x7FF0) { |
120 |
switch (idx) { |
121 |
case DP_LOG: |
122 |
putlog(LOG_MISC, "*", "%s", buf); |
123 |
break; |
124 |
case DP_STDOUT: |
125 |
tputs(STDOUT, buf, len); |
126 |
break; |
127 |
case DP_STDERR: |
128 |
tputs(STDERR, buf, len); |
129 |
break; |
130 |
case DP_SERVER: |
131 |
case DP_HELP: |
132 |
case DP_MODE: |
133 |
case DP_MODE_NEXT: |
134 |
case DP_SERVER_NEXT: |
135 |
case DP_HELP_NEXT: |
136 |
qserver(idx, buf, len); |
137 |
break; |
138 |
} |
139 |
return; |
140 |
} else { |
141 |
if (len > 500) { /* Truncate to fit */ |
142 |
buf[500] = 0; |
143 |
strcat(buf, "\n"); |
144 |
len = 501; |
145 |
} |
146 |
if (dcc[idx].type && ((long) (dcc[idx].type->output) == 1)) { |
147 |
char *p = add_cr(buf); |
148 |
|
149 |
tputs(dcc[idx].sock, p, strlen(p)); |
150 |
} else if (dcc[idx].type && dcc[idx].type->output) { |
151 |
dcc[idx].type->output(idx, buf, dcc[idx].u.other); |
152 |
} else |
153 |
tputs(dcc[idx].sock, buf, len); |
154 |
} |
155 |
} |
156 |
|
157 |
void chatout EGG_VARARGS_DEF(char *, arg1) |
158 |
{ |
159 |
int i, len; |
160 |
char *format; |
161 |
char s[601]; |
162 |
va_list va; |
163 |
|
164 |
format = EGG_VARARGS_START(char *, arg1, va); |
165 |
vsnprintf(s, 511, format, va); |
166 |
va_end(va); |
167 |
len = strlen(s); |
168 |
if (len > 511) |
169 |
len = 511; |
170 |
s[len + 1] = 0; |
171 |
|
172 |
for (i = 0; i < dcc_total; i++) |
173 |
if (dcc[i].type == &DCC_CHAT) |
174 |
if (dcc[i].u.chat->channel >= 0) |
175 |
dprintf(i, "%s", s); |
176 |
|
177 |
} |
178 |
|
179 |
/* Print to all on this channel but one. |
180 |
*/ |
181 |
void chanout_but EGG_VARARGS_DEF(int, arg1) |
182 |
{ |
183 |
int i, x, chan, len; |
184 |
char *format; |
185 |
char s[601]; |
186 |
va_list va; |
187 |
|
188 |
x = EGG_VARARGS_START(int, arg1, va); |
189 |
chan = va_arg(va, int); |
190 |
format = va_arg(va, char *); |
191 |
vsnprintf(s, 511, format, va); |
192 |
va_end(va); |
193 |
len = strlen(s); |
194 |
if (len > 511) |
195 |
len = 511; |
196 |
s[len + 1] = 0; |
197 |
|
198 |
for (i = 0; i < dcc_total; i++) |
199 |
if ((dcc[i].type == &DCC_CHAT) && (i != x)) |
200 |
if (dcc[i].u.chat->channel == chan) |
201 |
dprintf(i, "%s", s); |
202 |
|
203 |
} |
204 |
|
205 |
void dcc_chatter(int idx) |
206 |
{ |
207 |
int i, j; |
208 |
struct flag_record fr = {FR_GLOBAL | FR_CHAN | FR_ANYWH, 0, 0, 0, 0, 0}; |
209 |
|
210 |
get_user_flagrec(dcc[idx].user, &fr, NULL); |
211 |
show_motd(idx); |
212 |
i = dcc[idx].u.chat->channel; |
213 |
dcc[idx].u.chat->channel = 234567; |
214 |
j = dcc[idx].sock; |
215 |
strcpy(dcc[idx].u.chat->con_chan, "***"); |
216 |
check_tcl_chon(dcc[idx].nick, dcc[idx].sock); |
217 |
/* Still there? */ |
218 |
if ((idx >= dcc_total) || (dcc[idx].sock != j)) |
219 |
return; /* Nope */ |
220 |
/* Tcl script may have taken control */ |
221 |
if (dcc[idx].type == &DCC_CHAT) { |
222 |
if (!strcmp(dcc[idx].u.chat->con_chan, "***")) |
223 |
strcpy(dcc[idx].u.chat->con_chan, "*"); |
224 |
if (dcc[idx].u.chat->channel == 234567) { |
225 |
/* If the chat channel has already been altered it's *highly* |
226 |
* probably join/part messages have been broadcast everywhere, |
227 |
* so dont bother sending them |
228 |
*/ |
229 |
if (i == -2) |
230 |
i = 0; |
231 |
dcc[idx].u.chat->channel = i; |
232 |
if (dcc[idx].u.chat->channel >= 0) { |
233 |
if (dcc[idx].u.chat->channel < 100000) { |
234 |
botnet_send_join_idx(idx, -1); |
235 |
} |
236 |
} |
237 |
check_tcl_chjn(botnetnick, dcc[idx].nick, dcc[idx].u.chat->channel, |
238 |
geticon(idx), dcc[idx].sock, dcc[idx].host); |
239 |
} |
240 |
/* But *do* bother with sending it locally */ |
241 |
if (!dcc[idx].u.chat->channel) { |
242 |
chanout_but(-1, 0, "*** %s joined the party line.\n", dcc[idx].nick); |
243 |
} else if (dcc[idx].u.chat->channel > 0) { |
244 |
chanout_but(-1, dcc[idx].u.chat->channel, |
245 |
"*** %s joined the channel.\n", dcc[idx].nick); |
246 |
} |
247 |
} |
248 |
} |
249 |
|
250 |
/* Mark an entry as lost and deconstruct it's contents. It will be securely |
251 |
* removed from the dcc list in the main loop. |
252 |
*/ |
253 |
void lostdcc(int n) |
254 |
{ |
255 |
if (dcc[n].type && dcc[n].type->kill) |
256 |
dcc[n].type->kill(n, dcc[n].u.other); |
257 |
else if (dcc[n].u.other) |
258 |
free(dcc[n].u.other); |
259 |
memset(&dcc[n], 0, sizeof(struct dcc_t)); |
260 |
|
261 |
dcc[n].sock = (-1); |
262 |
dcc[n].type = &DCC_LOST; |
263 |
} |
264 |
|
265 |
/* Remove entry from dcc list. Think twice before using this function, |
266 |
* because it invalidates any variables that point to a specific dcc |
267 |
* entry! |
268 |
* |
269 |
* Note: The entry will be deconstructed if it was not deconstructed |
270 |
* already. This case should normally not occur. |
271 |
*/ |
272 |
void removedcc(int n) |
273 |
{ |
274 |
if (dcc[n].type && dcc[n].type->kill) |
275 |
dcc[n].type->kill(n, dcc[n].u.other); |
276 |
else if (dcc[n].u.other) |
277 |
free(dcc[n].u.other); |
278 |
|
279 |
memset(&dcc[n], 0, sizeof(struct dcc_t)); /* drummer */ |
280 |
} |
281 |
|
282 |
/* Clean up sockets that were just left for dead. |
283 |
*/ |
284 |
void dcc_remove_lost(void) |
285 |
{ |
286 |
int i; |
287 |
|
288 |
for (i = 0; i < dcc_total; i++) { |
289 |
if (dcc[i].type == &DCC_LOST) { |
290 |
dcc[i].type = NULL; |
291 |
removedcc(i); |
292 |
i--; |
293 |
} |
294 |
} |
295 |
} |
296 |
|
297 |
/* Show list of current dcc's to a dcc-chatter |
298 |
* positive value: idx given -- negative value: sock given |
299 |
*/ |
300 |
void tell_dcc(int zidx) |
301 |
{ |
302 |
int i, j, k; |
303 |
char other[160]; |
304 |
|
305 |
spaces[HANDLEN - 9] = 0; |
306 |
dprintf(zidx, "SOCK PORT NICK %s HOST TYPE\n" |
307 |
,spaces); |
308 |
dprintf(zidx, "---- ----- ---------%s -------------------------- ----\n" |
309 |
,spaces); |
310 |
spaces[HANDLEN - 9] = ' '; |
311 |
/* Show server */ |
312 |
for (i = 0; i < dcc_total; i++) { |
313 |
if (!dcc[i].type) continue; |
314 |
j = strlen(dcc[i].host); |
315 |
if (j > 26) |
316 |
j -= 26; |
317 |
else |
318 |
j = 0; |
319 |
if (dcc[i].type && dcc[i].type->display) |
320 |
dcc[i].type->display(i, other); |
321 |
else { |
322 |
sprintf(other, "?:%lX !! ERROR !!", (long) dcc[i].type); |
323 |
break; |
324 |
} |
325 |
k = HANDLEN - strlen(dcc[i].nick); |
326 |
spaces[k] = 0; |
327 |
dprintf(zidx, "%-4d %5d %s%s %-26s %s\n", i, |
328 |
dcc[i].port, dcc[i].nick, spaces, dcc[i].host + j, other); |
329 |
spaces[k] = ' '; |
330 |
} |
331 |
} |
332 |
|
333 |
/* Mark someone on dcc chat as no longer away |
334 |
*/ |
335 |
void not_away(int idx) |
336 |
{ |
337 |
if (dcc[idx].u.chat->away == NULL) { |
338 |
dprintf(idx, "You weren't away!\n"); |
339 |
return; |
340 |
} |
341 |
if (dcc[idx].u.chat->channel >= 0) { |
342 |
chanout_but(-1, dcc[idx].u.chat->channel, |
343 |
"*** %s is no longer away.\n", dcc[idx].nick); |
344 |
if (dcc[idx].u.chat->channel < 100000) { |
345 |
botnet_send_away(-1, botnetnick, dcc[idx].sock, NULL, idx); |
346 |
} |
347 |
} |
348 |
dprintf(idx, "You're not away any more.\n"); |
349 |
free_null(dcc[idx].u.chat->away); |
350 |
check_tcl_away(botnetnick, dcc[idx].sock, NULL); |
351 |
} |
352 |
|
353 |
void set_away(int idx, char *s) |
354 |
{ |
355 |
if (s == NULL) { |
356 |
not_away(idx); |
357 |
return; |
358 |
} |
359 |
if (!s[0]) { |
360 |
not_away(idx); |
361 |
return; |
362 |
} |
363 |
if (dcc[idx].u.chat->away != NULL) |
364 |
free(dcc[idx].u.chat->away); |
365 |
dcc[idx].u.chat->away = strdup(s); |
366 |
if (dcc[idx].u.chat->channel >= 0) { |
367 |
chanout_but(-1, dcc[idx].u.chat->channel, |
368 |
"*** %s is now away: %s\n", dcc[idx].nick, s); |
369 |
if (dcc[idx].u.chat->channel < 100000) { |
370 |
botnet_send_away(-1, botnetnick, dcc[idx].sock, s, idx); |
371 |
} |
372 |
} |
373 |
dprintf(idx, "You are now away.\n"); |
374 |
check_tcl_away(botnetnick, dcc[idx].sock, s); |
375 |
} |
376 |
|
377 |
/* Make a password, 10-15 random letters and digits |
378 |
*/ |
379 |
void makepass(char *s) |
380 |
{ |
381 |
int i; |
382 |
|
383 |
i = 10 + (random() % 6); |
384 |
make_rand_str(s, i); |
385 |
} |
386 |
|
387 |
void flush_lines(int idx, struct chat_info *ci) |
388 |
{ |
389 |
int c = ci->line_count; |
390 |
struct msgq *p = ci->buffer, *o; |
391 |
|
392 |
while (p && c < (ci->max_line)) { |
393 |
ci->current_lines--; |
394 |
tputs(dcc[idx].sock, p->msg, p->len); |
395 |
free(p->msg); |
396 |
o = p->next; |
397 |
free(p); |
398 |
p = o; |
399 |
c++; |
400 |
} |
401 |
if (p != NULL) { |
402 |
if (dcc[idx].status & STAT_TELNET) |
403 |
tputs(dcc[idx].sock, "[More]: ", 8); |
404 |
else |
405 |
tputs(dcc[idx].sock, "[More]\n", 7); |
406 |
} |
407 |
ci->buffer = p; |
408 |
ci->line_count = 0; |
409 |
} |
410 |
|
411 |
int new_dcc(struct dcc_table *type, int xtra_size) |
412 |
{ |
413 |
int i = dcc_total; |
414 |
|
415 |
for (i = 0; i < dcc_total; i++) { |
416 |
if (!dcc[i].type) break; |
417 |
} |
418 |
if (i == dcc_total) { |
419 |
if (dcc_total == max_dcc) { |
420 |
max_dcc += 5; |
421 |
dcc = (struct dcc_t *)realloc(dcc, max_dcc * sizeof(*dcc)); |
422 |
memset(dcc+dcc_total, 0, 5 * sizeof(*dcc)); |
423 |
} |
424 |
dcc_total++; |
425 |
} |
426 |
memset(&dcc[i], 0, sizeof(*dcc)); |
427 |
|
428 |
dcc[i].type = type; |
429 |
if (xtra_size) |
430 |
dcc[i].u.other = calloc(1, xtra_size); |
431 |
return i; |
432 |
} |
433 |
|
434 |
/* Changes the given dcc entry to another type. |
435 |
*/ |
436 |
void changeover_dcc(int i, struct dcc_table *type, int xtra_size) |
437 |
{ |
438 |
/* Free old structure. */ |
439 |
if (dcc[i].type && dcc[i].type->kill) |
440 |
dcc[i].type->kill(i, dcc[i].u.other); |
441 |
else if (dcc[i].u.other) |
442 |
free_null(dcc[i].u.other); |
443 |
|
444 |
dcc[i].type = type; |
445 |
if (xtra_size) |
446 |
dcc[i].u.other = calloc(1, xtra_size); |
447 |
} |
448 |
|
449 |
int detect_dcc_flood(time_t * timer, struct chat_info *chat, int idx) |
450 |
{ |
451 |
time_t t; |
452 |
|
453 |
if (!dcc_flood_thr) |
454 |
return 0; |
455 |
t = now; |
456 |
if (*timer != t) { |
457 |
*timer = t; |
458 |
chat->msgs_per_sec = 0; |
459 |
} else { |
460 |
chat->msgs_per_sec++; |
461 |
if (chat->msgs_per_sec > dcc_flood_thr) { |
462 |
/* FLOOD */ |
463 |
dprintf(idx, "*** FLOOD: %s.\n", _("Goodbye")); |
464 |
/* Evil assumption here that flags&DCT_CHAT implies chat type */ |
465 |
if ((dcc[idx].type->flags & DCT_CHAT) && chat && |
466 |
(chat->channel >= 0)) { |
467 |
char x[1024]; |
468 |
|
469 |
snprintf(x, sizeof x, _("%s has been forcibly removed for flooding.\n"), dcc[idx].nick); |
470 |
chanout_but(idx, chat->channel, "*** %s", x); |
471 |
if (chat->channel < 100000) |
472 |
botnet_send_part_idx(idx, x); |
473 |
} |
474 |
check_tcl_chof(dcc[idx].nick, dcc[idx].sock); |
475 |
if ((dcc[idx].sock != STDOUT) || backgrd) { |
476 |
killsock(dcc[idx].sock); |
477 |
lostdcc(idx); |
478 |
} else { |
479 |
dprintf(DP_STDOUT, "\n### SIMULATION RESET ###\n\n"); |
480 |
dcc_chatter(idx); |
481 |
} |
482 |
return 1; /* <- flood */ |
483 |
} |
484 |
} |
485 |
return 0; |
486 |
} |
487 |
|
488 |
/* Handle someone being booted from dcc chat. |
489 |
*/ |
490 |
void do_boot(int idx, char *by, char *reason) |
491 |
{ |
492 |
int files = (dcc[idx].type != &DCC_CHAT); |
493 |
|
494 |
dprintf(idx, _("-=- poof -=-\n")); |
495 |
dprintf(idx, _("Youve been booted from the %s by %s%s%s\n"), files ? "file section" : "bot", |
496 |
by, reason[0] ? ": " : ".", reason); |
497 |
/* If it's a partyliner (chatterer :) */ |
498 |
/* Horrible assumption that DCT_CHAT using structure uses same format |
499 |
* as DCC_CHAT */ |
500 |
if ((dcc[idx].type->flags & DCT_CHAT) && |
501 |
(dcc[idx].u.chat->channel >= 0)) { |
502 |
char x[1024]; |
503 |
|
504 |
snprintf(x, sizeof x, _("%s booted %s from the party line%s%s\n"), by, dcc[idx].nick, |
505 |
reason[0] ? ": " : "", reason); |
506 |
chanout_but(idx, dcc[idx].u.chat->channel, "*** %s.\n", x); |
507 |
if (dcc[idx].u.chat->channel < 100000) |
508 |
botnet_send_part_idx(idx, x); |
509 |
} |
510 |
check_tcl_chof(dcc[idx].nick, dcc[idx].sock); |
511 |
if ((dcc[idx].sock != STDOUT) || backgrd) { |
512 |
killsock(dcc[idx].sock); |
513 |
lostdcc(idx); |
514 |
/* Entry must remain in the table so it can be logged by the caller */ |
515 |
} else { |
516 |
dprintf(DP_STDOUT, "\n### SIMULATION RESET\n\n"); |
517 |
dcc_chatter(idx); |
518 |
} |
519 |
return; |
520 |
} |