#include "precomp.h"
#include "common_metapi.h"
#include <stdio.h>

#include <winsock2.h>
#include <ws2tcpip.h>

DWORD resolve_host(LPCSTR hostname, u_short ai_family, struct in_addr *result, struct in6_addr *result6)
{
	struct addrinfo hints, *list;
	struct in_addr addr;
	struct in6_addr addr6;
	struct sockaddr_in *sockaddr_ipv4;
	struct sockaddr_in6 *sockaddr_ipv6;
	int iResult;

	WSADATA wsaData;
	iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
	if (iResult != NO_ERROR)
	{
		dprintf("Could not initialise Winsock: %x.", iResult);
		return iResult;
	}

	memset(&hints, 0, sizeof(hints));
	hints.ai_socktype = SOCK_DGRAM;
	hints.ai_protocol = IPPROTO_UDP;
	hints.ai_family = ai_family;

	dprintf("Attempting to resolve '%s'", hostname);

	iResult = getaddrinfo(hostname, NULL, &hints, &list);

	if (iResult != NO_ERROR)
	{
		dprintf("Unable to resolve host Error: %x.", iResult);
		dprintf("Error msg: %s", gai_strerror(iResult));
	}
	else
	{
		switch (list->ai_family) {
		case AF_INET:
			sockaddr_ipv4 = (struct sockaddr_in *) list->ai_addr;
			addr = sockaddr_ipv4->sin_addr;
			memcpy((void*)result, &addr, sizeof(result));
		case AF_INET6:
			sockaddr_ipv6 = (struct sockaddr_in6 *) list->ai_addr;
			addr6 = sockaddr_ipv6->sin6_addr;
			memcpy((void*)result6, &addr6, sizeof(struct in6_addr));
		default:
			break;
		}
	}

	freeaddrinfo(list);
	WSACleanup();

	return iResult;
}

DWORD request_resolve_host(Remote *remote, Packet *packet)
{
	Packet *response = met_api->packet.create_response(packet);
	LPCSTR hostname = NULL;
	struct in_addr addr;
	struct in6_addr addr6;
	u_short ai_family = AF_INET;
	int iResult;

	hostname = met_api->packet.get_tlv_value_string(packet, TLV_TYPE_HOST_NAME);

	if (!hostname)
	{
		iResult = ERROR_INVALID_PARAMETER;
		dprintf("Hostname not set");
	}
	else
	{
		ai_family = met_api->packet.get_tlv_value_uint(packet, TLV_TYPE_ADDR_TYPE);
		iResult = resolve_host(hostname, ai_family, &addr, &addr6);
		if (iResult == NO_ERROR)
		{
			if (ai_family == AF_INET)
			{
				met_api->packet.add_tlv_raw(response, TLV_TYPE_IP, &addr, sizeof(struct in_addr));
			} else {
				met_api->packet.add_tlv_raw(response, TLV_TYPE_IP, &addr6, sizeof(struct in_addr6));
			}
			met_api->packet.add_tlv_uint(response, TLV_TYPE_ADDR_TYPE, ai_family);
		}
		else
		{
			dprintf("Unable to resolve_host %s error: %x", hostname, iResult);
		}
	}

	met_api->packet.transmit_response(iResult, remote, response);
	return ERROR_SUCCESS;
}

DWORD request_resolve_hosts(Remote *remote, Packet *packet)
{
	Packet *response = met_api->packet.create_response(packet);
	Tlv hostname = {0};
	int index = 0;
	int iResult;
	u_short ai_family = met_api->packet.get_tlv_value_uint(packet, TLV_TYPE_ADDR_TYPE);

	while( met_api->packet.enum_tlv( packet, index++, TLV_TYPE_HOST_NAME, &hostname ) == ERROR_SUCCESS )
	{
		struct in_addr addr = {0};
		struct in6_addr addr6 = {0};

		iResult = resolve_host((LPCSTR)hostname.buffer, ai_family, &addr, &addr6);

		if (iResult == NO_ERROR)
		{
			if (ai_family == AF_INET)
			{
				met_api->packet.add_tlv_raw(response, TLV_TYPE_IP, &addr, sizeof(struct in_addr));
			} else {
				met_api->packet.add_tlv_raw(response, TLV_TYPE_IP, &addr6, sizeof(struct in_addr6));
			}
		}
		else
		{
			dprintf("Unable to resolve_host %s error: %x", hostname.buffer, iResult);
			met_api->packet.add_tlv_raw(response, TLV_TYPE_IP, NULL, 0);
		}
		met_api->packet.add_tlv_uint(response, TLV_TYPE_ADDR_TYPE, ai_family);
	}

	met_api->packet.transmit_response(NO_ERROR, remote, response);
	return ERROR_SUCCESS;
}