| Current Path : /home/mudbot/eggdrop/src/ |
| Current File : //home/mudbot/eggdrop/src/dccutil.c |
/*
* dccutil.c -- handles:
* lots of little functions to send formatted text to
* varying types of connections
* '.who', '.whom', and '.dccstat' code
* memory management for dcc structures
* timeout checking for dcc connections
*/
/*
* Copyright (C) 1997 Robey Pointer
* Copyright (C) 1999 - 2019 Eggheads Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <sys/stat.h>
#include "main.h"
#include <errno.h>
#include "chan.h"
#include "modules.h"
#include "tandem.h"
extern struct dcc_t *dcc;
extern int dcc_total, dcc_flood_thr, backgrd, copy_to_tmp, max_socks;
extern char botnetnick[], version[];
extern time_t now;
extern sock_list *socklist;
char motdfile[121] = "text/motd"; /* File where the motd is stored */
int connect_timeout = 15; /* How long to wait before a telnet
* connection times out */
int reserved_port_min = 0;
int reserved_port_max = 0;
int max_dcc = 0; /* indicates the current dcc limit in the main thread */
/* This function is called to enlarge the static sockettable in a thread.
* It keeps the mainthread dcc table enlarging with the main thread sockettable
* If this fails because the upper limit max_socks is reached, -1 is returned.
* If this was called from the main thread, it updates the socklist variable
*
* increase_socks_max() can be called by Tcl threads
*/
int increase_socks_max()
{
struct threaddata *td = threaddata();
int osock = td->MAXSOCKS, max_dcc_new;
if (max_socks < 1)
max_socks = 1;
if (td->MAXSOCKS == max_socks) {
putlog(LOG_MISC, "*", "Maximum socket limit reached. Consider raising max-socks.");
return -1;
}
td->MAXSOCKS += 10;
if (td->MAXSOCKS > max_socks)
td->MAXSOCKS = max_socks;
if (td->socklist)
td->socklist = nrealloc(td->socklist, sizeof(sock_list) * td->MAXSOCKS);
else
td->socklist = nmalloc(sizeof(sock_list) * td->MAXSOCKS);
for (; osock < td->MAXSOCKS; osock++)
td->socklist[osock].flags = SOCK_UNUSED;
if (td->mainthread) {
max_dcc_new = td->MAXSOCKS - 10;
if (max_dcc_new > max_dcc)
max_dcc = max_dcc_new;
else if (max_dcc == 0)
max_dcc = 1;
else {
putlog(LOG_MISC, "*", "Maximum dcc limit reached. Consider raising max-socks.");
return -1;
}
if (dcc)
dcc = nrealloc(dcc, sizeof(struct dcc_t) * max_dcc);
else
dcc = nmalloc(sizeof(struct dcc_t) * max_dcc);
socklist = td->socklist;
}
return 0;
}
int expmem_dccutil()
{
int tot, i;
tot = sizeof(struct dcc_t) * max_dcc;
tot += sizeof(sock_list) * threaddata()->MAXSOCKS;
for (i = 0; i < dcc_total; i++) {
if (dcc[i].type && dcc[i].type->expmem)
tot += dcc[i].type->expmem(dcc[i].u.other);
}
return tot;
}
int findidx(int z)
{
int j;
for (j = 0; j < dcc_total; j++)
if ((dcc[j].sock == z) && (dcc[j].type->flags & DCT_VALIDIDX))
return j;
return -1;
}
int findanyidx(int z)
{
int j;
for (j = 0; j < dcc_total; j++)
if (dcc[j].sock == z)
return j;
return -1;
}
/* Replace \n with \r\n */
char *add_cr(char *buf)
{
static char WBUF[1024];
char *p, *q;
for (p = buf, q = WBUF; *p; p++, q++) {
if (*p == '\n')
*q++ = '\r';
*q = *p;
}
*q = *p;
return WBUF;
}
extern void (*qserver) (int, char *, int);
void dprintf EGG_VARARGS_DEF(int, arg1)
{
char buf[1024];
char *format;
int idx, len;
va_list va;
idx = EGG_VARARGS_START(int, arg1, va);
format = va_arg(va, char *);
egg_vsnprintf(buf, 1023, format, va);
va_end(va);
/* We can not use the return value vsnprintf() to determine where
* to null terminate. The C99 standard specifies that vsnprintf()
* shall return the number of bytes that would be written if the
* buffer had been large enough, rather then -1.
*/
/* We actually can, since if it's < 0 or >= sizeof(buf), we know it wrote
* sizeof(buf) bytes. But we're not doing that anyway.
*/
buf[sizeof(buf) - 1] = 0;
len = strlen(buf);
/* Send it out */
dprint(idx, buf, len);
}
void dprint(int idx, char *buf, int len)
{
if (idx < 0) {
tputs(-idx, buf, len);
} else if (idx > 0x7FF0) {
switch (idx) {
case DP_LOG:
putlog(LOG_MISC, "*", "%s", buf);
break;
case DP_STDOUT:
tputs(STDOUT, buf, len);
break;
case DP_STDERR:
tputs(STDERR, buf, len);
break;
case DP_SERVER:
case DP_HELP:
case DP_MODE:
case DP_MODE_NEXT:
case DP_SERVER_NEXT:
case DP_HELP_NEXT:
qserver(idx, buf, len);
break;
}
return;
} else {
if (len > 500) { /* Truncate to fit */
buf[500] = 0;
strcat(buf, "\n");
len = 501;
}
if (dcc[idx].type && ((long) (dcc[idx].type->output) == 1)) {
char *p = add_cr(buf);
tputs(dcc[idx].sock, p, strlen(p));
} else if (dcc[idx].type && dcc[idx].type->output)
dcc[idx].type->output(idx, buf, dcc[idx].u.other);
else
tputs(dcc[idx].sock, buf, len);
}
}
void chatout EGG_VARARGS_DEF(char *, arg1)
{
int i, len;
char *format;
char s[601];
va_list va;
format = EGG_VARARGS_START(char *, arg1, va);
egg_vsnprintf(s, 511, format, va);
va_end(va);
len = strlen(s);
if (len > 511)
len = 511;
s[len + 1] = 0;
for (i = 0; i < dcc_total; i++)
if (dcc[i].type == &DCC_CHAT)
if (dcc[i].u.chat->channel >= 0)
dprintf(i, "%s", s);
}
/* Print to all on this channel but one.
*/
void chanout_but EGG_VARARGS_DEF(int, arg1)
{
int i, x, chan, len;
char *format;
char s[601];
va_list va;
x = EGG_VARARGS_START(int, arg1, va);
chan = va_arg(va, int);
format = va_arg(va, char *);
egg_vsnprintf(s, 511, format, va);
va_end(va);
len = strlen(s);
if (len > 511)
len = 511;
s[len + 1] = 0;
for (i = 0; i < dcc_total; i++)
if ((dcc[i].type == &DCC_CHAT) && (i != x))
if (dcc[i].u.chat->channel == chan)
dprintf(i, "%s", s);
}
void dcc_chatter(int idx)
{
int i, j;
struct flag_record fr = { FR_GLOBAL | FR_CHAN | FR_ANYWH, 0, 0, 0, 0, 0 };
get_user_flagrec(dcc[idx].user, &fr, NULL);
show_motd(idx);
i = dcc[idx].u.chat->channel;
dcc[idx].u.chat->channel = 234567;
j = dcc[idx].sock;
strcpy(dcc[idx].u.chat->con_chan, "***");
check_tcl_chon(dcc[idx].nick, dcc[idx].sock);
/* Still there? */
if ((idx >= dcc_total) || (dcc[idx].sock != j))
return; /* Nope */
/* Tcl script may have taken control */
if (dcc[idx].type == &DCC_CHAT) {
if (!strcmp(dcc[idx].u.chat->con_chan, "***"))
strcpy(dcc[idx].u.chat->con_chan, "*");
if (dcc[idx].u.chat->channel == 234567) {
/* If the chat channel has already been altered it's *highly*
* probably join/part messages have been broadcast everywhere,
* so don't bother sending them
*/
if (i == -2)
i = 0;
dcc[idx].u.chat->channel = i;
if ((dcc[idx].u.chat->channel >= 0) &&
(dcc[idx].u.chat->channel < GLOBAL_CHANS))
botnet_send_join_idx(idx, -1);
check_tcl_chjn(botnetnick, dcc[idx].nick, dcc[idx].u.chat->channel,
geticon(idx), dcc[idx].sock, dcc[idx].host);
}
/* But *do* bother with sending it locally */
if (!dcc[idx].u.chat->channel) {
chanout_but(-1, 0, "*** %s joined the party line.\n", dcc[idx].nick);
} else if (dcc[idx].u.chat->channel > 0) {
chanout_but(-1, dcc[idx].u.chat->channel,
"*** %s joined the channel.\n", dcc[idx].nick);
}
}
}
/* Closes an open FD for transfer sockets. */
void killtransfer(int n)
{
int i, ok = 1;
if (dcc[n].type->flags & DCT_FILETRAN) {
if (dcc[n].u.xfer->f) {
fclose(dcc[n].u.xfer->f);
dcc[n].u.xfer->f = NULL;
}
if (dcc[n].u.xfer->filename && copy_to_tmp) {
for (i = 0; i < dcc_total; i++) {
if ((i != n) && (dcc[i].type->flags & DCT_FILETRAN) &&
(dcc[i].u.xfer->filename) &&
(!strcmp(dcc[i].u.xfer->filename, dcc[n].u.xfer->filename))) {
ok = 0;
break;
}
}
if (ok)
unlink(dcc[n].u.xfer->filename);
}
}
}
/* Mark an entry as lost and deconstruct it's contents. It will be securely
* removed from the dcc list in the main loop.
*/
void lostdcc(int n)
{
/* Make sure it's a valid dcc index. */
if (n < 0 || n >= max_dcc)
return;
if (dcc[n].type && dcc[n].type->kill)
dcc[n].type->kill(n, dcc[n].u.other);
else if (dcc[n].u.other)
nfree(dcc[n].u.other);
egg_bzero(&dcc[n], sizeof(struct dcc_t));
dcc[n].sock = -1;
dcc[n].type = &DCC_LOST;
}
/* Remove entry from dcc list. Think twice before using this function,
* because it invalidates any variables that point to a specific dcc
* entry!
*
* Note: The entry will be deconstructed if it was not deconstructed
* already. This case should normally not occur.
*/
void removedcc(int n)
{
if (dcc[n].type && dcc[n].type->kill)
dcc[n].type->kill(n, dcc[n].u.other);
else if (dcc[n].u.other)
nfree(dcc[n].u.other);
dcc_total--;
if (n < dcc_total)
memcpy(&dcc[n], &dcc[dcc_total], sizeof(struct dcc_t));
else
egg_bzero(&dcc[n], sizeof(struct dcc_t)); /* drummer */
}
/* Clean up sockets that were just left for dead.
*/
void dcc_remove_lost(void)
{
int i;
for (i = 0; i < dcc_total; i++) {
if (dcc[i].type == &DCC_LOST) {
dcc[i].type = NULL;
dcc[i].sock = -1;
removedcc(i);
i--;
}
}
}
/* Show list of current dcc's to a dcc-chatter
* positive value: idx given -- negative value: sock given
*/
void tell_dcc(int zidx)
{
int i, j, nicklen = 0;
char other[160];
char format[81];
/* calculate max nicklen */
for (i = 0; i < dcc_total; i++) {
if (strlen(dcc[i].nick) > nicklen)
nicklen = strlen(dcc[i].nick);
}
if (nicklen < 9)
nicklen = 9;
j = 60 - nicklen;
if (j < 15)
j = 15;
if (j > 40)
j = 40;
egg_snprintf(format, sizeof format, "%%-3s %%-%u.%us %%-6s %%-%u.%us %%s\n",
j, j, nicklen, nicklen);
dprintf(zidx, format, "IDX", "ADDR", "+ PORT", "NICK", "TYPE INFO");
dprintf(zidx, format, "---",
"------------------------------------------------------", "------",
"--------------------------------", "----- ---------");
egg_snprintf(format, sizeof format, "%%-3d %%-%u.%us %%c%%5d %%-%u.%us %%s\n",
j, j, nicklen, nicklen);
/* Show server */
for (i = 0; i < dcc_total; i++) {
if (dcc[i].type && dcc[i].type->display)
dcc[i].type->display(i, other);
else {
sprintf(other, "?:%lX !! ERROR !!", (long) dcc[i].type);
break;
}
dprintf(zidx, format, dcc[i].sock, iptostr(&dcc[i].sockname.addr.sa),
#ifdef TLS
dcc[i].ssl ? '+' : ' ', dcc[i].port, dcc[i].nick, other);
#else
' ', dcc[i].port, dcc[i].nick, other);
#endif
}
}
/* Mark someone on dcc chat as no longer away
*/
void not_away(int idx)
{
if (dcc[idx].u.chat->away == NULL) {
dprintf(idx, "You weren't away!\n");
return;
}
if (dcc[idx].u.chat->channel >= 0) {
chanout_but(-1, dcc[idx].u.chat->channel,
"*** %s is no longer away.\n", dcc[idx].nick);
if (dcc[idx].u.chat->channel < GLOBAL_CHANS) {
botnet_send_away(-1, botnetnick, dcc[idx].sock, NULL, idx);
}
}
dprintf(idx, "You're not away any more.\n");
nfree(dcc[idx].u.chat->away);
dcc[idx].u.chat->away = NULL;
check_tcl_away(botnetnick, dcc[idx].sock, NULL);
}
void set_away(int idx, char *s)
{
if (s == NULL) {
not_away(idx);
return;
}
if (!s[0]) {
not_away(idx);
return;
}
if (dcc[idx].u.chat->away != NULL)
nfree(dcc[idx].u.chat->away);
dcc[idx].u.chat->away = nmalloc(strlen(s) + 1);
strcpy(dcc[idx].u.chat->away, s);
if (dcc[idx].u.chat->channel >= 0) {
chanout_but(-1, dcc[idx].u.chat->channel,
"*** %s is now away: %s\n", dcc[idx].nick, s);
if (dcc[idx].u.chat->channel < GLOBAL_CHANS) {
botnet_send_away(-1, botnetnick, dcc[idx].sock, s, idx);
}
}
dprintf(idx, "You are now away.\n");
check_tcl_away(botnetnick, dcc[idx].sock, s);
}
/* This helps the memory debugging
*/
void *_get_data_ptr(int size, char *file, int line)
{
char *p;
#ifdef DEBUG_MEM
char x[1024];
p = strrchr(file, '/');
egg_snprintf(x, sizeof x, "dccutil.c:%s", p ? p + 1 : file);
p = n_malloc(size, x, line);
#else
p = nmalloc(size);
#endif
egg_bzero(p, size);
return p;
}
/* Make a password with (PASSWORDLEN - 1) random lower case letters and digits
*/
void makepass(char *pass)
{
make_rand_str(pass, PASSWORDLEN - 1);
}
void flush_lines(int idx, struct chat_info *ci)
{
int c = ci->line_count;
struct msgq *p = ci->buffer, *o;
while (p && c < (ci->max_line)) {
ci->current_lines--;
tputs(dcc[idx].sock, p->msg, p->len);
nfree(p->msg);
o = p->next;
nfree(p);
p = o;
c++;
}
if (p != NULL) {
if (dcc[idx].status & STAT_TELNET)
tputs(dcc[idx].sock, "[More]: ", 8);
else
tputs(dcc[idx].sock, "[More]\n", 7);
}
ci->buffer = p;
ci->line_count = 0;
}
int new_dcc(struct dcc_table *type, int xtra_size)
{
int i = dcc_total;
if (dcc_total == max_dcc && increase_socks_max())
return -1;
dcc_total++;
egg_bzero((char *) &dcc[i], sizeof(struct dcc_t));
dcc[i].type = type;
if (xtra_size) {
dcc[i].u.other = nmalloc(xtra_size);
egg_bzero(dcc[i].u.other, xtra_size);
}
return i;
}
/* Changes the given dcc entry to another type.
*/
void changeover_dcc(int i, struct dcc_table *type, int xtra_size)
{
/* Free old structure. */
if (dcc[i].type && dcc[i].type->kill)
dcc[i].type->kill(i, dcc[i].u.other);
else if (dcc[i].u.other) {
nfree(dcc[i].u.other);
dcc[i].u.other = NULL;
}
dcc[i].type = type;
if (xtra_size) {
dcc[i].u.other = nmalloc(xtra_size);
egg_bzero(dcc[i].u.other, xtra_size);
}
}
int detect_dcc_flood(time_t *timer, struct chat_info *chat, int idx)
{
time_t t;
if (!dcc_flood_thr || !chat)
return 0;
t = now;
if (*timer != t) {
*timer = t;
chat->msgs_per_sec = 0;
} else {
chat->msgs_per_sec++;
if (chat->msgs_per_sec > dcc_flood_thr) {
/* FLOOD */
dprintf(idx, "*** FLOOD: %s.\n", IRC_GOODBYE);
/* Evil assumption here that flags&DCT_CHAT implies chat type */
if ((dcc[idx].type->flags & DCT_CHAT) && (chat->channel >= 0)) {
char x[1024];
egg_snprintf(x, sizeof x, DCC_FLOODBOOT, dcc[idx].nick);
chanout_but(idx, chat->channel, "*** %s", x);
if (chat->channel < GLOBAL_CHANS)
botnet_send_part_idx(idx, x);
}
check_tcl_chof(dcc[idx].nick, dcc[idx].sock);
if ((dcc[idx].sock != STDOUT) || backgrd) {
killsock(dcc[idx].sock);
lostdcc(idx);
} else {
dprintf(DP_STDOUT, "\n### SIMULATION RESET ###\n\n");
dcc_chatter(idx);
}
return 1; /* <- flood */
}
}
return 0;
}
/* Handle someone being booted from dcc chat.
*/
void do_boot(int idx, char *by, char *reason)
{
int files = (dcc[idx].type != &DCC_CHAT);
dprintf(idx, DCC_BOOTED1);
dprintf(idx, DCC_BOOTED2, files ? "file section" : "bot",
by, reason[0] ? ": " : ".", reason);
/* If it's a partyliner (chatterer :) */
/* Horrible assumption that DCT_CHAT using structure uses same format
* as DCC_CHAT */
if ((dcc[idx].type->flags & DCT_CHAT) && (dcc[idx].u.chat->channel >= 0)) {
char x[1024];
egg_snprintf(x, sizeof x, DCC_BOOTED3, by, dcc[idx].nick,
reason[0] ? ": " : "", reason);
chanout_but(idx, dcc[idx].u.chat->channel, "*** %s.\n", x);
if (dcc[idx].u.chat->channel < GLOBAL_CHANS)
botnet_send_part_idx(idx, x);
}
check_tcl_chof(dcc[idx].nick, dcc[idx].sock);
if ((dcc[idx].sock != STDOUT) || backgrd) {
killsock(dcc[idx].sock);
lostdcc(idx);
/* Entry must remain in the table so it can be logged by the caller */
} else {
dprintf(DP_STDOUT, "\n### SIMULATION RESET\n\n");
dcc_chatter(idx);
}
return;
}