#include "winsock_error.h"
#include "io_ext.h"
#include <cstdio>
#define SOCK_NUM 3
#pragma comment(lib, "ws2_32.lib")
#pragma warning(disable : 4996)
int main()
{
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_INTENSITY);
	WSADATA filler;
	if (WSAStartup(WSA_VERSION, &filler))
	{
		output_error("Couldn't initialize WSA");
		return EXIT_FAILURE;
	}
	char host_name[MAXGETHOSTSTRUCT];
	enter_message("Please enter a host name: ", host_name);
	std::cout << std::endl;
	LPHOSTENT remote_host_info = gethostbyname(host_name);
	if (remote_host_info == NULL)
	{
		output_error("Couldn't get a host info");
		finalize();
		return EXIT_FAILURE;
	}
	if (remote_host_info->h_addrtype == AF_INET6)
	{
		output_error("Sorry, but R-Scan can't perform scanning of ipv6-hosts");
		finalize();
		return EXIT_SUCCESS;
	}
	//ALIASES SHOWING
	if (remote_host_info->h_aliases[0])
	{
		std::cout << "Server name aliases: " << std::endl;
		for (int i = 0; remote_host_info->h_aliases[i]; i++)
			std::cout << remote_host_info->h_aliases[i] << std::endl;
		std::cout << std::endl;
	}
	else
		std::cout << "Host has no aliases" << std::endl << std::endl;
	in_addr tmp;
	//ADDRESSES SHOWING
	if (remote_host_info->h_addr_list[1])
	{
		std::cout << "Server addresses: " << std::endl;
		for (int i = 0; remote_host_info->h_addr_list[i]; i++)
		{
			tmp.s_addr = *(LPWORD)remote_host_info->h_addr_list[i];
			std::cout << inet_ntoa(tmp) << std::endl;
		}
		std::cout << std::endl;
	}
	else
	{
		std::cout << "Server address: ";
		tmp.s_addr = *(u_long*)remote_host_info->h_addr;
		std::cout << inet_ntoa(tmp) << std::endl << std::endl;
	}
	WORD temp;
	BYTE max_hops_num;
	//MAX HOPS NUM INPUT
	while (true)
	{
		std::cout << "Please enter max number of hops: ";
		(std::cin >> temp).get();
		if (!std::cin || temp > TTL_MAX_VAL || temp == 0)
		{
			clear_istream(std::cin);
			std::cerr << "You entered wrong value; try again" << std::endl << std::endl;
		}
		else
			break;
	}
	__asm //firstly value is assigned to 16 bit temporary variable, then left octet of temporary variable is assigned to max_hops_num variable
	{
		mov ax, temp
		mov max_hops_num, al
	}
	fd_set read;
	SOCKET sock = ICMP_SOCKET;
	sockaddr_in server_data;
	ECHO_REQUEST echo_req;
	ECHO_REPLY echo_rep;
	timeval wait_time = { 2, 0 };
	//CONNECTION DATA FILLING
	server_data.sin_family = AF_INET;
	server_data.sin_port = 0;
	tmp.s_addr = *(LPWORD)&remote_host_info->h_addr;
	inet_pton(AF_INET, (PCSTR)inet_ntoa(tmp), &server_data.sin_addr);
	//ICMP HDR AND ECHO_REQUEST STRUCT FILLING
	echo_req.icmp_hdr.type = ICMP_ECHO_REQ;
	echo_req.icmp_hdr.code = 0;
	echo_req.icmp_hdr.id = 1;
	echo_req.icmp_hdr.seq = 0;
	echo_req.icmp_hdr.checksum = 0;
	echo_req.dw_time = GetTickCount();
	memset(echo_req.data, ECHO_FILLER, PACK_FRAME_SIZE);
	echo_req.icmp_hdr.checksum = checksum((LPWORD)&echo_req, sizeof(echo_req));
	//TRACERT LOOP
	for (BYTE TTL = 1; TTL <= max_hops_num; TTL++)
	{
		tmp.s_addr = 0;
		printf("%3d\t", TTL);
		//set TTL
		if (setsockopt(sock, IPPROTO_IP, IP_TTL, (PCSTR)&TTL, sizeof(TTL)) == SOCKET_ERROR)
		{
			error_to_close_socket("Couldn't set IP header TTL", sock);
			finalize();
			return EXIT_FAILURE;
		}
		//send 3 ICMP echo requests
		RANGE(0, SOCK_NUM)
		{
			echo_req.dw_time = GetTickCount();
			echo_req.icmp_hdr.checksum = checksum((LPWORD)&echo_req, sizeof(echo_req));
			if (sendto(sock, (LPSTR)&echo_req, sizeof(echo_req), 0, (LPSOCKADDR)&server_data, sizeof(server_data)) == SOCKET_ERROR)
			{
				error_to_close_socket("Couldn't perform a ping", sock);
				finalize();
				return EXIT_FAILURE;
			}
			FD_ZERO(&read);
			FD_SET(sock, &read);
			int ret = select(0, &read, NULL, NULL, &wait_time);
			if (ret == SOCKET_ERROR)
			{
				error_to_close_socket("Couldn't perform data awaiting", sock);
				finalize();
				return EXIT_FAILURE;
			}
			else if (ret == 0)
			{
				std::cout << " * \t";
			}
			else
			{
				if (recvfrom(sock, (LPSTR)&echo_rep, sizeof(ECHO_REPLY), 0, NULL, NULL) == SOCKET_ERROR)
				{
					error_to_close_socket("Couldn't receive ICMP", sock);
					finalize();
					return EXIT_FAILURE;
				}
				printf("%4d ms\t", GetTickCount() - echo_rep.echo_request.dw_time);
				tmp = echo_rep.ip_hdr.src_addr;
			}
		}
		if (tmp.s_addr == 0)
		{
			std::cout << "Waiting time exceeded" << std::endl;
			continue;
		}
		remote_host_info = gethostbyaddr((PCSTR)&echo_rep.ip_hdr.src_addr, AF_INET_ADDR_LEN, AF_INET);
		if (remote_host_info == NULL)
			std::cout << inet_ntoa(echo_rep.ip_hdr.src_addr) << std::endl;
		else
			printf("%s [%s]", remote_host_info->h_name, inet_ntoa(echo_rep.ip_hdr.src_addr));
		if (echo_rep.echo_request.icmp_hdr.type == 0 && echo_rep.echo_request.icmp_hdr.code == 0)
			break;
	}
}