#include <stdio.h>
#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#include <mstcpip.h>
#else
#include <stdlib.h>
#include <unistd.h>
#include <memory.h>
#include <netdb.h>
#include <signal.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <arpa/inet.h>
#ifndef __APPLE__
#include <linux/if_ether.h> 
#endif
#include <pcap.h>
#include <pthread.h>
#endif
#include <time.h>
#include "pscan.h"
#include "tblmgr.h"


#ifdef WIN32
#define PrintError(s)	fprintf(stderr, "%sError:%d\n", s,GetLastError())
#define sleep			Sleep
#define getprocessid	GetCurrentProcessId
#define HTHREAD			HANDLE
#define PROTO			IPPROTO_IP
#else
#define PrintError(s)	perror(s)
#define sleep(n)		usleep(n*1000)
#define closesocket		close
#define getprocessid	getpid
#define HTHREAD			pthread_t	//pid_t
#define PROTO			IPPROTO_TCP
#define SIZEOF_ETH_HDR	14		// size of ethernet header

typedef unsigned int	SOCKET;
typedef int				BOOL;
pcap_t *pcapHandle;
struct pcap_pkthdr pkt_header;
pcap_t* pcapStartup (char *);
#endif

// ip header
typedef struct ip_hdr
{
	unsigned char ip_header_len:4; 
	unsigned char ip_version :4;
	unsigned char ip_tos;
	unsigned short ip_total_length; 
	unsigned short ip_id;
	unsigned char ip_frag_offset :5; 
	unsigned char ip_more_fragment :1;
	unsigned char ip_dont_fragment :1;
	unsigned char ip_reserved_zero :1;
	unsigned char ip_frag_offset1; 
	unsigned char ip_ttl;
	unsigned char ip_protocol;
	unsigned short ip_checksum;
	unsigned int ip_srcaddr;
	unsigned int ip_destaddr;
} IP_HDR;

// TCP header
typedef struct tcp_hdr 
{
	unsigned short tcp_source_port;
	unsigned short tcp_dest_port;
	unsigned int tcp_sequence;
	unsigned int tcp_acknowledge;
	unsigned char tcp_ns :1;
	unsigned char tcp_reserved_part1:3; 
	unsigned char tcp_data_offset:4;

	unsigned char tcp_fin :1;
	unsigned char tcp_syn :1;
	unsigned char tcp_rst :1;
	unsigned char tcp_psh :1;
	unsigned char tcp_ack :1;
	unsigned char tcp_urg :1;
	unsigned char tcp_ecn :1;
	unsigned char tcp_cwr :1;
	
	unsigned short tcp_window;
	unsigned short tcp_checksum;
	unsigned short tcp_urgent;
} TCP_HDR;

// Pseudo Header
struct
{
	unsigned long src;
	unsigned long dst;
	unsigned char mbz;
	unsigned char proto;
	unsigned short len;
}pseudo_hdr;

extern int options;
extern long int* port_list;
extern unsigned short total_scanned;
extern unsigned long IP;		// target IP

struct sockaddr_in srcaddr = {0};
struct sockaddr_in destaddr = {0};
SOCKET SocketSend;
SOCKET SocketRecv;



// compute header checksum
unsigned short checksum(unsigned short *addr, int len)
{
	unsigned long cksum=0;
	while (len > 1)
	{
		cksum += *addr++;
		len -= 2;
	}
	if (len)
		cksum += *(unsigned char *)addr;

	while (cksum >> 16)
		cksum = (cksum & 0xffff) + (cksum >> 16);

	return (unsigned short) ~cksum;
}



// child created to receive datagrams
#ifdef WIN32
DWORD ThreadGetPackets (LPVOID lpdwThreadParam) 
#else
void *ThreadGetPackets()
#endif
{
	unsigned char buf[0xffff]= {0};
	unsigned char *bufptr;
	TCP_HDR *tcp_header;
	IP_HDR *ip_header;
	u_short openport;
 	*buf = 0;

	while(TRUE)
	{
#ifdef WIN32
		recv(SocketRecv, buf, sizeof(buf), 0);
		bufptr = buf;
#else
		if( (bufptr = (unsigned char *)pcap_next(pcapHandle, &pkt_header)) == NULL)
			continue;

		bufptr += SIZEOF_ETH_HDR;
#endif
		ip_header = (IP_HDR *) bufptr;
		bufptr += ip_header->ip_header_len *sizeof(unsigned long);
		tcp_header = (TCP_HDR *) bufptr;

		
		if(ip_header->ip_srcaddr == IP  &&	// filter packets coming from target ip
			tcp_header->tcp_dest_port == getprocessid()  &&	// filter pcks. from this process
			// check header structure and flags syn and ack
			ip_header->ip_header_len == 5  &&  ip_header->ip_protocol == IPPROTO_TCP &&
			tcp_header->tcp_data_offset >= 5  &&
			tcp_header->tcp_syn && tcp_header->tcp_ack
				)
		{
			openport = ntohs(tcp_header->tcp_source_port);
			// skip duplicates
			if(IsTblElement(openport) < 0)
			{
				on_response(OPEN, openport);
				RegisterTblElement(openport);
			}
		}
		
	}
}


// Perform a Syn scan
void SynScan(char *TargetIP, int PortCount)
{
	IP_HDR iphdr;
	TCP_HDR tcphdr;
	char packet[128];
	char *packptr;
	int optval;
 
	HTHREAD Thread;
	int CurrentPort;
	int StartPort=0;	// index of the first port to scan on the array of ports

#ifdef WIN32
	struct hostent *lh;
	unsigned char LocalHost[256];
	unsigned long InBuffer;
	unsigned long nbytes;


	// receive socket
	if((SocketRecv = socket(AF_INET, SOCK_RAW, PROTO)) < 0)
	{
		PrintError("Socket error: ");
		exit(0);
	}
	
	gethostname((char*)LocalHost,sizeof(LocalHost)-1);
	if((lh = (struct hostent *) gethostbyname((char*)LocalHost)) == NULL)
	{
		PrintError("gethostname/gethostbyname error: ");
		exit(0);
	}
	memcpy(&(srcaddr.sin_addr.s_addr), lh->h_addr, lh->h_length);
	srcaddr.sin_family	= AF_INET;
	srcaddr.sin_port	= htons(6464);

	if(bind(SocketRecv, (const struct sockaddr *) &srcaddr, sizeof(srcaddr)))
	{
		PrintError("Bind error: ");
		exit(0);
	}

	InBuffer = RCVALL_ON;
	if(WSAIoctl(SocketRecv, SIO_RCVALL, &InBuffer, sizeof(InBuffer),
		NULL, 0, &nbytes, 0, 0))
	{
		PrintError("ioctl error: ");
		exit(0);
	}

#else
	pcap_if_t *devs;
	char errorbuf[PCAP_ERRBUF_SIZE];

	if ((pcap_findalldevs(&devs, errorbuf)) < 0) 
	{
		PrintError(errorbuf);
		exit(0);
	}

	if(!devs)
	{
		PrintError("This program must be run with root privilege!!!\n");
		exit(0);
	}
	srcaddr = *(struct sockaddr_in *)(devs->addresses->next->addr);

	pcapHandle = pcapStartup(TargetIP);
#endif
 

	// send socket
	if((SocketSend = socket(AF_INET, SOCK_RAW, PROTO)) < 0)
	{
		PrintError("Socket error: ");
		exit(0);
	}

	// include header
	optval = 1;
	if(setsockopt(SocketSend, IPPROTO_IP, IP_HDRINCL, (char *)&optval, sizeof(optval)))
	{
		PrintError("setsockopt error: ");
		exit(0);
	}


	destaddr.sin_family		= AF_INET;
	destaddr.sin_port		= htons((u_short)port_list[StartPort]);
	destaddr.sin_addr.s_addr	= inet_addr(TargetIP);


	// create child to handle recv from all ports of target ip
#ifdef WIN32
	Thread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadGetPackets,0,0,0);
#else
	pthread_create(&Thread, NULL, ThreadGetPackets, 0);
#endif

	// give the child time go come up
	sleep(1000);

	for(CurrentPort = StartPort; CurrentPort < PortCount; CurrentPort++) 
	{
		// setup IP header
		iphdr.ip_header_len		= 5;
		iphdr.ip_version		= 4;
		iphdr.ip_tos			= 0;
		iphdr.ip_total_length	= /*htons*/(sizeof(IP_HDR)+sizeof(TCP_HDR));
		iphdr.ip_id=1; 

		iphdr.ip_frag_offset	= 0;
		iphdr.ip_more_fragment	= 0;
		iphdr.ip_dont_fragment	= 0;
		iphdr.ip_reserved_zero	= 0;
		iphdr.ip_frag_offset1	= 0;
	
		iphdr.ip_ttl			= 128;
		iphdr.ip_protocol		= IPPROTO_TCP;
		iphdr.ip_checksum		= 0;
		iphdr.ip_srcaddr		= srcaddr.sin_addr.s_addr;
		iphdr.ip_destaddr		= destaddr.sin_addr.s_addr;


		// setup TCP header
		tcphdr.tcp_source_port	= (unsigned short)getprocessid(); 	
							// this trick is necessary to avoid multiple processes
							// scanning the same ip to receive back
							// packets for ALL the other processes (UX only)
		tcphdr.tcp_dest_port	= htons((u_short)port_list[CurrentPort]);
		tcphdr.tcp_sequence		= htonl(0x13182CC);
		tcphdr.tcp_acknowledge	= 0;
		tcphdr.tcp_ns			= 0;
		tcphdr.tcp_reserved_part1 = 0;
		tcphdr.tcp_data_offset	= 5;

		tcphdr.tcp_fin = 0;
		tcphdr.tcp_syn = 1;
		tcphdr.tcp_rst = 0;
		tcphdr.tcp_psh = 0;
		tcphdr.tcp_ack = 0;
		tcphdr.tcp_urg = 0;
		tcphdr.tcp_ecn = 0;
		tcphdr.tcp_cwr = 0;

		tcphdr.tcp_window		= 0xffff;
		tcphdr.tcp_checksum		= 0;	// must be set to 0 before computing checksum
		tcphdr.tcp_urgent		= 0;


		// setup TCP pseudo header
		pseudo_hdr.src		= iphdr.ip_srcaddr;
		pseudo_hdr.dst		= iphdr.ip_destaddr;
		pseudo_hdr.mbz		= 0;
		pseudo_hdr.proto	= IPPROTO_TCP;
		pseudo_hdr.len		= htons(sizeof(tcphdr));

		// compute tcp header checksum
		memset(packet, '\0', sizeof(packet));
		packptr = packet;
		memcpy(packptr, &pseudo_hdr, sizeof(pseudo_hdr));
		packptr += sizeof(pseudo_hdr);
		memcpy(packptr, &tcphdr, sizeof(tcphdr));
		tcphdr.tcp_checksum=checksum((unsigned short *)packet,sizeof(pseudo_hdr)+sizeof(tcphdr));

		// prepare send buffer
		packptr = packet;
		memcpy(packptr, &iphdr, sizeof(iphdr));
		packptr += sizeof(iphdr);
		memcpy(packptr, &tcphdr, sizeof(tcphdr));
		packptr += sizeof(tcphdr);
		memset(packptr, 0, sizeof(long));


		// send to remote ip
		if(sendto(SocketSend, packet, sizeof(iphdr)+sizeof(tcphdr), 0,
				(struct sockaddr*) &destaddr, sizeof(destaddr)) < 0)
		{
			PrintError("sendto error: ");
			exit(0);
		}

		total_scanned++;
		sleep(30);		// need to slow down otherwise recv will lose packets
	}


	sleep(1000);	// we have to give the child enough time to receive all packets
#ifdef WIN32
	CloseHandle(Thread);
	closesocket(SocketRecv);
#endif

	closesocket(SocketSend);
}


#ifndef WIN32
pcap_t* pcapStartup (char * TargetIP)
{
	char errorbuf[PCAP_ERRBUF_SIZE];
	char err[512];
	struct bpf_program fp;
	char str[128];
	pcap_t* pcapHandle;

	if ((pcapHandle = pcap_open_live ("eth0", BUFSIZ, 0, 0, errorbuf)) == NULL) 
	{
		sprintf (err, "pcap: open live error: %s -", errorbuf);
		PrintError(err);
		exit (0);
	}

	sprintf(str, "src host %s", TargetIP);
	if (pcap_compile(pcapHandle, &fp, str, 0, 0) == -1) 
	{
		sprintf (err, "pcap: compile error: %s -", pcap_geterr(pcapHandle));
		PrintError(err);
		exit (0);
	}

	if (pcap_setfilter(pcapHandle, &fp) == -1) 
	{
		sprintf (err, "pcap: set filter error: %s -", pcap_geterr(pcapHandle));
		PrintError(err);
		exit (0);
	}
	
	return pcapHandle;
}
#endif
