/* Dieses Programm generiert ein TCP-SYN-Paket */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>

#define PAYLOAD_SIZE 0    /* SYN-Paket -> keine Nutzdaten */

/* Pseudo-Datenstrukturen zur Bildung der Pruefsumme */
struct pseudoheader
  {
  u_int32_t src_addr;
  u_int32_t dst_addr;
  u_int8_t padding;
  u_int8_t proto;
  u_int16_t length;
  };

unsigned short csum(unsigned short *ptr, int nbytes);
 
int main(int argc, char *argv[])
  {
  int s;                                       /* Socket-Deskriptor */
  int bytes;                                   /* Byte-Zaehler */
  int buffer_size;                             /* Paketgroesse (wird berechnet) */
  char *buffer;                                /* Pointer auf das Paket */
  struct iphdr *ip;                            /* Pointer auf IP-Struktur */ 
  struct tcphdr *tcp;                          /* Pointer auf TCP-Struktur */
  char *data;                                  /* Pointer auf Nutzdatenbereich */
  char *pseudogram;                            /* Pointer auf den Checksum-Puffer */
  struct sockaddr_in destination;              /* fuer Ziel-Adressierung */
  struct pseudoheader pshdr;                   /* der Pseudoheader */
  int on = 1;                                  /* fuer setsockopt() */


  // Anzahl der Programm-Parameter pruefen
  if (argc != 5)
    {
    fprintf(stderr, "Usage: %s <src-addr> ", argv[0]);
    fprintf(stderr, "<src-port> <dest-addr> <dest-port>\n");
    return 1;
    }

  /* RAW-Socket aufbauen */
  s = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
  if (s < 0)
    {
    perror("socket() ging schief");
    if (errno == EACCES) printf(" (EACCES)\n");
    if (errno == EAFNOSUPPORT) printf(" (EAFNOSUPPORT)\n");
    return 1;
    }

  /* IP-Header mache ich von Hand, deshalb: */
  if (setsockopt (s, IPPROTO_IP, IP_HDRINCL, (const char*)&on, sizeof (on)) == -1)
    {
    perror("setsockopt() - IP-HDRINCL ging schief");
    return 1;
    }

  buffer_size = sizeof (struct iphdr) + sizeof (struct tcphdr) + PAYLOAD_SIZE;
  buffer = (char *) malloc (buffer_size);

  /* Puffer mit Nullen fuellen */
  memset (buffer, 0, sizeof(buffer));

  /* IP-Header zusammenbasteln, der Pointer ip zeigt auf den Pufferanfang */
  ip = (struct iphdr*) buffer;
  ip->version = 4;
  ip->ihl = 5;
  ip->tot_len = htons(sizeof(struct iphdr) + sizeof(struct tcphdr) + PAYLOAD_SIZE);
  /* ID automatisch vom Kernel beziehen */
  ip->id = 0;
  ip->ttl = 255;
  ip->protocol = IPPROTO_TCP;
  ip->saddr = inet_addr(argv[1]); 
  ip->daddr = inet_addr(argv[3]);
  ip->check = 0;
  /* Checksum macht der Kernel */

  /* TCP Header mit Werten fuellen, der Pointer zeigt hinter den IP-Header */
  tcp = (struct tcphdr*) (buffer + sizeof(struct iphdr));
  tcp->source = htons(atol(argv[2]));
  tcp->dest = htons(atol(argv[4]));
  tcp->ack_seq = 0;
  tcp->seq = random();
  tcp->doff = 5;
  tcp->window = htons(65535);
  /* TCP-Flags setzen */
  tcp->fin=0;
  tcp->syn=1;
  tcp->rst=0;
  tcp->psh=0;
  tcp->ack=0;
  tcp->urg=0;

  /* Datenbereich, der Pointer zeigt hinter den TCP-Header */
  /* Hier auskommentiert, da ein SYN-Paket normalerweise 
   * keine Daten enthaelt - es wird nur gezeigt, wie man 
   * Daten eintragen wuerde 
   * data = (char*) (buffer + sizeof(struct iphdr) + sizeof(struct tcphdr));
   * strcpy(data, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
   */
  
  /* Pseudoheader erstellen */
  pshdr.src_addr = ip->saddr;
  pshdr.dst_addr = ip->daddr;
  pshdr.padding = 0;
  pshdr.proto = ip->protocol;
  pshdr.length = htons(sizeof(struct tcphdr) + PAYLOAD_SIZE);

  /* Pseudoheader, TCP-Header und Daten kombinieren */
  pseudogram = malloc(sizeof(struct pseudoheader) + sizeof(struct tcphdr) + PAYLOAD_SIZE);
  memcpy(pseudogram, (char*) &pshdr, sizeof(struct pseudoheader));
  memcpy(pseudogram + sizeof(struct pseudoheader), tcp, sizeof(struct tcphdr) + PAYLOAD_SIZE);
  tcp->check = csum((unsigned short*)pseudogram, 
               sizeof(struct pseudoheader) + sizeof(struct tcphdr) + PAYLOAD_SIZE);
  free(pseudogram); /* Speicher wieder freigeben */

  /* Statistik ausgeben */
  printf ("TCP Pruefsumme: %i\n", tcp->check);
  printf ("Ziel : %s:%i\n", argv[3], ntohs(tcp->dest));
  printf ("Quelle: %s:%i\n", argv[1], ntohs(tcp->source));


  /* Socket-Adress-Struktur fuer sendto() */
  destination.sin_addr.s_addr = ip->daddr;
  destination.sin_family = AF_INET;
  destination.sin_port = tcp->dest;

  /* Paket versenden */
  bytes = sendto(s, buffer, ntohs(ip->tot_len), 0,
          (struct sockaddr*)&destination, sizeof(destination));
  if (bytes == -1)
    {
    perror("sendto() ging schief");
    return 1;
    }

  return 0;
  }


unsigned short csum(unsigned short *ptr, int nbytes)
  {
  u_int32_t sum;       /* Summenvariable */
  u_int16_t oddbyte;   /* fuer Addition bei ungerader Blocklaenge */

  sum = 0;
  while(nbytes > 1)    /* ueber den Datenblock summieren */
    {
    sum += *ptr++;     /* 16-Bit-Wert summieren, daher nbytes um 2 vermindern */
    nbytes -= 2;
    }
  if(nbytes == 1)      /* ungerde Blocklaenge (eher selten) */
    {
    oddbyte = 0;       /* letztes Byte addieren */
    *((u_char*)&oddbyte) = *(u_char*)ptr;
    sum += oddbyte;
    }
  /* die oberen und unteren 16 Bit der 32-Bit-Summe addieren */
  sum = (sum >> 16) + (sum & 0xffff);
  sum = sum + (sum >> 16); /* Uebertrag beruecksichtigen */
  /* Einerkomplement bilden */
  return (u_int16_t)~sum;
  }

