#include "IPEventHandler.h"

extern DWORD icmping;

USHORT IPv4Checksum(PBYTE buffer, ULONG64 size)
{
	UINT32 sum = 0;
	for (int i = 0; i < size; i += 2)
	{
		sum += (buffer[i] << 8) + buffer[i + 1];
	}

	if ((size % 2) == 1)
	{
		sum += buffer[size - 1] << 8;
	}

	while (sum > 0xffff)
	{
		sum = (sum >> 16) + (sum & 0xffff);
	}

	return ~sum & 0xffff;
}

USHORT ICMPChecksum(PBYTE buffer, ULONG64 size)
{
	UINT32 sum = 0;
	for (int i = 0; i < size; i += 2)
	{
		sum += buffer[i] + (buffer[i + 1] << 8);
	}

	if ((size % 2) == 1)
	{
		sum += buffer[size - 1];
	}

	sum = (sum >> 16) + (sum & 0xffff);
	sum += (sum >> 16);

	return ~sum & 0xffff;
}

void ipSend(const char* buffer, int length, PNF_IP_PACKET_OPTIONS options)
{
	if (options->ip_family != AF_INET ||
		options->ipHeaderSize != 20   ||
		length < 28                   ||
		buffer[options->ipHeaderSize] != 0x08)
	{
		nf_ipPostSend(buffer, length, options);
		return;
	}

	auto data = new BYTE[length]();
	memcpy(data, buffer, length);

	{
		BYTE src[4];
		BYTE dst[4];
		memcpy(src, data + 12, 4);
		memcpy(dst, data + 16, 4);
		memcpy(data + 12, dst, 4);
		memcpy(data + 16, src, 4);
	}

	data[10] = 0x00;
	data[11] = 0x00;
	auto ipv4sum = IPv4Checksum(data, options->ipHeaderSize);
	data[10] = (ipv4sum >> 8);
	data[11] = ipv4sum & 0xff;

	data[options->ipHeaderSize] = 0x00;
	data[options->ipHeaderSize + 2] = 0x00;
	data[options->ipHeaderSize + 3] = 0x00;
	auto icmpsum = ICMPChecksum(data + options->ipHeaderSize, (ULONG64)length - options->ipHeaderSize);
	data[options->ipHeaderSize + 2] = icmpsum & 0xff;
	data[options->ipHeaderSize + 3] = (icmpsum >> 8);

	if (icmping > 0)
		this_thread::sleep_for(chrono::milliseconds(icmping));
	printf("[Redirector][IPEventHandler][ipSend] Fake ICMP response for %d.%d.%d.%d\n", data[12], data[13], data[14], data[15]);

	nf_ipPostReceive((char*)data, length, options);
	delete[] data;
}

void ipReceive(const char* buffer, int length, PNF_IP_PACKET_OPTIONS options)
{
	nf_ipPostReceive(buffer, length, options);
}
