#include <winsock2.h>
#include <iphlpapi.h>
#include <cstdio>
#include <cstdlib>
#pragma comment(lib, "IPHLPAPI.lib")

#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))

/* Note: could also use malloc() and free() */

int __cdecl main()
{
	/* Declare and initialize variables */
// It is possible for an adapter to have multiple
// IPv4 addresses, gateways, and secondary WINS servers
// assigned to the adapter.
//
// Note that this sample code only prints out the
// first entry for the IP address/mask, and gateway, and
// the primary and secondary WINS server for each adapter.
	PIP_ADAPTER_INFO pAdapterInfo;
	PIP_ADAPTER_INFO pAdapter = nullptr;
	DWORD dwRetVal = 0;
	UINT i;
	/* variables used to print DHCP time info */
	struct tm newtime {};
	char buffer[32];
	errno_t error;
	FIXED_INFO *FixedInfo;
	ULONG    ulOutBufLen;
	IP_ADDR_STRING *pIPAddr;
	FixedInfo = (FIXED_INFO *)MALLOC(sizeof(FIXED_INFO));
	ulOutBufLen = sizeof(FIXED_INFO);

	if (ERROR_BUFFER_OVERFLOW == GetNetworkParams(FixedInfo, &ulOutBufLen))
	{
		FREE(FixedInfo);
		FixedInfo = (FIXED_INFO *)MALLOC(ulOutBufLen);
	}

	if (dwRetVal = GetNetworkParams(FixedInfo, &ulOutBufLen))
	{
		printf("Call to GetNetworkParams failed. Return Value: %08x\n", dwRetVal);
	}
	else
	{
		printf("Host Name: %s\n", FixedInfo->HostName);
		printf("Domain Name: %s\n", FixedInfo->DomainName);
		printf("DNS Servers:\n");
		printf("\t%s\n", FixedInfo->DnsServerList.IpAddress.String);
		pIPAddr = FixedInfo->DnsServerList.Next;

		while (pIPAddr)
		{
			printf("\t%s\n", pIPAddr->IpAddress.String);
			pIPAddr = pIPAddr->Next;
		}
	}

	if (FixedInfo)
	{
		FREE(FixedInfo);
	}

	ulOutBufLen = sizeof(IP_ADAPTER_INFO);
	pAdapterInfo = (IP_ADAPTER_INFO *)MALLOC(sizeof(IP_ADAPTER_INFO));

	if (pAdapterInfo == nullptr)
	{
		printf("Error allocating memory needed to call GetAdaptersinfo\n");
		return 1;
	}

	// Make an initial call to GetAdaptersInfo to get
	// the necessary size into the ulOutBufLen variable
	if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW)
	{
		FREE(pAdapterInfo);
		pAdapterInfo = (IP_ADAPTER_INFO *)MALLOC(ulOutBufLen);

		if (pAdapterInfo == nullptr)
		{
			printf("Error allocating memory needed to call GetAdaptersinfo\n");
			return 1;
		}
	}

	if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR)
	{
		pAdapter = pAdapterInfo;

		while (pAdapter)
		{
			printf("\tComboIndex: \t%d\n", pAdapter->ComboIndex);
			printf("\tAdapter Name: \t%s\n", pAdapter->AdapterName);
			printf("\tAdapter Desc: \t%s\n", pAdapter->Description);
			printf("\tAdapter Addr: \t");

			for (i = 0; i < pAdapter->AddressLength; i++)
			{
				if (i == (pAdapter->AddressLength - 1))
				{
					printf("%.2X\n", (int)pAdapter->Address[i]);
				}
				else
				{
					printf("%.2X-", (int)pAdapter->Address[i]);
				}
			}

			printf("\tIndex: \t%d\n", pAdapter->Index);
			printf("\tType: \t");

			switch (pAdapter->Type)
			{
				case MIB_IF_TYPE_OTHER:
					printf("Other\n");
					break;

				case MIB_IF_TYPE_ETHERNET:
					printf("Ethernet\n");
					break;

				case MIB_IF_TYPE_TOKENRING:
					printf("Token Ring\n");
					break;

				case MIB_IF_TYPE_FDDI:
					printf("FDDI\n");
					break;

				case MIB_IF_TYPE_PPP:
					printf("PPP\n");
					break;

				case MIB_IF_TYPE_LOOPBACK:
					printf("Lookback\n");
					break;

				case MIB_IF_TYPE_SLIP:
					printf("Slip\n");
					break;

				default:
					printf("Unknown type %ld\n", pAdapter->Type);
					break;
			}

			printf("\tIP Address: \t%s\n",
			       pAdapter->IpAddressList.IpAddress.String);
			printf("\tIP Mask: \t%s\n", pAdapter->IpAddressList.IpMask.String);
			printf("\tGateway: \t%s\n", pAdapter->GatewayList.IpAddress.String);
			printf("\t***\n");

			if (pAdapter->DhcpEnabled)
			{
				printf("\tDHCP Enabled: Yes\n");
				printf("\t  DHCP Server: \t%s\n",
				       pAdapter->DhcpServer.IpAddress.String);
				printf("\t  Lease Obtained: ");
				/* Display local time */
				error = _localtime32_s(&newtime, (__time32_t *)&pAdapter->LeaseObtained);

				if (error)
				{
					printf("Invalid Argument to _localtime32_s\n");
				}
				else
				{
					// Convert to an ASCII representation
					error = asctime_s(buffer, 32, &newtime);

					if (error)
					{
						printf("Invalid Argument to asctime_s\n");
					}
					else
						/* asctime_s returns the string terminated by \n\0 */
					{
						printf("%s", buffer);
					}
				}

				printf("\t  Lease Expires:  ");
				error = _localtime32_s(&newtime, (__time32_t *)&pAdapter->LeaseExpires);

				if (error)
				{
					printf("Invalid Argument to _localtime32_s\n");
				}
				else
				{
					// Convert to an ASCII representation
					error = asctime_s(buffer, 32, &newtime);

					if (error)
					{
						printf("Invalid Argument to asctime_s\n");
					}
					else
						/* asctime_s returns the string terminated by \n\0 */
					{
						printf("%s", buffer);
					}
				}
			}
			else
			{
				printf("\tDHCP Enabled: No\n");
			}

			if (pAdapter->HaveWins)
			{
				printf("\tHave Wins: Yes\n");
				printf("\t  Primary Wins Server:    %s\n",
				       pAdapter->PrimaryWinsServer.IpAddress.String);
				printf("\t  Secondary Wins Server:  %s\n",
				       pAdapter->SecondaryWinsServer.IpAddress.String);
			}
			else
			{
				printf("\tHave Wins: No\n");
			}

			pAdapter = pAdapter->Next;
			printf("\n");
		}
	}
	else
	{
		printf("GetAdaptersInfo failed with error: %d\n", dwRetVal);
	}

	if (pAdapterInfo)
	{
		FREE(pAdapterInfo);
	}

	return 0;
}
