/*
	file2pcap
	Code by warlord
	www.nologin.org
	Started July 2009
	Version 0.85
*/


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define  __FAVOR_BSD
#include <netinet/ip.h>
#include <netinet/tcp.h>




#define VERSION 	"0.85"
#define INTERVAL	13000	//About 70 packets per second


struct 	{
		int PCAPMAGIC;
		const short major_version;
		short minor_version;
		int GMT;
		int timestamp_granularity;
		int entire_packet;
		int assume_ethernet;
	} pcap_header = { 0xA1B2C3D4,2,4,0,0,65535,1};

 struct packet_header 
	{
		int time;
		int usec;
		int length1;
		int length2;
	} ph;

int seq, ack_seq;
FILE *infile, *outfile;
unsigned short sport;
struct stat fileStat;	


/***************************************************************************************/


void usage() {
	char *helptext = 	"\nfile2pcap - written by warlord / nologin.org\n"\
				"Takes a file as input and creates a pcap showing a client downloading the specified file from a webserver.\n"\
				"Use: ./file2pcap infile outfile.pcap\n";

	printf("%s", helptext);
	printf("Version: %s\n\n", VERSION);


exit(0);
return;
}


/*******************************************************************************************************/


int rdtsc()
{
    __asm__ __volatile__("rdtsc");
}


/******************************************************************************************************/

unsigned short ipChecksum (unsigned short *ptr, int nbytes)
{
	register long sum = 0;
	register u_short answer;
	u_short oddbyte;

	while (nbytes > 1)
	{
		sum += *ptr++;
		nbytes -= 2;
	}

	if (nbytes == 1)
	{
		oddbyte = 0;
		*((u_char *) & oddbyte) = *(u_char *) ptr;
		sum += oddbyte;
	}

	sum = (sum >> 16) + (sum & 0xffff);
	sum += (sum >> 16);
	answer = ~sum;
	return (answer);
}


/*****************************************************************/
// direction: 0 == client to server. 1 == server to client.

int craftTcp(char *payload, int payloadSize, char direction, unsigned char flags) {
	unsigned int minimalLength;
	size_t packetLen;
	unsigned char packet[(packetLen = sizeof (struct ip) + sizeof (struct tcphdr) + payloadSize)];
	unsigned char minimalip[(minimalLength = 12 + sizeof (struct tcphdr) + payloadSize)];
	struct ip *iphdr = (struct ip *) packet;
	struct tcphdr *tcpheader = (struct tcphdr *) ((unsigned char *) packet + sizeof (struct ip));

	
	memset (packet, 0, packetLen);
	
	if(payload!=NULL)
		memcpy(((unsigned char*)(packet)) +sizeof (struct ip) + sizeof (struct tcphdr), payload, payloadSize);

	memset (minimalip, 0, minimalLength);
	if(direction==0)
	{
		*((unsigned long *) ((unsigned char *) minimalip + 0)) = inet_addr("192.168.0.1");
	        *((unsigned long *) ((unsigned char *) minimalip + 4)) = inet_addr("204.15.227.178");
	}
	else
	{
		*((unsigned long *) ((unsigned char *) minimalip + 0)) = inet_addr("204.15.227.178");
		*((unsigned long *) ((unsigned char *) minimalip + 4)) = inet_addr("192.168.0.1");
	}
	
	*((unsigned char *) ((unsigned char *) minimalip + 8)) = 0;
	*((unsigned char *) ((unsigned char *) minimalip + 9)) = IPPROTO_TCP;
	*((unsigned short *) ((unsigned char *) minimalip + 10)) = htons (packetLen - sizeof (struct ip));

	iphdr->ip_v = 4;
	iphdr->ip_hl = 5;

	//FIXME???? looks like thats ok
	srand(rdtsc());
	iphdr->ip_id = rand() & 0xFFFF;
	
	if(direction == 0)
	{
		iphdr->ip_src.s_addr = inet_addr("192.168.0.1");
		iphdr->ip_dst.s_addr = inet_addr("204.15.227.178");
	}
	else
	{
		iphdr->ip_src.s_addr = inet_addr("204.15.227.178");
		iphdr->ip_dst.s_addr = inet_addr("192.168.0.1");	
	}
	iphdr->ip_p = IPPROTO_TCP;
	iphdr->ip_ttl = 64;
	iphdr->ip_len = ntohs(packetLen);
	iphdr->ip_sum=0;
	iphdr->ip_sum = ipChecksum ((u_short *) iphdr, sizeof (struct ip));

	if(direction == 0)
	{
		tcpheader->th_sport = htons(sport);
		tcpheader->th_dport = htons(80);
	}
	else
	{
		tcpheader->th_sport = htons(80);
		tcpheader->th_dport = htons(sport);
	}
	

	if(direction==0)
	{
		tcpheader->th_seq = htonl(seq);
		tcpheader->th_ack = htonl(ack_seq);
	}
	else
	{
		tcpheader->th_seq = htonl(ack_seq);
		tcpheader->th_ack = htonl(seq);
	}

	tcpheader->th_off = ((sizeof (struct tcphdr)) / 4);

	tcpheader->th_flags = flags;	

	tcpheader->th_win = htons (5840);

	memcpy (minimalip + 12, ((unsigned char *) packet) + sizeof (struct ip), packetLen - sizeof (struct ip));
	tcpheader->th_sum = ipChecksum ((u_short *) & minimalip, minimalLength);

	write(fileno(outfile), packet, packetLen);

	if(direction==0)
		seq=seq+payloadSize;
	else
		ack_seq+=payloadSize;
	

return 0;
}


/**********************************************************************************************/


int write_handshake() {
	unsigned char ethernet_header[] = "\x00\x11\x22\x33\x44\x55\x00\x55\x44\x33\x22\x11\x08\x00";
	unsigned char ethernet_header_ret[] = "\x00\x55\x44\x33\x22\x11\x00\x11\x22\x33\x44\x55\x08\x00";
	int packetLen = (sizeof(ethernet_header)-1 + sizeof(struct ip) + sizeof(struct tcphdr));


       	ph.usec += INTERVAL;	
       	ph.length1 = packetLen;
       	ph.length2 = packetLen;


	srand(rdtsc());

	seq=rand() & 0xfff;
	seq+=1;
	ack_seq=0;

	//client to server SYN
	write(fileno(outfile), &ph, sizeof(struct packet_header));
	write(fileno(outfile), ethernet_header, sizeof(ethernet_header)-1);
       	craftTcp(NULL, 0, 0, TH_SYN);  //direction 0 - client to server

	srand(rdtsc());
	ack_seq=rand() & 0xfff;
	ack_seq+=1;
	seq+=1;

	//and now send the SYN/ACK
	ph.usec+=INTERVAL;
       	write(fileno(outfile), &ph, sizeof(struct packet_header));
       	write(fileno(outfile), ethernet_header_ret, sizeof(ethernet_header_ret)-1);
	craftTcp(NULL,0,1, TH_SYN|TH_ACK);	//direction 1 - server to client

	ack_seq+=1;

	//client to server ACK
       	ph.usec += INTERVAL;	
	write(fileno(outfile), &ph, sizeof(struct packet_header));
	write(fileno(outfile), ethernet_header, sizeof(ethernet_header)-1);
       	craftTcp(NULL, 0, 0, TH_ACK);  //direction 0 - client to server
	
return 0;
}

/*******************************************************************************************/

int write_get_request(char *filename) {
	char request_tail[] = 	"Host: wrl\r\n"
				"User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.17) Gecko/20081007 Firefox/2.0.0.17\r\n"
				"Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5\r\n"
				"Accept-Language: en-us,en;q=0.5\r\n"
				"Accept-Encoding: gzip,deflate\r\n"
				"Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"
				"Keep-Alive: 300\r\n"
				"Connection: keep-alive\r\n\r\n";
	unsigned char ethernet_header[] = "\x00\x11\x22\x33\x44\x55\x00\x55\x44\x33\x22\x11\x08\x00";
	unsigned char ethernet_header_ret[] = "\x00\x55\x44\x33\x22\x11\x00\x11\x22\x33\x44\x55\x08\x00";
	char tmp[650];
	int packetLen;


        snprintf(tmp, sizeof(tmp)-1,"GET /wrl/%s HTTP/1.1\r\n", filename);
	strcat(tmp, request_tail);
	packetLen = sizeof(ethernet_header)-1 + sizeof(struct ip) + sizeof(struct tcphdr) + strlen(tmp);

        ph.usec += INTERVAL;
        ph.length1 = packetLen;
        ph.length2 = packetLen;


	write(fileno(outfile), &ph, sizeof(struct packet_header));
	write(fileno(outfile), ethernet_header, sizeof(ethernet_header)-1);

        craftTcp(tmp, strlen(tmp), 0, TH_ACK|TH_PUSH);  //direction 0 - client to server
	
	//and now send the ack
	ph.usec+=INTERVAL;
	ph.length1=packetLen-strlen(tmp);
	ph.length2=packetLen-strlen(tmp);
        write(fileno(outfile), &ph, sizeof(struct packet_header));
        write(fileno(outfile), ethernet_header_ret, sizeof(ethernet_header_ret)-1);

	craftTcp(NULL,0,1, TH_ACK);	//direction 1 - server to client


return(0);
}

/******************************************************************************************/

int write_httpd_header() {
	char http_header[] = 	"HTTP/1.1 200 Ok\r\n"
				"Date: Wed, 29 Jul 2009 13:35:26 GMT\r\n"
				"Server: Apache/2.2.3 (Debian) PHP/5.2.0-8+etch10 mod_ssl/2.2.3 OpenSSL/0.9.8c\r\n"
				"Last-Modified: Sun, 20 Jan 2008 12:01:21 GMT\r\n"
				"ETag: \"a801c-1bbd1c-22416640\"\r\n"
				"Accept-Ranges: bytes\r\n"
				"Content-Length: ";
	char http_header_2[] =	"\r\n"
				"Keep-Alive: timeout=15, max=99\r\n"
				"Connection: Keep-Alive\r\n"
				"Content-Type: application/octet-stream\r\n\r\n";
	char headerBuffer[500];
	unsigned char ethernet_header[] = "\x00\x11\x22\x33\x44\x55\x00\x55\x44\x33\x22\x11\x08\x00";
	unsigned char ethernet_header_ret[] = "\x00\x55\x44\x33\x22\x11\x00\x11\x22\x33\x44\x55\x08\x00";
	int packetLen;

	snprintf(headerBuffer,sizeof(headerBuffer)-1,"%s%d%s", http_header, (unsigned int)fileStat.st_size, http_header_2);


	packetLen = sizeof(ethernet_header)-1 + sizeof(struct ip) + sizeof(struct tcphdr) + strlen(headerBuffer) ;



        ph.usec += INTERVAL;
        ph.length1 = packetLen;
        ph.length2 = packetLen;


	write(fileno(outfile), &ph, sizeof(struct packet_header));
	write(fileno(outfile), ethernet_header_ret, sizeof(ethernet_header_ret)-1);

        craftTcp(headerBuffer, strlen(headerBuffer), 1, TH_ACK|TH_PUSH);  //direction 1 - server to client
	
	//and now send the ack
	ph.usec+=INTERVAL;
	ph.length1=packetLen-strlen(headerBuffer);
	ph.length2=packetLen-strlen(headerBuffer);
        write(fileno(outfile), &ph, sizeof(struct packet_header));
        write(fileno(outfile), ethernet_header, sizeof(ethernet_header)-1);

	craftTcp(NULL,0,0, TH_ACK);	//direction 0 - client to server



return(0);
}

/******************************************************************************************/

int write_file() {
	unsigned char ethernet_header[] = "\x00\x11\x22\x33\x44\x55\x00\x55\x44\x33\x22\x11\x08\x00";
	unsigned char ethernet_header_ret[] = "\x00\x55\x44\x33\x22\x11\x00\x11\x22\x33\x44\x55\x08\x00";
	int packetLen = (sizeof(ethernet_header)-1 + sizeof(struct ip) + sizeof(struct tcphdr));
	unsigned int count;
	char buffer[1500];


	while(!(feof(infile)))
	{
		count=read(fileno(infile), buffer, 1200);
		
		if(count<=0)
		{
			return -1;
		}

        	ph.usec += INTERVAL;
		if((ph.usec + INTERVAL) >= 1000000)
		{
			ph.time+=1;
			ph.usec=0;
		}


        	ph.length1 = packetLen + count;
        	ph.length2 = packetLen + count;


		write(fileno(outfile), &ph, sizeof(struct packet_header));
		write(fileno(outfile), ethernet_header_ret, sizeof(ethernet_header_ret)-1);

        	craftTcp(buffer, count, 1, TH_ACK|TH_PUSH);  //direction 1 - server to client
		
		//and now send the ack
		ph.usec+=INTERVAL;
		ph.length1=packetLen;
		ph.length2=packetLen;
        	write(fileno(outfile), &ph, sizeof(struct packet_header));
        	write(fileno(outfile), ethernet_header, sizeof(ethernet_header)-1);

		craftTcp(NULL,0,0, TH_ACK);	//direction 0 - client to server
	}
printf("\n");


return(0);
}

/******************************************************************************************/

int write_termination() {
	unsigned char ethernet_header[] = "\x00\x11\x22\x33\x44\x55\x00\x55\x44\x33\x22\x11\x08\x00";
	unsigned char ethernet_header_ret[] = "\x00\x55\x44\x33\x22\x11\x00\x11\x22\x33\x44\x55\x08\x00";
	int packetLen = (sizeof(ethernet_header)-1 + sizeof(struct ip) + sizeof(struct tcphdr));


       	ph.usec += INTERVAL;
       	ph.length1 = packetLen;
       	ph.length2 = packetLen;


	//server to client FIN
	write(fileno(outfile), &ph, sizeof(struct packet_header));
	write(fileno(outfile), ethernet_header_ret, sizeof(ethernet_header_ret)-1);
       	craftTcp(NULL, 0, 1, TH_ACK|TH_FIN);  //direction 1 - server to client

	ack_seq+=1;
		
	//and now send the ack
	ph.usec+=INTERVAL;
       	write(fileno(outfile), &ph, sizeof(struct packet_header));
       	write(fileno(outfile), ethernet_header, sizeof(ethernet_header)-1);
	craftTcp(NULL,0,0, TH_ACK);	//direction 0 - client to server

	//client to server FIN
	ph.usec+=INTERVAL;
       	write(fileno(outfile), &ph, sizeof(struct packet_header));
	write(fileno(outfile), ethernet_header, sizeof(ethernet_header)-1);
       	craftTcp(NULL, 0, 0, TH_ACK|TH_FIN);  //direction 0 - client to server
	
	seq+=1;	

	//and now send the ack
	ph.usec+=INTERVAL;
       	write(fileno(outfile), &ph, sizeof(struct packet_header));
       	write(fileno(outfile), ethernet_header_ret, sizeof(ethernet_header_ret)-1);
	craftTcp(NULL,0,1, TH_ACK);	//direction 1 - server to client


	printf("Pcap replay length: %d second(s)\n", ph.time - 0x48f35358 + 1);

return(0);
}





/********************************************************************************************/

int main(int argc, char **argv) {

	
	if(argc < 3)
		usage();


	if((infile = fopen(argv[1], "r"))==NULL)
	{
		printf("Failed to open infile %s\n", argv[1]);
		exit(-1);
	}

	if((outfile = fopen(argv[2], "w"))==NULL)
	{
		printf("Failed to open outfile %s\n", argv[1]);
		exit(-1);
	}

	write(fileno(outfile), &pcap_header,sizeof(pcap_header));

	if(fstat(fileno(infile), &fileStat) == -1)
	{
		printf("Failed to read size of %s\n", argv[1]);
		exit(-1);
	}

	
	srand(rdtsc());
	sport = rand() & 0x7fff;	//0 <-> 32k
	sport +=1025;

        ph.time = 0x48f35358;
	ph.usec=0;
 

	write_handshake();
	write_get_request(argv[1]);
	write_httpd_header();
	write_file();	//Magic happens here	
	write_termination();	

	fclose(infile);
	fclose(outfile);

	
exit(0);
}


