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