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 /*
10 * Copyright (C) 1997 Robey Pointer
11 * Copyright (C) 1999 - 2021 Eggheads Development Team
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 */
27
28 #include <sys/stat.h>
29 #include "main.h"
30 #include <errno.h>
31 #include "chan.h"
32 #include "modules.h"
33 #include "tandem.h"
34
35 extern struct dcc_t *dcc;
36 extern int dcc_total, dcc_flood_thr, backgrd, copy_to_tmp, max_socks;
37 extern char botnetnick[], version[];
38 extern time_t now;
39 extern sock_list *socklist;
40
41 char motdfile[121] = "text/motd"; /* File where the motd is stored */
42 int connect_timeout = 15; /* How long to wait before a telnet
43 * connection times out */
44
45 int reserved_port_min = 0;
46 int reserved_port_max = 0;
47
48 int max_dcc = 0; /* indicates the current dcc limit in the main thread */
49
50 /* This function is called to enlarge the static sockettable in a thread.
51 * It keeps the mainthread dcc table enlarging with the main thread sockettable
52 * If this fails because the upper limit max_socks is reached, -1 is returned.
53 * If this was called from the main thread, it updates the socklist variable
54 *
55 * increase_socks_max() can be called by Tcl threads
56 */
increase_socks_max()57 int increase_socks_max()
58 {
59 struct threaddata *td = threaddata();
60 int osock = td->MAXSOCKS, max_dcc_new;
61
62 if (max_socks < 1)
63 max_socks = 1;
64
65 if (td->MAXSOCKS == max_socks) {
66 putlog(LOG_MISC, "*", "Maximum socket limit reached. Consider raising max-socks.");
67 return -1;
68 }
69
70 td->MAXSOCKS += 10;
71 if (td->MAXSOCKS > max_socks)
72 td->MAXSOCKS = max_socks;
73
74 if (td->socklist)
75 td->socklist = nrealloc(td->socklist, sizeof(sock_list) * td->MAXSOCKS);
76 else
77 td->socklist = nmalloc(sizeof(sock_list) * td->MAXSOCKS);
78 for (; osock < td->MAXSOCKS; osock++)
79 td->socklist[osock].flags = SOCK_UNUSED;
80
81 if (td->mainthread) {
82 max_dcc_new = td->MAXSOCKS - 10;
83 if (max_dcc_new > max_dcc)
84 max_dcc = max_dcc_new;
85 else if (max_dcc == 0)
86 max_dcc = 1;
87 else {
88 putlog(LOG_MISC, "*", "Maximum dcc limit reached. Consider raising max-socks.");
89 return -1;
90 }
91 if (dcc)
92 dcc = nrealloc(dcc, sizeof(struct dcc_t) * max_dcc);
93 else
94 dcc = nmalloc(sizeof(struct dcc_t) * max_dcc);
95 socklist = td->socklist;
96 }
97
98 return 0;
99 }
100
expmem_dccutil()101 int expmem_dccutil()
102 {
103 int tot, i;
104
105 tot = sizeof(struct dcc_t) * max_dcc;
106 tot += sizeof(sock_list) * threaddata()->MAXSOCKS;
107
108 for (i = 0; i < dcc_total; i++) {
109 if (dcc[i].type && dcc[i].type->expmem)
110 tot += dcc[i].type->expmem(dcc[i].u.other);
111 }
112 return tot;
113 }
114
findidx(int z)115 int findidx(int z)
116 {
117 int j;
118
119 for (j = 0; j < dcc_total; j++)
120 if ((dcc[j].sock == z) && (dcc[j].type->flags & DCT_VALIDIDX))
121 return j;
122 return -1;
123 }
124
findanyidx(int z)125 int findanyidx(int z)
126 {
127 int j;
128
129 for (j = 0; j < dcc_total; j++)
130 if (dcc[j].sock == z)
131 return j;
132 return -1;
133 }
134
135 /* Replace \n with \r\n */
add_cr(char * buf)136 char *add_cr(char *buf)
137 {
138 static char WBUF[1024];
139 char *p, *q;
140
141 for (p = buf, q = WBUF; *p; p++, q++) {
142 if (*p == '\n')
143 *q++ = '\r';
144 *q = *p;
145 }
146 *q = *p;
147 return WBUF;
148 }
149
150 extern void (*qserver) (int, char *, int);
151
EGG_VARARGS_DEF(int,arg1)152 void dprintf EGG_VARARGS_DEF(int, arg1)
153 {
154 char buf[LOGLINEMAX];
155 char *format;
156 int idx, len;
157 va_list va;
158
159 idx = EGG_VARARGS_START(int, arg1, va);
160 format = va_arg(va, char *);
161
162 egg_vsnprintf(buf, LOGLINEMAX-1, format, va);
163 va_end(va);
164 /* We can not use the return value vsnprintf() to determine where
165 * to null terminate. The C99 standard specifies that vsnprintf()
166 * shall return the number of bytes that would be written if the
167 * buffer had been large enough, rather then -1.
168 */
169 /* We actually can, since if it's < 0 or >= sizeof(buf), we know it wrote
170 * sizeof(buf) bytes. But we're not doing that anyway.
171 */
172 buf[sizeof(buf) - 1] = 0;
173 len = strlen(buf);
174
175 /* Send it out */
176 dprint(idx, buf, len);
177 }
178
dprint(int idx,char * buf,int len)179 void dprint(int idx, char *buf, int len)
180 {
181 if (idx < 0) {
182 tputs(-idx, buf, len);
183 } else if (idx > 0x7FF0) {
184 switch (idx) {
185 case DP_LOG:
186 putlog(LOG_MISC, "*", "%s", buf);
187 break;
188 case DP_STDOUT:
189 tputs(STDOUT, buf, len);
190 break;
191 case DP_STDERR:
192 tputs(STDERR, buf, len);
193 break;
194 case DP_SERVER:
195 case DP_HELP:
196 case DP_MODE:
197 case DP_MODE_NEXT:
198 case DP_SERVER_NEXT:
199 case DP_HELP_NEXT:
200 qserver(idx, buf, len);
201 break;
202 }
203 return;
204 } else {
205 if (len > LOGLINEMAX-11) { /* Truncate to fit */
206 buf[LOGLINEMAX-11] = '\n';
207 buf[LOGLINEMAX-10] = 0;
208 len = LOGLINEMAX-10;
209 }
210 if (dcc[idx].type && ((long) (dcc[idx].type->output) == 1)) {
211 char *p = add_cr(buf);
212
213 tputs(dcc[idx].sock, p, strlen(p));
214 } else if (dcc[idx].type && dcc[idx].type->output)
215 dcc[idx].type->output(idx, buf, dcc[idx].u.other);
216 else
217 tputs(dcc[idx].sock, buf, len);
218 }
219 }
220
EGG_VARARGS_DEF(char *,arg1)221 void chatout EGG_VARARGS_DEF(char *, arg1)
222 {
223 int i, len;
224 char *format;
225 char s[601];
226 va_list va;
227
228 format = EGG_VARARGS_START(char *, arg1, va);
229
230 egg_vsnprintf(s, 511, format, va);
231 va_end(va);
232 len = strlen(s);
233 if (len > 511)
234 len = 511;
235 s[len + 1] = 0;
236
237 for (i = 0; i < dcc_total; i++)
238 if (dcc[i].type == &DCC_CHAT)
239 if (dcc[i].u.chat->channel >= 0)
240 dprintf(i, "%s", s);
241
242 }
243
244 /* Print to all on this channel but one.
245 */
EGG_VARARGS_DEF(int,arg1)246 void chanout_but EGG_VARARGS_DEF(int, arg1)
247 {
248 int i, x, chan, len;
249 char *format;
250 char s[601];
251 va_list va;
252
253 x = EGG_VARARGS_START(int, arg1, va);
254 chan = va_arg(va, int);
255 format = va_arg(va, char *);
256
257 egg_vsnprintf(s, 511, format, va);
258 va_end(va);
259 len = strlen(s);
260 if (len > 511)
261 len = 511;
262 s[len + 1] = 0;
263
264 for (i = 0; i < dcc_total; i++)
265 if ((dcc[i].type == &DCC_CHAT) && (i != x))
266 if (dcc[i].u.chat->channel == chan)
267 dprintf(i, "%s", s);
268
269 }
270
dcc_chatter(int idx)271 void dcc_chatter(int idx)
272 {
273 int i, j;
274 struct flag_record fr = { FR_GLOBAL | FR_CHAN | FR_ANYWH, 0, 0, 0, 0, 0 };
275
276 get_user_flagrec(dcc[idx].user, &fr, NULL);
277 show_motd(idx);
278 i = dcc[idx].u.chat->channel;
279 dcc[idx].u.chat->channel = 234567;
280 j = dcc[idx].sock;
281 strcpy(dcc[idx].u.chat->con_chan, "***");
282 check_tcl_chon(dcc[idx].nick, dcc[idx].sock);
283 /* Still there? */
284 if ((idx >= dcc_total) || (dcc[idx].sock != j))
285 return; /* Nope */
286 /* Tcl script may have taken control */
287 if (dcc[idx].type == &DCC_CHAT) {
288 if (!strcmp(dcc[idx].u.chat->con_chan, "***"))
289 strcpy(dcc[idx].u.chat->con_chan, "*");
290 if (dcc[idx].u.chat->channel == 234567) {
291 /* If the chat channel has already been altered it's *highly*
292 * probably join/part messages have been broadcast everywhere,
293 * so don't bother sending them
294 */
295 if (i == -2)
296 i = 0;
297 dcc[idx].u.chat->channel = i;
298 if ((dcc[idx].u.chat->channel >= 0) &&
299 (dcc[idx].u.chat->channel < GLOBAL_CHANS))
300 botnet_send_join_idx(idx, -1);
301 check_tcl_chjn(botnetnick, dcc[idx].nick, dcc[idx].u.chat->channel,
302 geticon(idx), dcc[idx].sock, dcc[idx].host);
303 }
304 /* But *do* bother with sending it locally */
305 if (!dcc[idx].u.chat->channel) {
306 chanout_but(-1, 0, "*** %s joined the party line.\n", dcc[idx].nick);
307 } else if (dcc[idx].u.chat->channel > 0) {
308 chanout_but(-1, dcc[idx].u.chat->channel,
309 "*** %s joined the channel.\n", dcc[idx].nick);
310 }
311 }
312 }
313
314 /* Closes an open FD for transfer sockets. */
killtransfer(int n)315 void killtransfer(int n)
316 {
317 int i, ok = 1;
318
319 if (dcc[n].type->flags & DCT_FILETRAN) {
320 if (dcc[n].u.xfer->f) {
321 fclose(dcc[n].u.xfer->f);
322 dcc[n].u.xfer->f = NULL;
323 }
324 if (dcc[n].u.xfer->filename && copy_to_tmp) {
325 for (i = 0; i < dcc_total; i++) {
326 if ((i != n) && (dcc[i].type->flags & DCT_FILETRAN) &&
327 (dcc[i].u.xfer->filename) &&
328 (!strcmp(dcc[i].u.xfer->filename, dcc[n].u.xfer->filename))) {
329 ok = 0;
330 break;
331 }
332 }
333 if (ok)
334 unlink(dcc[n].u.xfer->filename);
335 }
336 }
337 }
338
339 /* Mark an entry as lost and deconstruct it's contents. It will be securely
340 * removed from the dcc list in the main loop.
341 */
lostdcc(int n)342 void lostdcc(int n)
343 {
344 /* Make sure it's a valid dcc index. */
345 if (n < 0 || n >= max_dcc)
346 return;
347
348 if (dcc[n].type && dcc[n].type->kill)
349 dcc[n].type->kill(n, dcc[n].u.other);
350 else if (dcc[n].u.other)
351 nfree(dcc[n].u.other);
352 egg_bzero(&dcc[n], sizeof(struct dcc_t));
353
354 dcc[n].sock = -1;
355 dcc[n].type = &DCC_LOST;
356 }
357
358 /* Remove entry from dcc list. Think twice before using this function,
359 * because it invalidates any variables that point to a specific dcc
360 * entry!
361 *
362 * Note: The entry will be deconstructed if it was not deconstructed
363 * already. This case should normally not occur.
364 */
removedcc(int n)365 void removedcc(int n)
366 {
367 if (dcc[n].type && dcc[n].type->kill)
368 dcc[n].type->kill(n, dcc[n].u.other);
369 else if (dcc[n].u.other)
370 nfree(dcc[n].u.other);
371 dcc_total--;
372 if (n < dcc_total)
373 memcpy(&dcc[n], &dcc[dcc_total], sizeof(struct dcc_t));
374 else
375 egg_bzero(&dcc[n], sizeof(struct dcc_t)); /* drummer */
376 }
377
378 /* Clean up sockets that were just left for dead.
379 */
dcc_remove_lost(void)380 void dcc_remove_lost(void)
381 {
382 int i;
383
384 for (i = 0; i < dcc_total; i++) {
385 if (dcc[i].type == &DCC_LOST) {
386 dcc[i].type = NULL;
387 dcc[i].sock = -1;
388 removedcc(i);
389 i--;
390 }
391 }
392 }
393
394 /* Show list of current dcc's to a dcc-chatter
395 * positive value: idx given -- negative value: sock given
396 */
tell_dcc(int zidx)397 void tell_dcc(int zidx)
398 {
399 int i, j, nicklen = 0;
400 char other[160];
401 char format[81];
402
403 /* calculate max nicklen */
404 for (i = 0; i < dcc_total; i++) {
405 if (strlen(dcc[i].nick) > nicklen)
406 nicklen = strlen(dcc[i].nick);
407 }
408 if (nicklen < 9)
409 nicklen = 9;
410
411 j = 60 - nicklen;
412 if (j < 15)
413 j = 15;
414 if (j > 40)
415 j = 40;
416
417 egg_snprintf(format, sizeof format, "%%-3s %%-%u.%us %%-6s %%-%u.%us %%s\n",
418 j, j, nicklen, nicklen);
419 dprintf(zidx, format, "IDX", "ADDR", "+ PORT", "NICK", "TYPE INFO");
420 dprintf(zidx, format, "---",
421 "------------------------------------------------------", "------",
422 "--------------------------------", "----- ---------");
423 egg_snprintf(format, sizeof format, "%%-3d %%-%u.%us %%c%%5d %%-%u.%us %%s\n",
424 j, j, nicklen, nicklen);
425
426 /* Show server */
427 for (i = 0; i < dcc_total; i++) {
428 if (dcc[i].type && dcc[i].type->display)
429 dcc[i].type->display(i, other);
430 else {
431 sprintf(other, "?:%lX !! ERROR !!", (long) dcc[i].type);
432 break;
433 }
434 dprintf(zidx, format, dcc[i].sock, iptostr(&dcc[i].sockname.addr.sa),
435 #ifdef TLS
436 dcc[i].ssl ? '+' : ' ', dcc[i].port, dcc[i].nick, other);
437 #else
438 ' ', dcc[i].port, dcc[i].nick, other);
439 #endif
440 }
441 }
442
443 /* Mark someone on dcc chat as no longer away
444 */
not_away(int idx)445 void not_away(int idx)
446 {
447 if (dcc[idx].u.chat->away == NULL) {
448 dprintf(idx, "You weren't away!\n");
449 return;
450 }
451 if (dcc[idx].u.chat->channel >= 0) {
452 chanout_but(-1, dcc[idx].u.chat->channel,
453 "*** %s is no longer away.\n", dcc[idx].nick);
454 if (dcc[idx].u.chat->channel < GLOBAL_CHANS) {
455 botnet_send_away(-1, botnetnick, dcc[idx].sock, NULL, idx);
456 }
457 }
458 dprintf(idx, "You're not away any more.\n");
459 nfree(dcc[idx].u.chat->away);
460 dcc[idx].u.chat->away = NULL;
461 check_tcl_away(botnetnick, dcc[idx].sock, NULL);
462 }
463
set_away(int idx,char * s)464 void set_away(int idx, char *s)
465 {
466 if (s == NULL) {
467 not_away(idx);
468 return;
469 }
470 if (!s[0]) {
471 not_away(idx);
472 return;
473 }
474 if (dcc[idx].u.chat->away != NULL)
475 nfree(dcc[idx].u.chat->away);
476 dcc[idx].u.chat->away = nmalloc(strlen(s) + 1);
477 strcpy(dcc[idx].u.chat->away, s);
478 if (dcc[idx].u.chat->channel >= 0) {
479 chanout_but(-1, dcc[idx].u.chat->channel,
480 "*** %s is now away: %s\n", dcc[idx].nick, s);
481 if (dcc[idx].u.chat->channel < GLOBAL_CHANS) {
482 botnet_send_away(-1, botnetnick, dcc[idx].sock, s, idx);
483 }
484 }
485 dprintf(idx, "You are now away.\n");
486 check_tcl_away(botnetnick, dcc[idx].sock, s);
487 }
488
489 /* This helps the memory debugging
490 */
_get_data_ptr(int size,char * file,int line)491 void *_get_data_ptr(int size, char *file, int line)
492 {
493 char *p;
494 #ifdef DEBUG_MEM
495 char x[1024];
496
497 p = strrchr(file, '/');
498 egg_snprintf(x, sizeof x, "dccutil.c:%s", p ? p + 1 : file);
499 p = n_malloc(size, x, line);
500 #else
501 p = nmalloc(size);
502 #endif
503 egg_bzero(p, size);
504 return p;
505 }
506
507 /* Make a password with max length and random lower case letters and digits
508 */
makepass(char * pass)509 void makepass(char *pass)
510 {
511 make_rand_str_from_chars(pass, PASSWORDMAX, CHARSET_PASSWORD);
512 }
513
flush_lines(int idx,struct chat_info * ci)514 void flush_lines(int idx, struct chat_info *ci)
515 {
516 int c = ci->line_count;
517 struct msgq *p = ci->buffer, *o;
518
519 while (p && c < (ci->max_line)) {
520 ci->current_lines--;
521 tputs(dcc[idx].sock, p->msg, p->len);
522 nfree(p->msg);
523 o = p->next;
524 nfree(p);
525 p = o;
526 c++;
527 }
528 if (p != NULL) {
529 if (dcc[idx].status & STAT_TELNET)
530 tputs(dcc[idx].sock, "[More]: ", 8);
531 else
532 tputs(dcc[idx].sock, "[More]\n", 7);
533 }
534 ci->buffer = p;
535 ci->line_count = 0;
536 }
537
new_dcc(struct dcc_table * type,int xtra_size)538 int new_dcc(struct dcc_table *type, int xtra_size)
539 {
540 int i = dcc_total;
541
542 if (dcc_total == max_dcc && increase_socks_max())
543 return -1;
544 dcc_total++;
545 egg_bzero((char *) &dcc[i], sizeof(struct dcc_t));
546
547 dcc[i].type = type;
548 if (xtra_size) {
549 dcc[i].u.other = nmalloc(xtra_size);
550 egg_bzero(dcc[i].u.other, xtra_size);
551 }
552 return i;
553 }
554
555 /* Changes the given dcc entry to another type.
556 */
changeover_dcc(int i,struct dcc_table * type,int xtra_size)557 void changeover_dcc(int i, struct dcc_table *type, int xtra_size)
558 {
559 /* Free old structure. */
560 if (dcc[i].type && dcc[i].type->kill)
561 dcc[i].type->kill(i, dcc[i].u.other);
562 else if (dcc[i].u.other) {
563 nfree(dcc[i].u.other);
564 dcc[i].u.other = NULL;
565 }
566
567 dcc[i].type = type;
568 if (xtra_size) {
569 dcc[i].u.other = nmalloc(xtra_size);
570 egg_bzero(dcc[i].u.other, xtra_size);
571 }
572 }
573
detect_dcc_flood(time_t * timer,struct chat_info * chat,int idx)574 int detect_dcc_flood(time_t *timer, struct chat_info *chat, int idx)
575 {
576 time_t t;
577
578 if (!dcc_flood_thr || !chat)
579 return 0;
580 t = now;
581 if (*timer != t) {
582 *timer = t;
583 chat->msgs_per_sec = 0;
584 } else {
585 chat->msgs_per_sec++;
586 if (chat->msgs_per_sec > dcc_flood_thr) {
587 /* FLOOD */
588 dprintf(idx, "*** FLOOD: %s.\n", IRC_GOODBYE);
589 /* Evil assumption here that flags&DCT_CHAT implies chat type */
590 if ((dcc[idx].type->flags & DCT_CHAT) && (chat->channel >= 0)) {
591 char x[1024];
592
593 egg_snprintf(x, sizeof x, DCC_FLOODBOOT, dcc[idx].nick);
594 chanout_but(idx, chat->channel, "*** %s", x);
595 if (chat->channel < GLOBAL_CHANS)
596 botnet_send_part_idx(idx, x);
597 }
598 check_tcl_chof(dcc[idx].nick, dcc[idx].sock);
599 if ((dcc[idx].sock != STDOUT) || backgrd) {
600 killsock(dcc[idx].sock);
601 lostdcc(idx);
602 } else {
603 dprintf(DP_STDOUT, "\n### SIMULATION RESET ###\n\n");
604 dcc_chatter(idx);
605 }
606 return 1; /* <- flood */
607 }
608 }
609 return 0;
610 }
611
612 /* Handle someone being booted from dcc chat.
613 */
do_boot(int idx,char * by,char * reason)614 void do_boot(int idx, char *by, char *reason)
615 {
616 int files = (dcc[idx].type != &DCC_CHAT);
617
618 dprintf(idx, DCC_BOOTED1);
619 dprintf(idx, DCC_BOOTED2, files ? "file section" : "bot",
620 by, reason[0] ? ": " : ".", reason);
621 /* If it's a partyliner (chatterer :) */
622 /* Horrible assumption that DCT_CHAT using structure uses same format
623 * as DCC_CHAT */
624 if ((dcc[idx].type->flags & DCT_CHAT) && (dcc[idx].u.chat->channel >= 0)) {
625 char x[1024];
626
627 egg_snprintf(x, sizeof x, DCC_BOOTED3, by, dcc[idx].nick,
628 reason[0] ? ": " : "", reason);
629 chanout_but(idx, dcc[idx].u.chat->channel, "*** %s.\n", x);
630 if (dcc[idx].u.chat->channel < GLOBAL_CHANS)
631 botnet_send_part_idx(idx, x);
632 }
633 check_tcl_chof(dcc[idx].nick, dcc[idx].sock);
634 if ((dcc[idx].sock != STDOUT) || backgrd) {
635 killsock(dcc[idx].sock);
636 lostdcc(idx);
637 /* Entry must remain in the table so it can be logged by the caller */
638 } else {
639 dprintf(DP_STDOUT, "\n### SIMULATION RESET\n\n");
640 dcc_chatter(idx);
641 }
642 return;
643 }
644