include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/ip_icmp.h>
#include <netdb.h>

unsigned short ip_cksum(unsigned char * buff, int len)
{
        unsigned long sum = 0;
        if (len > 3)
        {
                __asm__("clc\n"
                "1:\t"
                "lodsl\n\t"
                "adcl %%eax, %%ebx\n\t"
                "loop 1b\n\t"
                "adcl $0, %%ebx\n\t"
                "movl %%ebx, %%eax\n\t"
                "shrl $16, %%eax\n\t"
                "addw %%ax, %%bx\n\t"
                "adcw $0, %%bx"
                : "=b" (sum) , "=S" (buff)
                : "0" (sum), "c" (len >> 2) ,"1" (buff)
                : "ax", "cx", "si", "bx" );
        }
        if (len & 2)
        {
                __asm__("lodsw\n\t"
                "addw %%ax, %%bx\n\t"
                "adcw $0, %%bx"
                : "=b" (sum), "=S" (buff)
                : "0" (sum), "1" (buff)
                : "bx", "ax", "si");
        }
        if (len & 1)
        {
                __asm__("lodsb\n\t"
                "movb $0, %%ah\n\t"
                "addw %%ax, %%bx\n\t"
                "adcw $0, %%bx"
                : "=b" (sum), "=S" (buff)
                : "0" (sum), "1" (buff)
                : "bx", "ax", "si");
        }
        sum =~sum;
        return(sum & 0xffff);
}

unsigned short tcp_check(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" );

        /* We only want the bottom 16 bits, but we never cleared the top 16. */

        return((~sum) & 0xffff);
}

void resolve_address(struct sockaddr *addr, char *hostname, u_short port) {
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_port = htons(port);
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);
  }
 }
}

char *create_ip(u_long source, u_long dest, u_char protocol, u_char ttl, 
        u_short id, char *data, int data_len)
{
 char *ip_datagram;
 struct iphdr *ip_header; 
 ip_datagram = malloc(sizeof(struct iphdr) + data_len);
 ip_header = ip_datagram;
 ip_header->version   = 4;
 ip_header->tos       = 0;
 ip_header->frag_off  = 0;
 ip_header->check     = 0;
 ip_header->saddr     = source;
 ip_header->daddr     = dest;
 ip_header->protocol  = protocol;
 ip_header->ttl       = ttl;
 ip_header->id        = htons(id);
 ip_header->ihl       = 5;
 ip_header->tot_len   = htons(sizeof(struct iphdr) + data_len); 
 ip_header->check = htons(ip_cksum(ip_datagram,sizeof(struct iphdr)));
 bcopy(data,ip_datagram+sizeof(struct iphdr),data_len);
 return ip_datagram;
}

char *create_tcp(u_long source, u_long dest, u_short sport, u_short dport, 
        u_long seqnum, u_long acknum, u_char flags, char *data, int datalen) 
{
 char *wewt;
 struct tcphdr *tcp_header;
 wewt = malloc(sizeof(struct tcphdr) + datalen);
 tcp_header = wewt;
 tcp_header->th_sport = sport;
 tcp_header->th_dport = dport;
 tcp_header->th_seq   = seqnum;
 tcp_header->th_ack   = acknum;
 tcp_header->th_flags = flags;
 tcp_header->th_sum   = 0;
 tcp_header->th_sum = htons(tcp_check(tcp_header, sizeof(struct tcphdr), 
    source, dest));
 bcopy(data,wewt+sizeof(struct tcphdr),datalen);
 return wewt;
}

void sendpack(char *fromhost, int fromport, char *tohost, int toport) {
 char *packet;
 char *tcppacket;
 char *sendme;
 static struct sockaddr_in local, remote;
 static int sock = 0;
 if (!sock) {
   resolve_address((struct sockaddr *)&local, fromhost, fromport);
   resolve_address((struct sockaddr *)&remote, tohost, toport);
   sock = socket(AF_INET, SOCK_RAW, 255);
   if (sock == -1) { perror("Getting raw socket"); exit(-1); }
  }
   tcppacket = create_tcp(&local.sin_addr, &remote.sin_addr, 
        local.sin_port, remote.sin_port, 795930600, 0, TH_SYN,
        NULL, 0);
   packet = create_ip(&local.sin_addr, &remote.sin_addr,
        6, 24, 4, NULL, 0);
   sendme = (struct iphdr *)packet;
   bcopy(tcppacket, sendme+sizeof(struct iphdr), sizeof(tcppacket));
   printf("the ip header is %d bytes long.\n", sizeof(struct iphdr));
   printf("the tcp header is %d bytes long.\n", sizeof(struct tcphdr));
   printf("the ip packet is %d bytes long.\n", sizeof(packet));
   printf("the tcp packet is %d bytes long.\n", sizeof(tcppacket));
   printf("the final packet is %d bytes long.\n", sizeof(sendme));
  {
   int result;

   result = sendto(sock, packet, sizeof(packet), 0,
        (struct sockaddr *)&remote, sizeof(remote));
   if (result != sizeof(packet)) { perror("sending packet"); }
  }
}

main(int argc, char **argv) {
if (argc!=5) {
 printf("usage: %s <from host> <from port> <to host> <to port>\n", argv[0]);
 exit(-1);
}
 printf("forging packet from %s.%d to %s.%d\n", argv[1], atoi(argv[2]), 
        argv[3], atoi(argv[4]));
 sendpack(argv[1], atoi(argv[2]), argv[3], atoi(argv[4]));
}