2025-11-28 00:35:46 +09:00

372 lines
13 KiB
C++

/*
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
* PARTICULAR PURPOSE.
*
* Copyright © Microsoft Corporation. All Rights Reserved.
*
* Author: Yashlaxmi Gupta
* Abstract:
* Dump IPv4 leases across all the scopes configured on a DHCP server.
*
*/
/*
* Includes
*/
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <strsafe.h>
#include <intsafe.h>
#include <dhcpsapi.h>
#define SERVERNAME_BUF_SIZE 255
#define IPADDR_BUF_SIZE 36
#define MAX_DATE_TIME_BUF_SIZE 64
#define DHCP_DATE_TIME_INFINITE_LOW 0xFFFFFFFF
#define DHCP_DATE_TIME_INFINITE_HIGH 0x7FFFFFFF
#define DHCP_DATE_TIME_ZERO_LOW 0
#define DHCP_DATE_TIME_ZERO_HIGH 0
WCHAR *ConvertIpAddtoWstr(
__in DHCP_IP_ADDRESS addr
);
WCHAR *GetHardwareAddress(
__in DHCP_CLIENT_UID phyAdd
);
WCHAR *GetDateTimeString(
__in FILETIME ftTime
);
DWORD __cdecl main(int argc, char* argv[])
{
DHCP_RESUME_HANDLE ResumeHandleScope = 0;
LPDHCP_IP_ARRAY EnumScopes = NULL;
DWORD nRead = 0;
DWORD nTotal = 0;
DHCP_RESUME_HANDLE ResumeHandleClient = 0;
LPDHCP_CLIENT_INFO_ARRAY_V5 Clients = NULL;
DWORD nClientsRead = 0;
DWORD nClientsTotal = 0;
DWORD error1 = ERROR_SUCCESS, error2 = ERROR_SUCCESS;
WCHAR *szDateTimeStr = NULL, *szClientIp = NULL, *szSubnetMask = NULL, *szHardwareAdd = NULL, *szScopeIp = NULL;
WCHAR szServer[SERVERNAME_BUF_SIZE]={0};
if (2 != argc)
{
wprintf(L"Usage: DhcpServerShowLeasesV4.exe <Server IpAdd/Name>");
return ERROR_INVALID_PARAMETER;
}
MultiByteToWideChar(0, 0, argv[1], (int)strlen(argv[1]), szServer, SERVERNAME_BUF_SIZE);
do {
// enumerate all the IpV4 scopes on the server.
error1 = DhcpEnumSubnets(szServer, &ResumeHandleScope, (DWORD)~0, &EnumScopes, &nRead, &nTotal);
if (0 == nTotal)
{
wprintf(L"No scopes on this server.\n\n");
}
if (ERROR_NO_MORE_ITEMS == error1)
{
break;
}
if (ERROR_SUCCESS != error1 && ERROR_MORE_DATA != error1)
{
wprintf(L"DhcpServerShowLeasesV4 returned with error: %d\n",error1);
return error1;
}
// iterating over all the scopes one by one to get the clients for each scope.
for (unsigned int count=0; count < EnumScopes->NumElements; count++)
{
// converting scope IP address from DWORD to LPWSTR
szScopeIp = ConvertIpAddtoWstr(EnumScopes->Elements[count]);
wprintf(L"\nScope : %s\n\n",szScopeIp ? szScopeIp : L" ");
do {
// enumerating the clients on a specific IpV4 scope
error2 = DhcpEnumSubnetClientsV5(szServer, EnumScopes->Elements[count], &ResumeHandleClient, (DWORD)~0, &Clients, &nClientsRead, &nClientsTotal);
if (0 == nClientsTotal)
{
wprintf(L"No clients on this scope.\n\n");
}
if (ERROR_NO_MORE_ITEMS == error2)
{
break;
}
if (ERROR_SUCCESS != error2 && ERROR_MORE_DATA != error2)
{
wprintf(L"DhcpServerShowLeasesV4 returned with error: %d\n",error2);
return error2;
}
//iterating over all the clients leases on a specified scope.
//the leases shown include all the leases present and can be filtered out on the basic of Address State attribute of client.
for (unsigned int count = 0; count < Clients->NumElements; count++)
{
//converting client Ip from DWORD to LPWSTR
szClientIp = ConvertIpAddtoWstr(Clients->Clients[count]->ClientIpAddress);
wprintf(L"Client Address : %s\n",szClientIp ? szClientIp : L" ");
//converting scope subnet mask from DWORD to LPWSTR
szSubnetMask = ConvertIpAddtoWstr(Clients->Clients[count]->SubnetMask);
wprintf(L"Subnet Mask : %s\n",szSubnetMask ? szSubnetMask : L" ");
//converting client hardware address from byte * to LPWSTR
szHardwareAdd = GetHardwareAddress(Clients->Clients[count]->ClientHardwareAddress);
wprintf(L"Hardware Address : %s\n",szHardwareAdd ? szHardwareAdd : L" ");
// if lease duration is infinite then lease never expires.
if (DHCP_DATE_TIME_INFINITE_LOW == Clients->Clients[count]->ClientLeaseExpires.dwLowDateTime &&
DHCP_DATE_TIME_INFINITE_HIGH == Clients->Clients[count]->ClientLeaseExpires.dwHighDateTime)
{
wprintf(L"Lease Expires : Never\n");
}
// if lease duration is 0 the client is inactive.
else if (DHCP_DATE_TIME_ZERO_LOW == Clients->Clients[count]->ClientLeaseExpires.dwLowDateTime &&
DHCP_DATE_TIME_ZERO_HIGH == Clients->Clients[count]->ClientLeaseExpires.dwHighDateTime)
{
wprintf(L"Lease Expires : Inactive\n");
}
else
{
// converting the date time information from FILETIME * to LPWSTR.
szDateTimeStr = GetDateTimeString(*(FILETIME *)(&Clients->Clients[count]->ClientLeaseExpires));
wprintf(L"Lease Expires : %s\n", szDateTimeStr ? szDateTimeStr : L" ");
}
wprintf(L"Client Name : %s\n",Clients->Clients[count]->ClientName ? Clients->Clients[count]->ClientName : L" ");
wprintf(L"Client Comment : %s\n\n",Clients->Clients[count]->ClientComment ? Clients->Clients[count]->ClientComment : L" ");
if (NULL != szClientIp)
{
free(szClientIp);
szClientIp = NULL;
}
if (NULL != szSubnetMask)
{
free(szSubnetMask);
szSubnetMask = NULL;
}
if (NULL != szHardwareAdd)
{
free(szHardwareAdd);
szHardwareAdd = NULL;
}
if (NULL != szDateTimeStr)
{
free(szDateTimeStr);
szDateTimeStr = NULL;
}
}
if (NULL != Clients)
{
DhcpRpcFreeMemory(Clients);
Clients = NULL;
}
nClientsRead = 0;
nClientsTotal = 0;
ResumeHandleClient = 0;
} while (ERROR_MORE_DATA == error2);
if (NULL != szScopeIp)
{
free(szScopeIp);
szScopeIp = NULL;
}
}
if (NULL != EnumScopes)
{
DhcpRpcFreeMemory(EnumScopes);
EnumScopes = NULL;
}
nRead = 0;
nTotal = 0;
ResumeHandleScope = 0;
} while (ERROR_MORE_DATA == error1);
return 0;
}
WCHAR *ConvertIpAddtoWstr(
__in DHCP_IP_ADDRESS addr
)
{
// getting the individual bytes of the IP Address.
PUCHAR pAddress = (PUCHAR)&addr;
// allocating memory to IP Address string.
WCHAR *szIpAddress = (WCHAR *)malloc(IPADDR_BUF_SIZE);
if (NULL == szIpAddress)
{
return NULL;
}
memset(szIpAddress, 0, IPADDR_BUF_SIZE);
// creating the IP Address string
HRESULT HRes = StringCchPrintfW(szIpAddress, IPADDR_BUF_SIZE, L"%ld.%ld.%ld.%ld",pAddress[3],pAddress[2],pAddress[1],pAddress[0]);
if (FAILED(HRes))
{
return NULL;
}
return szIpAddress;
}
WCHAR *GetHardwareAddress(
__in DHCP_CLIENT_UID phyAdd
)
{
HRESULT HRes = S_OK;
WCHAR *szPhysicalAddress = NULL;
DWORD Size = 0;
DWORD i=0, j=0, num0=0, num1=0, num2=0;
if (NULL != phyAdd.Data && 0 < phyAdd.DataLength)
{
// calculating the ecount of the hardware address string.
// corressponding to each byte in the original string, there would be a hyphen and a '\0' character for the last one.
HRes = DWordMult(3, phyAdd.DataLength, &Size);
if (FAILED(HRes))
{
return NULL;
}
// calculating the bcount of the hardware address string.
HRes = DWordMult(Size, sizeof(WCHAR), &Size);
if (FAILED(HRes))
{
return NULL;
}
// allocating the memory for the hardware address string.
szPhysicalAddress = (WCHAR *)malloc(Size);
if (NULL == szPhysicalAddress)
{
return NULL;
}
memset(szPhysicalAddress, 0, Size);
for (i=0; i<phyAdd.DataLength; i++)
{
num0 = phyAdd.Data[i];
// getting the first hexadecimal character in the byte.
num1 = num0 / 16;
// getting the second hexadecimal character in the byte.
num2 = num0 % 16;
szPhysicalAddress[j++] = "0123456789ABCDEF"[num1];
szPhysicalAddress[j++] = "0123456789ABCDEF"[num2];
if (i < phyAdd.DataLength - 1)
{
// adding the separator hyphen.
szPhysicalAddress[j++] = L'-';
}
}
}
// adding the terminating null character.
szPhysicalAddress[j] = L'\0';
return szPhysicalAddress;
}
WCHAR *GetDateTimeString(
__in FILETIME ftTime
)
{
int cchFormat = 0, cchData = 0;
DWORD error = NO_ERROR;
HRESULT HRes = S_OK;
FILETIME ftLocalTime = {0};
SYSTEMTIME stTime = {0};
LPWSTR pwszFormat = NULL;
LPWSTR pwszDatetimestring = NULL;
pwszDatetimestring = (LPWSTR)malloc(MAX_DATE_TIME_BUF_SIZE * sizeof(WCHAR));
if (NULL == pwszDatetimestring)
{
return NULL;
}
// convert file time based on UTC to a local file time.
if (!FileTimeToLocalFileTime(&ftTime, &ftLocalTime))
{
error = GetLastError();
return NULL;
}
// convert a 64bit file time to system time format.
if (!FileTimeToSystemTime(&ftLocalTime, &stTime))
{
error = GetLastError();
return NULL;
}
// get the string length for the locale info.
cchFormat = GetLocaleInfo(LOCALE_SYSTEM_DEFAULT,
LOCALE_SSHORTDATE,
NULL,
0);
if (0 == cchFormat)
{
error = GetLastError();
return NULL;
}
// allocate buffer for locale info string using cchFormat(buffer length required) from above.
pwszFormat = (LPWSTR)malloc(cchFormat * sizeof(WCHAR));
if (NULL == pwszFormat)
{
error = ERROR_NOT_ENOUGH_MEMORY;
return NULL;
}
// get the locale info.
cchFormat = GetLocaleInfo(LOCALE_SYSTEM_DEFAULT,
LOCALE_SSHORTDATE,
pwszFormat,
cchFormat);
if (0 == cchFormat)
{
error = GetLastError();
return NULL;
}
// format the date as a date string for the specified(retieved) locale.
cchData = GetDateFormat(LOCALE_USER_DEFAULT,
0,
&stTime,
pwszFormat,
pwszDatetimestring,
MAX_DATE_TIME_BUF_SIZE);
if (0 == cchData)
{
error = GetLastError();
return NULL;
}
// add the space between date and time.
HRes = StringCchCatW(pwszDatetimestring,(MAX_DATE_TIME_BUF_SIZE-cchData),L" ");
if (FAILED(HRes))
{
return NULL;
}
// get the string length for the locale info.
cchFormat = GetLocaleInfo(LOCALE_SYSTEM_DEFAULT,
LOCALE_STIMEFORMAT,
NULL,
0);
if (0 == cchFormat)
{
error = GetLastError();
return NULL;
}
// allocate buffer for locale info string using cchFormat(buffer length required) from above.
pwszFormat = (LPWSTR)malloc(cchFormat * sizeof(WCHAR));
if (NULL == pwszFormat)
{
error = ERROR_NOT_ENOUGH_MEMORY;
return NULL;
}
// get the locale info.
cchFormat = GetLocaleInfo(LOCALE_SYSTEM_DEFAULT,
LOCALE_STIMEFORMAT,
pwszFormat,
cchFormat);
if (0 == cchFormat)
{
error = GetLastError();
return NULL;
}
// format time as a time string for the specified locale.
cchData = GetTimeFormat(LOCALE_USER_DEFAULT,
0,
&stTime,
pwszFormat,
pwszDatetimestring + cchData,
MAX_DATE_TIME_BUF_SIZE-cchData-1);
if (0 == cchData)
{
error = GetLastError();
return NULL;
}
// return formatted date and time string.
return pwszDatetimestring;
}