// Description:
//		The following example retrieves the IP address table to determine
//		the interface index for the first adapter, then adds the IP address
//		specified on command line to the first adapter. The IP address that was added is then deleted.
//
// Link to ws2_32.lib
#include <winsock2.h>
#include <ws2tcpip.h>
// Link to 	Iphlpapi.lib
#include <iphlpapi.h>
#include <stdio.h>

// Note: could also use malloc() and free()
#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))

int main(int argc, char **argv)
{
    // Variables used by GetIpAddrTable
    PMIB_IPADDRTABLE pIPAddrTable;
    DWORD dwSize = 0;
    DWORD dwRetVal = 0;
    IN_ADDR IPAddr;
    DWORD ifIndex;

    // IPv4 address and subnet mask we will be adding
    UINT iaIPAddress;
    UINT iaIPMask;

    // Variables where handles to the added IP are returned
    ULONG NTEContext = 0;
    ULONG NTEInstance = 0;

    // Variables used to return error message
    LPVOID lpMsgBuf;

    // Validate the parameters
    if (argc != 3)
	{
        printf("Usage: %s <IPAddress> <SubnetMask>\n", argv[0]);
        exit(1);
    }

    iaIPAddress = inet_addr(argv[1]);
    if (iaIPAddress == INADDR_NONE)
	{
        printf("Usage: %s <IPAddress> <SubnetMask>\n", argv[0]);
        exit(1);
    }

    iaIPMask = inet_addr(argv[2]);
    if (iaIPMask == INADDR_NONE)
	{
        printf("Usage: %s <IPAddress> <SubnetMask>\n", argv[0]);
        exit(1);
    }

    // Before calling AddIPAddress we use GetIpAddrTable to get
    // an adapter to which we can add the IP.
    pIPAddrTable = (MIB_IPADDRTABLE *) MALLOC(sizeof (MIB_IPADDRTABLE));
    if (pIPAddrTable == NULL)
	{
        printf("Error allocating memory needed to call GetIpAddrTable()\n");
        exit (1);
    }
	else
	{
        dwSize = 0;
        // Make an initial call to GetIpAddrTable to get the
        // necessary size into the dwSize variable
        if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER)
		{
			printf("Not enough space, re-allocate...\n");
            FREE(pIPAddrTable);
            pIPAddrTable = (MIB_IPADDRTABLE *) MALLOC(dwSize);

        }
        if (pIPAddrTable == NULL)
		{
            printf("Memory allocation failed for GetIpAddrTable()\n");
            exit(1);
        }
    }

    // Make a second call to GetIpAddrTable to get the
    // actual data we want
    if ((dwRetVal = GetIpAddrTable(pIPAddrTable, &dwSize, 0)) == NO_ERROR)
	{
		printf("Second call to GetIpAddrTable() is fine!\n");
        // Save the interface index to use for adding an IP address
        ifIndex = pIPAddrTable->table[0].dwIndex;
        printf("\n\tInterface Index:\t%ld\n", ifIndex);
        IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[0].dwAddr;
        printf("\tIP Address:       \t%s (%lu%)\n", inet_ntoa(IPAddr),
               pIPAddrTable->table[0].dwAddr);
        IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[0].dwMask;
        printf("\tSubnet Mask:      \t%s (%lu%)\n", inet_ntoa(IPAddr),
               pIPAddrTable->table[0].dwMask);
        IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[0].dwBCastAddr;
        printf("\tBroadCast Address:\t%s (%lu%)\n", inet_ntoa(IPAddr),
               pIPAddrTable->table[0].dwBCastAddr);
        printf("\tReassembly size:  \t%lu\n\n",
               pIPAddrTable->table[0].dwReasmSize);
    }
	else
	{
        printf("Call to GetIpAddrTable failed with error %d.\n", dwRetVal);
        if (pIPAddrTable)
            FREE(pIPAddrTable);
        exit(1);
    }

    if (pIPAddrTable)
	{
        FREE(pIPAddrTable);
        pIPAddrTable = NULL;
    }

    if ((dwRetVal = AddIPAddress(iaIPAddress, iaIPMask, ifIndex, &NTEContext, &NTEInstance)) == NO_ERROR)
	{
        printf("\tIPv4 address %s was successfully added.\n", argv[1]);
    }
	else
	{
        printf("AddIPAddress failed with error: %d\n", dwRetVal);

        if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwRetVal, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),       // Default language
                          (LPTSTR) & lpMsgBuf, 0, NULL))
		{
            printf("\tError: %S", lpMsgBuf);
            LocalFree(lpMsgBuf);
            exit(1);
        }
    }

	// If you want to 'se' the added address, comment out the
	// following part, add the IP address, then ping the address
	// or use ipconfig /all
	
	// Delete the IP we just added using the NTEContext
	// variable where the handle was returned
	if ((dwRetVal = DeleteIPAddress(NTEContext)) == NO_ERROR)
	{
		printf("\tIPv4 address %s was successfully deleted.\n", argv[1]);
    }
	else
	{
        printf("\tDeleteIPAddress failed with error: %d\n", dwRetVal);
        exit(1);
    }

    return 0;
}
