mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
819 lines
17 KiB
819 lines
17 KiB
/*++
|
|
|
|
Copyright (c) 1994 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
api.c
|
|
|
|
Abstract:
|
|
|
|
This file contains the API interface to the DHCP client.
|
|
|
|
Author:
|
|
|
|
Manny Weiser (mannyw) 24-Nov-1992
|
|
|
|
Environment:
|
|
|
|
User Mode - Win32
|
|
|
|
Revision History:
|
|
|
|
Madan Appiah (madana) 21-Oct-1993
|
|
|
|
--*/
|
|
|
|
|
|
#include <dhcpcli.h>
|
|
#include <dhcploc.h>
|
|
#include <dhcppro.h>
|
|
|
|
#include <lmcons.h>
|
|
|
|
#define DHCP_NAMED_PIPE_TIMEOUT 5000L
|
|
|
|
typedef enum _API_FUNCTION_CODE {
|
|
AcquireParametersOpCode,
|
|
ReleaseParametersOpCode,
|
|
EnableDhcpOpCode,
|
|
DisableDhcpOpCode,
|
|
} API_OPCODE, *LPAPI_OPCODE;
|
|
|
|
//
|
|
// The full duplex named pipe used for API interface.
|
|
//
|
|
|
|
#define DHCP_PIPE_NAME L"\\\\.\\Pipe\\DhcpClient"
|
|
|
|
//
|
|
// Format of an API request packet.
|
|
//
|
|
|
|
typedef struct _DHCP_REQUEST {
|
|
API_OPCODE WhatToDo;
|
|
WCHAR AdapterName[PATHLEN];
|
|
} DHCP_REQUEST, *PDHCP_REQUEST;
|
|
|
|
//
|
|
// Format of an API response packet.
|
|
//
|
|
|
|
typedef struct _DHCP_RESPONSE {
|
|
DWORD Status;
|
|
} DHCP_RESPONSE, *PDHCP_RESPONSE;
|
|
|
|
//
|
|
// Definition for the private API interface.
|
|
//
|
|
|
|
DWORD
|
|
DhcpClientApi(
|
|
IN API_OPCODE Opcode,
|
|
IN LPWSTR AdapterName
|
|
);
|
|
|
|
DWORD
|
|
DhcpApiInit(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function initializes the Dhcp Client API data structures.
|
|
Currently the API is implemented over RAW named pipe, so this
|
|
function call opens name pipe and listens to it. Also it creates an
|
|
event to track the API calls.
|
|
|
|
Arguments:
|
|
|
|
NONE.
|
|
|
|
Return Value:
|
|
|
|
Windows Error.
|
|
|
|
--*/
|
|
{
|
|
DWORD Error;
|
|
BOOL BoolError;
|
|
|
|
SECURITY_ATTRIBUTES SecurityAttributes;
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
|
|
SID_IDENTIFIER_AUTHORITY Authority = SECURITY_NT_AUTHORITY;
|
|
DWORD Length;
|
|
PACL Acl = NULL;
|
|
PSID AdminSid;
|
|
|
|
DhcpGlobalClientApiPipeEvent =
|
|
CreateEvent(
|
|
NULL, // no security.
|
|
TRUE, // manual reset.
|
|
// this event should be manually reset
|
|
// for overlapped io.
|
|
FALSE, // initial state is not-signaled.
|
|
NULL ); // no name.
|
|
|
|
if( DhcpGlobalClientApiPipeEvent == NULL ) {
|
|
Error = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
BoolError = AllocateAndInitializeSid(
|
|
&Authority,
|
|
2,
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_ADMINS,
|
|
0, 0, 0, 0, 0, 0,
|
|
&AdminSid
|
|
);
|
|
|
|
if( BoolError == FALSE ) {
|
|
Error = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
Length = (ULONG)sizeof(ACL) +
|
|
(ULONG)sizeof(ACCESS_ALLOWED_ACE) +
|
|
GetLengthSid( AdminSid ) +
|
|
8; // The 8 is just for good measure
|
|
|
|
Acl = DhcpAllocateMemory( Length );
|
|
|
|
if( Acl == NULL ) {
|
|
Error = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
BoolError = InitializeAcl( Acl, Length, ACL_REVISION2);
|
|
|
|
if( BoolError == FALSE ) {
|
|
Error = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
BoolError = AddAccessAllowedAce (
|
|
Acl,
|
|
ACL_REVISION2,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
AdminSid );
|
|
|
|
if( BoolError == FALSE ) {
|
|
Error = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
SecurityDescriptor =
|
|
DhcpAllocateMemory( SECURITY_DESCRIPTOR_MIN_LENGTH );
|
|
|
|
if( SecurityDescriptor == NULL ) {
|
|
Error = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
BoolError = InitializeSecurityDescriptor(
|
|
SecurityDescriptor,
|
|
SECURITY_DESCRIPTOR_REVISION );
|
|
|
|
if( BoolError == FALSE ) {
|
|
Error = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
BoolError = SetSecurityDescriptorDacl (
|
|
SecurityDescriptor,
|
|
TRUE,
|
|
Acl,
|
|
FALSE
|
|
);
|
|
|
|
if( BoolError == FALSE ) {
|
|
Error = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
SecurityAttributes.nLength = sizeof( SecurityAttributes );
|
|
SecurityAttributes.lpSecurityDescriptor = SecurityDescriptor;
|
|
SecurityAttributes.bInheritHandle = FALSE;
|
|
|
|
DhcpGlobalClientApiPipe =
|
|
CreateNamedPipe(
|
|
DHCP_PIPE_NAME,
|
|
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
|
|
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
|
|
PIPE_UNLIMITED_INSTANCES,
|
|
1024,
|
|
0,
|
|
10, // Client timeout
|
|
&SecurityAttributes );
|
|
|
|
if( DhcpGlobalClientApiPipe == INVALID_HANDLE_VALUE ) {
|
|
Error = GetLastError();
|
|
DhcpGlobalClientApiPipe = NULL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
DhcpGlobalClientApiOverLapBuffer.hEvent = DhcpGlobalClientApiPipeEvent;
|
|
if( ConnectNamedPipe(
|
|
DhcpGlobalClientApiPipe,
|
|
&DhcpGlobalClientApiOverLapBuffer ) == FALSE ) {
|
|
|
|
Error = GetLastError();
|
|
|
|
if( (Error != ERROR_IO_PENDING) &&
|
|
(Error != ERROR_PIPE_CONNECTED) ) {
|
|
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
Error = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
if( SecurityDescriptor != NULL ) {
|
|
DhcpFreeMemory( SecurityDescriptor );
|
|
}
|
|
|
|
if( Acl != NULL ) {
|
|
DhcpFreeMemory( Acl );
|
|
}
|
|
|
|
if( Error != ERROR_SUCCESS ) {
|
|
DhcpPrint(( DEBUG_ERRORS, "DhcpApiInit failed, %ld.\n", Error ));
|
|
}
|
|
|
|
return( Error );
|
|
}
|
|
|
|
VOID
|
|
DhcpApiCleanup(
|
|
VOID
|
|
)
|
|
{
|
|
if( DhcpGlobalClientApiPipe != NULL ) {
|
|
CloseHandle( DhcpGlobalClientApiPipe );
|
|
DhcpGlobalClientApiPipe = NULL;
|
|
}
|
|
|
|
if( DhcpGlobalClientApiPipeEvent != NULL ) {
|
|
CloseHandle( DhcpGlobalClientApiPipeEvent );
|
|
DhcpGlobalClientApiPipeEvent = NULL;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
DWORD
|
|
AcquireParameters(
|
|
LPWSTR AdapterName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This functions forces the DHCP client to acquire a lease for, or
|
|
renew the lease for the specified NIC immediately.
|
|
|
|
|
|
Arguments:
|
|
|
|
AdapterName - name of the adapter.
|
|
|
|
Return Value:
|
|
|
|
The status of the operation.
|
|
|
|
--*/
|
|
{
|
|
PDHCP_CONTEXT dhcpContext;
|
|
DWORD Error;
|
|
|
|
LOCK_RENEW_LIST();
|
|
dhcpContext = FindDhcpContextOnRenewalList( AdapterName );
|
|
|
|
//
|
|
// If the context is already on the timer list, take it off.
|
|
// Otherwise, look for it on the NIC list.
|
|
//
|
|
|
|
if ( dhcpContext == NULL ) {
|
|
dhcpContext = FindDhcpContextOnNicList( AdapterName );
|
|
if ( dhcpContext == NULL ) {
|
|
|
|
//
|
|
// No NIC with the specified AdapterName on this machine.
|
|
//
|
|
|
|
UNLOCK_RENEW_LIST();
|
|
return( ERROR_PATH_NOT_FOUND );
|
|
}
|
|
} else {
|
|
RemoveEntryList( &dhcpContext->RenewalListEntry );
|
|
}
|
|
|
|
//
|
|
// Attempt to obtain or renew parameters for this adapter.
|
|
//
|
|
|
|
if ( dhcpContext->IpAddress == 0) {
|
|
Error = ReObtainInitialParameters( dhcpContext, NULL );
|
|
} else {
|
|
Error = ReRenewParameters( dhcpContext, NULL );
|
|
}
|
|
|
|
UNLOCK_RENEW_LIST();
|
|
return( Error );
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
ReleaseParameters(
|
|
LPWSTR AdapterName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This functions forces the DHCP client to release the lease for the
|
|
specified NIC.
|
|
|
|
Arguments:
|
|
|
|
AdapterName - name of the adapter.
|
|
|
|
Return Value:
|
|
|
|
The status of the operation.
|
|
|
|
--*/
|
|
{
|
|
PDHCP_CONTEXT dhcpContext;
|
|
|
|
LOCK_RENEW_LIST();
|
|
dhcpContext = FindDhcpContextOnRenewalList( AdapterName );
|
|
|
|
//
|
|
// If the context is already on the timer list, take it off.
|
|
// Otherwise, look for it on the NIC list.
|
|
//
|
|
|
|
if ( dhcpContext == NULL ) {
|
|
dhcpContext = FindDhcpContextOnNicList( AdapterName );
|
|
if ( dhcpContext == NULL ) {
|
|
|
|
//
|
|
// No NIC with the specified AdapterName on this machine.
|
|
//
|
|
|
|
UNLOCK_RENEW_LIST();
|
|
return( ERROR_PATH_NOT_FOUND );
|
|
}
|
|
} else {
|
|
RemoveEntryList( &dhcpContext->RenewalListEntry );
|
|
}
|
|
|
|
//
|
|
// Attempt to obtain or renew parameters for this adapter.
|
|
//
|
|
|
|
if ( dhcpContext->IpAddress != 0) {
|
|
ReleaseIpAddress( dhcpContext );
|
|
}
|
|
|
|
UNLOCK_RENEW_LIST();
|
|
return( ERROR_SUCCESS );
|
|
}
|
|
|
|
|
|
DWORD
|
|
EnableDhcp(
|
|
LPWSTR AdapterName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This functions adds this adapter to the DHCP list if it is not
|
|
already enlisted.
|
|
|
|
Arguments:
|
|
|
|
AdapterName - name of the adapter.
|
|
|
|
Return Value:
|
|
|
|
The status of the operation.
|
|
|
|
--*/
|
|
{
|
|
DWORD Error;
|
|
PDHCP_CONTEXT DhcpContext;
|
|
WCHAR DeviceName[PATHLEN];
|
|
|
|
LOCK_RENEW_LIST();
|
|
DhcpContext = FindDhcpContextOnRenewalList( AdapterName );
|
|
|
|
//
|
|
// If the context is already on the timer list, return.
|
|
// Otherwise, look for it on the NIC list.
|
|
//
|
|
|
|
if( DhcpContext != NULL ) {
|
|
UNLOCK_RENEW_LIST();
|
|
return( ERROR_SUCCESS );
|
|
}
|
|
|
|
DhcpContext = FindDhcpContextOnNicList( AdapterName );
|
|
UNLOCK_RENEW_LIST();
|
|
|
|
//
|
|
// If this adapter is not in the NIC list then add to it.
|
|
//
|
|
|
|
if ( DhcpContext == NULL ) {
|
|
|
|
wcscpy( DeviceName, DHCP_ADAPTERS_DEVICE_STRING );
|
|
wcscat( DeviceName, AdapterName );
|
|
|
|
Error = DhcpAddNICtoList(
|
|
AdapterName,
|
|
DeviceName,
|
|
&DhcpContext );
|
|
|
|
if( Error != ERROR_SUCCESS ) {
|
|
return( Error );
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// set the renewal function, add it to the renewal list and
|
|
// set recompute event. Do this only when this is dhcp
|
|
// enabled entry.
|
|
//
|
|
|
|
if( DhcpContext != NULL ) {
|
|
|
|
//
|
|
// set the renewal function and schedule this NIC for
|
|
// renewal.
|
|
//
|
|
|
|
DhcpContext->RenewalFunction = ReObtainInitialParameters;
|
|
ScheduleWakeUp( DhcpContext, 0 );
|
|
}
|
|
|
|
return( Error );
|
|
}
|
|
|
|
|
|
DWORD
|
|
DisableDhcp(
|
|
LPWSTR AdapterName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This functions removes this adapter from the DHCP list if it is enlisted.
|
|
|
|
Arguments:
|
|
|
|
AdapterName - name of the adapter.
|
|
|
|
Return Value:
|
|
|
|
The status of the operation.
|
|
|
|
--*/
|
|
{
|
|
PDHCP_CONTEXT DhcpContext;
|
|
BOOL BoolError;
|
|
|
|
LOCK_RENEW_LIST();
|
|
DhcpContext = FindDhcpContextOnNicList( AdapterName );
|
|
|
|
//
|
|
// if this entry is not in the NIC, return success.
|
|
//
|
|
|
|
if( DhcpContext == NULL ) {
|
|
UNLOCK_RENEW_LIST();
|
|
return( ERROR_SUCCESS );
|
|
}
|
|
|
|
//
|
|
// remove this adapter entry from the renewal list.
|
|
//
|
|
|
|
if( (DhcpContext->RenewalListEntry.Flink != NULL) &&
|
|
(DhcpContext->RenewalListEntry.Blink != NULL) ) {
|
|
|
|
RemoveEntryList( &DhcpContext->RenewalListEntry );
|
|
}
|
|
|
|
//
|
|
// remove this adapter entry from the NIC list.
|
|
//
|
|
|
|
RemoveEntryList( &DhcpContext->NicListEntry );
|
|
|
|
//
|
|
// the main loop will stop the service if this is last
|
|
// adapter to go.
|
|
//
|
|
|
|
UNLOCK_RENEW_LIST();
|
|
|
|
//
|
|
// set recompute event, so that the main loop will compute
|
|
// the sleep time.
|
|
//
|
|
|
|
BoolError = SetEvent( DhcpGlobalRecomputeTimerEvent );
|
|
DhcpAssert( BoolError == TRUE );
|
|
|
|
return( ERROR_SUCCESS );
|
|
}
|
|
|
|
|
|
VOID
|
|
ProcessApiRequest(
|
|
HANDLE PipeHandle,
|
|
LPOVERLAPPED Overlap
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This functions handles a DHCP API request. It reads the request
|
|
message for the DHCP pipe, process the requests, and writes the
|
|
request status to the pipe.
|
|
|
|
Arguments:
|
|
|
|
PipeHandle - A handle to the pipe of the requestor.
|
|
|
|
Overlap - Pointer to a preinitialized overlap structure to use.
|
|
|
|
Return Value:
|
|
|
|
The status of the operation.
|
|
|
|
--*/
|
|
{
|
|
DHCP_REQUEST dhcpRequest;
|
|
DHCP_RESPONSE dhcpResponse;
|
|
DWORD bytesRead, bytesWritten;
|
|
BOOL success;
|
|
DWORD Error;
|
|
DWORD transfer;
|
|
|
|
//
|
|
// main thread opened the DHCP named pipe. Get the message,
|
|
// process it and send a response.
|
|
//
|
|
|
|
ResetEvent(Overlap->hEvent);
|
|
|
|
success = ReadFile(
|
|
PipeHandle,
|
|
&dhcpRequest,
|
|
sizeof( dhcpRequest ),
|
|
&bytesRead,
|
|
Overlap);
|
|
|
|
if (!success) {
|
|
if (GetLastError() == ERROR_IO_PENDING) {
|
|
|
|
//
|
|
// block in GetOverlappedResult until the data arrives
|
|
//
|
|
|
|
success = GetOverlappedResult(
|
|
PipeHandle,
|
|
Overlap,
|
|
&transfer,
|
|
TRUE);
|
|
|
|
if ( !success ) {
|
|
DhcpPrint(( DEBUG_ERRORS, "Read:GetOverlappedResult failed, err=%d\n", GetLastError() ));
|
|
return;
|
|
}
|
|
} else {
|
|
DhcpPrint(( DEBUG_ERRORS, "Read:ReadFile failed, err=%d\n", GetLastError() ));
|
|
return;
|
|
}
|
|
}
|
|
|
|
switch ( dhcpRequest.WhatToDo ) {
|
|
|
|
case AcquireParametersOpCode:
|
|
Error = AcquireParameters( dhcpRequest.AdapterName );
|
|
break;
|
|
|
|
case ReleaseParametersOpCode:
|
|
Error = ReleaseParameters( dhcpRequest.AdapterName );
|
|
break;
|
|
|
|
case EnableDhcpOpCode:
|
|
Error = EnableDhcp( dhcpRequest.AdapterName );
|
|
break;
|
|
|
|
case DisableDhcpOpCode:
|
|
Error = DisableDhcp( dhcpRequest.AdapterName );
|
|
break;
|
|
|
|
default:
|
|
DhcpPrint(( DEBUG_ERRORS,
|
|
"Invalid request %d\n", dhcpRequest.WhatToDo) );
|
|
Error = ERROR_INVALID_FUNCTION;
|
|
}
|
|
|
|
dhcpResponse.Status = Error;
|
|
|
|
ResetEvent(Overlap->hEvent);
|
|
|
|
success = WriteFile(
|
|
PipeHandle,
|
|
&dhcpResponse,
|
|
sizeof( dhcpResponse ),
|
|
&bytesWritten,
|
|
Overlap );
|
|
|
|
if (!success) {
|
|
if (GetLastError() == ERROR_IO_PENDING) {
|
|
|
|
//
|
|
// make sure that the data has made it all the way back to the
|
|
// client, otherwise we'll quit this function and shortly thereafter
|
|
// call DisconnectNamedPipe which can cause the client side handle
|
|
// to be closed. The result is that the client loses the data
|
|
//
|
|
|
|
success = FlushFileBuffers(PipeHandle);
|
|
|
|
if (!success) {
|
|
DhcpPrint((DEBUG_ERRORS, "DHCP: ProcessApiRequest: FlushFileBuffers() returns %d\n", GetLastError()));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// wait in GetOverlappedResult until the client has the data. This
|
|
// should not wait
|
|
//
|
|
|
|
success = GetOverlappedResult(
|
|
PipeHandle,
|
|
Overlap,
|
|
&transfer,
|
|
TRUE);
|
|
|
|
if ( !success ) {
|
|
DhcpPrint(( DEBUG_ERRORS,
|
|
"Write:GetOverlappedResult failed, err=%d\n", GetLastError() ));
|
|
return;
|
|
}
|
|
} else {
|
|
DhcpPrint((DEBUG_ERRORS, "Write:WriteFile failed, err=%d\n", GetLastError()));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
DWORD
|
|
APIENTRY
|
|
DhcpAcquireParameters(
|
|
LPWSTR AdapterName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This functions forces the DHCP client to acquire a lease for, or
|
|
renew the lease for the specified NIC immediately.
|
|
|
|
Arguments:
|
|
|
|
AdapterName - name of the adapter.
|
|
|
|
Return Value:
|
|
|
|
The status of the operation.
|
|
|
|
--*/
|
|
{
|
|
return DhcpClientApi(AcquireParametersOpCode, AdapterName);
|
|
}
|
|
|
|
|
|
DWORD
|
|
APIENTRY
|
|
DhcpReleaseParameters(
|
|
LPWSTR AdapterName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This functions forces the DHCP client to release the for the
|
|
specified NIC.
|
|
|
|
Arguments:
|
|
|
|
AdapterName - name of the adapter.
|
|
|
|
Return Value:
|
|
|
|
The status of the operation.
|
|
|
|
--*/
|
|
{
|
|
return DhcpClientApi(ReleaseParametersOpCode, AdapterName);
|
|
}
|
|
|
|
DWORD
|
|
APIENTRY
|
|
DhcpEnableDynamicConfig(
|
|
LPWSTR AdapterName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This functions adds this adapter to the DHCP list if it is not
|
|
already enlisted.
|
|
|
|
Arguments:
|
|
|
|
AdapterName - name of the adapter.
|
|
|
|
Return Value:
|
|
|
|
The status of the operation.
|
|
|
|
--*/
|
|
{
|
|
return DhcpClientApi(EnableDhcpOpCode, AdapterName);
|
|
}
|
|
|
|
DWORD
|
|
APIENTRY
|
|
DhcpDisableDynamicConfig(
|
|
LPWSTR AdapterName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This functions removes this adapter from the DHCP list if it is enlisted.
|
|
|
|
Arguments:
|
|
|
|
AdapterName - name of the adapter.
|
|
|
|
Return Value:
|
|
|
|
The status of the operation.
|
|
|
|
--*/
|
|
{
|
|
return DhcpClientApi(DisableDhcpOpCode, AdapterName);
|
|
}
|
|
|
|
DWORD
|
|
DhcpClientApi(
|
|
IN API_OPCODE Opcode,
|
|
IN LPWSTR AdapterName
|
|
)
|
|
{
|
|
DHCP_REQUEST dhcpRequest;
|
|
DHCP_RESPONSE dhcpResponse;
|
|
BOOL ok;
|
|
DWORD bytesRead;
|
|
|
|
dhcpRequest.WhatToDo = Opcode;
|
|
wcscpy(dhcpRequest.AdapterName, AdapterName);
|
|
|
|
ok = CallNamedPipe(DHCP_PIPE_NAME,
|
|
(LPVOID)&dhcpRequest,
|
|
sizeof(dhcpRequest.WhatToDo)
|
|
+ (wcslen(AdapterName) + 1) * sizeof(WCHAR),
|
|
(LPVOID)&dhcpResponse,
|
|
sizeof(dhcpResponse),
|
|
&bytesRead,
|
|
NMPWAIT_WAIT_FOREVER
|
|
);
|
|
|
|
if( ok ) {
|
|
DhcpAssert(bytesRead == sizeof(dhcpResponse));
|
|
}
|
|
|
|
return ok ? dhcpResponse.Status : GetLastError();
|
|
}
|