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

#include <netinet/ip.h>
#include <netinet/tcp.h>

uint16_t checksum (uint16_t *, int);

void make_and_send_synack( int sockfd, char *buf, int n,
			   struct iphdr *ip, struct tcphdr *tcp, struct sockaddr *sin );
uint16_t
tcp4_checksum (char *packet, struct iphdr *ip, struct tcphdr *tcp, int tcp_payload_len);

int main(){
     int sockfd, n;
     socklen_t clilen;
     const int on = 1;
     struct sockaddr_in cliaddr;
     char buf[IP_MAXPACKET];
     int i;
     
     sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
     if (sockfd < 0){
          perror("sock:");
          exit(1);
     }

     // Set flag so socket expects us to provide IPv4 header.
     if (setsockopt (sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof (on)) < 0) {
       perror ("setsockopt() failed to set IP_HDRINCL ");
       exit (1);
     }
     
     clilen = sizeof(struct sockaddr_in);    
     while(1){
          printf(" before recvfrom\n");   
          n = recvfrom(sockfd, buf, 10000, 0, (struct sockaddr *)&cliaddr, &clilen);
          printf(" rec'd %d bytes\n",n);
          
          struct iphdr *ip_hdr = (struct iphdr *)buf;
          
          printf("IP header is %d bytes.\n", ip_hdr->ihl*4);
          
          for (i = 0; i < n; i++) {
               printf("%02X%s", (uint8_t)buf[i], (i + 1)%16 ? " " : "\n");
          }
          printf("\n");
          
          struct tcphdr *tcp_hdr = (struct tcphdr *)((char *)ip_hdr + (4 * ip_hdr->ihl));

	  // Make like a tiny tcpdump
          printf("TCP sport=%d, dport=%d ", ntohs(tcp_hdr->source), ntohs(tcp_hdr->dest));
	  if (tcp_hdr->syn && ! tcp_hdr->ack)
	    printf("SYN");
	  else if( tcp_hdr->syn && tcp_hdr->ack )
	    printf("SYN+ACK");
	  else if( tcp_hdr->ack )
	    printf("ACK");
	  puts("");

	  if( tcp_hdr->syn ){ // respond with SYN+ACK
	       make_and_send_synack( sockfd, buf, n, ip_hdr, tcp_hdr, (struct sockaddr*)&cliaddr);
	  }
     }
}

void make_and_send_synack( int sockfd, char *buf, int n,
			   struct iphdr *ip, struct tcphdr *tcp, struct sockaddr *sin )
{
     char res[IP_MAXPACKET]; 
     memcpy(res, buf, n);
	    
     struct iphdr  *r_ip  = (struct iphdr*)  res;
     struct tcphdr *r_tcp = (struct tcphdr*) (res + 4*ip->ihl);
     r_ip->daddr = ip->saddr;
     r_ip->saddr = ip->daddr;
     r_tcp->source = tcp->dest;
     r_tcp->dest   = tcp->source;
     r_tcp->ack_seq = htonl( ntohl(tcp->seq)+1 ); // all of this to just add 1
     r_tcp->seq = htonl( 0xAAAABBBB );
     r_tcp->ack = 1;
     r_tcp->check = 0; 
     
     //recompute the checksums
     r_ip->check = checksum( (uint16_t *) res, 4 * ip->ihl);
     
     // pass by value here is weird, but we try...
     r_tcp->check = tcp4_checksum (res, r_ip, r_tcp, n - 4*ip->ihl - 4*tcp->doff);
     
     // Send packet.
     if (sendto (sockfd, res, n, 0,
		    sin, sizeof (struct sockaddr_in)) < 0)  {
	  perror ("sendto() failed ");
	  exit (1);
     }

}



// Build IPv4 TCP pseudo-header and call checksum function.
//   This could be done with zero copying, but it's left as an exercise
//   (or look it up in a TCP implementation).
uint16_t
tcp4_checksum (char *packet, struct iphdr *ip, struct tcphdr *tcp, int tcp_payload_len)
{
     char buf[IP_MAXPACKET];
     char *ptr;
     int chksumlen = 0;

     // ptr points to beginning of buffer buf
     ptr = &buf[0];
     
     struct pseudo_tcp_header
     {
	  in_addr_t saddr, daddr;
	  u_char reserved;
	  u_char protocol;
	  u_short tcp_size;
     } ps_head;

     uint16_t header_len = 4*tcp->doff;
     
     ps_head.saddr    = ip->saddr;
     ps_head.daddr    = ip->daddr;
     ps_head.reserved = 0;
     ps_head.protocol = ip->protocol;
     ps_head.tcp_size = htons(header_len + tcp_payload_len);
     
     /*
     // Copy source IP address into buf (32 bits)
     memcpy (ptr, &ip->saddr, sizeof (in_addr_t));
     ptr += sizeof (in_addr_t);
     
     // Copy destination IP address into buf (32 bits)
     memcpy (ptr, &ip->daddr, sizeof (in_addr_t));
     ptr += sizeof (in_addr_t);
     
     // Copy reserved zero field to buf (8 bits)
     *ptr = 0; ptr++;
     
     // Copy transport layer protocol to buf (8 bits). That's IPPROTO_TCP.
     memcpy (ptr, &ip->protocol, sizeof (ip->protocol));
     ptr += sizeof (ip->protocol);
     
     // Copy TCP length to buf (16 bits). That's header length + payload length
     int header_len = 4*tcp->doff;
     uint16_t svalue = htons(header_len + tcp_payload_len);
     memcpy (ptr, &svalue, sizeof (svalue));
     ptr += sizeof (svalue); */

     memcpy(ptr, &ps_head, sizeof(struct pseudo_tcp_header));
     ptr += sizeof(struct pseudo_tcp_header);
     
     // now copy TCP header
     memcpy (ptr, tcp, header_len);   // this must be result's TCP header!
     ptr += header_len;

     // ... and the TCP payload. 
     memcpy (ptr, packet + ip->ihl*4 + header_len, tcp_payload_len);
     ptr += tcp_payload_len;

     chksumlen = ptr - buf;
     
     return checksum ((uint16_t *) buf, chksumlen);
}

// Computing the internet checksum (RFC 1071).
uint16_t
checksum (uint16_t *addr, int len)
{
     int count = len;
     register uint32_t sum = 0;
     uint16_t answer = 0;

     // Sum up 2-byte values until none or only one byte left.
     while (count > 1) {
	  sum += *(addr++);
	  count -= 2;
     }
     
     // Add left-over byte, if any.
     if (count > 0) {
	  sum += *(uint8_t *) addr;
     }
     
     // Fold 32-bit sum into 16 bits; we lose information by doing this,
     // increasing the chances of a collision.
     // sum = (lower 16 bits) + (upper 16 bits shifted right 16 bits)
     while (sum >> 16) {
	  sum = (sum & 0xffff) + (sum >> 16);
     }
     
     // Checksum is one's compliment of sum.
     answer = ~sum;
     
     return (answer);
}

