energymech/src/net.c
2018-04-24 18:28:10 +02:00

1791 lines
35 KiB
C

/*
EnergyMech, IRC bot software
Copyright (c) 1997-2018 proton
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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#define NET_C
#include "config.h"
#ifdef BOTNET
#include "defines.h"
#include "structs.h"
#include "global.h"
#include "h.h"
#include "text.h"
#include "mcmd.h"
#if defined(SHACRYPT) || defined(MD5CRYPT)
char *CRYPT_FUNC(const char *, const char *);
#endif
const char banneropt[] = "BB%i %i PTA"
#ifdef SHACRYPT
" SHA"
#endif /* SHACRYPT */
#ifdef MD5CRYPT
" MD5"
#endif /* MD5CRYPT */
"\n";
#ifdef TELNET
char *telnetprompt = TEXT_ENTERNICKNAME;
#endif /* TELNET */
/*
* this is a partial copy of the BotNet struct
*/
LS struct
{
struct BotNet *next;
int sock;
int status;
int has_data;
} linksock = { NULL, -1, BN_LINKSOCK, 0 };
/*
* command lists
*/
typedef struct LinkCmd
{
char cmd[2];
void (*func)(BotNet *, char *);
int relay;
} LinkCmd;
#define RELAY_YES 1
#define RELAY_NO 0
LS const LinkCmd basicProto[] =
{
{ "BA", basicAuth, RELAY_NO },
{ "BB", basicBanner, RELAY_NO },
{ "BK", basicAuthOK, RELAY_NO },
{ "BL", basicLink, RELAY_NO },
{ "BQ", basicQuit, RELAY_NO },
{ "CO", netchanNeedop, RELAY_YES },
#ifdef SUPPRESS
{ "CS", netchanSuppress, RELAY_YES }, /* experimental command supression */
#endif /* SUPPRESS */
{ "PA", partyAuth, RELAY_YES },
#ifdef REDIRECT
{ "PC", partyCommand, RELAY_NO },
#endif /* REDIRECT */
{ "PM", partyMessage, RELAY_NO },
{ "UT", ushareTick, RELAY_NO },
{ "UU", ushareUser, RELAY_NO },
{ "UD", ushareDelete, RELAY_YES },
{ "\0\0", NULL, RELAY_NO },
};
LS int deadlinks = FALSE;
/*
*
* misc
*
*/
Mech *get_netbot(void) /*get local bot with the lowes guid to act as local master */
{
Mech *netbot,*bot;
int uid;
netbot = NULL;
uid = INT_MAX;
for(bot=botlist;bot;bot=bot->next)
{
if (bot->guid < uid)
{
uid = bot->guid;
netbot = bot;
}
}
return(netbot);
}
void reset_linkable(int guid)
{
NetCfg *cfg;
for(cfg=netcfglist;cfg;cfg=cfg->next)
{
if (cfg->guid == guid)
{
#ifdef DEBUG
debug("(reset_linkable) guid %i reset to linkable\n",guid);
#endif /* DEBUG */
cfg->linked = FALSE;
return;
}
}
}
void botnet_deaduplink(BotNet *bn)
{
bn->status = BN_DEAD;
deadlinks = TRUE;
reset_linkable(bn->guid);
}
NetCfg *find_netcfg(int guid)
{
NetCfg *cfg;
for(cfg=netcfglist;cfg;cfg=cfg->next)
{
if (cfg->guid == guid)
return(cfg);
}
return(NULL);
}
BotInfo *make_botinfo(int guid, int hops, char *nuh, char *server, char *version)
{
BotInfo *new;
set_mallocdoer(make_botinfo);
new = (BotInfo*)Calloc(sizeof(BotInfo) + StrlenX(nuh,server,version,NULL));
new->guid = guid;
new->hops = hops;
new->server = stringcat(new->nuh,nuh) + 1;
new->version = stringcat(new->server,server) + 1;
stringcpy(new->version,version);
return(new);
}
void botnet_relay(BotNet *source, char *format, ...)
{
BotNet *bn;
va_list msg;
int sz = 0;
for(bn=botnetlist;bn;bn=bn->next)
{
if ((bn == source) || (bn->status != BN_LINKED))
continue;
if (!sz)
{
va_start(msg,format);
vsprintf(globaldata,format,msg);
va_end(msg);
sz = strlen(globaldata);
}
if (write(bn->sock,globaldata,sz) < 0)
botnet_deaduplink(bn);
#ifdef DEBUG
debug("[bnr] {%i} %s",bn->sock,globaldata);
#endif /* DEBUG */
}
}
void botnet_refreshbotinfo(void)
{
Server *sv;
sv = find_server(current->server);
botnet_relay(NULL,"BL%i 0 %s!%s %s:%i %s %s\n", current->guid,current->nick,
(current->userhost) ? current->userhost : UNKNOWNATUNKNOWN,
(sv) ? ((*sv->realname) ? sv->realname : sv->name) : UNKNOWN,
(sv) ? sv->port : 0,BOTCLASS,VERSION);
#ifdef DEBUG
debug("(botnet_refreshbotinfo) sent refreshed information to botnet\n");
#endif /* DEBUG */
}
void botnet_binfo_relay(BotNet *source, BotInfo *binfo)
{
botnet_relay(source,"BL%i %i %s %s %s\n",binfo->guid,(binfo->hops + 1),
(binfo->nuh) ? binfo->nuh : UNKNOWNATUNKNOWN,
(binfo->server) ? binfo->server : UNKNOWN,
(binfo->version) ? binfo->version : "-");
}
void botnet_binfo_tofile(int sock, BotInfo *binfo)
{
to_file(sock,"BL%i %i %s %s %s\n",binfo->guid,(binfo->hops + 1),
(binfo->nuh) ? binfo->nuh : UNKNOWNATUNKNOWN,
(binfo->server) ? binfo->server : UNKNOWN,
(binfo->version) ? binfo->version : "-");
}
void botnet_dumplinklist(BotNet *bn)
{
BotInfo *binfo;
BotNet *bn2;
Mech *bot;
Server *sv;
/*
* send link lines
*/
for(bot=botlist;bot;bot=bot->next)
{
/*
* tell the other side who the local bots are
*/
sv = find_server(bot->server);
to_file(bn->sock,"BL%i %c %s!%s %s:%i %s %s\n",bot->guid,
(bot == bn->controller) ? '0' : '1',bot->nick,
(bot->userhost) ? bot->userhost : UNKNOWNATUNKNOWN,
(sv) ? ((*sv->realname) ? sv->realname : sv->name) : UNKNOWN,
(sv) ? sv->port : 0,BOTCLASS,VERSION);
}
for(bn2=botnetlist;bn2;bn2=bn2->next)
{
if ((bn2 == bn) || (bn2->status != BN_LINKED) || !(bn2->list_complete))
continue;
for(binfo=bn2->botinfo;binfo;binfo=binfo->next)
botnet_binfo_tofile(bn->sock,binfo);
}
/*
* tell remote end to sync
*/
to_file(bn->sock,"BL-\n");
}
int connect_to_bot(NetCfg *cfg)
{
BotNet *bn;
int s;
#ifdef DEBUG
debug("(connect_to_bot) NetCfg* "mx_pfmt" = { \"%s\", guid = %i, port = %i, ... }\n",
(mx_ptr)cfg,nullstr(cfg->host),cfg->guid,cfg->port);
#endif /* DEBUG */
if ((s = SockConnect(cfg->host,cfg->port,FALSE)) < 0)
return(-1);
/*
* set linked status
*/
cfg->linked = TRUE;
set_mallocdoer(connect_to_bot);
bn = (BotNet*)Calloc(sizeof(BotNet));
bn->sock = s;
bn->status = BN_CONNECT;
bn->when = now;
bn->guid = cfg->guid;
bn->next = botnetlist;
botnetlist = bn;
return(0);
}
void check_botjoin(Chan *chan, ChanUser *cu)
{
BotNet *bn;
BotInfo *binfo;
#ifdef DEBUG
debug("(check_botjoin) chan = %s; cu = %s!%s\n",chan->name,cu->nick,cu->userhost);
#endif /* DEBUG */
for(bn=botnetlist;bn;bn=bn->next)
{
if (bn->status != BN_LINKED)
continue;
for(binfo=bn->botinfo;binfo;binfo=binfo->next)
{
if (!nickcmp(cu->nick,binfo->nuh) &&
!stringcasecmp(cu->userhost,getuh(binfo->nuh)))
{
if ((cu = find_chanbot(chan,binfo->nuh)) == NULL)
return;
cu->flags |= CU_NEEDOP;
send_mode(chan,50,QM_CHANUSER,'+','o',(void*)cu);
#ifdef DEBUG
debug("(check_botjoin) CU_NEEDOP set, mode pushed\n");
#endif /* DEBUG */
return;
}
}
}
}
void check_botinfo(BotInfo *binfo, const char *channel)
{
Chan *chan;
ChanUser *cu;
Mech *backup;
char *userhost;
userhost = getuh(binfo->nuh);
backup = current;
for(current=botlist;current;current=current->next)
{
for(chan=current->chanlist;chan;chan=chan->next)
{
if (channel && stringcasecmp(channel,chan->name))
continue;
if ((cu = find_chanbot(chan,binfo->nuh)) == NULL)
continue;
if (!stringcasecmp(cu->userhost,userhost))
{
cu->flags |= CU_NEEDOP;
send_mode(chan,50,QM_CHANUSER,'+','o',(void*)cu);
}
}
}
current = backup;
}
/*
*
* protocol routines
*
*/
void basicAuth(BotNet *bn, char *rest)
{
NetCfg *cfg;
char *pass;
int authtype = -1;
if (bn->status != BN_WAITAUTH)
return;
if ((pass = chop(&rest)))
{
if (!stringcmp(pass,"PTA"))
authtype = BNAUTH_PLAINTEXT;
#ifdef SHACRYPT
if (!stringcmp(pass,"SHA"))
authtype = BNAUTH_SHA;
#endif /* SHACRYPT */
#ifdef MD5CRYPT
if (!stringcmp(pass,"MD5"))
authtype = BNAUTH_MD5;
#endif /* MD5CRYPT */
}
/*
* find the other bots' password so we can check if it matches
*/
pass = NULL;
for(cfg=netcfglist;cfg;cfg=cfg->next)
{
if (cfg->guid == bn->guid)
{
pass = cfg->pass;
break;
}
}
if (!pass || !*pass)
goto badpass;
if (linkpass == NULL || *linkpass == 0)
goto badpass;
switch(authtype)
{
case BNAUTH_PLAINTEXT:
/*
>> plain text given: "DomoOmiGato" stored "kooplook0988"
(reset_linkable) guid 1337 reset to linkable
(basicAuth) bad password [ guid = 1337 ]
*/
#ifdef DEBUG
debug(">> plain text given: \"%s\" stored \"%s\"\n",pass,rest);
#endif /* DEBUG */
if (stringcmp(pass,rest))
goto badpass;
break;
#ifdef SHACRYPT
case BNAUTH_SHA:
{
char *enc,temppass[24 + Strlen2(pass,linkpass)]; /* linkpass is never NULL */
/* "mypass theirpass REMOTEsid LOCALsid" */
sprintf(temppass,"%s %s %i %i",linkpass,pass,bn->rsid,bn->lsid);
#ifdef DEBUG
debug(">> sha pass exchange: \"%s\"\n",temppass);
#endif /* DEBUG */
enc = CRYPT_FUNC(temppass,rest);
#ifdef DEBUG
debug("(basicAuth) their = %s, mypass = %s :: sha = %s\n",
pass,linkpass,enc);
#endif /* DEBUG */
if (!stringcmp(enc,rest))
break;
}
#endif /* SHACRYPT */
#ifdef MD5CRYPT
case BNAUTH_MD5:
{
char *enc,temppass[24 + Strlen2(pass,linkpass)]; /* linkpass is never NULL */
/* "mypass theirpass REMOTEsid LOCALsid" */
sprintf(temppass,"%s %s %i %i",linkpass,pass,bn->rsid,bn->lsid);
#ifdef DEBUG
debug(">> md5 pass exchange: \"%s\"\n",temppass);
#endif /* DEBUG */
enc = CRYPT_FUNC(temppass,rest);
#ifdef DEBUG
debug("(basicAuth) their = %s, mypass = %s :: md5 = %s\n",
pass,linkpass,enc);
#endif /* DEBUG */
if (!stringcmp(enc,rest))
break;
}
#endif /* MD5CRYPT */
default:
badpass:
/*
* we can/should use deaduplink here since we set cfg->linked = TRUE in basicBanner()
*/
botnet_deaduplink(bn);
#ifdef DEBUG
debug("(basicAuth) bad password [ guid = %i ]\n",bn->guid);
#endif /* DEBUG */
return;
}
to_file(bn->sock,"BK\n");
bn->status = BN_LINKED;
botnet_dumplinklist(bn);
#ifdef DEBUG
debug("(basicAuth) bn->tick = 0\n");
#endif /* DEBUG */
bn->tick = 0;
bn->tick_last = now - 580; /* 10 minutes (10*60) - 20 seconds */
}
void basicAuthOK(BotNet *bn, char *rest)
{
if (bn->status != BN_WAITLINK)
return;
bn->status = BN_LINKED;
botnet_dumplinklist(bn);
#ifdef DEBUG
debug("(basicAuthOK) bn->tick = 0\n");
#endif /* DEBUG */
bn->tick = 0;
bn->tick_last = now - 580; /* 10 minutes (10*60) - 20 seconds */
}
void basicBanner(BotNet *bn, char *rest)
{
Mech *netbot;
NetCfg *cfg;
char *p;
int guid;
int authtype = -1;
/*
* we're only prepared to listen to banners in the first connection phase
*/
if (bn->status != BN_BANNERSENT && bn->status != BN_UNKNOWN)
return;
/*
* find out who's calling
*/
p = chop(&rest);
guid = asc2int(p);
/*
* bad guid received
*/
if (errno)
{
if (bn->guid)
{
/*
* we know who its supposed to be, ergo sum, we've set cfg->linked = TRUE, lets undo that
*/
reset_linkable(bn->guid);
}
bn->status = BN_DEAD;
deadlinks = TRUE;
return;
}
if (bn->guid && bn->guid != guid)
{
/*
* its not who we expected!
*/
#ifdef DEBUG
debug("(basicBanner) {%i} calling guid %i but got guid %i!\n",
bn->sock,bn->guid,guid);
#endif /* DEBUG */
botnet_deaduplink(bn);
return;
}
/*
* either (bn->guid == 0) || (bn->guid == guid), we dont need to check
*/
bn->guid = guid;
/*
* prevent circular linking
*/
if (bn->status == BN_UNKNOWN)
{
/*
* they are connecting to us
*/
if ((cfg = find_netcfg(guid)))
{
if (cfg->linked)
{
/*
* we already think this remote bot is connected and it's still connecting to us!
*/
bn->status = BN_DEAD;
deadlinks = TRUE;
#ifdef DEBUG
debug("(basicBanner) {%i} guid %i (connecting to us) is already linked!\n",
bn->sock,bn->guid);
#endif /* DEBUG */
return;
}
/*
* its not linked? well it is now!
*/
cfg->linked = TRUE;
}
}
/*
* get a session number
*/
p = chop(&rest);
bn->rsid = asc2int(p);
if (errno)
{
botnet_deaduplink(bn);
return;
}
/*
* parse banner options
*/
while((p = chop(&rest)))
{
if (!stringcmp(p,"PTA"))
bn->opt.pta = TRUE;
#ifdef SHACRYPT
if (!stringcmp(p,"SHA"))
bn->opt.sha = TRUE;
#endif /* SHACRYPT */
#ifdef MD5CRYPT
if (!stringcmp(p,"MD5"))
bn->opt.md5 = TRUE;
#endif /* MD5CRYPT */
}
/*
* update timestamp
*/
bn->when = now;
/*
* if the remote bot initiated the connection we need a valid pass from them
* before we send our own password to validate
*/
if (bn->status == BN_UNKNOWN)
{
bn->controller = netbot = get_netbot();
to_file(bn->sock,banneropt,netbot->guid,bn->lsid);
bn->status = BN_WAITAUTH;
return;
}
/*
* we're the one that initiated the connection, we now send our password
*/
if (!linkpass || !*linkpass)
{
botnet_deaduplink(bn);
return;
}
/*
* select authentication method
*/
if (bn->opt.pta && (BNAUTH_PLAINTEXT > authtype))
authtype = BNAUTH_PLAINTEXT;
#ifdef MD5CRYPT
if (bn->opt.md5 && (BNAUTH_MD5 > authtype))
authtype = BNAUTH_MD5;
#endif /* MD5CRYPT */
#ifdef SHACRYPT
if (bn->opt.sha && (BNAUTH_SHA > authtype))
authtype = BNAUTH_SHA;
#endif /* SHACRYPT */
switch(authtype)
{
case BNAUTH_PLAINTEXT:
to_file(bn->sock,"BAPTA %s\n",linkpass);
break;
#ifdef SHACRYPT
case BNAUTH_SHA:
if ((cfg = find_netcfg(guid)))
{
if (cfg->pass && *cfg->pass)
{
char *enc,salt[8];
char temppass[24 + Strlen2(cfg->pass,linkpass)]; /* linkpass(procvar) is not NULL */
/* "theirpass mypass LOCALsid REMOTEsid" */
sprintf(temppass,"%s %s %i %i",cfg->pass,linkpass,bn->lsid,bn->rsid);
#ifdef DEBUG
debug(">> sha pass exchange: \"%s\"\n",temppass);
#endif /* DEBUG */
sprintf(salt,"$6$%04x",(rand() >> 16));
enc = CRYPT_FUNC(temppass,salt);
to_file(bn->sock,"BASHA %s\n",enc);
break;
}
}
#endif /* SHACRYPT */
#ifdef MD5CRYPT
case BNAUTH_MD5:
if ((cfg = find_netcfg(guid)))
{
if (cfg->pass && *cfg->pass)
{
char *enc,salt[8];
char temppass[24 + Strlen2(cfg->pass,linkpass)]; /* linkpass(procvar) is not NULL */
/* "theirpass mypass LOCALsid REMOTEsid" */
sprintf(temppass,"%s %s %i %i",cfg->pass,linkpass,bn->lsid,bn->rsid);
#ifdef DEBUG
debug(">> md5 pass exchange: \"%s\"\n",temppass);
#endif /* DEBUG */
sprintf(salt,"$1$%04x",(rand() >> 16));
enc = CRYPT_FUNC(temppass,salt);
to_file(bn->sock,"BAMD5 %s\n",enc);
break;
}
}
#endif /* MD5CRYPT */
default:
botnet_deaduplink(bn);
return;
}
bn->status = BN_WAITLINK;
}
void basicLink(BotNet *bn, char *version)
{
BotInfo *binfo,*delete,**pp;
NetCfg *cfg;
char *nuh,*server;
int guid,hops;
if (bn->status != BN_LINKED)
return;
/*
* BL-
*/
if (*version == '-')
{
/*
* check all links for conflicts
*/
binfo = (bn->botinfo) ? bn->botinfo->next : NULL;
for(;binfo;binfo=binfo->next)
{
if ((cfg = find_netcfg(binfo->guid)) == NULL)
continue;
if (cfg->linked == TRUE)
break;
}
if (binfo)
{
#ifdef DEBUG
debug("(basicLink) circular linking: guid == %i\n",binfo->guid);
#endif /* DEBUG */
/*
* drop everything we've gotten sofar...
*/
botnet_deaduplink(bn);
return;
}
/*
* we're done, we're fine, we're linked!
*/
for(binfo=bn->botinfo;binfo;binfo=binfo->next)
{
botnet_binfo_relay(bn,binfo);
check_botinfo(binfo,NULL);
if ((cfg = find_netcfg(binfo->guid)) == NULL)
continue;
cfg->linked = TRUE;
}
bn->list_complete = TRUE;
return;
}
/*
* BL<guid> <hops> <nick>!<userhost> <server> <version>
*/
nuh = chop(&version);
guid = asc2int(nuh);
if (errno)
return;
nuh = chop(&version);
hops = asc2int(nuh);
if (errno)
return;
nuh = chop(&version);
server = chop(&version);
if (!*version)
return;
binfo = make_botinfo(guid,hops,nuh,server,version);
if (bn->botinfo == NULL)
send_global(SPYSTR_BOTNET,"connecting to %s [guid %i]",nickcpy(NULL,nuh),bn->guid);
pp = &bn->botinfo;
while(*pp)
{
delete = *pp;
if (guid == delete->guid)
{
*pp = delete->next;
Free((char**)&delete);
break;
}
pp = &delete->next;
}
binfo->next = *pp;
*pp = binfo;
if (bn->list_complete)
{
if ((cfg = find_netcfg(guid)))
cfg->linked = TRUE;
/*
* broadcast the new link
*/
botnet_binfo_relay(bn,binfo);
check_botinfo(binfo,NULL);
}
}
void basicQuit(BotNet *bn, char *rest)
{
BotInfo *binfo,**pp_binfo;
int guid;
if (bn->status != BN_LINKED)
return;
guid = asc2int(rest);
if (errno)
return;
pp_binfo = &bn->botinfo;
while(*pp_binfo)
{
binfo = *pp_binfo;
if (binfo->guid == guid)
{
send_global(SPYSTR_BOTNET,"quit: %s (from guid %i)",rest,bn->guid);
*pp_binfo = binfo->next;
reset_linkable(guid);
Free((char**)&binfo);
break;
}
pp_binfo = &binfo->next;
}
botnet_relay(bn,"BQ%s\n",rest);
}
/*
* netchan protocol routines
*/
void netchanNeedop(BotNet *source, char *rest)
{
BotNet *bn;
BotInfo *binfo;
char *channel;
int guid;
guid = asc2int(chop(&rest));
channel = chop(&rest);
if (errno || guid < 1 || !channel)
return;
for(bn=botnetlist;bn;bn=bn->next)
{
if (bn->status != BN_LINKED)
continue;
for(binfo=bn->botinfo;binfo;binfo=binfo->next)
{
if (binfo->guid == guid)
check_botinfo(binfo,channel);
}
}
}
#ifdef SUPPRESS
void netchanSuppress(BotNet *source, char *rest)
{
Mech *backup;
const char *cmd;
int crc,i,j;
cmd = chop(&rest);
/* convert command to const command */
for(i=0;mcmd[i].name;i++)
{
j = stringcasecmp(mcmd[i].name,cmd);
if (j < 0)
continue;
if (j > 0)
return;
cmd = mcmd[i].name;
break;
}
if (mcmd[i].name == NULL)
return;
crc = asc2int(rest);
/* to all local bots */
for(backup=botlist;backup;backup=backup->next)
{
backup->supres_cmd = cmd;
backup->supres_crc = crc;
}
}
#endif /* SUPPRESS */
/*
* paryline protocol routines
*/
void partyAuth(BotNet *bn, char *rest)
{
User *user;
Strp *ump;
char *name,*userhost,*checksum;
int m;
name = chop(&rest);
userhost = chop(&rest);
if ((checksum = chop(&rest)) == NULL)
checksum = "";
for(current=botlist;current;current=current->next)
{
for(user=current->userlist;user;user=user->next)
{
for(ump=user->mask;ump;ump=ump->next)
{
if (!matches(ump->p,userhost))
{
#ifdef DEBUG
debug("(partyAuth) testing auth on %s\n",user->name);
#endif /* DEBUG */
m = 0;
if (user->pass)
{
sprintf(globaldata,"%s %s %s",userhost,user->name,user->pass);
m = passmatch(globaldata,checksum);
}
if (m)
{
make_auth(userhost,user);
}
}
}
}
}
}
#ifdef REDIRECT
int commandlocal(int dg, int sg, char *from, char *command)
{
Client mydcc;
User *user;
Strp *sp;
char tempdata[MSGLEN];
char *p1,*p2,*uh;
#ifdef DEBUG
debug("(commandlocal) %i %i %s %s\n",dg,sg,from,command);
#endif /* DEBUG */
uh = getuh(from);
nickcpy(CurrentNick,from);
for(current=botlist;current;current=current->next)
{
if (dg != -1 && current->guid != dg)
continue;
user = get_authuser(from,ANY_CHANNEL);
if (!user && (user = find_handle(CurrentNick)))
{
for(sp=user->mask;sp;sp=sp->next)
{
if (!stringcmp(sp->p,uh))
break;
}
}
if (user)
{
mydcc.user = CurrentUser = user;
CurrentDCC = &mydcc;
CurrentShit = NULL;
CurrentChan = NULL;
redirect.method = R_BOTNET;
redirect.guid = sg;
set_mallocdoer(commandlocal);
redirect.to = stringdup(CurrentNick);
p1 = tempdata;
p2 = stringcpy(p1,from);
p2++; /* skip past '0' */
*p2 = current->setting[CHR_CMDCHAR].char_var;
stringcpy((*p2 == *command) ? p2 : p2+1,command);
on_msg(p1,current->nick,p2);
CurrentDCC = NULL;
}
if (dg == -1)
return(TRUE);
}
return(FALSE);
}
/*
* PC<targetguid> <sourceguid> <sourceuserhost> <command>
*/
void partyCommand(BotNet *bn, char *rest)
{
BotNet *bn2;
BotInfo *binfo;
char *dguid,*sguid,*userhost;
int idguid,isguid;
#ifdef DEBUG
debug("(partyCommand) %s\n",rest);
#endif /* DEBUG */
dguid = chop(&rest);
sguid = chop(&rest);
if ((userhost = chop(&rest)) == NULL || *rest == 0)
return;
isguid = asc2int(sguid);
if (errno)
return;
idguid = asc2int(dguid);
if (errno) /* == "*" */
{
commandlocal(-1,isguid,userhost,rest);
}
else
{
if (commandlocal(idguid,isguid,userhost,rest))
return;
for(bn2=botnetlist;bn2;bn2=bn2->next)
{
if (bn2->status != BN_LINKED)
continue;
for(binfo=bn2->botinfo;binfo;binfo=binfo->next)
{
if (idguid == binfo->guid)
{
to_file(bn2->sock,"PC%s %s %s %s\n",dguid,sguid,userhost,rest);
return;
}
}
}
}
botnet_relay(bn,"PC%s %s %s %s\n",dguid,sguid,userhost,rest);
}
#endif /* REDIRECT */
/*
* PM<targetguid> <targetuserhost> <source> <message>
*/
void partyMessage(BotNet *bn, char *rest)
{
BotNet *bn2;
BotInfo *binfo;
Client *client;
char *src,*dst,*userhost;
int guid;
dst = chop(&rest);
userhost = chop(&rest);
if ((src = chop(&rest)) == NULL || *rest == 0)
return;
guid = asc2int(dst);
if (errno) /* == "*" */
{
stringcpy_n(CurrentNick,src,NUHLEN-1);
/*
* partyline_broadcast() uses CurrentNick for the first %s in the format
*/
if (*rest == 1)
partyline_broadcast(NULL,"* %s %s\n",rest+1);
else
partyline_broadcast(NULL,"<%s> %s\n",rest);
botnet_relay(bn,"PM* * %s %s\n",src,rest);
}
else
{
for(current=botlist;current;current=current->next)
{
if (guid == current->guid)
{
if ((client = find_client(userhost)))
{
to_file(client->sock,"[%s] %s\n",src,rest);
return;
}
}
}
for(bn2=botnetlist;bn2;bn2=bn2->next)
{
if (bn2->status != BN_LINKED)
continue;
for(binfo=bn2->botinfo;binfo;binfo=binfo->next)
{
if (guid == binfo->guid)
{
to_file(bn2->sock,"PM%s %s %s %s\n",dst,userhost,src,rest);
return;
}
}
}
botnet_relay(bn,"PM%s %s %s %s\n",dst,userhost,src,rest);
}
}
/*
* use sharing protocol routines
*/
void ushareUser(BotNet *bn, char *rest)
{
User *user,tempuser;
char c,*handle,*pass;
int i,tick,modcount,uaccess;
c = *(rest++);
tick = asc2int(chop(&rest));
if (errno)
return;
if ((c != '-' && bn->tick >= tick) && (c != '+' && bn->tick != tick))
return;
switch(c)
{
case '+':
if (tick > bn->tick)
{
#ifdef DEBUG
debug("(ushareUser) bumping tick to %i\n",tick);
#endif /* DEBUG */
bn->tick = tick;
}
/* `UU+tick modcount access handle chan pass' */
/* UU+0 4 100 proton * $1$3484$AxMkHvZijkeqb8hA6h9AB/ */
i = 0;
modcount = asc2int(chop(&rest));
i += errno;
uaccess = asc2int(chop(&rest));
i += errno;
handle = chop(&rest);
pass = chop(&rest);
if (i == 0 && handle && pass && *pass)
{
if (!stringcmp(pass,"none"))
pass = NULL;
i = 0;
bn->addsession = (rand() | 1);
for(current=botlist;current;current=current->next)
{
/* accept users from other bots */
if (current->setting[TOG_NETUSERS].int_var == 0)
continue;
user = find_handle(handle);
if (user && user->x.x.readonly)
continue;
if (user && user->modcount < modcount)
{
/* user with higher modcount overwrites */
remove_user(user);
user = NULL;
}
if (!user)
{
#ifdef DEBUG
debug("(ushareUser) user %s ++ re-creating\n",handle);
#endif /* DEBUG */
user = add_user(handle,pass,uaccess);
user->modcount = modcount;
user->guid = bn->guid; /* we got new user from <guid> this session */
user->tick = global_tick;
user->addsession = bn->addsession;
i++;
}
}
if (i)
{
#ifdef DEBUG
debug("(ushareUser) bn->tick = %i\n",tick);
#endif /* DEBUG */
bn->tick = tick;
global_tick++;
}
}
break;
case '-':
#ifdef DEBUG
debug("(ushareUser) ticking to next user %i ++\n",bn->tick);
#endif /* DEBUG */
for(current=botlist;current;current=current->next)
for(user=current->userlist;user;user=user->next)
if (user->guid == bn->guid && user->addsession)
{
user->addsession = 0;
mirror_user(user); /* copy to other local bots */
}
bn->addsession = 0;
bn->tick++;
to_file(bn->sock,"UT%i\n",bn->tick);
bn->tick_last = now;
break;
case '*':
case '#':
if ((rest = chop(&rest)) == NULL)
return;
for(current=botlist;current;current=current->next)
{
for(user=current->userlist;user;user=user->next)
{
if (user->guid == bn->guid && user->addsession == bn->addsession)
{
#ifdef DEBUG
debug("(ushareUser) user %s ++ mask/chan %s\n",user->name,rest);
#endif /* DEBUG */
addtouser((c == '*') ? &user->mask : &user->chan,rest,TRUE);
}
}
}
break;
case '!':
user = cfgUser;
cfgUser = memset(&tempuser,0,sizeof(User));
cfg_opt(chop(&rest));
cfgUser = user;
for(current=botlist;current;current=current->next)
{
for(user=current->userlist;user;user=user->next)
{
if (user->guid == bn->guid && user->addsession == bn->addsession)
{
#ifdef DEBUG
debug("(ushareUser) user %s ++ touching flags\n",user->name);
debug("(ushareUser) %s %s %s prot%i\n",(tempuser.x.x.aop) ? "aop" : "",(tempuser.x.x.echo) ? "echo" : "",
(tempuser.x.x.avoice) ? "avoice" : "",tempuser.x.x.prot);
#endif /* DEBUG */
user->x.x.aop = tempuser.x.x.aop;
user->x.x.echo = tempuser.x.x.echo;
user->x.x.avoice = tempuser.x.x.avoice;
#ifdef BOUNCE
user->x.x.bounce = tempuser.x.x.bounce;
#endif /* BOUNCE */
}
}
}
break;
}
#ifdef DEBUG
current = NULL;
#endif /* DEBUG */
}
void ushareTick(BotNet *bn, char *rest)
{
Strp *pp;
Mech *mech;
User *user,*senduser;
int i;
i = asc2int(rest);
if (errno)
return;
#ifdef DEBUG
debug("(ushareTick) remote bot ticked %i\n",i);
#endif /* DEBUG */
senduser = 0;
for(mech=botlist;mech;mech=mech->next)
{
for(user=mech->userlist;user;user=user->next)
{
if (i <= user->tick && !user->x.x.noshare)
{
if (!senduser)
senduser = user;
/* user->guid != bn->guid :: dont send users back to the bot we got them from */
if (user->tick < senduser->tick && user->guid != bn->guid)
senduser = user;
}
}
}
if (senduser)
{
char *p,temp[8];
user = senduser;
#ifdef DEBUG
debug("(ushareTick) user %s, user->tick = %i (%i)\n",user->name,user->tick,i);
#endif /* DEBUG */
to_file(bn->sock,"UU+%i %i %i %s %s\n",user->tick,user->modcount,
user->x.x.access,user->name,(user->pass) ? user->pass : "none");
*(p = temp) = 0;
if (user->x.x.aop)
*(p++) = 'a';
#ifdef BOUNCE
if (user->x.x.bounce)
*(p++) = 'b';
#endif /* BOUNCE */
if (user->x.x.echo)
*(p++) = 'e';
if (user->x.x.avoice)
*(p++) = 'v';
*(p++) = 'p';
*(p++) = '0' + user->x.x.prot;
*p = 0;
to_file(bn->sock,"UU!%i %s\n",user->tick,temp);
for(pp=user->mask;pp;pp=pp->next)
{
to_file(bn->sock,"UU*%i %s\n",user->tick,pp->p);
}
for(pp=user->chan;pp;pp=pp->next)
{
to_file(bn->sock,"UU#%i %s\n",user->tick,pp->p);
}
to_file(bn->sock,"UU-%i\n",user->tick);
return;
}
}
void ushareDelete(BotNet *bn, char *rest)
{
User *user;
char *orig;
int modcount;
orig = rest;
modcount = asc2int(chop(&rest));
if (errno)
return;
for(current=botlist;current;current=current->next)
{
if (current->setting[TOG_NETUSERS].int_var)
{
user = find_handle(rest);
if (user && user->modcount == modcount)
{
reset_userlink(user,NULL);
remove_user(user);
}
}
}
}
/*
*
*/
void parse_botnet(BotNet *bn, char *rest)
{
int i;
#ifdef TELNET
if (bn->status == BN_UNKNOWN)
{
if (rest[0] == 'B' && rest[1] == 'B' && (attrtab[(uchar)rest[2]] & NUM))
{
basicBanner(bn,rest+2);
return;
}
#ifdef NETCFG
if (strncmp(rest,"netcfg ",7) == 0)
{
goto not_a_botnet_connection;
}
#endif /* NETCFG */
if (check_telnet(bn->sock,rest))
{
not_a_botnet_connection:
bn->sock = -1;
bn->status = BN_DEAD;
deadlinks = TRUE;
return;
}
}
#endif /* TELNET */
if (*rest != 'B' && bn->status != BN_LINKED)
return;
for(i=0;basicProto[i].func;i++)
{
if (basicProto[i].cmd[0] == rest[0] && basicProto[i].cmd[1] == rest[1])
{
if (basicProto[i].relay == RELAY_YES)
botnet_relay(bn,"%s\n",rest);
basicProto[i].func(bn,rest+2);
return;
}
}
/*
* relay to bots that know/want the protocol
*/
}
void botnet_newsock(void)
{
BotNet *bn;
int s;
/*
* accept the connection
*/
if ((s = SockAccept(linksock.sock)) < 0)
return;
#ifdef DEBUG
debug("(botnet_newsock) {%i} new socket\n",s);
#endif /* DEBUG */
#ifdef TELNET
to_file(s,FMT_PLAINLINE,telnetprompt);
#endif /* TELNET */
set_mallocdoer(botnet_newsock);
bn = (BotNet*)Calloc(sizeof(BotNet));
bn->sock = s;
bn->status = BN_UNKNOWN;
bn->lsid = rand();
bn->when = now;
bn->next = botnetlist;
botnetlist = bn;
/*
* crude... but, should work
*/
last_autolink = now + AUTOLINK_DELAY;
}
/*
*
*/
void select_botnet(void)
{
BotNet *bn;
/*
* handle incoming connections
*/
if (linkport && linksock.sock == -1)
{
linksock.sock = SockListener(linkport);
if (linksock.sock != -1)
{
linksock.next = botnetlist;
botnetlist = (BotNet*)&linksock;
#ifdef DEBUG
debug("(doit) {%i} Linksocket is active (%i)\n",linksock.sock,linkport);
#endif /* DEBUG */
}
}
/*
* autolink
*/
if (autolink && (now > last_autolink))
{
last_autolink = now + AUTOLINK_DELAY;
if (autolink_cfg)
autolink_cfg = autolink_cfg->next;
if (!autolink_cfg)
autolink_cfg = netcfglist;
if (autolink_cfg && !autolink_cfg->linked &&
autolink_cfg->host && autolink_cfg->pass)
{
/*
* this thing isnt linked yet!
*/
connect_to_bot(autolink_cfg);
}
}
short_tv &= ~TV_BOTNET;
for(bn=botnetlist;bn;bn=bn->next)
{
chkhigh(bn->sock);
if (bn->status == BN_CONNECT)
{
FD_SET(bn->sock,&write_fds);
short_tv |= TV_BOTNET;
}
else
{
FD_SET(bn->sock,&read_fds);
}
}
}
void process_botnet(void)
{
BotNet *bn,**pp;
BotInfo *binfo;
Mech *netbot;
char *rest,linebuf[MSGLEN];
for(bn=botnetlist;bn;bn=bn->next)
{
/*
* usersharing tick, 10 minute period
*/
if (bn->status == BN_LINKED && (bn->tick_last + 600) < now)
{
#ifdef DEBUG
debug("(process_botnet) {%i} periodic ushare tick\n",bn->sock);
#endif /* DEBUG */
bn->tick_last = now;
to_file(bn->sock,"UT%i\n",bn->tick);
}
/*
*
*/
if (bn->has_data)
goto has_data;
/*
* outgoing connection established
*/
if (FD_ISSET(bn->sock,&write_fds))
{
bn->lsid = rand();
bn->controller = netbot = get_netbot();
if (to_file(bn->sock,banneropt,netbot->guid,bn->lsid) < 0)
{
botnet_deaduplink(bn);
}
else
{
bn->status = BN_BANNERSENT;
bn->when = now;
}
/* write_fds is only set for sockets where reading is not needed */
continue;
}
/*
* incoming data read
*/
if (FD_ISSET(bn->sock,&read_fds))
{
/*
* ye trusty old linksock
*/
if (bn->status == BN_LINKSOCK)
botnet_newsock();
else
{
has_data:
/*
* Commands might change the botnetlist,
* so returns here are needed
*/
rest = sockread(bn->sock,bn->sockdata,linebuf);
bn->has_data = (rest) ? TRUE : FALSE;
if (rest)
{
parse_botnet(bn,rest);
if (!deadlinks)
goto has_data; /* process more lines if link list is unchanged */
goto clean;
}
switch(errno)
{
default:
#ifdef DEBUG
debug("(process_botnet) {%i} sockread() errno = %i\n",bn->sock,errno);
#endif /* DEBUG */
botnet_deaduplink(bn);
case EAGAIN:
case EINTR:
goto clean;
}
}
}
if ((bn->status == BN_CONNECT) && ((now - bn->when) > LINKTIME))
{
#ifdef DEBUG
debug("(process_botnet) {%i} Life is good; but not for this guy (guid == %i). Timeout!\n",
bn->sock,bn->guid);
#endif /* DEBUG */
botnet_deaduplink(bn);
}
}
clean:
/*
* quit/delete BN_DEAD links
*/
if (!deadlinks)
return;
pp = &botnetlist;
while(*pp)
{
bn = *pp;
if (bn->status == BN_DEAD)
{
*pp = bn->next;
send_global(SPYSTR_BOTNET,"quit: guid %i",bn->guid);
#ifdef DEBUG
debug("(process_botnet) botnet quit: guid %i\n",bn->guid);
#endif /* DEBUG */
while((binfo = bn->botinfo))
{
bn->botinfo = binfo->next;
#ifdef DEBUG
debug("(process_botnet) botnet quit: guid %i child of %i on socket %i\n",
binfo->guid,bn->guid,bn->sock);
#endif /* DEBUG */
if (bn->list_complete)
{
send_global(SPYSTR_BOTNET,"quit: guid %i (child of %i)",
binfo->guid,bn->guid);
botnet_relay(bn,"BQ%i\n",binfo->guid);
reset_linkable(binfo->guid);
}
Free((char**)&binfo);
}
if (bn->list_complete)
{
botnet_relay(bn,"BQ%i\n",bn->guid);
}
close(bn->sock);
Free((char**)&bn);
continue;
}
pp = &bn->next;
}
deadlinks = FALSE;
}
/*
*
* commands related to botnet
*
*/
void do_link(COMMAND_ARGS)
{
/*
* on_msg checks: GAXS
*/
NetCfg *cfg,**pp;
char *guid,*pass,*host,*port;
int iguid,iport;
int mode;
/*
* list all the known links
*/
if (!*rest)
{
table_buffer("guid\tpass\thost\tport");
for(cfg=netcfglist;cfg;cfg=cfg->next)
{
table_buffer("%i\t%s\t%s\t%i",cfg->guid,(cfg->pass) ? cfg->pass : "",
(cfg->host) ? cfg->host : "",cfg->port);
}
table_send(from,2);
return;
}
guid = chop(&rest);
if (*guid == '+' || *guid == '-')
mode = *guid++;
else
mode = 0;
iguid = asc2int(guid);
if (errno)
{
usage:
usage(from); /* usage for CurrentCmd->name */
return;
}
pp = &netcfglist;
while((cfg = *pp))
{
if (cfg->guid == iguid)
break;
pp = &cfg->next;
}
if (CurrentUser == &CoreUser || mode == '+')
{
if (cfg)
{
to_user(from,"guid %i already exists",iguid);
return;
}
pass = chop(&rest);
host = chop(&rest);
port = chop(&rest);
iport = asc2int(port);
if (!pass || (host && !port) || (port && (errno || iport < 1 || iport > 65535)))
goto usage;
set_mallocdoer(do_link);
cfg = (NetCfg*)Calloc(sizeof(NetCfg) + StrlenX(pass,host,NULL)); /* host might be NULL, StrlenX() handles NULLs, Strlen2() does not. */
cfg->guid = iguid;
cfg->port = iport;
cfg->host = stringcat(cfg->pass,pass) + 1;
if (host)
stringcpy(cfg->host,host);
else
cfg->host = NULL;
cfg->next = netcfglist;
netcfglist = cfg;
return;
}
if (!cfg)
{
to_user(from,"unknown guid: %i",iguid);
return;
}
if (mode == '-')
{
*pp = cfg->next;
Free((char**)&cfg);
return;
}
if (!cfg->host)
{
to_user(from,"unknown host/port for guid %i",iguid);
return;
}
if (cfg->linked)
{
to_user(from,"guid %i is already connected",iguid);
return;
}
if (connect_to_bot(cfg) < 0)
{
to_user(from,"unable to create connection");
return;
}
send_global(SPYSTR_BOTNET,"connecting to guid %i [%s:%i]",iguid,cfg->host,cfg->port);
}
#ifdef REDIRECT
void do_cmd(COMMAND_ARGS)
{
Mech *backup;
char tempdata[MAXLEN];
char *target,*orig = rest;
int guid;
target = chop(&rest);
guid = asc2int(target);
if (errno)
{
unchop(orig,rest);
rest = orig;
target = MATCH_ALL;
}
else
if (*rest == 0)
{
usage(from);
return;
}
if (STRCHR(from,'!'))
sprintf(tempdata,"%s %i %s %s",target,current->guid,from,rest);
else
sprintf(tempdata,"%s %i %s!%s %s",target,current->guid,from,CurrentUser->mask->p,rest);
backup = current;
partyCommand(NULL,tempdata);
current = backup;
}
#endif /* REDIRECT */
#endif /* BOTNET */