// ChangeIP.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <windows.h>
#include <commctrl.h>

#include "stdafx.h"
#include <ndis.h>
#include <Winsock2.h>

/* 
viene aperta la chiave di configurazione del device di rete "adaptername"
L'elenco dei device di rete puņ essere recuperato usando l'API GetAdaptersInfo.
Nel caso di devices PCI il nome del device deve essere completo del prefisso \PCI
*/
DWORD OpenTCPParmsKey(TCHAR* adaptername,HKEY* key)
{
	ASSERT(adaptername);
	ASSERT(key);

	TCHAR keypath[_MAX_PATH+1];

	_tcscpy(keypath,TEXT("\\Comm\\"));
	_tcscat(keypath,adaptername);
	_tcscat(keypath,TEXT("\\Parms\\TCPIP"));

	return RegOpenKeyEx(HKEY_LOCAL_MACHINE,keypath,0,KEY_ALL_ACCESS,key);
}

/*
 Configura l'utilizzo di un server DHCP
 */
DWORD SetDHCP(TCHAR* adaptername)
{
	HKEY	key;
	DWORD	err;

	if (err=OpenTCPParmsKey(adaptername,&key))
		return err;

	DWORD dhcpstate=1;

	if (err=RegSetValueEx(key,TEXT("EnableDHCP"),0,REG_DWORD,(BYTE*)&dhcpstate,sizeof(DWORD)))
	{
		RegCloseKey(key);
		return err;
	}


	return 0;
}

/*
Configura l'utilizzo di un IP statico con relativa subnet mask.
Gateway e DNS possono non essere specificati, passando una stringa vuota.
*/
DWORD SetIPAddr(TCHAR* adaptername,TCHAR* ipaddress,TCHAR* subnetmask,TCHAR* gateway,TCHAR* dns)
{
	HKEY	key;
	DWORD	err;

	if (err=OpenTCPParmsKey(adaptername,&key))
		return err;

	DWORD dhcpstate=0;

	if (err=RegSetValueEx(key,TEXT("EnableDHCP"),0,REG_DWORD,(BYTE*)&dhcpstate,sizeof(DWORD)))
	{
		RegCloseKey(key);
		return err;
	}

	if (err=RegSetValueEx(key,TEXT("IpAddress"),0,REG_SZ,(BYTE*)ipaddress,_tcslen(ipaddress)*sizeof(TCHAR)))
	{
		RegCloseKey(key);
		return err;
	}

	if (err=RegSetValueEx(key,TEXT("Subnetmask"),0,REG_SZ,(BYTE*)subnetmask,_tcslen(subnetmask)*sizeof(TCHAR)))
	{
		RegCloseKey(key);
		return err;
	}

	if (err=RegSetValueEx(key,TEXT("DefaultGateway"),0,REG_SZ,(BYTE*)gateway,_tcslen(gateway)*sizeof(TCHAR)))
	{
		RegCloseKey(key);
		return err;
	}

	if (err=RegSetValueEx(key,TEXT("DNS"),0,REG_SZ,(BYTE*)dns,_tcslen(dns)*sizeof(TCHAR)))
	{
		RegCloseKey(key);
		return err;
	}

	return 0;
}

/*
  Forza un refresh dei parametri di configurazione da parte del driver di rete.
  NB: Se questo codice viene eseguito in debug e il debugger di Visual Studio utilizza una connessione TCP/IP
  č probabile che il debugger si blocchi non appena viene effettivamente modificato l'indirizzo IP utilizzato.
*/
DWORD UpdateIPSettings(TCHAR* adaptername)
{
	TCHAR adapternamestr[257];	

	_tcsncpy(adapternamestr,adaptername,255);	

	adapternamestr[_tcslen(adapternamestr)]=0;
	adapternamestr[_tcslen(adapternamestr)+1]=0;
	
	HANDLE hndis = CreateFile(DD_NDIS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);

	DWORD outbytes;

	if (!DeviceIoControl(hndis, IOCTL_NDIS_REBIND_ADAPTER, adapternamestr, (_tcslen(adapternamestr)+2)*sizeof(TCHAR), NULL, 0, &outbytes, NULL))
	{
		DWORD err=GetLastError();

		CloseHandle(hndis);
		return err;
	}

	return 0;
}

void printUsage()
{
	_tprintf(TEXT(	"usage: ChangeIP adapter */ipaddress <subnetmask> <gateway>\r\n\
					adapter\tadapter name (use ipconfig for a list of IPV4 compatible adapters on your system)\r\n\
					*\tUses DHCP to configure network,no other parameters required\r\n\
					ipaddress\tNew static IP address (use xxx.xxx.xxx.xxx notation)\r\n\
					subnetmask\t(optional)\tSubnet mask (use xxx.xxx.xxx.xxx notation) - default: 255.255.255.0\
					gateway\t(optional)Gateway (use xxx.xxx.xxx.xxx notation) - default: none\
					dns\t(optional)DNS (use xxx.xxx.xxx.xxx notation) - default: none\
					\r\n"));
}


int _tmain(int argc, _TCHAR* argv[])
{
	DWORD err;

	if (argc<3)
	{
		printUsage();
		return -1;
	}

	if (!_tcscmp(argv[2],TEXT("*")))
	{
		if (argc!=3)
		{
			printUsage();
			return -1;
		}

		err=SetDHCP(argv[1]);

		if (err)
		{
			_tprintf(TEXT("Error %d(%08X) setting DHCP parameters\r\n"),err,err);
			return err;
		}
	}
	else
	{
		if (argc>5)
		{
			printUsage();
			return -1;
		}

		char dummy[256];

		WideCharToMultiByte(CP_OEMCP,0,argv[2],-1,dummy,255,NULL,NULL);

		if (inet_addr(dummy)==INADDR_NONE)
		{
			_tprintf(TEXT("%s is not a valid IP address.\r\n"),argv[2]);
		}

		TCHAR subnet[255];
		TCHAR gateway[255];
		TCHAR dns[255];

		if (argc==3)
		{
			_tcscpy(subnet,TEXT("255.255.255.0"));
			_tcscpy(gateway,TEXT(""));
		}
		else
		{
			WideCharToMultiByte(CP_OEMCP,0,argv[3],-1,dummy,255,NULL,NULL);

			if (inet_addr(dummy)==INADDR_NONE)
			{
				_tprintf(TEXT("%s is not a valid subnet mask.\r\n"),argv[3]);
			}

			_tcscpy(subnet,argv[3]);
		}

		if (argc<=4)
		{
			_tcscpy(gateway,TEXT(""));
		}
		else
		{
			WideCharToMultiByte(CP_OEMCP,0,argv[4],-1,dummy,255,NULL,NULL);

			if (inet_addr(dummy)==INADDR_NONE)
			{
				_tprintf(TEXT("%s is not a valid IP address.\r\n"),argv[4]);
			}

			_tcscpy(gateway,argv[4]);
		}

		if (argc<=5)
		{
			_tcscpy(dns,TEXT(""));
		}
		else
		{
			WideCharToMultiByte(CP_OEMCP,0,argv[5],-1,dummy,255,NULL,NULL);

			if (inet_addr(dummy)==INADDR_NONE)
			{
				_tprintf(TEXT("%s is not a valid IP address.\r\n"),argv[4]);
			}

			_tcscpy(dns,argv[5]);
		}

		err=SetIPAddr(argv[1],argv[2],subnet,gateway,dns);

		if (err)
		{
			_tprintf(TEXT("Error %d(%08X) setting IP parameters\r\n"),err,err);
			return err;
		}
	}

	err=UpdateIPSettings(argv[1]);

	if (err)
	{
		_tprintf(TEXT("Error %d(%08X) rebinding network adapter\r\n"),err,err);
		return err;
	}
	return 0;
}

