/*++ Copyright (C) 1998 Microsoft Corporation Module Name: change.c Abstract: handles some change notifications for dhcp. --*/ #include "precomp.h" #include #include #include #include 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 //================================================================================