Windows2003-3790/inetcore/wininet/apdetect/optreg.cxx
2020-09-30 16:53:55 +02:00

777 lines
28 KiB
C++
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//--------------------------------------------------------------------------------
// Copyright (C) Micorosoft Confidential 1997
// Author: RameshV
// Description: Option related registry handling -- common between NT and VxD
//--------------------------------------------------------------------------------
#include <wininetp.h>
#include "aproxp.h"
#ifndef OPTREG_H
#define OPTREG_H
//--------------------------------------------------------------------------------
// function definitions
//--------------------------------------------------------------------------------
POPTION // option from which more appends can occur
DhcpAppendSendOptions( // append all configured options
IN OUT PDHCP_CONTEXT DhcpContext, // this is the context to append for
IN PLIST_ENTRY SendOptionsList,
IN LPBYTE ClassName, // current class
IN DWORD ClassLen, // len of above in bytes
IN LPBYTE BufStart, // start of buffer
IN LPBYTE BufEnd, // how far can we go in this buffer
IN OUT LPBYTE SentOptions, // BoolArray[OPTION_END+1] to avoid repeating options
IN OUT LPBYTE VSentOptions, // to avoid repeating vendor specific options
IN OUT LPBYTE VendorOpt, // Buffer[OPTION_END+1] Holding Vendor specific options
OUT LPDWORD VendorOptLen // the # of bytes filled into that
);
DWORD // status
DhcpDestroyOptionsList( // destroy a list of options, freeing up memory
IN OUT PLIST_ENTRY OptionsList, // this is the list of options to destroy
IN PLIST_ENTRY ClassesList // this is where to remove classes off
);
DWORD // win32 status
DhcpClearAllOptions( // remove all turds from off registry
IN OUT PDHCP_CONTEXT DhcpContext // the context to clear for
);
#endif OPTREG_H
// internal private function that takes the lock on OPTIONS_LIST
DWORD // status
DhcpRegClearOptDefs( // clear all standard options
IN LPTSTR AdapterName // clear for this adapter
);
//
// options related lists
//
LIST_ENTRY DhcpGlobalRecvFromList;
LPSTR DhcpGlobalClientClassInfo = NULL;
LPBYTE // ptr to buf loc where more appends can occur
DhcpAppendParamRequestList( // append the param request list option
IN PDHCP_CONTEXT DhcpContext, // the context to append for
IN PLIST_ENTRY SendOptionsList,// look thru this list
IN LPBYTE ClassName, // which class does this belong to?
IN DWORD ClassLen, // size of above in bytes
IN LPBYTE BufStart, // where to start adding this option
IN LPBYTE BufEnd // limit for this option
) {
BYTE Buffer[OPTION_END+1];
LPBYTE Tmp;
DWORD FirstSize;
DWORD Size;
PLIST_ENTRY ThisEntry;
PDHCP_OPTION ThisOpt;
DWORD i, j;
Size = FirstSize = 0;
Buffer[Size++] = OPTION_SUBNET_MASK; // standard requested options
Buffer[Size++] = OPTION_DOMAIN_NAME;
Buffer[Size++] = OPTION_ROUTER_ADDRESS;
Buffer[Size++] = OPTION_DOMAIN_NAME_SERVERS;
Buffer[Size++] = OPTION_NETBIOS_NAME_SERVER;
Buffer[Size++] = OPTION_NETBIOS_NODE_TYPE;
Buffer[Size++] = OPTION_NETBIOS_SCOPE_OPTION;
Buffer[Size++] = OPTION_VENDOR_SPEC_INFO;
Buffer[Size++] = OPTION_USER_CLASS;
Buffer[Size++] = OPTION_WPAD_URL;
ThisEntry = SendOptionsList->Flink;
while( ThisEntry != SendOptionsList ) {
ThisOpt = CONTAINING_RECORD(ThisEntry, DHCP_OPTION, OptionList);
ThisEntry = ThisEntry->Flink;
if( ThisOpt->IsVendor) continue;
if( ThisOpt->ClassLen ) {
if( ThisOpt->ClassLen != ClassLen ) continue;
if( ThisOpt->ClassName != ClassName )
continue; // this option is not used for this client
}
if( OPTION_PARAMETER_REQUEST_LIST != ThisOpt->OptionId ) {
//
// only if the option is param_request_list do we request..
//
continue;
}
for( i = 0; i < ThisOpt->DataLen ; i ++ ) {
for( j = 0; j < Size; j ++ )
if( ThisOpt->Data[i] == Buffer[j] ) break;
if( j < Size ) continue; // option already plugged in
Buffer[Size++] = ThisOpt->Data[i]; // add this option
}
if( 0 == FirstSize ) FirstSize = Size;
}
if( 0 == FirstSize ) FirstSize = Size;
Tmp = BufStart;
BufStart = (LPBYTE)DhcpAppendOption( // now add the param request list
(POPTION)BufStart,
(BYTE)OPTION_PARAMETER_REQUEST_LIST,
Buffer,
(BYTE)Size,
BufEnd
);
if( Tmp == BufStart ) { // did not really add the option
BufStart = (LPBYTE)DhcpAppendOption( // now try adding the first request we saw instead of everything
(POPTION)BufStart,
(BYTE)OPTION_PARAMETER_REQUEST_LIST,
Buffer,
(BYTE)FirstSize,
BufEnd
);
}
return BufStart;
}
POPTION // option from which more appends can occur
DhcpAppendSendOptions( // append all configured options
IN OUT PDHCP_CONTEXT DhcpContext, // this is the context to append for
IN PLIST_ENTRY SendOptionsList,
IN LPBYTE ClassName, // current class
IN DWORD ClassLen, // len of above in bytes
IN LPBYTE BufStart, // start of buffer
IN LPBYTE BufEnd, // how far can we go in this buffer
IN OUT LPBYTE SentOptions, // BoolArray[OPTION_END+1] to avoid repeating options
IN OUT LPBYTE VSentOptions, // to avoid repeating vendor specific options
IN OUT LPBYTE VendorOpt, // Buffer[OPTION_END+1] Holding Vendor specific options
OUT LPDWORD VendorOptLen // the # of bytes filled into that
) {
PLIST_ENTRY ThisEntry;
PDHCP_OPTION ThisOpt;
DhcpAssert(FALSE == SentOptions[OPTION_PARAMETER_REQUEST_LIST]);
BufStart = DhcpAppendParamRequestList(
DhcpContext,
SendOptionsList,
ClassName,
ClassLen,
BufStart,
BufEnd
);
SentOptions[OPTION_PARAMETER_REQUEST_LIST] = TRUE;
ThisEntry = SendOptionsList->Flink;
while( ThisEntry != SendOptionsList ) {
ThisOpt = CONTAINING_RECORD(ThisEntry, DHCP_OPTION, OptionList);
ThisEntry = ThisEntry->Flink;
if( ThisOpt->IsVendor ? VSentOptions[ThisOpt->OptionId] : SentOptions[ThisOpt->OptionId] )
continue;
// if( ThisOpt->IsVendor) continue; // No vendor specific information this round through
if( ThisOpt->ClassLen ) {
if( ThisOpt->ClassLen != ClassLen ) continue;
if( ThisOpt->ClassName != ClassName )
continue; // this option is not used for this client
}
if( !ThisOpt->IsVendor ) { // easy to add non-vendor spec options
SentOptions[ThisOpt->OptionId] = TRUE;
BufStart = (LPBYTE)DhcpAppendOption(
(POPTION)BufStart,
ThisOpt->OptionId,
ThisOpt->Data,
(BYTE)ThisOpt->DataLen,
BufEnd
);
} else { // ENCAPSULATE vendor specific options
if( SentOptions[OPTION_VENDOR_SPEC_INFO] )
continue; // Vendor spec info already added
VSentOptions[ThisOpt->OptionId] = TRUE;
if( ThisOpt->DataLen + 2 + *VendorOptLen > OPTION_END )
continue; // this option overflows the buffer
VendorOpt[(*VendorOptLen)++] = ThisOpt->OptionId;
VendorOpt[(*VendorOptLen)++] = (BYTE)ThisOpt->DataLen;
memcpy(&VendorOpt[*VendorOptLen], ThisOpt->Data, ThisOpt->DataLen);
(*VendorOptLen) += ThisOpt->DataLen;
}
}
return (POPTION)BufStart;
}
DWORD // status
DhcpDestroyOptionsList( // destroy a list of options, freeing up memory
IN OUT PLIST_ENTRY OptionsList, // this is the list of options to destroy
IN PLIST_ENTRY ClassesList // this is where to remove classes off
) {
PLIST_ENTRY ThisEntry;
PDHCP_OPTION ThisOption;
DWORD Error;
DWORD LastError;
LastError = ERROR_SUCCESS;
while(!IsListEmpty(OptionsList) ) { // for each element of this list
ThisEntry = RemoveHeadList(OptionsList);
ThisOption = CONTAINING_RECORD(ThisEntry, DHCP_OPTION, OptionList);
if( NULL != ThisOption->ClassName ) { // if there is a class, deref it
Error = DhcpDelClass(
ClassesList,
ThisOption->ClassName,
ThisOption->ClassLen
);
if( ERROR_SUCCESS != Error ) {
DhcpAssert( ERROR_SUCCESS == Error);
LastError = Error;
}
}
DhcpFreeMemory(ThisOption); // now really free this
}
return LastError;
}
DWORD // win32 status
DhcpClearAllOptions( // clear all the options information
IN OUT PDHCP_CONTEXT DhcpContext // the context to clear for
) {
PLIST_ENTRY ThisEntry;
PDHCP_OPTION ThisOption;
DWORD LocalError;
//(void) DhcpRegClearOptDefs(DhcpContext->AdapterName));
ThisEntry = DhcpContext->RecdOptionsList.Flink;
while(ThisEntry != &DhcpContext->RecdOptionsList) {
ThisOption = CONTAINING_RECORD(ThisEntry, DHCP_OPTION, OptionList);
ThisEntry = ThisEntry->Flink;
// bug bug, need to return space?
ThisOption->Data = NULL;
ThisOption->DataLen = 0;
//LocalError = DhcpMarkParamChangeRequests(
// DhcpContext->AdapterName,
// ThisOption->OptionId,
// ThisOption->IsVendor,
// ThisOption->ClassName
//);
DhcpAssert(ERROR_SUCCESS == LocalError);
}
return ERROR_SUCCESS;
}
POPTION // buffer after filling option
DhcpAppendClassIdOption( // fill class id if exists
IN OUT PDHCP_CONTEXT DhcpContext, // the context to fillfor
OUT LPBYTE BufStart, // start of message buffer
IN LPBYTE BufEnd // end of message buffer
) {
DWORD Size;
Size = (DWORD)(BufEnd - BufStart);
if( DhcpContext->ClassId ) {
DhcpAssert(DhcpContext->ClassIdLength);
BufStart = (LPBYTE)DhcpAppendOption(
(POPTION)BufStart,
OPTION_USER_CLASS,
DhcpContext->ClassId,
(BYTE)DhcpContext->ClassIdLength,
BufEnd
);
}
return (POPTION) BufStart;
}
LPOPTION
DhcpAppendOption(
LPOPTION Option,
BYTE OptionType,
PVOID OptionValue,
ULONG OptionLength,
LPBYTE OptionEnd
)
/*++
Routine Description:
This function writes a DHCP option to message buffer.
Arguments:
Option - A pointer to a message buffer.
OptionType - The option number to append.
OptionValue - A pointer to the option data.
OptionLength - The lenght, in bytes, of the option data.
OptionEnd - End of Option Buffer.
Return Value:
A pointer to the end of the appended option.
--*/
{
DWORD i;
if ( OptionType == OPTION_END ) {
//
// we should alway have atleast one BYTE space in the buffer
// to append this option.
//
DhcpAssert( (LPBYTE)Option < OptionEnd );
Option->OptionType = OPTION_END;
return( (LPOPTION) ((LPBYTE)(Option) + 1) );
}
if ( OptionType == OPTION_PAD ) {
//
// add this option only iff we have enough space in the buffer.
//
if(((LPBYTE)Option + 1) < (OptionEnd - 1) ) {
Option->OptionType = OPTION_PAD;
return( (LPOPTION) ((LPBYTE)(Option) + 1) );
}
DhcpPrint(("DhcpAppendOption failed to append Option "
"%ld, Buffer too small.\n", OptionType ));
return Option;
}
//
// add this option only iff we have enough space in the buffer.
//
if(((LPBYTE)Option + 2 + OptionLength) >= (OptionEnd - 1) ) {
DhcpPrint(("DhcpAppendOption failed to append Option "
"%ld, Buffer too small.\n", OptionType ));
return Option;
}
if( OptionLength <= 0xFF ) {
// simple option.. no need to use OPTION_MSFT_CONTINUED
Option->OptionType = OptionType;
Option->OptionLength = (BYTE)OptionLength;
memcpy( Option->OptionValue, OptionValue, OptionLength );
return( (LPOPTION) ((LPBYTE)(Option) + Option->OptionLength + 2) );
}
// option size is > 0xFF --> need to continue it using multiple ones..
// there are OptionLenght / 0xFF occurances using 0xFF+2 bytes + one
// using 2 + (OptionLength % 0xFF ) space..
// check to see if we have the space first..
if( 2 + (OptionLength%0xFF) + 0x101*(OptionLength/0xFF)
+ (LPBYTE)Option >= (OptionEnd - 1) ) {
DhcpPrint(("DhcpAppendOption failed to append Option "
"%ld, Buffer too small.\n", OptionType ));
return Option;
}
// first finish off all chunks of 0xFF size that we can do..
i = OptionLength/0xFF;
while(i) {
Option->OptionType = OptionType;
Option->OptionLength = 0xFF;
memcpy(Option->OptionValue, OptionValue, 0xFF);
OptionValue = 0xFF+(LPBYTE)OptionValue;
Option = (LPOPTION)(0x101 + (LPBYTE)Option);
OptionType = OPTION_MSFT_CONTINUED; // all but the first use this ...
OptionLength -= 0xFF;
}
// now finish off the remaining stuff..
DhcpAssert(OptionLength <= 0xFF);
Option->OptionType = OPTION_MSFT_CONTINUED;
Option->OptionLength = (BYTE)OptionLength;
memcpy(Option->OptionValue, OptionValue, OptionLength);
Option = (LPOPTION)(2 + OptionLength + (LPBYTE)Option);
DhcpAssert((LPBYTE)Option < OptionEnd);
return Option;
}
LPBYTE
DhcpAppendMagicCookie(
LPBYTE Option,
LPBYTE OptionEnd
)
/*++
Routine Description:
This routine appends magic cookie to a DHCP message.
Arguments:
Option - A pointer to the place to append the magic cookie.
OptionEnd - End of Option buffer.
Return Value:
A pointer to the end of the appended cookie.
Note : The magic cookie is :
--------------------
| 99 | 130 | 83 | 99 |
--------------------
--*/
{
DhcpAssert( (Option + 4) < (OptionEnd - 1) );
if( (Option + 4) < (OptionEnd - 1) ) {
*Option++ = (BYTE)DHCP_MAGIC_COOKIE_BYTE1;
*Option++ = (BYTE)DHCP_MAGIC_COOKIE_BYTE2;
*Option++ = (BYTE)DHCP_MAGIC_COOKIE_BYTE3;
*Option++ = (BYTE)DHCP_MAGIC_COOKIE_BYTE4;
}
return( Option );
}
LPOPTION
DhcpAppendClientIDOption(
LPOPTION Option,
BYTE ClientHWType,
LPBYTE ClientHWAddr,
BYTE ClientHWAddrLength,
LPBYTE OptionEnd
)
/*++
Routine Description:
This routine appends client ID option to a DHCP message.
History:
8/26/96 Frankbee Removed 16 byte limitation on the hardware
address
Arguments:
Option - A pointer to the place to append the option request.
ClientHWType - Client hardware type.
ClientHWAddr - Client hardware address
ClientHWAddrLength - Client hardware address length.
OptionEnd - End of Option buffer.
Return Value:
A pointer to the end of the newly appended option.
Note : The client ID option will look like as below in the message:
-----------------------------------------------------------------
| OpNum | Len | HWType | HWA1 | HWA2 | ..... | HWAn |
-----------------------------------------------------------------
--*/
{
#pragma warning(disable : 4200) // disable 0-sized array warning
struct _CLIENT_ID {
BYTE bHardwareAddressType;
BYTE pbHardwareAddress[0];
} *pClientID;
LPOPTION lpNewOption;
pClientID = (_CLIENT_ID *) DhcpAllocateMemory( sizeof( struct _CLIENT_ID ) + ClientHWAddrLength );
//
// currently there is no way to indicate failure. simply return unmodified option
// list
//
if ( !pClientID )
return Option;
pClientID->bHardwareAddressType = ClientHWType;
memcpy( pClientID->pbHardwareAddress, ClientHWAddr, ClientHWAddrLength );
lpNewOption = DhcpAppendOption(
Option,
OPTION_CLIENT_ID,
(LPBYTE)pClientID,
(BYTE)(ClientHWAddrLength + sizeof(BYTE)),
OptionEnd );
DhcpFreeMemory( pClientID );
return lpNewOption;
}
// data locks on ClassesList must be taken before calling this function
PDHCP_CLASSES PRIVATE // the required classes struct
DhcpFindClass( // find a specified class
IN OUT PLIST_ENTRY ClassesList, // list of classes to srch in
IN LPBYTE Data, // non-NULL data bytes
IN DWORD Len // # of bytes of above, > 0
) {
PLIST_ENTRY ThisEntry;
PDHCP_CLASSES ThisClass;
ThisEntry = ClassesList->Flink; // first element in list
while( ThisEntry != ClassesList ) { // search the full list
ThisClass = CONTAINING_RECORD( ThisEntry, DHCP_CLASSES, ClassList );
ThisEntry = ThisEntry->Flink;
if( ThisClass->ClassLen == Len ) { // lengths must match
if( ThisClass->ClassName == Data ) // data ptrs can match OR data can match
return ThisClass;
if( 0 == memcmp(ThisClass->ClassName, Data, Len) )
return ThisClass;
}
}
return NULL;
}
// locks on ClassesList should be taken when using this function
LPBYTE // data bytes, or NULL
DhcpAddClass( // add a new class
IN OUT PLIST_ENTRY ClassesList, // list to add to
IN LPBYTE Data, // input class name
IN DWORD Len // # of bytes of above
) {
PDHCP_CLASSES Class;
DWORD MemSize; // amt of memory reqd
if( NULL == ClassesList ) { // invalid parameter
DhcpAssert( NULL != ClassesList );
return NULL;
}
if( 0 == Len || NULL == Data ) { // invalid parameters
DhcpAssert(0 != Len && NULL != Data );
return NULL;
}
Class = DhcpFindClass(ClassesList,Data,Len); // already there in list?
if(NULL != Class) { // yes, found
Class->RefCount++; // increase ref-count
return Class->ClassName;
}
MemSize = sizeof(*Class)+Len; // amt of memory reqd
Class = (PDHCP_CLASSES) DhcpAllocateMemory(MemSize);
if( NULL == Class ) { // not enough memory
DhcpAssert( NULL != Class);
return NULL;
}
Class->ClassLen = Len;
Class->RefCount = 1;
Class->ClassName = ((LPBYTE)Class) + sizeof(*Class);
memcpy(Class->ClassName, Data, Len);
InsertHeadList(ClassesList, &Class->ClassList);
return Class->ClassName;
}
// locks on ClassesList must be taken before calling this function
DWORD // status
DhcpDelClass( // de-refernce a class
IN OUT PLIST_ENTRY ClassesList, // the list to delete off
IN LPBYTE Data, // the data ptr
IN DWORD Len // the # of bytes of above
) {
PDHCP_CLASSES Class;
if( NULL == ClassesList ) {
DhcpAssert( NULL != ClassesList );
return ERROR_INVALID_PARAMETER;
}
if( 0 == Len || NULL == Data ) { // invalid parameter
DhcpAssert( 0 != Len && NULL != Data );
return ERROR_INVALID_PARAMETER;
}
Class = DhcpFindClass(ClassesList,Data,Len);
if( NULL == Class ) { // did not find this class?
DhcpAssert( NULL != Class );
return ERROR_FILE_NOT_FOUND;
}
Class->RefCount --;
if( 0 == Class->RefCount ) { // all references removed
RemoveEntryList( &Class->ClassList ); // remove this from the list
DhcpFreeMemory(Class); // free it
}
return ERROR_SUCCESS;
}
// locks on ClassesList must be taken before calling this function
VOID // always succeed
DhcpFreeAllClasses( // free each elt of the list
IN OUT PLIST_ENTRY ClassesList // input list of classes
) {
PDHCP_CLASSES ThisClass;
PLIST_ENTRY ThisEntry;
if( NULL == ClassesList ) {
DhcpAssert( NULL != ClassesList && "DhcpFreeAllClasses" );
return ;
}
while( !IsListEmpty(ClassesList) ) {
ThisEntry = RemoveHeadList(ClassesList);
ThisClass = CONTAINING_RECORD(ThisEntry, DHCP_CLASSES, ClassList);
if( ThisClass->RefCount ) {
DhcpPrint(("Freeing with refcount = %ld\n", ThisClass->RefCount));
}
DhcpFreeMemory(ThisClass);
}
InitializeListHead(ClassesList);
}
//--------------------------------------------------------------------------------
// exported functions, options
//--------------------------------------------------------------------------------
// data locks need to be taken on OptionsList before calling this function
PDHCP_OPTION // the reqd structure or NULL
DhcpFindOption( // find a specific option
IN OUT PLIST_ENTRY OptionsList, // the list of options to search
IN BYTE OptionId, // the option id to search for
IN BOOL IsVendor, // is it vendor specific?
IN LPBYTE ClassName, // is there a class associated?
IN DWORD ClassLen // # of bytes of above parameter
) {
PLIST_ENTRY ThisEntry;
PDHCP_OPTION ThisOption;
if( NULL == OptionsList ) {
DhcpAssert( NULL != OptionsList );
return NULL;
}
ThisEntry = OptionsList->Flink;
while( ThisEntry != OptionsList ) { // search the set of options
ThisOption = CONTAINING_RECORD( ThisEntry, DHCP_OPTION, OptionList );
ThisEntry = ThisEntry->Flink;
if( ThisOption->OptionId != OptionId ) continue;
if( ThisOption->IsVendor != IsVendor ) continue;
if( ThisOption->ClassLen != ClassLen ) continue;
if( ClassLen && ThisOption->ClassName != ClassName )
continue; // mismatched so far
return ThisOption; // found the option
}
return NULL; // did not find any match
}
// locks on OptionsList need to be taken before calling this function
DWORD // status
DhcpDelOption( // remove a particular option
IN PDHCP_OPTION ThisOption // option to delete
) {
if( NULL == ThisOption) // nope, did not find this option
return ERROR_FILE_NOT_FOUND;
RemoveEntryList( &ThisOption->OptionList); // found it. remove and free
DhcpFreeMemory(ThisOption);
return ERROR_SUCCESS;
}
// locks on OptionsList need to be taken before calling this function
DWORD // status
DhcpAddOption( // add a new option
IN OUT PLIST_ENTRY OptionsList, // list to add to
IN BYTE OptionId, // option id to add
IN BOOL IsVendor, // is it vendor specific?
IN LPBYTE ClassName, // what is the class?
IN DWORD ClassLen, // size of above in bytes
IN LPBYTE Data, // data for this option
IN DWORD DataLen, // # of bytes of above
IN time_t ExpiryTime // when the option expires
) {
PDHCP_OPTION ThisOption;
DWORD MemSize;
if( NULL == OptionsList ) {
DhcpAssert( NULL != OptionsList && "DhcpAddOption" );
return ERROR_INVALID_PARAMETER;
}
if( 0 != ClassLen && NULL == ClassName ) {
DhcpAssert( 0 == ClassLen || NULL != ClassName && "DhcpAddOption" );
return ERROR_INVALID_PARAMETER;
}
if( 0 != DataLen && NULL == Data ) {
DhcpAssert( 0 == DataLen || NULL != Data && "DhcpAddOption" );
return ERROR_INVALID_PARAMETER;
}
MemSize = sizeof(DHCP_OPTION) + DataLen ;
ThisOption = (PDHCP_OPTION) DhcpAllocateMemory(MemSize);
if( NULL == ThisOption ) // could not allocate memory
return ERROR_NOT_ENOUGH_MEMORY;
ThisOption->OptionId = OptionId;
ThisOption->IsVendor = IsVendor;
ThisOption->ClassName = ClassName;
ThisOption->ClassLen = ClassLen;
ThisOption->ExpiryTime = ExpiryTime;
ThisOption->DataLen = DataLen;
ThisOption->Data = ((LPBYTE)ThisOption) + sizeof(DHCP_OPTION);
memcpy(ThisOption->Data, Data, DataLen);
InsertHeadList( OptionsList, &ThisOption->OptionList );
return ERROR_SUCCESS;
}
//================================================================================
// end of file
//================================================================================