/* botnet.c -- handles: keeping track of which bot's connected where in the chain dumping a list of bots or a bot tree to a user channel name associations on the party line rejecting a bot linking, unlinking, and relaying to another bot pinging the bots periodically and checking leaf status dprintf'ized, 28nov95 */ /* This file is part of the eggdrop source code copyright (c) 1997 Robey Pointer and is distributed according to the GNU general public license. For full details, read the top of 'main.c' or the file called COPYING that was distributed with this code. */ #include "main.h" #include "tandem.h" extern int dcc_total, backgrd; extern struct dcc_t * dcc; extern int connect_timeout; extern int max_dcc; extern time_t now; extern int egg_numver; extern struct userrec * userlist; extern Tcl_Interp * interp; extern char spaces[]; extern char spaces2[]; /* keep track of tandem bots on the botnet */ tand_t *tandbot; /* keep track of people on the botnet */ party_t *party; /* maximum space for party line members currently */ static int maxparty = 50; /* number of bots on the botnet */ int tands = 0; /* number of people on the botnet */ int parties = 0; /* botnet nickname */ char botnetnick[HANDLEN+1] = ""; /* allow remote unlinks of my sharebots ? */ int share_unlinks = 0; int expmem_botnet() { int size = 0, i; tand_t * bot; for (bot = tandbot;bot;bot=bot->next) size += sizeof(tand_t); context; size += (maxparty * sizeof(party_t)); for (i = 0; i < parties; i++) { if (party[i].away) size += strlen(party[i].away)+1; if (party[i].from) size += strlen(party[i].from)+1; } return size; } void init_bots() { tandbot = NULL; /* grab space for 50 bots for now -- expand later as needed */ maxparty = 50; party = (party_t *) nmalloc(maxparty * sizeof(party_t)); } tand_t * findbot (char * who) { tand_t * ptr = tandbot; while (ptr) { if (!strcasecmp(ptr->bot,who)) return ptr; ptr = ptr->next; } return NULL; } /* add a tandem bot to our chain list */ void addbot (char * who, char * from, char * next,char flag, int vernum) { tand_t ** ptr = &tandbot, * ptr2; context; while (*ptr) { if (!strcasecmp((*ptr)->bot, who)) putlog(LOG_BOTS,"*","!!! Duplicate botnet bot entry!!"); ptr = &((*ptr)->next); } ptr2 = nmalloc(sizeof(tand_t)); strncpy(ptr2->bot, who, HANDLEN); ptr2->bot[HANDLEN] = 0; ptr2->share = flag; ptr2->ver = vernum; ptr2->next = *ptr; *ptr = ptr2; /* may be via itself */ ptr2->via = findbot(from); if (!strcasecmp(next,botnetnick)) ptr2->uplink = (tand_t *)1; else ptr2->uplink = findbot(next); tands ++; } void updatebot (int idx, char * who, char share, int vernum) { tand_t * ptr = findbot(who); context; if (ptr) { if (share) ptr->share = share; if (vernum) ptr->ver = vernum; botnet_send_update(idx, ptr); } } /* for backward 1.0 compatibility: */ /* grab the (first) sock# for a user on another bot */ int partysock (char * bot, char * nick) { int i; for (i = 0; i < parties; i++) { if ((strcasecmp(party[i].bot, bot) == 0) && (strcasecmp(party[i].nick, nick) == 0)) return party[i].sock; } return 0; } /* new botnet member */ int addparty (char * bot, char * nick, int chan, char flag, int sock, char * from, int * idx) { int i; context; for (i = 0; i < parties; i++) { /* just changing the channel of someone already on? */ if (!strcasecmp(party[i].bot, bot) && (party[i].sock == sock)) { int oldchan = party[i].chan; party[i].chan = chan; party[i].timer = now; if (from[0]) { if (flag == ' ') flag = '-'; party[i].flag = flag; if (party[i].from) nfree(party[i].from); party[i].from = nmalloc(strlen(from) + 1); strcpy(party[i].from, from); } *idx = i; return oldchan; } } /* new member */ if (parties == maxparty) { maxparty += 50; party = (party_t *) nrealloc((void *) party, maxparty * sizeof(party_t)); } strncpy(party[parties].nick, nick,HANDLEN); party[parties].nick[HANDLEN] = 0; strncpy(party[parties].bot, bot,HANDLEN); party[parties].bot[HANDLEN] = 0; party[parties].chan = chan; party[parties].sock = sock; party[parties].status = 0; party[parties].away = 0; party[parties].timer = now; /* cope. */ if (from[0]) { if (flag == ' ') flag = '-'; party[parties].flag = flag; party[parties].from = nmalloc(strlen(from) + 1); strcpy(party[parties].from,from); } else { party[parties].flag = ' '; party[parties].from = nmalloc(10); strcpy(party[parties].from, "(unknown)"); } *idx = parties; parties++; return -1; } /* alter status flags for remote party-line user */ void partystat (char * bot, int sock, int add, int rem) { int i; for (i = 0; i < parties; i++) { if ((strcasecmp(party[i].bot, bot) == 0) && (party[i].sock == sock)) { party[i].status |= add; party[i].status &= ~rem; } } } /* other bot is sharing idle info */ void partysetidle (char * bot, int sock, int secs) { int i; for (i = 0; i < parties; i++) { if ((strcasecmp(party[i].bot, bot) == 0) && (party[i].sock == sock)) { party[i].timer = (now - (time_t) secs); } } } /* return someone's chat channel */ int getparty (char * bot, int sock) { int i; for (i = 0; i < parties; i++) { if (!strcasecmp(party[i].bot, bot) && (party[i].sock == sock)) { return i; } } return -1; } /* un-idle someone */ int partyidle (char * bot, char * nick) { int i, ok = 0; for (i = 0; i < parties; i++) { if ((strcasecmp(party[i].bot, bot) == 0) && (strcasecmp(party[i].nick, nick) == 0)) { party[i].timer = now; ok = 1; } } return ok; } /* change someone's nick */ int partynick (char * bot, int sock, char * nick) { char work[HANDLEN+1]; int i; for (i = 0; i < parties; i++) { if (!strcasecmp(party[i].bot, bot) && (party[i].sock == sock)) { strcpy(work,party[i].nick); strncpy(party[i].nick,nick,HANDLEN); party[i].nick[HANDLEN] = 0; strcpy(nick,work); return i; } } return -1; } /* set away message */ void partyaway (char * bot, int sock, char * msg) { int i; for (i = 0; i < parties; i++) { if ((strcasecmp(party[i].bot, bot) == 0) && (party[i].sock == sock)) { if (party[i].away) nfree(party[i].away); if (msg[0]) { party[i].away = nmalloc(strlen(msg)+1); strcpy(party[i].away,msg); } else party[i].away = 0; } } } /* remove a tandem bot from the chain list */ void rembot (char * who) { tand_t ** ptr = &tandbot, *ptr2; context; while (*ptr) { if (!strcasecmp((*ptr)->bot, who)) break; ptr = &((*ptr)->next); } if (!*ptr) /* may have just .unlink *'d */ return; check_tcl_disc(who); ptr2 = *ptr; *ptr = ptr2->next; nfree(ptr2); tands--; } void remparty (char * bot, int sock) { int i; context; for (i = 0; i < parties; i++) if ((strcasecmp(party[i].bot, bot) == 0) && (party[i].sock == sock)) { parties--; if (party[i].from) nfree(party[i].from); if (party[i].away) nfree(party[i].away); if (i < parties) { strcpy(party[i].bot, party[parties].bot); strcpy(party[i].nick, party[parties].nick); party[i].chan = party[parties].chan; party[i].sock = party[parties].sock; party[i].flag = party[parties].flag; party[i].status = party[parties].status; party[i].timer = party[parties].timer; party[i].from = party[parties].from; party[i].away = party[parties].away; } } } /* cancel every user that was on a certain bot */ void rempartybot (char * bot) { int i; for (i = 0; i < parties; i++) if (strcasecmp(party[i].bot, bot) == 0) { check_tcl_chpt(bot, party[i].nick,party[i].sock,party[i].chan); remparty(bot, party[i].sock); i--; } } void tandout_but (); /* remove every bot linked 'via' bot */ void unvia (int idx, tand_t * who) { tand_t * bot, * bot2; if (!who) return; /*safety*/ rempartybot(who->bot); bot = tandbot; while (bot) { if (bot->uplink == who) { unvia(idx,bot); bot2 = bot->next; rembot(bot->bot); bot = bot2; } else bot = bot->next; } #ifndef NO_OLD_BOTNET /* every bot unvia's bots behind anyway, so why send msg's for * EVERY one? - will this break things?! */ tandout_but(idx, "unlinked %s\n", who->bot); #endif } /* return index into dcc list of the bot that connects us to bot */ int nextbot (char * who) { int j; tand_t * bot = findbot(who); if (!bot) return -1; for (j = 0; j < dcc_total; j++) if (bot->via && !strcasecmp(bot->via->bot, dcc[j].nick) && (dcc[j].type == &DCC_BOT)) return j; return -1; /* we're not connected to 'via' */ } /* return name of the bot that is directly connected to bot X */ char *lastbot (char * who) { tand_t * bot = findbot(who); if (!bot) return "*"; else if (bot->uplink == (tand_t *)1) return botnetnick; else return bot->uplink->bot; } /* modern version of 'whom' (use local data) */ void answer_local_whom (int idx, int chan) { char c, idle[40], spaces2[33] = " "; int i, len, len2; context; if (chan == (-1)) dprintf(idx, "%s (+: %s, *: %s)\n", BOT_BOTNETUSERS, BOT_PARTYLINE, BOT_LOCALCHAN); else if (chan > 0) { simple_sprintf(idle,"assoc %d",chan); if ((Tcl_Eval(interp,idle) != TCL_OK) || !interp->result[0]) dprintf(idx, "%s %s%d:\n", BOT_USERSONCHAN, (chan < 100000) ? "" : "*", chan % 100000); else dprintf(idx, "%s '%s%s' (%s%d):\n", BOT_USERSONCHAN, (chan < 100000) ? "" : "*", interp->result, (chan < 100000) ? "" : "*", chan % 100000); } spaces[HANDLEN-9] = 0; dprintf(idx, " Nick %s Bot %s Host\n", spaces, spaces); dprintf(idx, "----------%s ---------%s --------------------\n", spaces, spaces); spaces[HANDLEN-9] = ' '; for (i = 0; i < dcc_total; i++) if (dcc[i].type == &DCC_CHAT) { if ((chan == (-1)) || ((chan >= 0) && (dcc[i].u.chat->channel == chan))) { c = geticon(i); if (c == '-') c = ' '; if (now - dcc[i].timeval > 300) { unsigned long days, hrs, mins; days = (now - dcc[i].timeval) / 86400; hrs = ((now - dcc[i].timeval) - (days * 86400)) / 3600; mins = ((now - dcc[i].timeval) - (hrs * 3600)) / 60; if (days > 0) sprintf(idle, " [idle %lud%luh]", days, hrs); else if (hrs > 0) sprintf(idle, " [idle %luh%lum]", hrs, mins); else sprintf(idle, " [idle %lum]", mins); } else idle[0] = 0; spaces[len = HANDLEN-strlen(dcc[i].nick)] = 0; spaces2[len2 = HANDLEN-strlen(botnetnick)] = 0; dprintf(idx, "%c%s%s %c %s%s %s%s\n", c, dcc[i].nick, spaces, (dcc[i].u.chat->channel == 0) && (chan == (-1)) ? '+' : (dcc[i].u.chat->channel > 100000) && (chan == (-1)) ? '*' : ' ', botnetnick, spaces2, dcc[i].host, idle); spaces[len] = ' '; spaces2[len2] = ' '; if (dcc[i].u.chat->away != NULL) dprintf(idx, " AWAY: %s\n", dcc[i].u.chat->away); } } for (i = 0; i < parties; i++) { if ((chan == (-1)) || ((chan >= 0) && (party[i].chan == chan))) { c = party[i].flag; if (c == '-') c = ' '; if (party[i].timer == 0L) strcpy(idle, " [idle?]"); else if (now - party[i].timer > 300) { unsigned long days, hrs, mins; days = (now - party[i].timer) / 86400; hrs = ((now - party[i].timer) - (days * 86400)) / 3600; mins = ((now - party[i].timer) - (hrs * 3600)) / 60; if (days > 0) sprintf(idle, " [idle %lud%luh]", days, hrs); else if (hrs > 0) sprintf(idle, " [idle %luh%lum]", hrs, mins); else sprintf(idle, " [idle %lum]", mins); } else idle[0] = 0; spaces[len = HANDLEN-strlen(party[i].nick)] = 0; spaces2[len2 = HANDLEN-strlen(party[i].bot)] = 0; dprintf(idx, "%c%s%s %c %s%s %s%s\n", c, party[i].nick, spaces, (party[i].chan == 0) && (chan == (-1)) ? '+' : ' ', party[i].bot, spaces2, party[i].from, idle); spaces[len] = ' '; spaces2[len2] = ' '; if (party[i].status & PLSTAT_AWAY) dprintf(idx, " %s: %s\n", MISC_AWAY, safe_str(party[i].away)); } } } /* show z a list of all bots connected */ void tell_bots (int idx) { char s[512]; int i; tand_t * bot; if (!tands) { dprintf(idx, "%s\n", BOT_NOBOTSLINKED); return; } strcpy(s, botnetnick); i = strlen(botnetnick); for (bot = tandbot;bot;bot = bot->next) { if (i > (500 - HANDLEN)) { dprintf(idx, "Bots: %s\n", s); s[0] = 0; i = 0; } if (i) { s[i++] = ','; s[i++] = ' '; } strcpy(s+i, bot->bot); i += strlen(bot->bot); } if (s[0]) dprintf(idx, "Bots: %s\n", s); dprintf(idx, "(%s: %d)\n", MISC_TOTAL, tands + 1); } /* show a simpleton bot tree */ void tell_bottree (int idx, int showver) { char s[161]; tand_t * last[20],* this,* bot, *bot2=NULL; int lev = 0, more = 1, mark[20], ok, cnt,i,imark; char work[1024]; int tothops = 0; if (tands == 0) { dprintf(idx, "%s\n", BOT_NOBOTSLINKED); return; } s[0] = 0; i = 0; for (bot = tandbot;bot;bot=bot->next) if (!bot->uplink) { if (i) { s[i++] = ','; s[i++] = ' '; } strcpy(s+i, bot->bot); i += strlen(bot->bot); } if (s[0]) dprintf(idx, "(%s %s)\n", BOT_NOTRACEINFO, s); if (showver) dprintf(idx, "%s (%d.%d.%d.%d)\n", botnetnick, egg_numver/1000000, egg_numver%1000000/10000, egg_numver%10000/100, egg_numver%100); else dprintf(idx, "%s\n", botnetnick); this = (tand_t *)1; work[0] = 0; while (more) { if (lev == 20) { dprintf(idx, "\n%s\n",BOT_COMPLEXTREE); return; } cnt = 0; tothops += lev; for (bot = tandbot;bot;bot=bot->next) if (bot->uplink == this) cnt++; if (cnt) { imark = 0; for (i = 0; i < lev; i++) { if (mark[i]) strcpy(work+imark, " | "); else strcpy(work+imark, " "); imark += 5; } if (cnt > 1) strcpy(work+imark, " |-"); else strcpy(work+imark, " `-"); s[0] = 0; bot = tandbot; while (!s[0]) { if (bot->uplink == this) { if (bot->ver) { i = sprintf(s, "%c%s", bot->share, bot->bot); if (showver) sprintf(s+i," (%d.%d.%d.%d)", bot->ver/1000000, bot->ver%1000000/10000, bot->ver%10000/100, bot->ver%100); } else { sprintf(s, "-%s",bot->bot); } } else bot = bot->next; } dprintf(idx, "%s%s\n", work, s); if (cnt > 1) mark[lev] = 1; else mark[lev] = 0; work[0] = 0; last[lev] = this; this = bot; lev++; more = 1; } else { while (cnt == 0) { /* no subtrees from here */ if (lev == 0) { dprintf(idx, "(( tree error ))\n"); return; } ok = 0; for (bot = tandbot;bot;bot=bot->next) { if (bot->uplink == last[lev-1]) { if (this == bot) ok = 1; else if (ok) { cnt++; if (cnt == 1) { bot2 = bot; if (bot->ver) { i = sprintf(s, "%c%s", bot->share, bot->bot); if (showver) sprintf(s+i," (%d.%d.%d.%d)", bot->ver/1000000, bot->ver%1000000/10000, bot->ver%10000/100, bot->ver%100); } else { sprintf(s,"-%s",bot->bot); } } } } } if (cnt) { imark = 0; for (i = 1; i < lev; i++) { if (mark[i - 1]) strcpy(work+imark, " | "); else strcpy(work+imark, " "); imark += 5; } more = 1; if (cnt > 1) dprintf(idx, "%s |-%s\n", work, s); else dprintf(idx, "%s `-%s\n", work, s); this = bot2; work[0] = 0; if (cnt > 1) mark[lev - 1] = 1; else mark[lev - 1] = 0; } else { /* this was the last child */ lev--; if (lev == 0) { more = 0; cnt = 999; } else { more = 1; this = last[lev]; } } } } } /* hop information: (9d) */ dprintf(idx, "Average hops: %3.1f, total bots: %d\n", ((float) tothops) / ((float) tands), tands + 1); } /* dump list of links to a new bot */ void dump_links (int z) { int i, l; char x[1024]; tand_t * bot; context; for (bot = tandbot;bot;bot=bot->next) { char * p; if (bot->uplink == (tand_t *)1) p = botnetnick; else p = bot->uplink->bot; #ifndef NO_OLD_BOTNET if (b_numver(z) < NEAT_BOTNET) l = simple_sprintf(x, "nlinked %s %s %c%d\n", bot->bot, p, bot->share, bot->ver); else #endif l = simple_sprintf(x,"n %s %s %c%D\n", bot->bot, p, bot->share, bot->ver); tputs(dcc[z].sock,x,l); } context; if (!(bot_flags(dcc[z].user) & BOT_ISOLATE)) { /* dump party line members */ for (i = 0; i < dcc_total; i++) { if (dcc[i].type == &DCC_CHAT) { if ((dcc[i].u.chat->channel >= 0) && (dcc[i].u.chat->channel < 100000)) { #ifndef NO_OLD_BOTNET if (b_numver(z) < NEAT_BOTNET) l = simple_sprintf(x, "join %s %s %d %c%d %s\n", botnetnick, dcc[i].nick, dcc[i].u.chat->channel, geticon(i), dcc[i].sock, dcc[i].host); else #endif l = simple_sprintf(x, "j !%s %s %D %c%D %s\n", botnetnick, dcc[i].nick, dcc[i].u.chat->channel, geticon(i), dcc[i].sock, dcc[i].host); tputs(dcc[z].sock, x, l); #ifndef NO_OLD_BOTNET if (b_numver(z) < NEAT_BOTNET) { if (dcc[i].u.chat->away) { l = simple_sprintf(x,"away %s %d %s\n", botnetnick, dcc[i].sock, dcc[i].u.chat->away); tputs(dcc[z].sock, x, l); } l = simple_sprintf(x,"idle %s %d %d\n", botnetnick, dcc[i].sock, now - dcc[i].timeval); } else #endif l = simple_sprintf(x,"i %s %D %D %s\n", botnetnick, dcc[i].sock, now - dcc[i].timeval, dcc[i].u.chat->away ? dcc[i].u.chat->away : ""); tputs(dcc[z].sock, x, l); } } } context; for (i = 0; i < parties; i++) { #ifndef NO_OLD_BOTNET if (b_numver(z) < NEAT_BOTNET) l = simple_sprintf(x, "join %s %s %d %c%d %s\n", party[i].bot, party[i].nick, party[i].chan, party[i].flag, party[i].sock, party[i].from); else #endif l = simple_sprintf(x, "j %s %s %D %c%D %s\n", party[i].bot, party[i].nick, party[i].chan, party[i].flag, party[i].sock, party[i].from); tputs(dcc[z].sock, x, l); if ((party[i].status & PLSTAT_AWAY) || (party[i].timer != 0)) { #ifndef NO_OLD_BOTNET if (b_numver(z) < NEAT_BOTNET) { if (party[i].status & PLSTAT_AWAY) { l = simple_sprintf(x,"away %s %d %s\n", party[i].bot, party[i].sock, party[i].away); tputs(dcc[z].sock, x, l); } l = simple_sprintf(x,"idle %s %d %d\n", party[i].bot, party[i].sock, now - party[i].timer); } else #endif l = simple_sprintf(x,"i %s %D %D %s\n", party[i].bot, party[i].sock, now - party[i].timer, party[i].away ? party[i].away : ""); tputs(dcc[z].sock, x, l); } } } context; } int in_chain (char * who) { if (findbot(who)) return 1; if (strcasecmp(who, botnetnick) == 0) return 1; return 0; } /* break link with a tandembot */ int botunlink (int idx, char * nick, char * reason) { char s[20]; int i; context; if (nick[0] == '*') dprintf(idx, "%s\n", BOT_UNLINKALL); for (i = 0; i < dcc_total; i++) { if ((nick[0] == '*') || !strcasecmp(dcc[i].nick, nick)) { if (dcc[i].type == &DCC_FORK_BOT) { if (idx >= 0) dprintf(idx, "%s: %s -> %s.\n", BOT_KILLLINKATTEMPT, dcc[i].nick, dcc[i].host); putlog(LOG_BOTS, "*", "%s: %s -> %s:%d", BOT_KILLLINKATTEMPT, dcc[i].nick, dcc[i].host, dcc[i].port); killsock(dcc[i].sock); lostdcc(i); if (nick[0] != '*') return 1; } else if (dcc[i].type == &DCC_BOT_NEW) { if (idx >= 0) dprintf(idx, "%s %s.\n", BOT_ENDLINKATTEMPT, dcc[i].nick); putlog(LOG_BOTS, "*", "%s %s @ %s:%d", "Stopped trying to link", dcc[i].nick, dcc[i].host, dcc[i].port); killsock(dcc[i].sock); lostdcc(i); if (nick[0] != '*') return 1; else i--; } else if (dcc[i].type == &DCC_BOT) { char s[1024]; context; if (idx >= 0) dprintf(idx, "%s %s.\n", BOT_BREAKLINK, dcc[i].nick); else if ((idx == -3) && (b_status(i) & STAT_SHARE) && !share_unlinks) return -1; dprintf(i, "bye\n"); if (reason && reason[0]) { simple_sprintf(s,"%s %s (%s)", BOT_UNLINKEDFROM, dcc[i].nick,reason); } else { simple_sprintf(s,"%s %s", BOT_UNLINKEDFROM, dcc[i].nick); } chatout("*** %s\n", s); botnet_send_unlinked(i, dcc[i].nick, s); killsock(dcc[i].sock); lostdcc(i); if (nick[0] != '*') return 1; else i--; } } } context; if ((idx >= 0) && (nick[0] != '*')) dprintf(idx, "%s\n", BOT_NOTCONNECTED); if (nick[0] == '*') { dprintf(idx, "%s\n", BOT_WIPEBOTTABLE); while (tandbot) rembot(tandbot->bot); while (parties) { parties--; check_tcl_chpt(party[i].bot,party[i].nick,party[i].sock, party[i].chan); } strcpy(s, "killassoc &"); Tcl_Eval(interp, s); } return 0; } /* link to another bot */ int botlink (char * linker, int idx, char * nick) { struct bot_addr * bi; struct userrec * u; int i; context; u = get_user_by_handle(userlist,nick); if (!u || !(u->flags & USER_BOT)) { if (idx >= 0) dprintf(idx, "%s %s\n", nick, BOT_BOTUNKNOWN); } else if (!strcasecmp(nick, botnetnick)) { if (idx >= 0) dprintf(idx, "%s\n", BOT_CANTLINKMYSELF); } else if (in_chain(nick) && (idx != -3)) { if (idx >= 0) dprintf(idx, "%s\n", BOT_ALREADYLINKED); } else { for (i = 0; i < dcc_total; i++) if ((dcc[i].user == u) && ((dcc[i].type == &DCC_FORK_BOT) || (dcc[i].type == &DCC_BOT_NEW))) { if (idx >= 0) dprintf(idx,"%s\n", BOT_ALREADYLINKING); return 0; } /* address to connect to is in 'info' */ bi = (struct bot_addr *)get_user(&USERENTRY_BOTADDR,u); if (!bi) { if (idx >= 0) { dprintf(idx, "%s '%s'.\n", BOT_NOTELNETADDY, nick); dprintf(idx, "%s .chaddr %s %s\n", MISC_USEFORMAT, nick, MISC_CHADDRFORMAT); } } else if (dcc_total == max_dcc) { if (idx >= 0) dprintf(idx, "%s\n", DCC_TOOMANYDCCS1); } else { context; correct_handle(nick); i = new_dcc (&DCC_FORK_BOT,sizeof(struct bot_info)); dcc[i].port = bi->telnet_port; strcpy(dcc[i].nick, nick); strcpy(dcc[i].host, bi->address); dcc[i].timeval = now; strcpy(dcc[i].u.bot->linker, linker); strcpy(dcc[i].u.bot->version, "(primitive bot)"); if (idx > -2) putlog(LOG_BOTS, "*", "%s %s at %s:%d ...", BOT_LINKING, nick, bi->address, bi->telnet_port); dcc[i].u.bot->numver = idx; dcc[i].timeval = now; dcc[i].u.bot->port = dcc[i].port; /* remember where i started */ dcc[i].sock = getsock(SOCK_STRONGCONN); dcc[i].user = u; if (open_telnet_raw(dcc[i].sock, bi->address, dcc[i].port) >= 0) return 1; failed_link(i); } } return 0; } static void failed_tandem_relay (int idx) { int uidx = (-1), i; context; for (i = 0; i < dcc_total; i++) if ((dcc[i].type == &DCC_PRE_RELAY) && (dcc[i].u.relay->sock == dcc[idx].sock)) uidx = i; if (uidx < 0) { putlog(LOG_MISC, "*", "%s %d -> %d", BOT_CANTFINDRELAYUSER, dcc[idx].sock, dcc[idx].u.relay->sock); killsock(dcc[idx].sock); lostdcc(idx); return; } if (dcc[idx].port >= dcc[idx].u.relay->port + 3) { struct chat_info *ci = dcc[uidx].u.relay->chat; dprintf(uidx, "%s %s.\n", BOT_CANTLINKTO, dcc[idx].nick); nfree(dcc[uidx].u.relay); dcc[uidx].u.chat = ci; dcc[uidx].type = &DCC_CHAT; dcc[uidx].status = dcc[uidx].u.relay->old_status; killsock(dcc[idx].sock); lostdcc(idx); return; } killsock(dcc[idx].sock); dcc[idx].sock = getsock(SOCK_STRONGCONN); dcc[uidx].u.relay->sock = dcc[idx].sock; dcc[idx].port++; dcc[idx].timeval = now; if (open_telnet_raw(dcc[idx].sock, dcc[idx].host, dcc[idx].port) < 0) failed_tandem_relay(idx); } /* relay to another tandembot */ void tandem_relay (int idx, char * nick,int i) { struct chat_info *ci; struct userrec * u; struct bot_addr * bi; context; u = get_user_by_handle(userlist,nick); if (!u || !(u->flags & USER_BOT)) { dprintf(idx, "%s %s\n", nick, BOT_BOTUNKNOWN); return; } if (strcasecmp(nick, botnetnick) == 0) { dprintf(idx, "%s\n", BOT_CANTRELAYMYSELF); return; } /* address to connect to is in 'info' */ bi = (struct bot_addr *)get_user(&USERENTRY_BOTADDR,u); if (!bi) { dprintf(idx, "%s '%s'.\n", BOT_NOTELNETADDY, nick); dprintf(idx, "%s .chaddr %s %s\n", MISC_USEFORMAT, nick, MISC_CHADDRFORMAT); return; } if (dcc_total == max_dcc) { dprintf(idx, "%s\n", DCC_TOOMANYDCCS1); return; } i = new_dcc(&DCC_FORK_RELAY,sizeof(struct relay_info)); dcc[i].u.relay->chat = get_data_ptr(sizeof(struct chat_info)); dcc[i].port = bi->relay_port; dcc[i].u.relay->port = dcc[i].port; dcc[i].addr = 0L; strcpy(dcc[i].nick, nick); dcc[i].user = u; strcpy(dcc[i].host, bi->address); dcc[i].u.relay->chat->away = NULL; dcc[i].u.relay->old_status = dcc[i].status; dcc[i].status = 0; dcc[i].timeval = now; dcc[i].u.relay->chat->msgs_per_sec = 0; dcc[i].u.relay->chat->con_flags = 0; dcc[i].u.relay->chat->buffer = NULL; dcc[i].u.relay->chat->max_line = 0; dcc[i].u.relay->chat->line_count = 0; dcc[i].u.relay->chat->current_lines = 0; dprintf(idx, "%s %s @ %s:%d ...\n", BOT_CONNECTINGTO, nick, bi->address, bi->relay_port); dprintf(idx, "%s\n", BOT_BYEINFO1); ci = dcc[idx].u.chat; dcc[idx].u.relay = get_data_ptr(sizeof(struct relay_info)); dcc[idx].u.relay->chat = ci; dcc[idx].type = &DCC_PRE_RELAY; dcc[i].sock = getsock(SOCK_STRONGCONN); dcc[idx].u.relay->sock = dcc[i].sock; dcc[i].u.relay->sock = dcc[idx].sock; dcc[i].timeval = now; if (open_telnet_raw(dcc[i].sock, dcc[i].host, dcc[i].port) < 0) failed_tandem_relay(i); } /* input from user before connect is ready */ static void pre_relay (int idx, char * buf,int i) { int tidx = (-1); context; for (i = 0; i < dcc_total; i++) if ((dcc[i].type == &DCC_FORK_RELAY) && (dcc[i].u.relay->sock == dcc[idx].sock)) tidx = i; if (tidx < 0) { putlog(LOG_MISC, "*", "%s %d -> %d", BOT_CANTFINDRELAYUSER, dcc[i].sock, dcc[i].u.relay->sock); killsock(dcc[i].sock); lostdcc(i); return; } context; if (strcasecmp(buf, "*bye*") == 0) { /* disconnect */ struct chat_info *ci = dcc[idx].u.relay->chat; dprintf(idx, "%s %s.\n", BOT_ABORTRELAY1, dcc[tidx].nick); dprintf(idx, "%s %s.\n\n", BOT_ABORTRELAY2, botnetnick); putlog(LOG_MISC, "*", "%s %s -> %s", BOT_ABORTRELAY3, dcc[idx].nick, dcc[tidx].nick); nfree(dcc[idx].u.relay); dcc[idx].u.chat = ci; dcc[idx].status = dcc[idx].u.relay->old_status; dcc[idx].type = &DCC_CHAT; killsock(dcc[tidx].sock); lostdcc(tidx); return; } context; } /* user disconnected before her relay had finished connecting */ static void failed_pre_relay (int idx) { int tidx = (-1), i; context; for (i = 0; i < dcc_total; i++) if ((dcc[i].type == &DCC_FORK_RELAY) && (dcc[i].u.relay->sock == dcc[idx].sock)) tidx = i; if (tidx < 0) { putlog(LOG_MISC, "*", "%s %d -> %d", BOT_CANTFINDRELAYUSER, dcc[i].sock, dcc[i].u.relay->sock); killsock(dcc[i].sock); lostdcc(i); return; } putlog(LOG_MISC, "*", "%s [%s]%s/%d", BOT_LOSTDCCUSER, dcc[idx].nick, dcc[idx].host, dcc[idx].port); putlog(LOG_MISC, "*", "(%s %s)", BOT_DROPPINGRELAY, dcc[tidx].nick); if ((dcc[tidx].sock != STDOUT) || backgrd) { if (idx > tidx) { int t = tidx; tidx = idx; idx = t; } killsock(dcc[tidx].sock); lostdcc(tidx); } else { fatal("Lost my terminal??!?!?!",0); } killsock(dcc[idx].sock); lostdcc(idx); } static void cont_tandem_relay (int idx,char * buf,int i) { int uidx = (-1); struct relay_info * ri; context; for (i = 0; i < dcc_total; i++) if ((dcc[i].type == &DCC_PRE_RELAY) && (dcc[i].u.relay->sock == dcc[idx].sock)) uidx = i; if (uidx < 0) { putlog(LOG_MISC, "*", "%s %d -> %d", BOT_CANTFINDRELAYUSER, dcc[i].sock, dcc[i].u.relay->sock); killsock(dcc[i].sock); lostdcc(i); return; } dcc[idx].type = &DCC_RELAY; dcc[idx].u.relay->sock = dcc[uidx].sock; dcc[uidx].u.relay->sock = dcc[idx].sock; dprintf(uidx, "%s %s ...\n", BOT_RELAYSUCCESS, dcc[idx].nick); dprintf(uidx, "%s\n\n", BOT_BYEINFO2); putlog(LOG_MISC, "*", "%s %s -> %s", BOT_RELAYLINK, dcc[uidx].nick, dcc[idx].nick); ri = dcc[uidx].u.relay; /* YAEH */ dcc[uidx].type = &DCC_CHAT; dcc[uidx].u.chat = ri->chat; if (dcc[uidx].u.chat->channel >= 0) { chanout_but(-1,dcc[uidx].u.chat->channel, "*** %s %s\n", dcc[uidx].nick, BOT_PARTYLEFT); if (dcc[uidx].u.chat->channel < 100000) botnet_send_part_idx(uidx, NULL); check_tcl_chpt(botnetnick, dcc[uidx].nick, dcc[uidx].sock, dcc[uidx].u.chat->channel); } check_tcl_chof(dcc[uidx].nick, dcc[uidx].sock); dcc[uidx].type = &DCC_RELAYING; dcc[uidx].u.relay = ri; } static void eof_dcc_relay (int idx) { int j; struct chat_info *ci; for (j = 0; dcc[j].sock != dcc[idx].u.relay->sock; j++); /* in case echo was off, turn it back on: */ if (dcc[j].status & STAT_TELNET) dprintf(j, "\377\374\001\r\n"); putlog(LOG_MISC, "*", "%s: %s -> %s", BOT_ENDRELAY1, dcc[j].nick, dcc[idx].nick); dprintf(j, "\n\n*** %s %s\n", BOT_ENDRELAY2, botnetnick); ci = dcc[j].u.relay->chat; nfree(dcc[j].u.relay); dcc[j].u.chat = ci; dcc[j].type = &DCC_CHAT; if (dcc[j].u.chat->channel >= 0) { chanout_but(-1,dcc[j].u.chat->channel, "*** %s %s.\n", dcc[j].nick, BOT_PARTYREJOINED); context; if (dcc[j].u.chat->channel < 100000) botnet_send_join_idx(j,-1); } dcc[j].status = dcc[j].u.relay->old_status; check_tcl_chon(dcc[j].nick, dcc[j].sock); check_tcl_chjn(botnetnick, dcc[j].nick, dcc[j].u.chat->channel, geticon(j), dcc[j].sock, dcc[j].host); killsock(dcc[idx].sock); lostdcc(idx); } static void eof_dcc_relaying (int idx) { int j, x = dcc[idx].u.relay->sock; putlog(LOG_MISC, "*", "%s [%s]%s/%d", BOT_LOSTDCCUSER, dcc[idx].nick, dcc[idx].host, dcc[idx].port); killsock(dcc[idx].sock); lostdcc(idx); for (j = 0; (dcc[j].sock != x) || (dcc[j].type == &DCC_FORK_RELAY) || (dcc[j].type == &DCC_LOST); j++); putlog(LOG_MISC, "*", "(%s %s)", BOT_DROPPEDRELAY, dcc[j].nick); killsock(dcc[j].sock); lostdcc(j); /* drop connection to the bot */ } static void dcc_relay (int idx, char * buf,int j) { unsigned char *p = (unsigned char *) buf; int mark; for (j = 0; (dcc[j].sock != dcc[idx].u.relay->sock) || (dcc[j].type != &DCC_RELAYING); j++); /* if redirecting to a non-telnet user, swallow telnet codes */ if (!(dcc[j].status & STAT_TELNET)) { while (*p != 0) { while ((*p != 255) && (*p != 0)) p++; /* search for IAC */ if (*p == 255) { mark = 2; if (!*(p + 1)) mark = 1; /* bogus */ if ((*(p + 1) >= 251) || (*(p + 1) <= 254)) { mark = 3; if (!*(p + 2)) mark = 2; /* bogus */ } strcpy((char *) p, (char *) (p + mark)); } } if (!buf[0]) dprintf(-dcc[idx].u.relay->sock, " \n"); else dprintf(-dcc[idx].u.relay->sock, "%s\n", buf); return; } /* telnet user */ if (!buf[0]) dprintf(-dcc[idx].u.relay->sock, " \r\n"); else dprintf(-dcc[idx].u.relay->sock, "%s\r\n", buf); } static void dcc_relaying (int idx, char * buf,int j) { struct chat_info *ci; if (strcasecmp(buf, "*BYE*") != 0) { dprintf(-dcc[idx].u.relay->sock, "%s\n", buf); return; } for (j = 0; (dcc[j].sock != dcc[idx].u.relay->sock) || (dcc[j].type != &DCC_RELAY); j++); /* in case echo was off, turn it back on: */ if (dcc[idx].status & STAT_TELNET) dprintf(idx, "\377\374\001\r\n"); dprintf(idx, "\n(%s %s.)\n", BOT_BREAKRELAY, dcc[j].nick); dprintf(idx, "%s %s.\n\n", BOT_ABORTRELAY2, botnetnick); putlog(LOG_MISC, "*", "%s: %s -> %s", BOT_RELAYBROKEN, dcc[idx].nick, dcc[j].nick); if (dcc[idx].u.relay->chat->channel >= 0) { chanout_but(-1,dcc[idx].u.relay->chat->channel, "*** %s joined the party line.\n", dcc[idx].nick); context; if (dcc[idx].u.relay->chat->channel < 100000) botnet_send_join_idx(idx,-1); } ci = dcc[idx].u.relay->chat; nfree(dcc[idx].u.relay); dcc[idx].u.chat = ci; dcc[idx].type = &DCC_CHAT; dcc[idx].status = dcc[idx].u.relay->old_status; check_tcl_chon(dcc[idx].nick,dcc[idx].sock); if (dcc[idx].u.chat->channel >= 0) check_tcl_chjn(botnetnick, dcc[idx].nick, dcc[idx].u.chat->channel, geticon(idx), dcc[idx].sock, dcc[idx].host); killsock(dcc[j].sock); lostdcc(j); } static void display_relay (int i,char * other) { sprintf(other,"rela -> sock %d", dcc[i].u.relay->sock); } static void display_relaying (int i,char * other) { sprintf(other,">rly -> sock %d", dcc[i].u.relay->sock); } static void display_tandem_relay (int i,char * other) { strcpy(other,"other rela"); } static void display_pre_relay (int i,char * other) { strcpy(other,"other >rly"); } static int expmem_relay (void * x) { register struct relay_info * p = (struct relay_info *)x; int tot = sizeof(struct relay_info); if (p->chat) tot += DCC_CHAT.expmem(p->chat); return tot; } static void kill_relay (int idx, void * x) { register struct relay_info * p = (struct relay_info *)x; if (p->chat) DCC_CHAT.kill(idx,p->chat); nfree(p); } struct dcc_table DCC_RELAY = { "RELAY", 0, /* flags */ eof_dcc_relay, dcc_relay, 0, 0, display_relay, expmem_relay, kill_relay, 0 }; static void out_relay (int idx,char * buf, void * x) { register struct relay_info * p = (struct relay_info *)x; if (p && p->chat) DCC_CHAT.output(idx,buf,p->chat); else tputs(dcc[idx].sock,buf,strlen(buf)); } struct dcc_table DCC_RELAYING = { "RELAYING", 0, /* flags */ eof_dcc_relaying, dcc_relaying, 0, 0, display_relaying, expmem_relay, kill_relay, out_relay }; struct dcc_table DCC_FORK_RELAY = { "FORK_RELAY", 0, /* flags */ failed_tandem_relay, cont_tandem_relay, & connect_timeout, failed_tandem_relay, display_tandem_relay, expmem_relay, kill_relay, 0 }; struct dcc_table DCC_PRE_RELAY = { "PRE_RELAY", 0, /* flags */ failed_pre_relay, pre_relay, 0, 0, display_pre_relay, expmem_relay, kill_relay, 0 }; /* once a minute, send 'ping' to each bot -- no exceptions */ void check_botnet_pings() { int i; context; for (i = 0; i < dcc_total; i++) if (dcc[i].type == &DCC_BOT) if (dcc[i].status & STAT_PINGED) { char s[1024]; putlog(LOG_BOTS, "*", "%s: %s", BOT_PINGTIMEOUT, dcc[i].nick); simple_sprintf(s,"%s: %s", BOT_PINGTIMEOUT, dcc[i].nick); chatout("*** %s\n", s); botnet_send_unlinked(i, dcc[i].nick, s); killsock(dcc[i].sock); lostdcc(i); } for (i = 0; i < dcc_total; i++) if (dcc[i].type == &DCC_BOT) { botnet_send_ping(i); dcc[i].status |= STAT_PINGED; } for (i = 0; i < dcc_total; i++) if ((dcc[i].type == &DCC_BOT) && (dcc[i].status & STAT_LEAF)) { tand_t * bot, * via = findbot(dcc[i].nick); for (bot = tandbot; bot; bot=bot->next) { if ((via == bot->via) && (bot != via)) { /* not leaflike behavior */ if (dcc[i].status & STAT_WARNED) { char s[1024]; putlog(LOG_BOTS, "*", "%s %s (%s).", BOT_DISCONNECTED, dcc[i].nick, BOT_BOTNOTLEAFLIKE); dprintf(i, "bye\n"); simple_sprintf(s,"%s %s (%s)",BOT_DISCONNECTED,dcc[i].nick, BOT_BOTNOTLEAFLIKE); chatout("*** %s\n",s); botnet_send_unlinked(i, dcc[i].nick, s); killsock(dcc[i].sock); lostdcc(i); } else { botnet_send_reject(i, botnetnick, NULL, bot->bot, NULL, NULL); dcc[i].status |= STAT_WARNED; } } else dcc[i].status &= ~STAT_WARNED; } } context; } void zapfbot (int idx) { char s[1024]; simple_sprintf(s,"%s: %s", BOT_BOTDROPPED, dcc[idx].nick); chatout("*** %s\n", s); botnet_send_unlinked(idx, dcc[idx].nick, s); killsock(dcc[idx].sock); dcc[idx].sock = (long)dcc[idx].type; dcc[idx].type = &DCC_LOST; } void restart_chons() { int i; /* dump party line members */ context; for (i = 0; i < dcc_total; i++) { if (dcc[i].type == &DCC_CHAT) { check_tcl_chon(dcc[i].nick, dcc[i].sock); check_tcl_chjn(botnetnick, dcc[i].nick, dcc[i].u.chat->channel, geticon(i), dcc[i].sock, dcc[i].host); } } for (i = 0; i < parties; i++) { check_tcl_chjn(party[i].bot, party[i].nick, party[i].chan, party[i].flag, party[i].sock, party[i].from); } context; }