windows-nt/Source/XPSP1/NT/net/dhcp/client/nt/change.c

669 lines
24 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (C) 1998 Microsoft Corporation
Module Name:
change.c
Abstract:
handles some change notifications for dhcp.
--*/
#include "precomp.h"
#include <dhcpcapi.h>
#include <apiappl.h>
#include <dhcploc.h>
#include <dhcppro.h>
DWORD
ConvertNetworkString(
IN OUT LPBYTE Buf,
IN ULONG Size
)
/*++
Routine Description:
This routine converts a wide character string where each
character is in network-order, to host-order so that the
string can be used (for ex, for displaying).
Arguments:
Buf -- wide character string buffer, replace in-place.
Size -- size of above buffer in BYTES.
Return Value:
ERROR_INTERNAL_ERROR or ERROR_SUCCESS
--*/
{
if( 0 == Size ) return ERROR_INTERNAL_ERROR;
if( Size % sizeof(WCHAR)) return ERROR_INTERNAL_ERROR;
while( Size ) {
*(LPWSTR) Buf = ntohs(*(LPWSTR)Buf);
Buf += sizeof(WCHAR);
Size -= sizeof(WCHAR);
}
return ERROR_SUCCESS;
}
DWORD
ConvertFromBufferToClassInfo(
IN LPBYTE Data,
IN ULONG nData,
IN OUT LPDHCP_CLASS_INFO ClassesArray,
IN OUT LPDWORD Size
)
/*++
Routine Description:
This routine converts a buffer which has wire-data to the
DHCP_CLASS_INFO array structure. The wire-data format is as
follows: it is a set of triples, where each triple contains the
actual user-class-id used on wire (binary), its name (LPWSTR), and
its description (LPWSTR). Each of these three items have their
length specified first using two bytes -- the hi-byte followed by
the lo-byte.
N.B. If the first of the triple (user-class-id) has length X,
then the actual number of bytes of data on-wire would be X rounded
off to 4 to preserve alignment. This is taken care of in this
routine.
N.B #2. The pointers within the ClassesArray all point to the
buffer (Data) provided.
Arguments:
Data -- the wire-data buffer
nData -- number of bytes of above
ClassesArray -- input buffer that will be formatted with info.
Size -- on input, this must be size of above buffer in bytes.
on output, if the routine failed with ERROR_MORE_DATA, then
this will contain the required number of bytes. If the
routine succeeded, then this will contain the number of
elements in above array that got filled.
Return Values:
ERROR_SUCCESS -- success
ERROR_MORE_DATA -- data buffer provided in ClassesArray must be at
least as many bytes as "Size."
Win32 errors
--*/
{
ULONG ReqdSize, ThisSize, nBytes, nClasses;
LPBYTE Buf;
Buf = Data; nBytes = 0;
ReqdSize = 0; nClasses = 0;
do {
//
// Require length (hi-byte, lo-byte)
//
if( nBytes + 2 > nData ) return ERROR_INTERNAL_ERROR;
//
// user-classid binary blob of specified size.
// N.B. length must be rounded off to nearest
// multiple of 4.
//
ThisSize = ((Buf[0])<<8)+Buf[1];
if( 0 == ThisSize ) return ERROR_INTERNAL_ERROR;
ThisSize = (ThisSize + 3) & ~3;
//
// Go over class id.
//
Buf += 2 + ThisSize;
nBytes += 2 + ThisSize;
ReqdSize += ThisSize;
if( nBytes + 2 > nData ) return ERROR_INTERNAL_ERROR;
//
// user class name.. size must be multiple of sizeof(WCHAR)
//
ThisSize = ((Buf[0])<<8)+Buf[1];
Buf += 2 + ThisSize;
nBytes += 2 + ThisSize;
if( (ThisSize % 2 ) ) return ERROR_INTERNAL_ERROR;
ReqdSize += ThisSize + sizeof(L'\0');
if( nBytes + 2 > nData ) return ERROR_INTERNAL_ERROR;
//
// user class description..
//
ThisSize = ((Buf[0])<<8)+Buf[1];
Buf += 2 + ThisSize;
nBytes += 2 + ThisSize;
if( (ThisSize % 2 ) ) return ERROR_INTERNAL_ERROR;
ReqdSize += ThisSize + sizeof(L'\0');
nClasses ++;
} while( nBytes < nData );
//
// Check if we have the required size.
//
ReqdSize += sizeof(DHCP_CLASS_INFO)*nClasses;
if( (*Size) < ReqdSize ) {
*Size = ReqdSize;
return ERROR_MORE_DATA;
} else {
*Size = nClasses;
}
//
// Assemble the array.
//
Buf = nClasses*sizeof(DHCP_CLASS_INFO) + ((LPBYTE)ClassesArray);
nClasses = 0;
do {
ClassesArray[nClasses].Version = DHCP_CLASS_INFO_VERSION_0;
//
// user class id binary info
//
ThisSize = ((Data[0])<<8)+Data[1];
Data += 2;
ClassesArray[nClasses].ClassData = Buf;
ClassesArray[nClasses].ClassDataLen = ThisSize;
memcpy(Buf, Data, ThisSize);
ThisSize = (ThisSize + 3)&~3;
Buf += ThisSize; Data += ThisSize;
//
// class name
//
ThisSize = ((Data[0])<<8)+Data[1];
Data += 2;
ClassesArray[nClasses].ClassName = (LPWSTR)Buf;
memcpy(Buf, Data, ThisSize);
if( ERROR_SUCCESS != ConvertNetworkString(Buf, ThisSize) ){
return ERROR_INTERNAL_ERROR;
}
Buf += ThisSize; Data += ThisSize;
*(LPWSTR)Buf = L'\0'; Buf += sizeof(WCHAR);
//
// Class description
//
ThisSize = ((Data[0])<<8)+Data[1];
Data += 2;
if( 0 == ThisSize ) {
ClassesArray[nClasses].ClassDescr = NULL;
} else {
ClassesArray[nClasses].ClassDescr = (LPWSTR)Buf;
memcpy(Buf, Data, ThisSize);
if( ERROR_SUCCESS != ConvertNetworkString(Buf, ThisSize) ) {
return ERROR_INTERNAL_ERROR;
}
}
Buf += ThisSize; Data += ThisSize;
*(LPWSTR)Buf = L'\0'; Buf += sizeof(WCHAR);
nClasses ++;
} while( nClasses < *Size );
return ERROR_SUCCESS;
}
//DOC DhcpEnumClasses enumerates the list of classes available on the system for configuration.
//DOC This is predominantly going to be used by the NetUI. (in which case the ClassData part of the
//DOC DHCP_CLASS_INFO structure is essentially useless).
//DOC Note that Flags is used for future use.
//DOC The AdapterName can currently be only GUIDs but may soon be EXTENDED to be IpAddress strings or
//DOC h-w addresses or any other user friendly means of denoting the Adapter. Note that if the Adapter
//DOC Name is NULL (not the empty string L""), then it refers to either ALL adapters.
//DOC The Size parameter is an input/output parameter. The input value is the # of bytes of allocated
//DOC space in the ClassesArray buffer. On return, the meaning of this value depends on the return value.
//DOC If the function returns ERROR_SUCCESS, then, this parameter would return the # of elements in the
//DOC array ClassesArray. If the function returns ERROR_MORE_DATA, then, this parameter refers to the
//DOC # of bytes space that is actually REQUIRED to hold the information.
//DOC In all other cases, the values in Size and ClassesArray dont mean anything.
//DOC
//DOC Return Values:
//DOC ERROR_DEVICE_DOES_NOT_EXIST The AdapterName is illegal in the given context
//DOC ERROR_INVALID_PARAMETER
//DOC ERROR_MORE_DATA ClassesArray is not a large enough buff..
//DOC ERROR_FILE_NOT_FOUND The DHCP Client is not running and could not be started up.
//DOC ERROR_NOT_ENOUGH_MEMORY This is NOT the same as ERROR_MORE_DATA
//DOC Win32 errors
//DOC
//DOC Remarks:
//DOC To notify DHCP that some class has changed, please use the DhcpHandlePnPEvent API.
DWORD
DhcpEnumClasses( // enumerate the list of classes available
IN DWORD Flags, // currently must be zero
IN LPWSTR AdapterName, // currently must be AdapterGUID (cannot be NULL yet)
IN OUT DWORD *Size, // input # of bytes available in BUFFER, output # of elements in array
IN OUT DHCP_CLASS_INFO *ClassesArray // pre-allocated buffer
)
{
DHCPAPI_PARAMS SendParams; // we need to get something from teh dhcp server..
PDHCPAPI_PARAMS RecdParams;
DWORD Result;
DWORD nBytesToAllocate;
DWORD nRecdParams;
BYTE Opt;
if( 0 != Flags || NULL == AdapterName || NULL == Size ) {
return ERROR_INVALID_PARAMETER; // sanity check
}
if( NULL == ClassesArray && 0 != *Size ) {
return ERROR_INVALID_DATA; // if *Size is non-Zero, then the ClassesArray buffer should exist
}
Opt = OPTION_USER_CLASS; // intiialize request packet for this option..
SendParams.OptionId = (BYTE)OPTION_PARAMETER_REQUEST_LIST;
SendParams.IsVendor = FALSE;
SendParams.Data = &Opt;
SendParams.nBytesData = sizeof(Opt);
nBytesToAllocate = 0;
Result = DhcpRequestParameters // try to get this either directly from client or via INFORM
(
/* LPWSTR AdapterName */ AdapterName,
/* LPBYTE ClassId */ NULL,
/* DWORD ClassIdLen */ 0,
/* PDHCPAPI_PARAMS SendParams */ &SendParams,
/* DWORD nSendParams */ 1,
/* DWORD Flags */ 0,
/* PDHCPAPI_PARAMS RecdParams */ NULL,
/* LPDWORD pnRecdParamsBytes*/ &nBytesToAllocate
);
if( ERROR_MORE_DATA != Result ) { // either an error or dont have anything to return?
return Result;
}
DhcpAssert(nBytesToAllocate); // if it were 0, Result would have been ERROR_SUCCESS
RecdParams = DhcpAllocateMemory(nBytesToAllocate);
if( NULL == RecdParams ) { // um? dont have memory? cant really happen?
return ERROR_NOT_ENOUGH_MEMORY;
}
nRecdParams = nBytesToAllocate;
Result = DhcpRequestParameters // try to get this either directly from client or via INFORM
(
/* LPWSTR AdapterName */ AdapterName,
/* LPBYTE ClassId */ NULL,
/* DWORD ClassIdLen */ 0,
/* PDHCPAPI_PARAMS SendParams */ &SendParams,
/* DWORD nSendParams */ 1,
/* DWORD Flags */ 0,
/* PDHCPAPI_PARAMS RecdParams */ RecdParams,
/* LPDWORD pnRecdParamsBytes*/ &nRecdParams
);
if( ERROR_SUCCESS == Result && 1 != nRecdParams ) {
DhcpAssert(FALSE);
Result = ERROR_INTERNAL_ERROR; // dont expect this to happen..
}
if( ERROR_SUCCESS != Result ) { // aw, comeone, cant happen now...
// it is possible for instance to have a PnP event in the middle that would cause
// the adapter to go away. In this case ERROR_FILE_NOT_FOUND (2) is returned.
// no point for the assert - otherwise returning the error up to the caller is
// just fine.
//DhcpAssert(FALSE);
DhcpPrint((DEBUG_ERRORS, "DhcpRequestParams: 0x%lx (%ld)\n", Result, Result));
DhcpFreeMemory(RecdParams);
return Result;
}
DhcpAssert(NULL != RecdParams);
Result = ConvertFromBufferToClassInfo( // convert from straight bytes to classinfo struct
RecdParams->Data,
RecdParams->nBytesData,
ClassesArray,
Size
);
DhcpFreeMemory(RecdParams);
return Result;
}
ULONG _inline // status
GetRegistryClassIdName( // get registr string class id name written by ui
IN LPWSTR AdapterName, // for this adapter
OUT LPWSTR *ClassIdName
)
{
ULONG Error;
LPBYTE Value;
ULONG ValueType, ValueSize;
*ClassIdName = NULL;
ValueSize = 0; Value = NULL;
Error = DhcpRegReadFromLocation(
DEFAULT_USER_CLASS_UI_LOC_FULL,
AdapterName,
&Value,
&ValueType,
&ValueSize
);
if( ERROR_SUCCESS != Error ) return Error; // failed?
if( REG_SZ != ValueType ) {
if( Value ) DhcpFreeMemory(Value);
DhcpPrint((DEBUG_ERRORS, "DhcpClassId Type is incorrect: %ld\n", ValueType));
DhcpAssert(FALSE);
return ERROR_INVALID_DATA; // uh ? should not have happened!
}
*ClassIdName = (LPWSTR)Value;
return ERROR_SUCCESS;
}
ULONG _inline // status
ConvertClassIdNameToBin( // get corresponding value, return itself in ASCII o/w
IN LPWSTR AdapterName, // for this adapter
IN LPWSTR ClassIdName, // ClassIdName for which we are searching
IN BOOL SkipClassEnum, // to skip dhcp class enumeration during initialization
OUT LPBYTE *ClassIdBin, // fill this ptr up
OUT ULONG *ClassIdBinSize // fill this with size of memory allocated
)
{
ULONG Error, Size, i;
PDHCP_CLASS_INFO pDhcpClassInfo;// array
LPBYTE BinData;
ULONG BinDataLen;
pDhcpClassInfo = NULL;
BinData = NULL; BinDataLen = 0;
*ClassIdBin = NULL; *ClassIdBinSize = 0;
// do not perform class enumeration before Dhcp is initialized.
if (!SkipClassEnum) {
do { // not a loop, just to avoid GOTOs
Size = 0; // buffer yet be allocated..
Error = DhcpEnumClasses(
/* Flags */ 0,
/* AdapterName */ AdapterName,
/* Size */ &Size,
/* ClassesArray */ NULL
);
if( ERROR_MORE_DATA != Error ) { // failed?
break;
}
DhcpAssert(0 != Size);
pDhcpClassInfo = DhcpAllocateMemory(Size);
if( NULL == pDhcpClassInfo ) return ERROR_NOT_ENOUGH_MEMORY;
Error = DhcpEnumClasses(
/* Flags */ 0,
/* AdapterName */ AdapterName,
/* Size */ &Size,
/* ClassesArray */ pDhcpClassInfo
);
if( ERROR_SUCCESS != Error ) { // This call should not fail!
DhcpPrint((DEBUG_ERRORS, "DhcpEnumClasses failed %ld\n", Error));
DhcpAssert(FALSE);
DhcpFreeMemory(pDhcpClassInfo);
return Error;
}
DhcpAssert(0 != Size);
for( i = 0; i != Size ; i ++ ) {
if( 0 == wcscmp(pDhcpClassInfo[i].ClassName, ClassIdName) )
break;
}
if( i != Size ) { // found a match
BinData = pDhcpClassInfo[i].ClassData;
BinDataLen = pDhcpClassInfo[i].ClassDataLen;
} else {
DhcpFreeMemory(pDhcpClassInfo);
}
} while(0); // not a loop just to avoid GOTOs
}
// BinData and BinDataLen holds the info we know..
if( NULL == BinData ) { // couldn't find the class..
DhcpPrint((DEBUG_ERRORS, "Could not find the class <%ws>\n", ClassIdName));
BinDataLen = wcslen(ClassIdName);
BinData = DhcpAllocateMemory(BinDataLen);
if( NULL == BinData ) { // could not allocate this mem?
return ERROR_NOT_ENOUGH_MEMORY;
}
Error = wcstombs(BinData, ClassIdName, BinDataLen);
if( -1 == Error ) { // failed conversion?
Error = GetLastError();
DhcpPrint((DEBUG_ERRORS, "Failed ot convert %ws\n", ClassIdName));
DhcpAssert(FALSE);
DhcpFreeMemory(BinData);
return Error;
}
*ClassIdBin = BinData; *ClassIdBinSize = BinDataLen;
return ERROR_SUCCESS;
}
// successfully got the bindata etc..
DhcpAssert(pDhcpClassInfo); // this is where the string is..
*ClassIdBin = DhcpAllocateMemory(BinDataLen); // try allocating memory
if( NULL == *ClassIdBin ) { // failed
return ERROR_NOT_ENOUGH_MEMORY;
}
memcpy(*ClassIdBin, BinData, BinDataLen);
*ClassIdBinSize = BinDataLen;
if (pDhcpClassInfo != NULL)
DhcpFreeMemory(pDhcpClassInfo); // free allocated ptr
return ERROR_SUCCESS;
}
LPWSTR _inline // String (allocated)
GetRegClassIdBinLocation( // find where to store bin
IN LPWSTR AdapterName // for this adapter
)
{
ULONG Error;
LPWSTR Value, RetVal;
ULONG ValueSize, ValueType;
ValueSize = 0; Value = NULL;
Error = DhcpRegReadFromLocation(
DHCP_CLIENT_PARAMETER_KEY REGISTRY_CONNECT_STRING DHCP_CLASS_LOCATION_VALUE,
AdapterName,
&(LPBYTE)Value,
&ValueType,
&ValueSize
);
if( ERROR_SUCCESS != Error ) { // couldn't find it? choose default!
ValueSize = 0; // didn't allocate nothing..
} else if( ValueType != DHCP_CLASS_LOCATION_TYPE ) {
DhcpPrint((DEBUG_ERRORS, "DhcpLocationType is %ld\n", ValueType));
DhcpAssert(FALSE);
ValueSize = 0;
}
if( 0 == ValueSize ) { // choose default..
Value = DEFAULT_USER_CLASS_LOC_FULL;
}
Error = DhcpRegExpandString( // replace '?' with AdapterName
Value,
AdapterName,
&RetVal,
NULL
);
if( 0 != ValueSize ) DhcpFreeMemory(Value); // free only if we didn't allocate
if( ERROR_SUCCESS != Error ) return NULL; // can't return error codes?
return RetVal;
}
ULONG _inline // status
SetRegistryClassIdBin( // write the binary classid value
IN LPWSTR AdapterName, // for this adapter
IN LPBYTE ClassIdBin, // Binary value to write
IN ULONG ClassIdBinSize // size of entry..
)
{
ULONG Error;
LPWSTR RegLocation; // registry location..
LPWSTR RegValue;
HKEY RegKey;
RegLocation = GetRegClassIdBinLocation(AdapterName);
if( NULL == RegLocation ) return ERROR_NOT_ENOUGH_MEMORY;
RegValue = wcsrchr(RegLocation, REGISTRY_CONNECT);
if( NULL == RegValue ) { // invalid string?
return ERROR_INVALID_DATA;
}
*RegValue ++ = L'\0'; // separate key and value..
Error = RegOpenKeyEx( // open the key..
HKEY_LOCAL_MACHINE,
RegLocation,
0 /* Reserved */,
DHCP_CLIENT_KEY_ACCESS,
&RegKey
);
if( ERROR_SUCCESS != Error ) {
DhcpPrint((DEBUG_ERRORS, "Could not open key: %ws : %ld\n", RegLocation, Error));
DhcpFreeMemory(RegLocation);
return Error;
}
Error = RegSetValueEx(
RegKey,
RegValue,
0 /* Reserved */,
REG_BINARY,
ClassIdBin,
ClassIdBinSize
);
RegCloseKey(RegKey);
if( ERROR_SUCCESS != Error ) {
DhcpPrint((DEBUG_ERRORS, "Could not save value:"
"%ws / %ws: %ld\n", RegLocation, RegValue, Error));
}
DhcpFreeMemory(RegLocation);
return Error;
}
ULONG // status
FixupDhcpClassId( // fix ClassIdBin value in registry based on ClassId
IN LPWSTR AdapterName,
IN BOOL SkipClassEnum
)
{
LPWSTR ClassIdName; // as written by UI
LPBYTE ClassIdBin; // as needs to be written in registry
ULONG ClassIdBinSize;// the # of bytes of above..
ULONG Error; // status
ClassIdName = NULL;
Error = GetRegistryClassIdName(AdapterName, &ClassIdName);
if( ERROR_SUCCESS != Error || NULL == ClassIdName
|| L'\0' == *ClassIdName ) {
DhcpPrint((DEBUG_ERRORS, "Could not read ClassId: %ld\n", Error));
ClassIdName = NULL;
}
ClassIdBinSize = 0; ClassIdBin = NULL;
if( ClassIdName ) {
Error = ConvertClassIdNameToBin(
AdapterName, ClassIdName, SkipClassEnum, &ClassIdBin, &ClassIdBinSize
);
DhcpFreeMemory(ClassIdName);// Dont need this memory anymore
} else {
Error = ERROR_SUCCESS;
}
if( ERROR_SUCCESS != Error || NULL == ClassIdBin ) {
DhcpPrint((DEBUG_ERRORS, "Could not convert classid.. making it NULL\n"));
ClassIdBin = NULL;
ClassIdBinSize = 0;
}
Error = SetRegistryClassIdBin(AdapterName, ClassIdBin, ClassIdBinSize);
if( ClassIdBin ) DhcpFreeMemory(ClassIdBin);
return Error;
}
//DOC DhcpHandlePnpEvent can be called as an API by any process (excepting that executing within the
//DOC DHCP process itself) when any of the registry based configuration has changed and DHCP client has to
//DOC re-read the registry. The Flags parameter is for future expansion.
//DOC The AdapterName can currently be only GUIDs but may soon be EXTENDED to be IpAddress strings or
//DOC h-w addresses or any other user friendly means of denoting the Adapter. Note that if the Adapter
//DOC Name is NULL (not the empty string L""), then it refers to either GLOBAL parameters or ALL adapters
//DOC depending on which BOOL has been set. (this may not get done for BETA2).
//DOC The Changes structure gives the information on what changed.
//DOC Currently, only a few of the defined BOOLs would be supported (for BETA2 NT5).
//DOC
//DOC Return Values:
//DOC ERROR_DEVICE_DOES_NOT_EXIST The AdapterName is illegal in the given context
//DOC ERROR_INVALID_PARAMETER
//DOC ERROR_CALL_NOT_SUPPORTED The particular parameter that has changed is not completely pnp yet.
//DOC Win32 errors
DWORD
WINAPI
DhcpHandlePnPEvent(
IN DWORD Flags, // MUST BE ZERO
IN DWORD Caller, // currently must be DHCP_CALLER_TCPUI
IN LPWSTR AdapterName, // currently must be the adapter GUID or NULL if global
IN LPDHCP_PNP_CHANGE Changes, // specify what changed
IN LPVOID Reserved // reserved for future use..
)
{
ULONG Error;
if( 0 != Flags || DHCP_CALLER_TCPUI != Caller ||
NULL != Reserved || NULL == Changes ) {
return ERROR_INVALID_PARAMETER; // sanity check
}
if( Changes->Version > DHCP_PNP_CHANGE_VERSION_0 ) {
return ERROR_NOT_SUPPORTED; // this version is not supported
}
if( Changes->ClassIdChanged ) { // the classid got changed..
// The UI writes any changes to the "DhcpClassId" registry value... but
// this is just the name of the class and not the actual ClassId binary value
// So we read this compare and write the correct value..
(void) FixupDhcpClassId(AdapterName, FALSE); // figure the binary classid value
}
Error = DhcpStaticRefreshParams(AdapterName); // refresh all static params..
if( ERROR_SUCCESS != Error ) return Error;
if( Changes->ClassIdChanged ) {
//
// If ClassID changes, we have to refresh lease..
//
(void) DhcpAcquireParameters(AdapterName);
}
return ERROR_SUCCESS;
}
//================================================================================
// end of file
//================================================================================