windows-nt/Source/XPSP1/NT/net/dhcp/client/nt/dhcp.c
2020-09-26 16:20:57 +08:00

3394 lines
100 KiB
C
Raw 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) 1994 Microsoft Corporation
Module Name:
dhcp.c
Abstract:
This file contains specific to NT dhcp service.
Author:
Madan Appiah (madana) 7-Dec-1993.
Environment:
User Mode - Win32
Revision History:
--*/
#include "precomp.h"
#include <optchg.h>
#define GLOBAL_DATA_ALLOCATE
#include "dhcpglobal.h"
#include <dhcploc.h>
#include <dhcppro.h>
#include <lmsname.h>
#include <align.h>
#include <ipexport.h>
#include <dnsapi.h>
#include <iphlpapi.h>
#include <apiimpl.h>
#include <ntlsa.h>
#include <ntddndis.h>
#include "nlanotif.h"
extern
DWORD
MadcapInitGlobalData(VOID);
extern
VOID
MadcapCleanupGlobalData(VOID);
BOOL DhcpGlobalServiceRunning = FALSE;
HANDLE DhcpGlobalMediaSenseHandle = NULL;
HANDLE DhcpLsaDnsDomChangeNotifyHandle = NULL;
BOOL Initialized = FALSE;
//
// local protos
//
DWORD
DhcpInitMediaSense(
VOID
);
VOID
MediaSenseDetectionLoop(
VOID
);
DWORD
ProcessAdapterBindingEvent(
LPWSTR adapterName,
DWORD ipInterfaceContext,
IP_STATUS mediaStatus
);
DWORD
ProcessMediaSenseEvent(
LPWSTR adapterName,
DWORD ipInterfaceContext,
IP_STATUS mediaStatus
);
DWORD
DhcpInitGlobalData(
VOID
);
VOID
DhcpCleanupGlobalData(
VOID
);
CHAR DhcpGlobalHostNameBuf[MAX_COMPUTERNAME_LENGTH+300];
WCHAR DhcpGlobalHostNameBufW[MAX_COMPUTERNAME_LENGTH+300];
BOOLEAN
DhcpClientDllInit (
IN PVOID DllHandle,
IN ULONG Reason,
IN PCONTEXT Context OPTIONAL
)
/*++
Routine Description:
This is the DLL initialization routine for dhcpcsvc.dll.
Arguments:
Standard.
Return Value:
TRUE iff initialization succeeded.
--*/
{
DWORD Error = ERROR_SUCCESS;
BOOL BoolError;
DWORD Length;
UNREFERENCED_PARAMETER(DllHandle); // avoid compiler warnings
UNREFERENCED_PARAMETER(Context); // avoid compiler warnings
//
// Handle attaching netlogon.dll to a new process.
//
if (Reason == DLL_PROCESS_ATTACH) {
if ( !DisableThreadLibraryCalls( DllHandle ) ) {
return( FALSE );
}
DhcpGlobalWinSockInitialized = FALSE;
Error = DhcpInitGlobalData();
if( ERROR_SUCCESS != Error ) return FALSE;
Error = DhcpInitializeParamChangeRequests();
if( ERROR_SUCCESS != Error ) return FALSE;
} else if (Reason == DLL_PROCESS_DETACH) {
//
// Handle detaching dhcpcsvc.dll from a process.
//
DhcpCleanupGlobalData();
}
return( TRUE );
}
DWORD
UpdateStatus(
VOID
)
/*++
Routine Description:
This function updates the dhcp service status with the Service
Controller.
Arguments:
None.
Return Value:
Return code from SetServiceStatus.
--*/
{
DWORD Error = ERROR_SUCCESS;
if ( ((SERVICE_STATUS_HANDLE)0) != DhcpGlobalServiceStatusHandle ) {
DhcpGlobalServiceStatus.dwCheckPoint++;
if (!SetServiceStatus(
DhcpGlobalServiceStatusHandle,
&DhcpGlobalServiceStatus)) {
Error = GetLastError();
DhcpPrint((DEBUG_ERRORS, "SetServiceStatus failed, %ld.\n", Error ));
}
}
return(Error);
}
DWORD
ServiceControlHandler(
IN DWORD Opcode,
DWORD EventType,
PVOID EventData,
PVOID pContext
)
/*++
Routine Description:
This is the service control handler of the dhcp service.
Arguments:
Opcode - Supplies a value which specifies the action for the
service to perform.
Return Value:
None.
--*/
{
DWORD dwStatus = NO_ERROR;
switch (Opcode) {
case SERVICE_CONTROL_SHUTDOWN:
DhcpGlobalIsShuttingDown = TRUE;
case SERVICE_CONTROL_STOP:
if (DhcpGlobalServiceStatus.dwCurrentState != SERVICE_STOP_PENDING) {
DhcpPrint(( DEBUG_MISC, "Service is stop pending.\n"));
DhcpGlobalServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
DhcpGlobalServiceStatus.dwCheckPoint = 0;
//
// Send the status response.
//
UpdateStatus();
if (! SetEvent(DhcpGlobalTerminateEvent)) {
//
// Problem with setting event to terminate dhcp
// service.
//
DhcpPrint(( DEBUG_ERRORS,
"Error setting Terminate Event %lu\n",
GetLastError() ));
DhcpAssert(FALSE);
}
return dwStatus;
}
break;
case SERVICE_CONTROL_PAUSE:
DhcpGlobalServiceStatus.dwCurrentState = SERVICE_PAUSED;
DhcpPrint(( DEBUG_MISC, "Service is paused.\n"));
break;
case SERVICE_CONTROL_CONTINUE:
DhcpGlobalServiceStatus.dwCurrentState = SERVICE_RUNNING;
DhcpPrint(( DEBUG_MISC, "Service is Continued.\n"));
break;
case SERVICE_CONTROL_INTERROGATE:
DhcpPrint(( DEBUG_MISC, "Service is interrogated.\n"));
break;
default:
dwStatus = ERROR_CALL_NOT_IMPLEMENTED;
DhcpPrint(( DEBUG_MISC, "Service received unknown control.\n"));
break;
}
//
// Send the status response.
//
UpdateStatus();
return dwStatus;
}
VOID
ScheduleWakeUp(
PDHCP_CONTEXT DhcpContext,
DWORD TimeToSleep
)
/*++
Routine Description:
This functions schedules a DHCP routine to run.
Arguments:
Context - A pointer to a DHCP context block.
TimeToSleep - The time to sleep before running the renewal function,
in seconds.
Return Value:
The status of the operation.
--*/
{
time_t TimeNow;
BOOL BoolError;
if ( TimeToSleep == INFINIT_LEASE ) {
DhcpContext->RunTime = INFINIT_TIME;
} else {
TimeNow = time( NULL);
DhcpContext->RunTime = TimeNow + TimeToSleep;
if( DhcpContext->RunTime < TimeNow ) { // time wrapped around
DhcpContext->RunTime = INFINIT_TIME;
}
}
//
// Append this work item to the DhcpGlobalRenewList and kick the list event.
// Also, release the semaphore on this context, so that someone else can enter.
//
LOCK_RENEW_LIST();
// RenewalListEntry could be non-empty as there could be another thread scheduled on this.
// This could easily happen if:
// ProcessDhcpRequestForEver spawns a thread, but API Threads grabs semaphore and
// completes renewal process.. so it goes back into the RenewalList. Now Renewal thread
// completes, comes here and our list entry is not empty. So do this only when
// our list entry is not empty.
if( IsListEmpty(&DhcpContext->RenewalListEntry ) ) {
InsertTailList( &DhcpGlobalRenewList, &DhcpContext->RenewalListEntry );
}
UNLOCK_RENEW_LIST();
BoolError = SetEvent( DhcpGlobalRecomputeTimerEvent );
DhcpAssert( BoolError == TRUE );
}
SOCKET DhcpGlobalSocketForZeroAddress = INVALID_SOCKET;
ULONG DhcpGlobalNOpensForZeroAddress = 0;
CRITICAL_SECTION DhcpGlobalZeroAddressCritSect;
DWORD
OpenDhcpSocket(
PDHCP_CONTEXT DhcpContext
)
{
DWORD Error;
PLOCAL_CONTEXT_INFO localInfo;
DhcpAssert(!IS_MDHCP_CTX( DhcpContext ) ) ;
localInfo = DhcpContext->LocalInformation;
if ( localInfo->Socket != INVALID_SOCKET ) {
return ( ERROR_SUCCESS );
}
if( IS_DHCP_DISABLED(DhcpContext) ) {
//
// For static IP addresses, always bind to IP address of context.
//
Error = InitializeDhcpSocket(
&localInfo->Socket,
DhcpContext->IpAddress,
IS_APICTXT_ENABLED(DhcpContext)
);
goto Cleanup;
}
if( !IS_ADDRESS_PLUMBED(DhcpContext) || DhcpIsInitState(DhcpContext) ) {
// need to bind to zero address.. try to use the global one..
EnterCriticalSection(&DhcpGlobalZeroAddressCritSect);
if( ++DhcpGlobalNOpensForZeroAddress == 1 ) {
// open DhcpGlobalSocketForZeroAddress bound to zero address..
Error = InitializeDhcpSocket(&DhcpGlobalSocketForZeroAddress,0, IS_APICTXT_ENABLED(DhcpContext));
if( ERROR_SUCCESS != Error ) {
--DhcpGlobalNOpensForZeroAddress;
}
} else {
Error = ERROR_SUCCESS;
}
LeaveCriticalSection(&DhcpGlobalZeroAddressCritSect);
if( ERROR_SUCCESS == Error )
{
DhcpAssert(INVALID_SOCKET != DhcpGlobalSocketForZeroAddress);
if( INVALID_SOCKET == DhcpGlobalSocketForZeroAddress )
{
Error = ERROR_GEN_FAILURE;
goto Cleanup;
}
localInfo->Socket = DhcpGlobalSocketForZeroAddress;
}
goto Cleanup;
}
//
// create a socket for the dhcp protocol. it's important to bind the
// socket to the correct ip address. There are currently three cases:
//
// 1. If the interface has been autoconfigured, it already has an address,
// say, IP1. If the client receives a unicast offer from a dhcp server
// the offer will be addressed to IP2, which is the client's new dhcp
// address. If we bind the dhcp socket to IP1, the client won't be able
// to receive unicast responses. So, we bind the socket to 0.0.0.0.
// This will allow the socket to receive a unicast datagram addressed to
// any address.
//
// 2. If the interface in not plumbed (i.e. doesn't have an address) bind
// the socket to 0.0.0.0
//
// 3. If the interface has been plumbed has in *not* autoconfigured, then
// bind to the current address.
Error = InitializeDhcpSocket(
&localInfo->Socket,
( IS_ADDRESS_PLUMBED(DhcpContext) &&
!DhcpContext->IPAutoconfigurationContext.Address
) ?
DhcpContext->IpAddress : (DHCP_IP_ADDRESS)(0),
IS_APICTXT_ENABLED(DhcpContext)
);
Cleanup:
if( Error != ERROR_SUCCESS )
{
localInfo->Socket = INVALID_SOCKET;
DhcpPrint((DEBUG_ERRORS, "Socket open failed: 0x%lx\n", Error));
}
return(Error);
}
DWORD
CloseDhcpSocket(
PDHCP_CONTEXT DhcpContext
)
{
DWORD Error = ERROR_SUCCESS;
PLOCAL_CONTEXT_INFO localInfo;
DWORD Error1;
localInfo = DhcpContext->LocalInformation;
if( localInfo->Socket != INVALID_SOCKET ) {
if( DhcpGlobalSocketForZeroAddress == localInfo->Socket ) {
EnterCriticalSection(&DhcpGlobalZeroAddressCritSect);
if( 0 == --DhcpGlobalNOpensForZeroAddress ) {
// last open socket..
Error = closesocket( localInfo->Socket );
DhcpGlobalSocketForZeroAddress = INVALID_SOCKET;
}
LeaveCriticalSection(&DhcpGlobalZeroAddressCritSect);
} else {
Error = closesocket( localInfo->Socket );
}
if( Error != ERROR_SUCCESS ) {
DhcpPrint(( DEBUG_ERRORS, " Socket close failed, %ld\n", Error ));
}
localInfo->Socket = INVALID_SOCKET;
//
// Reset the IP stack to send DHCP broadcast to first
// uninitialized stack.
//
if (!IS_MDHCP_CTX(DhcpContext)) {
Error1 = IPResetInterface(localInfo->IpInterfaceContext);
// DhcpAssert( Error1 == ERROR_SUCCESS );
}
}
return( Error );
}
BEGIN_EXPORT
DWORD // status
UninitializeInterface( // close the scoket and unplumb the address
IN OUT PDHCP_CONTEXT DhcpContext // interface to unplumb
) END_EXPORT {
DWORD Error;
DWORD ReturnError;
PLOCAL_CONTEXT_INFO LocalInfo;
if( IS_ADDRESS_UNPLUMBED(DhcpContext) ) { // if address is not plumbed
DhcpPrint((DEBUG_ASSERT,"UninitializeInterface:Already unplumbed\n"));
return ERROR_SUCCESS;
}
LocalInfo = DhcpContext->LocalInformation;
ReturnError = CloseDhcpSocket( DhcpContext );
/*
* If the adapter is unbound, there is no point to reset the IP.
* The stack may re-use the IpInterfaceContext for other adapter.
* We cannot depend on the order of the event, ie, the new adapter which
* re-uses a context will be indicated to us later than the adapter which
* is going away.
*/
if (!IS_MEDIA_UNBOUND(DhcpContext)) {
Error = IPResetIPAddress( // remove the address we got before
LocalInfo->IpInterfaceContext,
DhcpContext->SubnetMask
);
}
if( Error != ERROR_SUCCESS ) ReturnError = Error;
ADDRESS_UNPLUMBED(DhcpContext);
if( ReturnError != ERROR_SUCCESS ) {
DhcpPrint(( DEBUG_ERRORS,"UninitializeInterface:0x%ld\n", ReturnError));
}
return ReturnError;
}
BEGIN_EXPORT
DWORD // status
InitializeInterface( // plumb address and open socket
IN OUT PDHCP_CONTEXT DhcpContext // context to initialize
) END_EXPORT {
PLOCAL_CONTEXT_INFO LocalInfo;
DWORD Error;
DWORD ReturnError;
if( IS_ADDRESS_PLUMBED(DhcpContext) ) { // if already plumbed, nothing to do
DhcpPrint((DEBUG_ASSERT, "InitializeInterface:Already plumbed\n"));
return ERROR_SUCCESS;
}
LocalInfo = DhcpContext->LocalInformation;
ADDRESS_PLUMBED(DhcpContext);
Error = IPSetIPAddress( // set new ip address, mask with ip
LocalInfo->IpInterfaceContext, // identify context
DhcpContext->IpAddress,
DhcpContext->SubnetMask
);
if( ERROR_SUCCESS != Error ) { // if anything fails, got to be address conflict
DhcpPrint((DEBUG_TRACK, "IPSetIPAddress %ld,%ld,%ld : 0x%lx\n",
LocalInfo->IpInterfaceContext,
DhcpContext->IpAddress, DhcpContext->SubnetMask,
Error
));
Error = ERROR_DHCP_ADDRESS_CONFLICT;
} else { // everything went fine, open the socket for future
Error = OpenDhcpSocket( DhcpContext );
}
if( ERROR_SUCCESS != Error ) {
DhcpPrint((DEBUG_ERRORS, "InitializeInterface:0x%lx\n", Error));
}
return Error;
}
HKEY
DhcpRegGetAltKey(
IN LPCWSTR AdapterName
)
/*++
Routine Description:
Try to open the old format registry key for the adapter.
Arguments:
AdapterName -- adapter device name (no \Device\.. prefix)
Return Value:
NULL if key could not be opened..
valid HKEY otherwise.
--*/
{
HKEY AdapterKey, ReturnKey;
ULONG Error;
if( NULL == AdapterName ) return NULL;
Error = RegOpenKeyEx(
DhcpGlobalServicesKey,
AdapterName,
0 /* Reserved */,
KEY_ALL_ACCESS,
&AdapterKey
);
if( ERROR_SUCCESS != Error ) return NULL;
Error = RegOpenKeyEx(
AdapterKey,
DHCP_ADAPTER_PARAMETERS_KEY_OLD,
0 /* Reserved */,
KEY_ALL_ACCESS,
&ReturnKey
);
RegCloseKey(AdapterKey);
if( ERROR_SUCCESS != Error ) return NULL;
return ReturnKey;
}
#ifdef BOOTPERF
VOID
DhcpSaveQuickBootValuesToRegistry(
IN PDHCP_CONTEXT DhcpContext,
IN BOOL fDelete
)
/*++
Routine Description:
If quick boot is enabled for this interface as well as fDelete is
not FALSE, then this routine will save the IP address, mask and
lease time options to the registry. Otherwise, it would delete
these optiosn from the registry.
This routine also checks to see if current time has gone past the
T1 time and if so, it would clear the registry values..
Arguments:
DhcpContext -- the context to do this for
fDelete -- shoudl the values be deleted?
--*/
{
ULONG Error;
ULONG Now = (ULONG)time(NULL);
ULONGLONG NtSysTime;
//
// Check if quick boot is enabled on the context,
// if the context is dhcp enabled or not,
// Check if we are past t1 time or lease expiration..
//
if( TRUE == fDelete ||
FALSE == DhcpContext->fQuickBootEnabled ||
IS_DHCP_DISABLED(DhcpContext) ||
0 == DhcpContext->IpAddress ||
( IS_ADDRESS_DHCP(DhcpContext) &&
( Now >= (ULONG)DhcpContext->T2Time ||
Now >= (ULONG)DhcpContext->LeaseExpires
) ) ) {
fDelete = TRUE;
}
//
// Now we know if we are going to delete the values
// or create them. If creating the values, we need to
// save the lease time in NT system time format.
//
if( TRUE == fDelete ) {
DhcpRegDeleteQuickBootValues(
DhcpContext->AdapterInfoKey
);
} else {
ULONG Diff;
ULONGLONG Diff64;
GetSystemTimeAsFileTime((FILETIME *)&NtSysTime);
if( IS_ADDRESS_DHCP(DhcpContext) ) {
Diff = ((ULONG)DhcpContext->T2Time) - Now;
//DhcpAssert(Diff == DhcpContext->T2 );
//
// Now add the diff to the system time.
// (Diff is in seconds. 10000*1000 times this is
// in 100-nanoseconds like the file time)
//
Diff64 = ((ULONGLONG)Diff);
Diff64 *= (ULONGLONG)10000;
Diff64 *= (ULONGLONG)1000;
NtSysTime += Diff64;
} else {
//
// For autonet addresses, time is infinte
//
LARGE_INTEGER Li = {0,0};
Li.HighPart = 0x7FFFFFF;
Li.LowPart = 0xFFFFFFFF;
NtSysTime = *(ULONGLONG*)&Li;
}
//
// Now save the IP address, Mask and Lease time.
// We will leave default gateways alone.
//
DhcpRegSaveQuickBootValues(
DhcpContext->AdapterInfoKey,
DhcpContext->IpAddress,
DhcpContext->SubnetMask,
NtSysTime
);
}
}
#endif BOOTPERF
BEGIN_EXPORT
DWORD // win32 status
DhcpSetAllRegistryParameters( // update the registry completely
IN PDHCP_CONTEXT DhcpContext, // input context to save stuff
IN DHCP_IP_ADDRESS ServerAddress // which server is this about?
) END_EXPORT {
DWORD i;
DWORD Error;
DWORD LastError;
HKEY AltKey;
PLOCAL_CONTEXT_INFO LocalInfo;
struct /* anonymous */ {
LPWSTR ValueName; // where to store this in the registry?
DWORD Value; // what is the value to store
DWORD RegValueType; // dword or string?
} DwordArray[] = {
DHCP_IP_ADDRESS_STRING, DhcpContext->IpAddress, DHCP_IP_ADDRESS_STRING_TYPE,
DHCP_SUBNET_MASK_STRING, DhcpContext->SubnetMask, DHCP_SUBNET_MASK_STRING_TYPE,
DHCP_SERVER, ServerAddress, DHCP_SERVER_TYPE,
DHCP_LEASE, DhcpContext->Lease, DHCP_LEASE_TYPE,
DHCP_LEASE_OBTAINED_TIME, (DWORD) DhcpContext->LeaseObtained, DHCP_LEASE_OBTAINED_TIME_TYPE,
DHCP_LEASE_T1_TIME, (DWORD) DhcpContext->T1Time, DHCP_LEASE_T1_TIME_TYPE,
DHCP_LEASE_T2_TIME, (DWORD) DhcpContext->T2Time, DHCP_LEASE_T2_TIME_TYPE,
DHCP_LEASE_TERMINATED_TIME, (DWORD) DhcpContext->LeaseExpires, DHCP_LEASE_TERMINATED_TIME_TYPE,
//
// Sentinel -- all values from below this won't get
// save to fake AdapterKey (for Server Apps portability).
//
NULL, 0, REG_NONE,
DHCP_IPAUTOCONFIGURATION_ADDRESS, DhcpContext->IPAutoconfigurationContext.Address, DHCP_IPAUTOCONFIGURATION_ADDRESS_TYPE,
DHCP_IPAUTOCONFIGURATION_MASK, DhcpContext->IPAutoconfigurationContext.Mask, DHCP_IPAUTOCONFIGURATION_MASK_TYPE,
DHCP_IPAUTOCONFIGURATION_SEED, DhcpContext->IPAutoconfigurationContext.Seed, DHCP_IPAUTOCONFIGURATION_SEED_TYPE,
DHCP_ADDRESS_TYPE_VALUE, IS_ADDRESS_AUTO(DhcpContext)?ADDRESS_TYPE_AUTO:ADDRESS_TYPE_DHCP, DHCP_ADDRESS_TYPE_TYPE,
};
LocalInfo = ((PLOCAL_CONTEXT_INFO) DhcpContext->LocalInformation);
LOCK_OPTIONS_LIST();
Error = DhcpRegSaveOptions( // save the options info - ignore error
&DhcpContext->RecdOptionsList,
LocalInfo->AdapterName,
DhcpContext->ClassId,
DhcpContext->ClassIdLength
);
UNLOCK_OPTIONS_LIST();
LastError = ERROR_SUCCESS;
AltKey = DhcpRegGetAltKey(LocalInfo->AdapterName);
for( i = 0; i < sizeof(DwordArray)/sizeof(DwordArray[0]); i ++ ) {
if( REG_DWORD == DwordArray[i].RegValueType ) {
Error = RegSetValueEx(
DhcpContext->AdapterInfoKey,
DwordArray[i].ValueName,
0 /* Reserved */,
REG_DWORD,
(LPBYTE)&DwordArray[i].Value,
sizeof(DWORD)
);
if( NULL != AltKey ) {
(void)RegSetValueEx(
AltKey, DwordArray[i].ValueName,
0, REG_DWORD, (LPBYTE)&DwordArray[i].Value,
sizeof(DWORD)
);
}
} else if( REG_NONE == DwordArray[i].RegValueType ) {
if( NULL != AltKey ) {
RegCloseKey(AltKey);
AltKey = NULL;
}
} else {
Error = RegSetIpAddress(
DhcpContext->AdapterInfoKey,
DwordArray[i].ValueName,
DwordArray[i].RegValueType,
DwordArray[i].Value
);
if( NULL != AltKey ) {
(void)RegSetIpAddress(
AltKey, DwordArray[i].ValueName,
DwordArray[i].RegValueType, DwordArray[i].Value
);
}
}
if( ERROR_SUCCESS != Error ) {
DhcpPrint((DEBUG_ERRORS,"SetAllRegistryParams:RegSetValueEx(%ws):0x%lx\n", DwordArray[i].ValueName,Error));
LastError = Error;
}
}
if( NULL != AltKey ) {
RegCloseKey(AltKey);
AltKey = NULL;
}
#ifdef BOOTPERF
DhcpSaveQuickBootValuesToRegistry(DhcpContext, FALSE);
#endif BOOTPERF
return LastError;
}
DWORD // win32 status
CheckForAddressConflict( // send ARP and see if the address conflicts..
IN DHCP_IP_ADDRESS Address, // address to send gratuitous ARP for..
IN ULONG nRetries // how many attempts to do?
)
{
DWORD HwAddressBufDummy[20]; // HwAddress cant be larger than50*sizeof(DWORD)
ULONG HwAddressBufSize;
DWORD Error;
if( 0 == Address ) return NO_ERROR; // nothing to do if we are resetting address
while( nRetries -- ) { // keep trying as many times are required..
HwAddressBufSize = sizeof(HwAddressBufDummy);
// even though src and dest addr are same below, tcpip discards the src address we give
// here (it just uses it to find the interface to send on) and uses the addr of interface..
Error = SendARP( // send an ARP Request
Address, // destination address to ARP for
Address, // dont use zero -- tcpip asserts, use same addres..
HwAddressBufDummy,
&HwAddressBufSize
);
if( ERROR_SUCCESS == Error && 0 != HwAddressBufSize ) {
DhcpPrint((DEBUG_ERRORS, "Address conflict detected for RAS\n"));
return ERROR_DHCP_ADDRESS_CONFLICT; // some other client has got this address!!!!
} else {
DhcpPrint((DEBUG_ERRORS, "RAS stuff: SendARP returned 0x%lx\n", Error));
}
}
return ERROR_SUCCESS;
}
BEGIN_EXPORT
DWORD // status
SetDhcpConfigurationForNIC( // plumb registry, stack and notify clients
IN OUT PDHCP_CONTEXT DhcpContext, // input context to do work for
IN PDHCP_FULL_OPTIONS DhcpOptions, // options to plumb registry with
IN DHCP_IP_ADDRESS Address, // address to plumb stack with
IN DHCP_IP_ADDRESS ServerAddress, // need to plumb registry
IN BOOL fNewAddress // TRUE==>plumb stack, FALSE=> dont plumb stack
) END_EXPORT {
DWORD Error;
DWORD BoolError;
DWORD LeaseTime;
DWORD_PTR LeaseObtainedTime;
DWORD_PTR LeaseExpiresTime;
DWORD_PTR T1Time;
DWORD_PTR T2Time;
DWORD SubnetMask;
LIST_ENTRY ExpiredOptions;
PLOCAL_CONTEXT_INFO LocalInfo;
ULONG OldIp, OldMask;
DWORD NotifyDnsCache(void);
BOOLEAN fSomethingChanged = FALSE;
LocalInfo = (PLOCAL_CONTEXT_INFO)DhcpContext->LocalInformation;
#ifdef BOOTPERF
OldIp = LocalInfo->OldIpAddress;
OldMask = LocalInfo->OldIpMask;
LocalInfo->OldIpAddress = LocalInfo->OldIpMask = 0;
if( Address && fNewAddress && OldIp && Address != OldIp
&& IS_ADDRESS_UNPLUMBED(DhcpContext) ) {
//
// If the first time the address is being set, and for some reason
// the address being set is NOT the address we are trying to set,
// then the machine already has an IP. Bad Bad thing.
// So, we just reset the old IP to avoid spurious address conflict
// errors..
//
Error = IPResetIPAddress(
LocalInfo->IpInterfaceContext, DhcpContext->SubnetMask
);
}
#endif BOOTPERF
InitializeListHead(&ExpiredOptions);
DhcpGetExpiredOptions(&DhcpContext->RecdOptionsList, &ExpiredOptions);
LOCK_OPTIONS_LIST();
Error = DhcpDestroyOptionsList(&ExpiredOptions, &DhcpGlobalClassesList);
UNLOCK_OPTIONS_LIST();
DhcpAssert(ERROR_SUCCESS == Error);
if( Address && (DWORD)-1 == ServerAddress ) // mark address type as auto or dhcp..
ACQUIRED_AUTO_ADDRESS(DhcpContext);
else ACQUIRED_DHCP_ADDRESS(DhcpContext);
SubnetMask = ntohl(DhcpDefaultSubnetMask(Address));
if( IS_ADDRESS_AUTO(DhcpContext) ) {
LeaseTime = 0;
} else {
LeaseTime = htonl(DHCP_MINIMUM_LEASE);
}
T1Time = 0;
T2Time = 0;
if( DhcpOptions && DhcpOptions->SubnetMask ) SubnetMask = *(DhcpOptions->SubnetMask);
if( DhcpOptions && DhcpOptions->LeaseTime) LeaseTime = *(DhcpOptions->LeaseTime);
if( DhcpOptions && DhcpOptions->T1Time) T1Time = *(DhcpOptions->T1Time);
if( DhcpOptions && DhcpOptions->T2Time) T2Time = *(DhcpOptions->T2Time);
LeaseTime = ntohl(LeaseTime);
LeaseObtainedTime = time(NULL);
if( 0 == T1Time ) T1Time = LeaseTime/2;
else {
T1Time = ntohl((DWORD) T1Time);
DhcpAssert(T1Time < LeaseTime);
}
T1Time += LeaseObtainedTime;
if( T1Time < LeaseObtainedTime ) T1Time = INFINIT_TIME;
if( 0 == T2Time ) T2Time = LeaseTime * 7/8;
else {
T2Time = ntohl((DWORD)T2Time);
DhcpAssert(T2Time < DhcpContext->Lease );
DhcpAssert(T2Time > T1Time - LeaseObtainedTime);
}
T2Time += LeaseObtainedTime;
if( T2Time < LeaseObtainedTime ) T2Time = INFINIT_TIME;
LeaseExpiresTime = LeaseObtainedTime + LeaseTime;
if( LeaseExpiresTime < LeaseObtainedTime ) LeaseExpiresTime = INFINIT_TIME;
if( IS_ADDRESS_AUTO(DhcpContext) ) {
LeaseExpiresTime = INFINIT_TIME;
// DhcpContext->IPAutoconfigurationContext.Address = 0;
}
if( IS_ADDRESS_DHCP(DhcpContext) ) {
DhcpContext->DesiredIpAddress = Address? Address : DhcpContext->IpAddress;
DhcpContext->SubnetMask = SubnetMask;
DhcpContext->IPAutoconfigurationContext.Address = 0;
}
DhcpContext->IpAddress = Address;
DhcpContext->Lease = LeaseTime;
DhcpContext->LeaseObtained = LeaseObtainedTime;
DhcpContext->T1Time = T1Time;
DhcpContext->T2Time = T2Time;
DhcpContext->LeaseExpires = LeaseExpiresTime;
if( IS_APICTXT_ENABLED(DhcpContext) ) { // dont do anything when called thru lease api's for RAS
if( IS_ADDRESS_DHCP(DhcpContext) ) { // dont do any conflict detection for dhcp addresses..
return ERROR_SUCCESS;
}
Error = CheckForAddressConflict(Address,2);
if( ERROR_SUCCESS != Error ) { // address did conflict with something
DhcpPrint((DEBUG_ERRORS, "RAS AddressConflict: 0x%lx\n", Error));
return Error;
}
return ERROR_SUCCESS;
}
if( DhcpIsInitState(DhcpContext) &&
(Address == 0 || IS_FALLBACK_DISABLED(DhcpContext)) )
{ // lost address, lose options
Error = DhcpClearAllOptions(DhcpContext);
}
/*
* Check if something is changed before the registry is overwritten
*/
if (!fNewAddress) {
fSomethingChanged = DhcpRegIsOptionChanged( // Check if something is really changed
&DhcpContext->RecdOptionsList,
LocalInfo->AdapterName,
DhcpContext->ClassId,
DhcpContext->ClassIdLength
);
} else {
fSomethingChanged = TRUE;
}
Error = DhcpSetAllRegistryParameters( // save all the registry parameters
DhcpContext,
ServerAddress
);
if( fNewAddress && 0 == DhcpContext->IpAddress ) {
Error = DhcpSetAllStackParameters( // reset all stack parameters, and also DNS
DhcpContext,
DhcpOptions
);
}
if( !fNewAddress ) { // address did not change, but ask NetBT to read from registry
NetBTNotifyRegChanges(LocalInfo->AdapterName);
} else { // address change -- reset the stack
Error = UninitializeInterface(DhcpContext);
if(ERROR_SUCCESS != Error ) return Error;
if( Address ) {
Error = InitializeInterface(DhcpContext);
if( ERROR_SUCCESS != Error) return Error;
}
BoolError = PulseEvent(DhcpGlobalNewIpAddressNotifyEvent);
if (FALSE == BoolError) {
DhcpPrint((DEBUG_ERRORS, "PulseEvent failed: 0x%lx\n", GetLastError()));
DhcpAssert(FALSE);
}
}
if( !fNewAddress || 0 != DhcpContext->IpAddress ) {
Error = DhcpSetAllStackParameters( // reset all stack parameters, and also DNS
DhcpContext,
DhcpOptions
);
}
NotifyDnsCache();
if (fSomethingChanged) {
NLANotifyDHCPChange();
}
return Error;
}
BEGIN_EXPORT
DWORD // win32 status
SetAutoConfigurationForNIC( // autoconfigured address-set dummy options before calling SetDhcp..
IN OUT PDHCP_CONTEXT DhcpContext, // the context to set info for
IN DHCP_IP_ADDRESS Address, // autoconfigured address
IN DHCP_IP_ADDRESS Mask // input mask
) END_EXPORT
{
DWORD Error = ERROR_SUCCESS;
PDHCP_OPTIONS pOptions = NULL;
if (Address != 0 && IS_FALLBACK_ENABLED(DhcpContext))
{
// we rely that DhcpAllocateMemory is using
// calloc() hence zeroes all the structure
pOptions = DhcpAllocateMemory(sizeof (DHCP_OPTIONS));
if (pOptions == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
// replace DhcpContext->RecdOptionsList w/ FbOptionsList
// and filter out the fallback IpAddress & SubnetMask
Error = DhcpCopyFallbackOptions(DhcpContext, &Address, &Mask);
// pOptions->SubnetMask has to point to the fallback mask address
// it is safe to get pOptions->SubnetMask to point to a local variable
// since pOptions will not live more than Mask.
pOptions->SubnetMask = &Mask;
}
// if no error has been hit so far go further and try to
// plumb in the autonet/fallback configuration.
if (Error == ERROR_SUCCESS)
{
DhcpContext->SubnetMask = Mask;
Error = SetDhcpConfigurationForNIC(
DhcpContext,
pOptions,
Address,
(DWORD)-1,
TRUE
);
}
if (pOptions != NULL)
DhcpFreeMemory(pOptions);
return Error;
}
DWORD
SystemInitialize(
VOID
)
/*++
Routine Description:
This function performs implementation specific initialization
of DHCP.
Arguments:
None.
Return Value:
The status of the operation.
--*/
{
DWORD Error;
HKEY OptionInfoKey = NULL;
DHCP_KEY_QUERY_INFO QueryInfo;
DWORD OptionInfoSize;
DWORD Index;
#if 0
PLIST_ENTRY listEntry;
PDHCP_CONTEXT dhcpContext;
#endif
DWORD Version;
//
// Init Global variables.
//
DhcpGlobalOptionCount = 0;
DhcpGlobalOptionInfo = NULL;
DhcpGlobalOptionList = NULL;
//
// Seed the random number generator for Transaction IDs.
//
srand( (unsigned int) time( NULL ) );
//
// make host comment, windows' version.
//
DhcpGlobalHostComment = DhcpAllocateMemory( HOST_COMMENT_LENGTH );
if( DhcpGlobalHostComment == NULL ) {
Error = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
Version = GetVersion();
sprintf( DhcpGlobalHostComment, "%s %d.%d, BUILD %d",
((Version & 0x80000000) ? WINDOWS_32S : WINDOWS_NT),
Version & 0xFF,
(Version >> 8) & 0xFF,
((Version >> 16) & 0x7FFF) );
//
// Obtain a handle to the message file.
//
DhcpGlobalMessageFileHandle = LoadLibrary( DHCP_SERVICE_DLL );
//
// Register for global machine domain name changes.
//
DhcpLsaDnsDomChangeNotifyHandle = CreateEvent(NULL, FALSE, FALSE, NULL);
if( NULL == DhcpLsaDnsDomChangeNotifyHandle ) {
Error = GetLastError();
goto Cleanup;
}
Error = LsaRegisterPolicyChangeNotification(
PolicyNotifyDnsDomainInformation,
DhcpLsaDnsDomChangeNotifyHandle
);
Error = LsaNtStatusToWinError(Error);
if( ERROR_SUCCESS != Error ) {
DhcpPrint((DEBUG_INIT, "LsaRegisterPolicyChangeNotification failed %lx\n", Error));
goto Cleanup;
}
Error = ERROR_SUCCESS;
// Start DNS Thread now..
if( UseMHAsyncDns ) {
Error = DnsAsyncRegisterInit(NULL);
// If we could not start Async Dns.. do not try to quit it.
if( ERROR_SUCCESS != Error ) UseMHAsyncDns = 0;
//
// ignore any Dns register init errors..
//
Error = ERROR_SUCCESS;
}
Cleanup:
return( Error );
}
DWORD
DhcpInitData(
VOID
)
/*++
Routine Description:
This function initializes DHCP Global data.
Arguments:
None.
Return Value:
Windows Error.
--*/
{
DWORD Error;
DhcpGlobalMessageFileHandle = NULL;
DhcpGlobalRecomputeTimerEvent = NULL;
DhcpGlobalTerminateEvent = NULL;
DhcpGlobalClientApiPipe = NULL;
DhcpGlobalClientApiPipeEvent = NULL;
UseMHAsyncDns = DEFAULT_USEMHASYNCDNS;
DhcpGlobalHostComment = NULL;
DhcpGlobalNewIpAddressNotifyEvent = NULL;
InitializeListHead( &DhcpGlobalNICList );
InitializeListHead( &DhcpGlobalRenewList );
DhcpGlobalMsgPopupThreadHandle = NULL;
DhcpGlobalDisplayPopup = TRUE;
DhcpGlobalParametersKey = NULL;
DhcpGlobalTcpipParametersKey = NULL;
DhcpGlobalServicesKey = NULL;
UseMHAsyncDns = DEFAULT_USEMHASYNCDNS;
AutonetRetriesSeconds = EASYNET_ALLOCATION_RETRY;
DhcpGlobalUseInformFlag = TRUE;
DhcpGlobalDontPingGatewayFlag = FALSE;
DhcpGlobalIsShuttingDown = FALSE;
#ifdef BOOTPERF
DhcpGlobalQuickBootEnabledFlag = TRUE;
#endif BOOTPERF
//
// init service status data.
//
DhcpGlobalServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
DhcpGlobalServiceStatus.dwCurrentState = SERVICE_START_PENDING;
DhcpGlobalServiceStatus.dwControlsAccepted = (
SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_SHUTDOWN
);
DhcpGlobalServiceStatus.dwCheckPoint = 1;
DhcpGlobalServiceStatus.dwWaitHint = 25000;
// should be larger than the wait before the last retry.
DhcpGlobalServiceStatus.dwWin32ExitCode = ERROR_SUCCESS;
DhcpGlobalServiceStatus.dwServiceSpecificExitCode = 0;
//
// Initialize dhcp to receive service requests by registering the
// control Handler.
//
DhcpGlobalServiceStatusHandle = RegisterServiceCtrlHandlerEx(
SERVICE_DHCP,
ServiceControlHandler,
NULL );
if ( DhcpGlobalServiceStatusHandle == 0 ) {
Error = GetLastError();
DhcpPrint(( DEBUG_INIT,
"RegisterServiceCtrlHandlerW failed, %ld.\n", Error ));
//return(Error);
}
//
// Tell Service Controller that we are start pending.
//
UpdateStatus();
Error = DhcpInitRegistry();
if( ERROR_SUCCESS != Error ) goto Cleanup;
// create the waitable timer.
DhcpGlobalWaitableTimerHandle = CreateWaitableTimer(
NULL,
FALSE,
NULL );
if( DhcpGlobalWaitableTimerHandle == NULL ) {
Error = GetLastError();
goto Cleanup;
}
DhcpGlobalRecomputeTimerEvent =
CreateEvent(
NULL, // no security.
FALSE, // automatic reset.
TRUE, // initial state is signaled.
NULL ); // no name.
if( DhcpGlobalRecomputeTimerEvent == NULL ) {
Error = GetLastError();
goto Cleanup;
}
DhcpGlobalTerminateEvent =
CreateEvent(
NULL, // no security.
TRUE, // manual reset
FALSE, // initial state is signaled.
NULL ); // no name.
if( DhcpGlobalTerminateEvent == NULL ) {
Error = GetLastError();
goto Cleanup;
}
//
// create a named event that notifies the ip address changes to
// external apps.
//
DhcpGlobalNewIpAddressNotifyEvent = DhcpOpenGlobalEvent();
if( DhcpGlobalNewIpAddressNotifyEvent == NULL ) {
Error = GetLastError();
goto Cleanup;
}
Error = DhcpApiInit();
if( Error != ERROR_SUCCESS ) {
goto Cleanup;
}
Error = ERROR_SUCCESS;
Cleanup:
if( Error != ERROR_SUCCESS ) {
DhcpPrint(( DEBUG_ERRORS, "DhcpInitData failed, %ld.\n", Error ));
}
return( Error );
}
BOOL
DhcpDoReleaseOnShutDown(
IN PDHCP_CONTEXT DhcpContext
)
/*++
Routine Description:
This routine checks to see if the context has release on
shutdown enabled.
Release on shutdown is enabled if either deliberately enabled
via the registry. It is disabled if deliberately disabled
via the registry. If neither, then the vendor option is searched
for to see if the particular option is present. If present,
then the value in that is used. If not present, then this is
not considered enabled.
Return Value:
TRUE -- Release on shutdown enabled.
FALSE -- Release on shutdown disabled.
--*/
{
BOOL fFound;
DWORD Result;
if( DhcpContext->ReleaseOnShutdown != RELEASE_ON_SHUTDOWN_OBEY_DHCP_SERVER ) {
//
// The user deliberately specified the behaviour. Do as instructed.
//
return DhcpContext->ReleaseOnShutdown != RELEASE_ON_SHUTDOWN_NEVER;
}
//
// Need to do as requested by the server. In this case, need to
// look for vendor option
//
fFound = DhcpFindDwordOption(
DhcpContext,
OPTION_MSFT_VENDOR_FEATURELIST,
TRUE,
&Result
);
if( fFound ) {
//
// Found the option? then do what the server specified.
//
return (
(Result & BIT_RELEASE_ON_SHUTDOWN) == BIT_RELEASE_ON_SHUTDOWN
);
}
//
// Didn't find the option. By default, we have this turned off.
//
return FALSE;
}
VOID
DhcpCleanup(
DWORD Error
)
/*++
Routine Description:
This function cleans up DHCP Global data before stopping the
service.
Arguments:
None.
Return Value:
Windows Error.
--*/
{
DWORD WaitStatus;
DhcpPrint(( DEBUG_MISC,
"Dhcp Client service is shutting down, %ld.\n", Error ));
if( Error != ERROR_SUCCESS ) {
DhcpLogEvent( NULL, EVENT_DHCP_SHUTDOWN, Error );
}
//
// Service is shuting down, may be due to some service problem or
// the administrator is stopping the service. Inform the service.
//
DhcpGlobalServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
DhcpGlobalServiceStatus.dwCheckPoint = 0;
UpdateStatus();
if( FALSE == DhcpGlobalWinSockInitialized ||
FALSE == Initialized ) {
goto Done;
}
DhcpGlobalServiceRunning = FALSE;
if( FALSE == DhcpGlobalIsShuttingDown ) {
if( NULL != DhcpGlobalMediaSenseHandle ) {
// MediaSenseDetectionLoop can do discovers, etc. That has to die before
// any other data is killed
WaitStatus = WaitForSingleObject( DhcpGlobalMediaSenseHandle, 3000 );
if( WAIT_OBJECT_0 != WaitStatus ) {
// Should have completed by now. Since it did not, kill it!
if( TerminateThread( DhcpGlobalMediaSenseHandle, (DWORD)-1) ) {
DhcpPrint((DEBUG_ERRORS, "MediaSenseDetectionLoop killed!\n"));
} else {
DhcpPrint((DEBUG_ERRORS, "Could not kill MediaSense Loop: %ld\n", GetLastError()));
}
}
CloseHandle(DhcpGlobalMediaSenseHandle);
DhcpGlobalMediaSenseHandle = NULL;
}
if ( NULL != DhcpGlobalWaitableTimerHandle ) {
DhcpCancelWaitableTimer( DhcpGlobalWaitableTimerHandle );
CloseHandle( DhcpGlobalWaitableTimerHandle );
DhcpGlobalWaitableTimerHandle = NULL;
}
}
if( FALSE == DhcpGlobalIsShuttingDown ) {
if( NULL != DhcpLsaDnsDomChangeNotifyHandle ) {
LsaUnregisterPolicyChangeNotification(
PolicyNotifyDnsDomainInformation,
DhcpLsaDnsDomChangeNotifyHandle
);
CloseHandle(DhcpLsaDnsDomChangeNotifyHandle);
DhcpLsaDnsDomChangeNotifyHandle = NULL;
}
}
LOCK_RENEW_LIST();
while( !IsListEmpty(&DhcpGlobalNICList) ) {
PLIST_ENTRY NextEntry;
PDHCP_CONTEXT DhcpContext;
DWORD DefaultSubnetMask;
PLOCAL_CONTEXT_INFO LocalInfo;
DWORD LocalError;
NextEntry = RemoveHeadList(&DhcpGlobalNICList);
DhcpContext = CONTAINING_RECORD( NextEntry, DHCP_CONTEXT, NicListEntry );
InitializeListHead(&DhcpContext->NicListEntry);
RemoveEntryList( &DhcpContext->RenewalListEntry );
InitializeListHead( &DhcpContext->RenewalListEntry );
LocalInfo = DhcpContext->LocalInformation;
DefaultSubnetMask = DhcpDefaultSubnetMask(0);
//
// Close the semaphore handle after first acquiring it
//
if( FALSE == DhcpGlobalIsShuttingDown ) {
UNLOCK_RENEW_LIST();
DhcpAssert( NULL != DhcpContext->RenewHandle);
LocalError = WaitForSingleObject(DhcpContext->RenewHandle, INFINITE);
DhcpAssert( WAIT_OBJECT_0 == LocalError);
//
// There may be other threads waiting on this, but nevermind..
// DhcpAssert(1 == DhcpContext->RefCount);
//
CloseHandle(DhcpContext->RenewHandle);
DhcpContext->RenewHandle = NULL;
if (DhcpContext->CancelEvent != WSA_INVALID_EVENT) {
WSACloseEvent(DhcpContext->CancelEvent);
DhcpContext->CancelEvent = WSA_INVALID_EVENT;
}
LOCK_RENEW_LIST();
}
//
// reset the stack since dhcp is going away and we dont want IP to keep
// using an expired address if we are not brought back up
//
if ( IS_DHCP_ENABLED(DhcpContext) ) {
if( TRUE == DhcpGlobalIsShuttingDown
&& !DhcpIsInitState(DhcpContext)
&& DhcpDoReleaseOnShutDown(DhcpContext) ) {
//
// Shutting down. Check if Release On Shutdown is enabled
// For the adapter in question. If so, then do it.
//
LocalError = ReleaseIpAddress(DhcpContext);
if( ERROR_SUCCESS != LocalError ) {
DhcpPrint((DEBUG_ERRORS, "ReleaseAddress failed: %ld\n"));
}
}
}
if( FALSE == DhcpGlobalIsShuttingDown ) {
if( 0 == InterlockedDecrement(&DhcpContext->RefCount) ) {
//
// Ok, we lost the context.. Just free it now..
//
DhcpDestroyContext(DhcpContext);
}
}
}
UNLOCK_RENEW_LIST();
if( FALSE == DhcpGlobalIsShuttingDown ) {
DhcpCleanupParamChangeRequests();
}
if( DhcpGlobalMsgPopupThreadHandle != NULL ) {
DWORD WaitStatus;
WaitStatus = WaitForSingleObject(
DhcpGlobalMsgPopupThreadHandle,
0 );
if ( WaitStatus == 0 ) {
//
// This shouldn't be a case, because we close this handle at
// the end of popup thread.
//
DhcpAssert( WaitStatus == 0 );
CloseHandle( DhcpGlobalMsgPopupThreadHandle );
DhcpGlobalMsgPopupThreadHandle = NULL;
} else {
DhcpPrint((DEBUG_ERRORS,
"Cannot WaitFor message popup thread: %ld\n",
WaitStatus ));
if( TerminateThread(
DhcpGlobalMsgPopupThreadHandle,
(DWORD)(-1)) == TRUE) {
DhcpPrint(( DEBUG_ERRORS, "Terminated popup Thread.\n" ));
}
else {
DhcpPrint(( DEBUG_ERRORS,
"Can't terminate popup Thread %ld.\n",
GetLastError() ));
}
}
}
if( FALSE == DhcpGlobalIsShuttingDown ) {
DhcpApiCleanup();
if( DhcpGlobalOptionInfo != NULL) {
DhcpFreeMemory( DhcpGlobalOptionInfo );
DhcpGlobalOptionInfo = NULL;
}
if( DhcpGlobalOptionList != NULL) {
DhcpFreeMemory( DhcpGlobalOptionList );
DhcpGlobalOptionList = NULL;
}
if( DhcpGlobalMessageFileHandle != NULL ) {
FreeLibrary( DhcpGlobalMessageFileHandle );
DhcpGlobalMessageFileHandle = NULL;
}
if( DhcpGlobalTerminateEvent != NULL ) {
CloseHandle( DhcpGlobalTerminateEvent );
DhcpGlobalTerminateEvent = NULL;
}
if( DhcpGlobalNewIpAddressNotifyEvent != NULL ) {
CloseHandle( DhcpGlobalNewIpAddressNotifyEvent );
DhcpGlobalNewIpAddressNotifyEvent = NULL;
}
if( DhcpGlobalRecomputeTimerEvent != NULL ) {
CloseHandle( DhcpGlobalRecomputeTimerEvent );
DhcpGlobalRecomputeTimerEvent = NULL;
}
}
if( UseMHAsyncDns && FALSE == DhcpGlobalIsShuttingDown ) {
DWORD Error = DnsAsyncRegisterTerm();
if(ERROR_SUCCESS != Error) {
DhcpPrint((DEBUG_ERRORS, "DnsTerm: %ld\n", Error));
}
}
Done:
#if DBG
if (NULL != DhcpGlobalDebugFile) {
CloseHandle(DhcpGlobalDebugFile);
DhcpGlobalDebugFile = NULL;
}
#endif
//
// stop winsock.
//
if( DhcpGlobalWinSockInitialized == TRUE ) {
WSACleanup();
DhcpGlobalWinSockInitialized = FALSE;
}
DhcpGlobalServiceStatus.dwCurrentState = SERVICE_STOPPED;
DhcpGlobalServiceStatus.dwWin32ExitCode = Error;
DhcpGlobalServiceStatus.dwServiceSpecificExitCode = 0;
DhcpGlobalServiceStatus.dwCheckPoint = 0;
DhcpGlobalServiceStatus.dwWaitHint = 0;
UpdateStatus();
return;
}
typedef struct _DHCP_THREAD_CTXT { // params to the thread
HANDLE Handle; // semaphore handle
PDHCP_CONTEXT DhcpContext;
} DHCP_THREAD_CTXT, *PDHCP_THREAD_CTXT;
DWORD // status
DhcpRenewThread( // renew the context
IN OUT PDHCP_THREAD_CTXT ThreadCtxt // the context to run...
)
{
DWORD Error; // return value
srand((ULONG)( // set the per-thread rand seed
time(NULL) + (ULONG_PTR)ThreadCtxt
));
DhcpAssert( NULL != ThreadCtxt && NULL != ThreadCtxt->Handle );
DhcpPrint((DEBUG_TRACE, ".. Getting RenewHandle %d ..\n",ThreadCtxt->Handle));
Error = WaitForSingleObject(ThreadCtxt->Handle, INFINITE);
if( WAIT_OBJECT_0 != Error ) { // could happen if this context just disappeared
Error = GetLastError();
DhcpPrint((DEBUG_ERRORS, "WaitForSingleObject: %ld\n", Error));
DhcpAssert(FALSE); // not that likely is it?
if( 0 == InterlockedDecrement(&ThreadCtxt->DhcpContext->RefCount) ) {
DhcpDestroyContext(ThreadCtxt->DhcpContext);
}
} else {
Error = ERROR_SUCCESS;
DhcpPrint((DEBUG_TRACE, "[-- Acquired RenewHandle %d --\n",ThreadCtxt->Handle));
if( 1 == ThreadCtxt->DhcpContext->RefCount ) {
//
// Last reference to this context. No need to do any refresh.
//
DhcpAssert(IsListEmpty(&ThreadCtxt->DhcpContext->NicListEntry));
} else if( IS_DHCP_ENABLED(ThreadCtxt->DhcpContext)) {
//
// Perform this only on DHCP enabled interfaces.
// It is possible that the interface got converted to static
// when the thread was waiting for it.
//
Error = ThreadCtxt->DhcpContext->RenewalFunction(ThreadCtxt->DhcpContext,NULL);
}
DhcpPrint((DEBUG_TRACE, "-- Releasing RenewHandle %d --]\n",ThreadCtxt->Handle));
//
// Do this while we still hold the semaphore to synchronize the access to the registry
//
if( 0 == InterlockedDecrement(&ThreadCtxt->DhcpContext->RefCount) ) {
DhcpDestroyContext(ThreadCtxt->DhcpContext);
} else {
BOOL BoolError;
BoolError = ReleaseSemaphore(ThreadCtxt->Handle, 1, NULL);
DhcpPrint((DEBUG_TRACE, ".. Released RenewHandle %d ..\n", ThreadCtxt->Handle));
DhcpAssert( FALSE != BoolError );
}
}
DhcpFreeMemory(ThreadCtxt);
return Error;
}
DWORD // Status
DhcpCreateThreadAndRenew( // renew in a separate thread
IN OUT PDHCP_CONTEXT DhcpContext // the context to renew
)
{
DWORD Error; // return value
HANDLE RenewThread;
DWORD Unused;
BOOL BoolError;
PDHCP_THREAD_CTXT ThreadCtxt;
ThreadCtxt = DhcpAllocateMemory(sizeof(DHCP_THREAD_CTXT));
if( NULL == ThreadCtxt ) {
DhcpPrint((DEBUG_ERRORS, "DhcpCreateThreadAndRenew:Alloc:NULL\n"));
return ERROR_NOT_ENOUGH_MEMORY;
}
ThreadCtxt->Handle = DhcpContext->RenewHandle;
ThreadCtxt->DhcpContext = DhcpContext;
InterlockedIncrement(&DhcpContext->RefCount);
DhcpPrint((DEBUG_TRACE, "Creating thread in DhcpCreateThreadAndRenew\n"));
RenewThread = CreateThread( // thread that does the real renew
NULL, // no securtiy
0, // default process stack size
(LPTHREAD_START_ROUTINE) DhcpRenewThread, // the function to start off with
(LPVOID) ThreadCtxt, // the only parameter to the function
0, // start the other thread right away
&Unused // Dont care about thread id
);
if( NULL == RenewThread) { // create thread failed for some reason
Error = GetLastError();
DhcpPrint((DEBUG_ERRORS, "CreateThread(DhcpCreateThreadAndRenew): %ld\n", Error));
if( ERROR_NOT_ENOUGH_MEMORY != Error && ERROR_NO_SYSTEM_RESOURCES != Error ) {
// DhcpAssert(FALSE); // this assert is bothering lots of ppl
}
DhcpFreeMemory(ThreadCtxt);
if( 0 == InterlockedDecrement(&DhcpContext->RefCount) ) {
DhcpDestroyContext(DhcpContext);
}
return Error;
}
BoolError = CloseHandle(RenewThread); // Dont need the handle, close it
return ERROR_SUCCESS;
}
VOID
HandleFailedRenewals(
VOID
)
/*++
Routine Description:
This routine handles all contexts that have failed to
create a separate thread to renew... by doing the renewal
inline. Note that if several contexts have this problem,
then this may take a long time.
The algorithm used is to walk the list of all contexts,
looking for one which has failed renewal. If none found,
the routine returns. If anything found, then an inline renewal
is attempted.
--*/
{
ULONG Error, BoolError;
PDHCP_CONTEXT DhcpContext;
PLIST_ENTRY List;
while( TRUE ) {
BOOL bFound = FALSE;
//
// Find a desirable context.
//
LOCK_RENEW_LIST();
for( List = DhcpGlobalNICList.Flink;
List != &DhcpGlobalNICList;
List = List->Flink
) {
DhcpContext = CONTAINING_RECORD(
List, DHCP_CONTEXT, NicListEntry
);
//
// if not failed renewal give up.
//
if( FALSE == DhcpContext->bFailedRenewal ) {
continue;
}
DhcpContext->bFailedRenewal = FALSE;
//
// if not DHCP enabled give up.
//
if( IS_DHCP_DISABLED(DhcpContext)) {
continue;
}
//
// If list entry is not empty, ignore
//
if( !IsListEmpty(&DhcpContext->RenewalListEntry) ) {
continue;
}
//
// Got a context, break!
//
bFound = TRUE;
InterlockedIncrement(&DhcpContext->RefCount);
break;
}
UNLOCK_RENEW_LIST();
//
// If no contexts, quit
//
if( FALSE == bFound ) return;
//
// Acquire context and perform renewal
//
Error = WaitForSingleObject(DhcpContext->RenewHandle, INFINITE);
if( WAIT_OBJECT_0 != Error ) {
Error = GetLastError();
DhcpPrint((DEBUG_ERRORS, "WaitForSingleObject: 0x%lx\n", Error));
DhcpAssert(FALSE);
} else {
Error = ERROR_SUCCESS;
if( 1 == DhcpContext->RefCount ) {
//
// Last reference to this context?
//
DhcpAssert(IsListEmpty(&DhcpContext->NicListEntry));
} else if ( IS_DHCP_ENABLED(DhcpContext) ) {
//
// Work only for DHCP enabled contexts.
//
Error = DhcpContext->RenewalFunction(DhcpContext, NULL);
}
BoolError = ReleaseSemaphore(DhcpContext->RenewHandle, 1, NULL);
DhcpAssert(FALSE != BoolError );
}
if( 0 == InterlockedDecrement(&DhcpContext->RefCount) ) {
//
// Last reference went away..
//
DhcpDestroyContext(DhcpContext);
}
}
//
// dead code.
//
DhcpAssert(FALSE);
}
DWORD // win32 status; returns only on STOP of dhcp
ProcessDhcpRequestForever( // process renewal requests and api requests
IN DWORD TimeToSleep // initial time to sleep
) {
#define TERMINATE_EVENT 0
#define TIMER_EVENT 1
#define PIPE_EVENT 2
#define GLOBAL_DOM_CHANGE 3
#define EVENT_COUNT 4
HANDLE WaitHandle[EVENT_COUNT];
DWORD LocalTimeToSleep = TimeToSleep;
DWORD ElapseTime;
LIST_ENTRY CurrentRenewals;
DWORD ResumeTime;
DWORD Length, BoolError;
BOOL bFailedRenewal;
// Wait and Process the following work items:
//
// 1. Wait for Timer recompute event for Client renewal.
// 2. DHCP Client APIs.
WaitHandle[TIMER_EVENT] = DhcpGlobalRecomputeTimerEvent;
WaitHandle[PIPE_EVENT] = DhcpGlobalClientApiPipeEvent;
WaitHandle[TERMINATE_EVENT] = DhcpGlobalTerminateEvent;
WaitHandle[GLOBAL_DOM_CHANGE] = DhcpLsaDnsDomChangeNotifyHandle;
DhcpGlobalDoRefresh = 0;
ResumeTime = 0;
bFailedRenewal = FALSE;
for(;;) {
DWORD Waiter;
DWORD SleepTimeMsec;
if( INFINITE == LocalTimeToSleep ) {
SleepTimeMsec = INFINITE;
} else {
SleepTimeMsec = LocalTimeToSleep * 1000;
if( SleepTimeMsec < LocalTimeToSleep ) {
SleepTimeMsec = INFINITE ;
}
}
// There is a flaw in the way resume is done below in that if the machine
// suspends while we are actually doing dhcp on any of the adapter, we will not get
// around in doing DhcpStartWaitableTime. One simpler way to fix this is to restart
// the waitable timer, immediately after we get out of WaitForMultipleObjects but that is
// ugly and also that we will be able to wakeup the system but will not be able to detect
// that it did happen. The best way to fix this is to run the waitable timer on a separate
// thread and just signal this loop here whenever necessary. This should be fixed after
// new client code is checked in so that merge can be
// avoided.
// -- The above problem should be fixed because a new
// thread is now created for each renewal
// Resumed = FALSE;
if (INFINITE != ResumeTime) {
DhcpStartWaitableTimer(
DhcpGlobalWaitableTimerHandle,
ResumeTime );
} else {
DhcpCancelWaitableTimer( DhcpGlobalWaitableTimerHandle );
}
//
// Need to wait to see what happened.
//
DhcpPrint((DEBUG_MISC, "ProcessDhcpRequestForever sleeping 0x%lx msec\n",
SleepTimeMsec));
Waiter = WaitForMultipleObjects(
EVENT_COUNT, // num. of handles.
WaitHandle, // handle array.
FALSE, // wait for any.
SleepTimeMsec // timeout in msecs.
);
//
// Initialize sleep value to zero so that if we need to recompute
// time to sleep after we process the event, it will be done automatically
//
LocalTimeToSleep = 0 ;
switch( Waiter ) {
case GLOBAL_DOM_CHANGE:
//
// If domain name has changed, all we got to do is set
// the global refresh flag and fall-through.
// That will fall to the wait_timeout case and then
// refresh all NICs.
//
DhcpGlobalDoRefresh = TRUE;
case TIMER_EVENT:
//
// FALL THROUGH and recompute
//
case WAIT_TIMEOUT: { // we timed out or were woken up -- recompute timers
PDHCP_CONTEXT DhcpContext;
time_t TimeNow;
PLIST_ENTRY ListEntry;
DhcpPrint((DEBUG_TRACE,"ProcessDhcpRequestForever - processing WAIT_TIMEOUT\n"));
if( bFailedRenewal ) {
HandleFailedRenewals();
bFailedRenewal = FALSE;
}
LocalTimeToSleep = ResumeTime = INFINIT_LEASE;
TimeNow = time( NULL );
LOCK_RENEW_LIST(); // with pnp, it is ok to have no adapters; sleep till we get one
// recalculate timers and do any required renewals.. ScheduleWakeup would re-schedule these renewals
for( ListEntry = DhcpGlobalNICList.Flink;
ListEntry != &DhcpGlobalNICList;
) {
DhcpContext = CONTAINING_RECORD(ListEntry,DHCP_CONTEXT,NicListEntry );
ListEntry = ListEntry->Flink;
//
// For static adapters, we may need to refresh params ONLY if we're asked to..
// Otherwise we can ignore them..
//
if( IS_DHCP_DISABLED(DhcpContext) ) {
if( 0 == DhcpGlobalDoRefresh ) continue;
StaticRefreshParams(DhcpContext);
continue;
}
//
// If it is time to run this renewal function, remove the
// renewal context from the list. If the power just resumed on this
// system, we need to re-run all the contexts in INIT-REBOOT mode,
// coz during suspend the machine may have been moved to a different
// network.
//
if ( 0 != DhcpGlobalDoRefresh || DhcpContext->RunTime <= TimeNow ) {
RemoveEntryList( &DhcpContext->RenewalListEntry );
if( IsListEmpty( &DhcpContext->RenewalListEntry ) ) {
//
// This context is already being processed, so ignore it
//
} else {
InitializeListHead( &DhcpContext->RenewalListEntry);
if( NO_ERROR != DhcpCreateThreadAndRenew(DhcpContext)) {
DhcpContext->bFailedRenewal = TRUE;
bFailedRenewal = TRUE;
SetEvent(DhcpGlobalRecomputeTimerEvent);
}
}
} else if( INFINIT_TIME != DhcpContext->RunTime ) {
// if RunTime is INFINIT_TIME then we never want to schedule it...
ElapseTime = (DWORD)(DhcpContext->RunTime - TimeNow);
if ( LocalTimeToSleep > ElapseTime ) {
LocalTimeToSleep = ElapseTime;
//
// if this adapter is in the autonet mode, dont let
// the 5 minute retry timer wake the machine up.
//
// Also, if context not enabled for WOL, don't do this
//
if ( DhcpContext->fTimersEnabled
&& IS_ADDRESS_DHCP( DhcpContext ) ) {
// shorten the resumetime by half minute so that we can process power up event
// before normal timer fires. this we can guarantee we start with INIT-REBOOT
// sequence after power up.
if (LocalTimeToSleep > 10 ) {
ResumeTime = LocalTimeToSleep - 10;
} else {
ResumeTime = 0;
}
}
}
}
}
DhcpGlobalDoRefresh = 0;
UNLOCK_RENEW_LIST();
break;
}
case PIPE_EVENT: {
BOOL BoolError;
DhcpPrint( (DEBUG_TRACE,"ProcessDhcpRequestForever - processing PIPE_EVENT\n"));
ProcessApiRequest(DhcpGlobalClientApiPipe,&DhcpGlobalClientApiOverLapBuffer );
// Disconnect from the current client, setup listen for next client;
BoolError = DisconnectNamedPipe( DhcpGlobalClientApiPipe );
DhcpAssert( BoolError );
// ensure the event handle in the overlapped structure is reset
// before we initiate putting the pipe into listening state
ResetEvent(DhcpGlobalClientApiPipeEvent);
BoolError = ConnectNamedPipe(
DhcpGlobalClientApiPipe,
&DhcpGlobalClientApiOverLapBuffer );
if( ! DhcpGlobalDoRefresh ) {
//
// Completed processing!
//
break;
}
//
// Need to do refresh DNS host name etc..
//
Length = sizeof(DhcpGlobalHostNameBufW)/sizeof(WCHAR);
DhcpGlobalHostNameW = DhcpGlobalHostNameBufW;
BoolError = GetComputerNameExW(
ComputerNameDnsHostname,
DhcpGlobalHostNameW,
&Length
);
if( FALSE == BoolError ) {
KdPrint(("DHCP:GetComputerNameExW failed %ld\n", GetLastError()));
DhcpAssert(FALSE);
break;
}
DhcpUnicodeToOem( DhcpGlobalHostNameW, DhcpGlobalHostNameBuf);
DhcpGlobalHostName = DhcpGlobalHostNameBuf;
//
// We need to re-visit each context to refresh. So, hack that with
// setting LocalTimeToSleep to zero
break;
}
case TERMINATE_EVENT:
return( ERROR_SUCCESS );
case WAIT_FAILED:
DhcpPrint((DEBUG_ERRORS,"WaitForMultipleObjects failed, %ld.\n", GetLastError() ));
break;
default:
DhcpPrint((DEBUG_ERRORS,"WaitForMultipleObjects received invalid handle, %ld.\n",Waiter));
break;
}
}
}
DWORD
ResetStaticInterface(
IN PDHCP_CONTEXT DhcpContext
)
{
ULONG Error;
DWORD IpInterfaceContext;
//
// Try to delete all the non-primary interfaces for the adapter..
//
Error = IPDelNonPrimaryAddresses(DhcpAdapterName(DhcpContext));
if( ERROR_SUCCESS != Error ) {
DhcpAssert(FALSE);
return Error;
}
//
// Now we have to set the primary address to zero..
//
Error = GetIpInterfaceContext(
DhcpAdapterName(DhcpContext),
0,
&IpInterfaceContext
);
if( ERROR_SUCCESS != Error ) {
DhcpAssert(FALSE);
return Error;
}
//
// Got hte interface context.. just set address to zero for that..
//
Error = IPResetIPAddress(
IpInterfaceContext,
DhcpDefaultSubnetMask(0)
);
return Error;
}
DWORD
DhcpDestroyContextEx(
IN PDHCP_CONTEXT DhcpContext,
IN ULONG fKeepInformation
)
{
ULONG Error;
BOOL bNotifyNLA = TRUE;
DWORD NotifyDnsCache(VOID);
if (NdisWanAdapter(DhcpContext))
InterlockedDecrement(&DhcpGlobalNdisWanAdaptersCount);
DhcpAssert( IS_MDHCP_CTX( DhcpContext ) || IsListEmpty(&DhcpContext->NicListEntry) );
if(!IsListEmpty(&DhcpContext->RenewalListEntry) ) {
LOCK_RENEW_LIST();
RemoveEntryList( &DhcpContext->RenewalListEntry );
InitializeListHead( &DhcpContext->RenewalListEntry );
UNLOCK_RENEW_LIST();
}
#ifdef BOOTPERF
//
// No matter what, if the context is going away, we
// clear out the quickboot values.
//
if( NULL != DhcpContext->AdapterInfoKey ) {
DhcpRegDeleteQuickBootValues(
DhcpContext->AdapterInfoKey
);
}
#endif BOOTPERF
if (!IS_MDHCP_CTX( DhcpContext ) ) {
if( IS_DHCP_DISABLED(DhcpContext) ) {
Error = ERROR_SUCCESS;
//Error = ResetStaticInterface(DhcpContext);
} else {
if( FALSE == fKeepInformation ) {
Error = SetDhcpConfigurationForNIC(
DhcpContext,
NULL,
0,
(DWORD)-1,
TRUE
);
// if we get here, NLA is notified through SetDhcpConfigurationForNIC
// hence avoid sending a second notification later
if (Error == ERROR_SUCCESS)
bNotifyNLA = FALSE;
Error = DhcpRegDeleteIpAddressAndOtherValues(
DhcpContext->AdapterInfoKey
);
} else {
LOCK_OPTIONS_LIST();
(void) DhcpRegClearOptDefs(DhcpAdapterName(DhcpContext));
UNLOCK_OPTIONS_LIST();
Error = UninitializeInterface(DhcpContext);
}
//DhcpAssert(ERROR_SUCCESS == Error);
}
DhcpRegisterWithDns(DhcpContext, TRUE);
NotifyDnsCache();
}
LOCK_OPTIONS_LIST();
DhcpDestroyOptionsList(&DhcpContext->SendOptionsList, &DhcpGlobalClassesList);
DhcpDestroyOptionsList(&DhcpContext->RecdOptionsList, &DhcpGlobalClassesList);
// all fallback options are not supposed to have classes, so &DhcpGlobalClassesList below
// is actually not used.
DhcpDestroyOptionsList(&DhcpContext->FbOptionsList, &DhcpGlobalClassesList);
if( DhcpContext->ClassIdLength ) { // remove any class id we might have
DhcpDelClass(&DhcpGlobalClassesList, DhcpContext->ClassId, DhcpContext->ClassIdLength);
}
UNLOCK_OPTIONS_LIST();
CloseHandle(DhcpContext->AdapterInfoKey); // close the open handles to the registry
CloseHandle(DhcpContext->RenewHandle); // and synchronization events
if (DhcpContext->CancelEvent != WSA_INVALID_EVENT) {
WSACloseEvent(DhcpContext->CancelEvent);
DhcpContext->CancelEvent = WSA_INVALID_EVENT;
}
CloseDhcpSocket( DhcpContext ); // close the socket if it's open.
DhcpFreeMemory(DhcpContext); // done!
if (bNotifyNLA)
NLANotifyDHCPChange(); // notify NLA the adapter went away
return ERROR_SUCCESS; // always return success
}
DWORD
DhcpDestroyContext(
IN PDHCP_CONTEXT DhcpContext
)
{
return DhcpDestroyContextEx(
DhcpContext,
FALSE
);
}
DWORD
DhcpCommonInit( // initialize common stuff for service as well as APIs
VOID
)
{
ULONG Length;
BOOL BoolError;
DWORD Error;
#if DBG
LPWSTR DebugFileName = NULL;
#endif
#if DBG
//
// This is very funcky.
// Initialized won't be reset to FALSE after it gets a TRUE value
// As a result, DhcpCommonInit will be called only once.
//
// DhcpGlobalDebugFlag is updated in DhcpInitRegistry each time
// the service is stoped and started.
//
DebugFileName = NULL;
Error = DhcpGetRegistryValue(
DHCP_CLIENT_PARAMETER_KEY,
DHCP_DEBUG_FILE_VALUE,
DHCP_DEBUG_FILE_VALUE_TYPE,
&DebugFileName
);
if (DebugFileName) {
if (DhcpGlobalDebugFile) {
CloseHandle(DhcpGlobalDebugFile);
}
DhcpGlobalDebugFile = CreateFileW(
DebugFileName,
GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_ALWAYS,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL
);
DhcpFreeMemory(DebugFileName);
DebugFileName = NULL;
}
#endif
if( Initialized ) return ERROR_SUCCESS;
EnterCriticalSection(&DhcpGlobalApiCritSect);
if( Initialized ) {
Error = ERROR_SUCCESS ;
goto Cleanup;
}
Length = sizeof(DhcpGlobalHostNameBufW)/sizeof(WCHAR);
DhcpGlobalHostNameW = DhcpGlobalHostNameBufW;
BoolError = GetComputerNameExW(
ComputerNameDnsHostname,
DhcpGlobalHostNameW,
&Length
);
if( FALSE == BoolError ) {
Error = GetLastError();
#if DBG
KdPrint(("DHCP:GetComputerNameExW failed %ld\n", Error));
#endif
goto Cleanup;
}
DhcpUnicodeToOem(DhcpGlobalHostNameBufW, DhcpGlobalHostNameBuf);
DhcpGlobalHostName = DhcpGlobalHostNameBuf;
#if DBG
Error = DhcpGetRegistryValue(
DHCP_CLIENT_PARAMETER_KEY,
DHCP_DEBUG_FLAG_VALUE,
DHCP_DEBUG_FLAG_VALUE_TYPE,
(PVOID *)&DhcpGlobalDebugFlag
);
if( Error != ERROR_SUCCESS ) {
DhcpGlobalDebugFlag = 0;
}
if( DhcpGlobalDebugFlag & DEBUG_BREAK_POINT ) {
DbgBreakPoint();
}
#endif
Error = ERROR_SUCCESS;
Initialized = TRUE;
Cleanup:
LeaveCriticalSection(&DhcpGlobalApiCritSect);
return Error;
}
VOID
ServiceMain ( // (SVC_main) this thread quits when dhcp is unloaded
IN DWORD argc, // unused
IN LPTSTR argv[] // unused
)
{
DWORD Error;
DWORD timeToSleep = 1;
UNREFERENCED_PARAMETER(argc);
UNREFERENCED_PARAMETER(argv);
Error = WSAStartup( 0x0101, &DhcpGlobalWsaData );
if( ERROR_SUCCESS != Error ) { // initialize winsock first
goto Cleanup;
}
DhcpGlobalWinSockInitialized = TRUE;
Error = DhcpCommonInit();
if( ERROR_SUCCESS != Error ) goto Cleanup;
Error = DhcpInitData();
if( ERROR_SUCCESS != Error ) goto Cleanup; // should not happen, we abort if this happens
UpdateStatus(); // send heart beat to the service controller.
Error = DhcpInitialize( &timeToSleep ); // with pnp, this does not get any addresses
if( Error != ERROR_SUCCESS ) goto Cleanup; // this should succeed without any problems
Error = DhcpInitMediaSense(); // this would handle the notifications for arrival/departure of
if( Error != ERROR_SUCCESS ) goto Cleanup;
DhcpGlobalServiceStatus.dwCurrentState = SERVICE_RUNNING;
UpdateStatus(); // placate the service controller -- we are running.
DhcpPrint(( DEBUG_MISC, "Service is running.\n"));
DhcpGlobalServiceRunning = TRUE;
Error = ProcessDhcpRequestForever( // this gets the address for any adapters that may come up
timeToSleep
);
Cleanup:
DhcpCleanup( Error );
return;
}
DWORD
DhcpInitMediaSense(
VOID
)
/*++
Routine Description:
This function initializes the media sense detection code.
It creates a thread which basically just waits for media
sense notifications from tcpip.
Arguments:
None.
Return Value:
Success or Failure.
--*/
{
DWORD Error = ERROR_SUCCESS;
DWORD threadId;
DhcpGlobalIPEventSeqNo = 0;
DhcpGlobalMediaSenseHandle = CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE)MediaSenseDetectionLoop,
NULL,
0,
&threadId
);
if ( DhcpGlobalMediaSenseHandle == NULL ) {
Error = GetLastError();
DhcpPrint((DEBUG_INIT, "DhcpInitMediaSense: Can't create MediaThread, %ld.\n", Error));
return(Error);
}
DhcpPrint((DEBUG_INIT, "DhcpInitMediaSense succeded\n", Error));
return(Error);
}
VOID
DoInterfaceMetricChange(
IN LPWSTR AdapterName,
IN ULONG IpInterfaceContext
)
/*++
Routine Description:
This routine handles interface metric changes for the context
specified by the AdapterName or IpInterfaceContext values.
Arguments:
AdapterName -- name of adapter for which interface metric is
changing.
IpInterfaceContext -- nte_context value for interface
--*/
{
PDHCP_CONTEXT DhcpContext;
DHCP_FULL_OPTIONS DhcpOptions;
ULONG Error;
LOCK_RENEW_LIST();
do {
DhcpContext = FindDhcpContextOnNicList(
AdapterName, IpInterfaceContext
);
if( NULL == DhcpContext ) {
//
// If there is no context, we can't do much.
//
break;
}
InterlockedIncrement( &DhcpContext->RefCount );
break;
} while ( 0 );
UNLOCK_RENEW_LIST();
if( NULL == DhcpContext ) {
//
// We never found the context. Just have to return.
//
return;
}
//
// Since we found the context, we have to acquire it.
//
Error = WaitForSingleObject( DhcpContext->RenewHandle, INFINITE);
if( WAIT_OBJECT_0 == Error ) {
//
// Now set the interface gateways again.
//
RtlZeroMemory(&DhcpOptions, sizeof(DhcpOptions));
DhcpOptions.nGateways = DhcpContext->nGateways;
DhcpOptions.GatewayAddresses = DhcpContext->GatewayAddresses;
DhcpSetGateways(DhcpContext, &DhcpOptions, TRUE);
} else {
//
// Shouldn't really happen.
//
Error = GetLastError();
DhcpAssert( ERROR_SUCCESS == Error );
}
(void) ReleaseSemaphore( DhcpContext->RenewHandle, 1, NULL);
if( 0 == InterlockedDecrement( &DhcpContext->RefCount ) ) {
//
// Last reference to the context.. Destroy it.
//
DhcpDestroyContext( DhcpContext );
}
}
VOID
DoWOLCapabilityChange(
IN LPWSTR AdapterName,
IN ULONG IpInterfaceContext
)
/*++
Routine Description:
This routine handles interface metric changes for the context
specified by the AdapterName or IpInterfaceContext values.
Arguments:
AdapterName -- name of adapter for which interface metric is
changing.
IpInterfaceContext -- nte_context value for interface
--*/
{
PDHCP_CONTEXT DhcpContext;
ULONG Error;
LOCK_RENEW_LIST();
do {
DhcpContext = FindDhcpContextOnNicList(
AdapterName, IpInterfaceContext
);
if( NULL == DhcpContext ) {
//
// If there is no context, we can't do much.
//
break;
}
InterlockedIncrement( &DhcpContext->RefCount );
break;
} while ( 0 );
UNLOCK_RENEW_LIST();
if( NULL == DhcpContext ) {
//
// We never found the context. Just have to return.
//
return;
}
//
// Since we found the context, we have to acquire it.
//
Error = WaitForSingleObject( DhcpContext->RenewHandle, INFINITE);
if( WAIT_OBJECT_0 == Error ) {
//
// Now set the interface gateways again.
//
ULONG Caps;
BOOL fTimersEnabled;
Error = IPGetWOLCapability(
DhcpIpGetIfIndex(DhcpContext), &Caps
);
if( ERROR_SUCCESS != Error ) {
DhcpPrint((DEBUG_ERRORS, "Failed IPGetWOLCapability: 0x%lx\n", Error));
} else {
fTimersEnabled = ((Caps& NDIS_DEVICE_WAKE_UP_ENABLE)!= 0);
if( fTimersEnabled != DhcpContext->fTimersEnabled ) {
DhcpContext->fTimersEnabled = fTimersEnabled;
DhcpPrint((DEBUG_MISC, "WOL Capability: %ld\n", fTimersEnabled));
if( IS_DHCP_ENABLED(DhcpContext)
&& !DhcpIsInitState(DhcpContext) ) {
//
// Cause processdhcpprocessdiscoverforeever to wakeup
// to take care of this timer issue..
//
SetEvent(DhcpGlobalRecomputeTimerEvent);
}
}
}
} else {
//
// Shouldn't really happen.
//
Error = GetLastError();
DhcpAssert( ERROR_SUCCESS == Error );
}
(void) ReleaseSemaphore( DhcpContext->RenewHandle, 1, NULL);
if( 0 == InterlockedDecrement( &DhcpContext->RefCount ) ) {
//
// Last reference to the context.. Destroy it.
//
DhcpDestroyContext( DhcpContext );
}
}
VOID
MediaSenseDetectionLoop(
VOID
)
/*++
Routine Description:
This function is the starting point for the main MediaSenseDetection thread.
It loops to process queued messages, and sends replies.
Arguments:
none.
Return Value:
None.
--*/
{
#define TERMINATION_EVENT 0
#define MEDIA_SENSE_EVENT 1
#undef EVENT_COUNT
#define EVENT_COUNT 2
IP_STATUS MediaStatus;
HANDLE WaitHandle[EVENT_COUNT];
HANDLE tcpHandle = NULL;
PIP_GET_IP_EVENT_RESPONSE responseBuffer;
DWORD responseBufferSize;
IO_STATUS_BLOCK ioStatusBlock;
NTSTATUS status;
DWORD Error,result;
PDHCP_CONTEXT dhcpContext;
BOOL serviceStopped = FALSE;
responseBuffer = NULL; // Bug 292526: in case that OpenDriver and CreateEvent fails.
WaitHandle[TERMINATION_EVENT] = DhcpGlobalTerminateEvent;
WaitHandle[MEDIA_SENSE_EVENT] = CreateEvent(
NULL, // no security
FALSE, // no manual reset
FALSE, // initial state not signalled
NULL // no name
);
if ( !WaitHandle[MEDIA_SENSE_EVENT] ) {
DhcpPrint( (DEBUG_ERRORS,"MediaSenseDetectionLoop: OpenDriver failed %lx\n",GetLastError()));
goto Exit;
}
Error = OpenDriver(&tcpHandle, DD_IP_DEVICE_NAME);
if (Error != ERROR_SUCCESS) {
DhcpPrint( (DEBUG_ERRORS,"MediaSenseDetectionLoop: OpenDriver failed %lx\n",Error));
goto Exit;
}
//
// Allocate large enough buffer to hold adapter name
//
responseBufferSize = sizeof(IP_GET_IP_EVENT_RESPONSE)+ ADAPTER_STRING_SIZE;
responseBuffer = DhcpAllocateMemory(responseBufferSize);
if( responseBuffer == NULL ) {
Error = ERROR_NOT_ENOUGH_MEMORY;
goto Exit;
}
do {
ZeroMemory( responseBuffer, responseBufferSize );
DhcpPrint((DEBUG_MEDIA, "-->IPGetIPEventRequest(..%d..)\n", DhcpGlobalIPEventSeqNo));
status = IPGetIPEventRequest(
tcpHandle,
WaitHandle[MEDIA_SENSE_EVENT],
DhcpGlobalIPEventSeqNo,
responseBuffer,
responseBufferSize,
&ioStatusBlock);
if ( (STATUS_PENDING != status) && (STATUS_SUCCESS != status) )
{
DhcpPrint( (DEBUG_ERRORS,"Media Sense request ioctl failed with error %lx\n",status));
Error = RtlNtStatusToDosError(status);
break;
} else {
DhcpPrint( (DEBUG_TRACE,"Media Sense request ioctl sent\n"));
//
// Note: even in case of a immediate success from IPGetIPEventRequest,
// we do waitformultipleobjects. This is to make sure that we catch terminate
// event in case where we are getting bombarded with media sense events.
//
result = WaitForMultipleObjects(
EVENT_COUNT, // num. of handles.
WaitHandle, // handle array.
FALSE, // wait for any.
( status == STATUS_SUCCESS ? 0 : INFINITE )); // timeout in msecs
switch( result ) {
case TERMINATION_EVENT:
DhcpPrint( (DEBUG_TRACE,"MediaSenseDetectionLoop: rcvd terminate event\n"));
if ( status == STATUS_PENDING ) {
Error = IPCancelIPEventRequest(
tcpHandle,
&ioStatusBlock);
}
//
// the service is asked to stop, break the loop.
//
serviceStopped = TRUE;
break;
default:
DhcpAssert( result == WAIT_FAILED );
DhcpPrint( (DEBUG_TRACE,"WaitForMultipleObjects returned %lx\n",result));
//
// when IPGetIPEventRequest gives immediate return code,
// we may here. So we should never fall here if it returned
// STATUS_PENDING
//
if ( status == STATUS_PENDING ) {
Error = GetLastError();
DhcpPrint( (DEBUG_ERRORS,"WaitForMultipleObjects failed with error %lx\n",Error));
break;
}
//
// THERE IS NO BREAK HERE.
//
case MEDIA_SENSE_EVENT:
if ( status != STATUS_SUCCESS && status != STATUS_PENDING ) {
DhcpPrint( (DEBUG_ERRORS,"Media Sense ioctl failed with error %lx\n",status));
break;
}
DhcpPrint((DEBUG_MEDIA,"Wait-> SequenceNo=%d; MediaStatus=%d\n",
responseBuffer->SequenceNo,
responseBuffer->MediaStatus));
DhcpPrint((DEBUG_MEDIA,"DhcpGlobalIPEventSeqNo=%d\n", DhcpGlobalIPEventSeqNo));
//
// remap the adaptername buffer from kernel space to user space
//
responseBuffer->AdapterName.Buffer = (PWSTR)(
(char *)responseBuffer + sizeof(IP_GET_IP_EVENT_RESPONSE)
);
//
// nul-terminate the string for adapter name: HACKHACK!
//
{
DWORD Size = strlen("{16310E8D-F93B-42C7-B952-00F695E40ECF}");
responseBuffer->AdapterName.Buffer[Size] = L'\0';
}
if ( responseBuffer->MediaStatus == IP_INTERFACE_METRIC_CHANGE ) {
//
// Handle interface metric change requests..
//
DoInterfaceMetricChange(
responseBuffer->AdapterName.Buffer,
responseBuffer->ContextStart
);
if( responseBuffer->SequenceNo > DhcpGlobalIPEventSeqNo ) {
DhcpGlobalIPEventSeqNo = responseBuffer->SequenceNo;
} else {
DhcpAssert(FALSE);
}
break;
}
if( responseBuffer->MediaStatus == IP_INTERFACE_WOL_CAPABILITY_CHANGE ) {
//
// Handle WOL capabilities change.
//
DoWOLCapabilityChange(
responseBuffer->AdapterName.Buffer,
responseBuffer->ContextStart
);
if( responseBuffer->SequenceNo > DhcpGlobalIPEventSeqNo ) {
DhcpGlobalIPEventSeqNo = responseBuffer->SequenceNo;
} else {
DhcpAssert(FALSE);
}
break;
}
if ( responseBuffer->MediaStatus != IP_MEDIA_CONNECT &&
responseBuffer->MediaStatus != IP_BIND_ADAPTER &&
responseBuffer->MediaStatus != IP_UNBIND_ADAPTER &&
responseBuffer->MediaStatus != IP_MEDIA_DISCONNECT &&
responseBuffer->MediaStatus != IP_INTERFACE_METRIC_CHANGE
) {
DhcpPrint( (DEBUG_ERRORS, "Media Sense ioctl received unknown event %lx"
"for %ws, context %x\n",
responseBuffer->MediaStatus,
responseBuffer->AdapterName.Buffer,
responseBuffer->ContextStart));
break;
}
Error = ProcessAdapterBindingEvent(
responseBuffer->AdapterName.Buffer,
responseBuffer->ContextStart,
responseBuffer->MediaStatus);
DhcpGlobalIPEventSeqNo = responseBuffer->SequenceNo;
break;
} // end of switch
}
} while (!serviceStopped);
Exit:
if ( WaitHandle[MEDIA_SENSE_EVENT] ) CloseHandle( WaitHandle[MEDIA_SENSE_EVENT] );
if ( responseBuffer) DhcpFreeMemory(responseBuffer);
if ( tcpHandle ) NtClose(tcpHandle);
return;
}
DWORD
ProcessAdapterBindingEvent(
IN LPWSTR adapterName,
IN DWORD ipInterfaceContext,
IN IP_STATUS bindingStatus
)
/*++
Routine Description:
This routine handles both the media sense for a card as well as
bind-unbind notifications.
It heavily assumes the fact that this is routine is called
synchronously by a single thread (thereby, connect and disconnect
cannot happen in parallel).
bindingStatus can be any of the four values IP_BIND_ADAPTER,
IP_UNBIND_ADAPTER, IP_MEDIA_CONNECT or IP_MEDIA_DISCONNECT ---
Of these, the first and third are treated exactly the same and
so is the second and fourth.
On BIND/CONNECT, this routine creates a DHCP Context structure
initializing the RefCount to ONE on it. But if the context
already existed, then just a refresh is done on the context.
(Assuming the router isn't present at that time etc).
On UNBIND/DISCONNECT, the refcount is temporarily bumped up
until the context semaphore can be obtained -- after that, the
context refcount is bumped down twice and if that hits zero,
the context is released. If the context refcount didn't fall to
zero, then some other thread is waiting to acquire the context
and that thread would acquire and do its work and when done,
it would bump down the refcount and at that time the refount
would fall to zero.
Arguments:
adapterName -- name of adapter all this si being done on
ipInterfaceContext -- interface context # (nte_context)_
bindingStatus -- bind/unbind/connect/disconnect indication.
Return Values:
Various useless Win32 errors.
--*/
{
DWORD Error = ERROR_SUCCESS;
PDHCP_CONTEXT dhcpContext;
DhcpPrint((DEBUG_MEDIA, "ProcessAdapterBindingEvent(%d) for %ws.\n",
bindingStatus,
adapterName));
if ( bindingStatus == IP_BIND_ADAPTER ||
bindingStatus == IP_MEDIA_CONNECT ) {
//
// New adapter or adapter re-connecting.
//
LOCK_RENEW_LIST();
dhcpContext = FindDhcpContextOnNicList(
adapterName, ipInterfaceContext
);
if( NULL == dhcpContext ) {
//
// Create new context now!
//
DhcpPrint(( DEBUG_MEDIA, "New Adapter (Event %ld)\n", bindingStatus ));
Error = DhcpAddNICtoListEx(
adapterName,
ipInterfaceContext,
&dhcpContext
);
if (Error != ERROR_SUCCESS ) {
//
// Failed to create a context? PnP hazard. Just ignore error
// and print debug info.
//
UNLOCK_RENEW_LIST();
//DhcpAssert(FALSE);
DhcpLogEvent(NULL, EVENT_COULD_NOT_INITIALISE_INTERFACE, Error);
return Error;
}
//
// Now handle new adapter. Static case first followed by DHCP case.
//
if ( IS_DHCP_DISABLED(dhcpContext) ) {
StaticRefreshParams(dhcpContext);
UNLOCK_RENEW_LIST();
return Error;
}
//
// No prior-DHCP address case (INIT state) or INIT-REBOOT state?
//
if( DhcpIsInitState(dhcpContext) ) {
dhcpContext->RenewalFunction = ReObtainInitialParameters;
} else {
dhcpContext->RenewalFunction = ReRenewParameters;
}
//
// Do this on a separate thread..
//
ScheduleWakeUp(dhcpContext, 0);
UNLOCK_RENEW_LIST();
return ERROR_SUCCESS;
}
//
// Ok we already have a context.
//
DhcpPrint((DEBUG_MEDIA, "bind/connect for an existing adapter (context %p).\n",dhcpContext));
if( IS_DHCP_DISABLED(dhcpContext) ) {
//
// For static addresses, nothing to do.
//
UNLOCK_RENEW_LIST();
return ERROR_SUCCESS;
}
//
// For DHCP enabled, we need to call ProcessMediaConnectEvent
//
InterlockedIncrement( &dhcpContext->RefCount );
UNLOCK_RENEW_LIST();
Error = LockDhcpContext(dhcpContext, TRUE);
if( WAIT_OBJECT_0 == Error ) {
LOCK_RENEW_LIST();
//
// do not remove from renewal list at all..
// schedulewakeup is what is called in processmediaconnectevent
// and that can take care of renewallist being present..
//
// RemoveEntryList( &dhcpContext->RenewalListEntry);
// InitializeListHead( &dhcpContext->RenewalListEntry );
//
Error = ProcessMediaConnectEvent(
dhcpContext,
bindingStatus
);
UNLOCK_RENEW_LIST();
DhcpPrint((DEBUG_MEDIA, "-- media: releasing RenewHandle %d --]\n", dhcpContext->RenewHandle));
UnlockDhcpContext(dhcpContext);
} else {
//
// Shouldn't really happen..
//
Error = GetLastError();
DhcpAssert( ERROR_SUCCESS == Error );
}
if( 0 == InterlockedDecrement (&dhcpContext->RefCount ) ) {
//
// Can't really be as only this current thread can
// remove refcount on unbind/unconnect..
//
DhcpAssert(FALSE);
DhcpDestroyContext(dhcpContext);
}
return Error;
}
//
// Unbind or disconnect.
//
DhcpAssert( bindingStatus == IP_UNBIND_ADAPTER ||
bindingStatus == IP_MEDIA_DISCONNECT );
DhcpPrint((DEBUG_MEDIA, "ProcessAdapterBindingEvent: rcvd"
" unbind event for %ws, ipcontext %lx\n",
adapterName, ipInterfaceContext));
LOCK_RENEW_LIST();
dhcpContext = FindDhcpContextOnNicList(
adapterName, ipInterfaceContext
);
if( NULL == dhcpContext) {
//
// Can happen... We take this opportunity to clear registry.
//
UNLOCK_RENEW_LIST();
LOCK_OPTIONS_LIST();
(void) DhcpRegClearOptDefs( adapterName );
UNLOCK_OPTIONS_LIST();
return ERROR_FILE_NOT_FOUND;
}
InterlockedIncrement( &dhcpContext->RefCount );
UNLOCK_RENEW_LIST();
Error = LockDhcpContext(dhcpContext, TRUE);
if( WAIT_OBJECT_0 == Error ) {
LOCK_RENEW_LIST();
RemoveEntryList( &dhcpContext->RenewalListEntry );
InitializeListHead( &dhcpContext->RenewalListEntry );
RemoveEntryList( &dhcpContext->NicListEntry);
InitializeListHead( &dhcpContext->NicListEntry );
UNLOCK_RENEW_LIST();
InterlockedDecrement( &dhcpContext->RefCount );
DhcpAssert( dhcpContext->RefCount );
Error = ERROR_SUCCESS;
} else {
//
// Wait can't fail really. Nevermind.
//
Error = GetLastError();
DhcpAssert(ERROR_SUCCESS == Error);
}
//
// Now decrease ref-count and if it goes to zero destroy
// context.
//
if (bindingStatus == IP_UNBIND_ADAPTER) {
/*
* Set the state properly so that UninitializeInterface won't reset the stack
* When adapter is unbound, the IpInterfaceContext may be re-used.
*/
MEDIA_UNBOUND(dhcpContext);
}
if( 0 == InterlockedDecrement(&dhcpContext->RefCount ) ) {
//
// Last person to hold onto context? Destroy context.
//
DhcpAssert(ERROR_SUCCESS == Error);
DhcpDestroyContextEx(
dhcpContext,
(bindingStatus == IP_MEDIA_DISCONNECT)
);
} else {
//
// Some other thread attempting to hold onto context.
//
ULONG BoolError = UnlockDhcpContext(dhcpContext);
DhcpAssert( FALSE != BoolError );
}
return Error;
}
LPWSTR
DhcpAdapterName(
IN PDHCP_CONTEXT DhcpContext
)
{
return ((PLOCAL_CONTEXT_INFO)DhcpContext->LocalInformation)->AdapterName;
}
static
DWORD DhcpGlobalInit = 0; // did we do any global initialization at all?
extern CRITICAL_SECTION MadcapGlobalScopeListCritSect;
DWORD DhcpGlobalNumberInitedCriticalSections = 0;
PCRITICAL_SECTION DhcpGlobalCriticalSections[] = {
(&DhcpGlobalOptionsListCritSect),
(&DhcpGlobalSetInterfaceCritSect),
(&DhcpGlobalRecvFromCritSect),
(&DhcpGlobalZeroAddressCritSect),
(&DhcpGlobalApiCritSect),
(&DhcpGlobalRenewListCritSect),
(&MadcapGlobalScopeListCritSect),
(&gNLA_LPC_CS),
#if DBG
(&DhcpGlobalDebugFileCritSect),
#endif
(&DhcpGlobalPopupCritSect)
};
#define NUM_CRITICAL_SECTION (sizeof(DhcpGlobalCriticalSections)/sizeof(DhcpGlobalCriticalSections[0]))
extern REGISTER_HOST_STATUS DhcpGlobalHostStatus;
DWORD // win32 status
DhcpInitGlobalData( // initialize the dhcp module spec data (included for RAS etc)
VOID
)
{
DWORD i;
InitializeListHead(&DhcpGlobalClassesList);
InitializeListHead(&DhcpGlobalOptionDefList);
InitializeListHead(&DhcpGlobalRecvFromList);
try {
for (DhcpGlobalNumberInitedCriticalSections = 0;
DhcpGlobalNumberInitedCriticalSections < NUM_CRITICAL_SECTION;
DhcpGlobalNumberInitedCriticalSections++) {
InitializeCriticalSection(DhcpGlobalCriticalSections[DhcpGlobalNumberInitedCriticalSections]);
}
} except (EXCEPTION_EXECUTE_HANDLER) {
for (i = 0; i < DhcpGlobalNumberInitedCriticalSections; i++) {
DeleteCriticalSection(DhcpGlobalCriticalSections[i]);
}
DhcpGlobalNumberInitedCriticalSections = 0;
return ERROR_OUTOFMEMORY;
}
DhcpGlobalHostStatus.hDoneEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
AutonetRetriesSeconds = EASYNET_ALLOCATION_RETRY;
DhcpGlobalClientClassInfo = DHCP_DEFAULT_CLIENT_CLASS_INFO;
DhcpGlobalDoRefresh = 0;
DhcpGlobalInit ++;
DhcpGlobalServerPort = DHCP_SERVR_PORT;
DhcpGlobalClientPort = DHCP_CLIENT_PORT;
return ERROR_SUCCESS;
}
VOID
DhcpCleanupGlobalData( // cleanup data intialized via DhcpInitGlobalData
VOID
)
{
DWORD i;
if( 0 == DhcpGlobalInit ) return;
for (i = 0; i < DhcpGlobalNumberInitedCriticalSections; i++) {
DeleteCriticalSection(DhcpGlobalCriticalSections[i]);
}
DhcpGlobalNumberInitedCriticalSections = 0;
if (ghNLA_LPC_Port != NULL)
{
CloseHandle(ghNLA_LPC_Port);
ghNLA_LPC_Port = NULL;
}
if (NULL != DhcpGlobalHostStatus.hDoneEvent) {
CloseHandle(DhcpGlobalHostStatus.hDoneEvent);
DhcpGlobalHostStatus.hDoneEvent = NULL;
}
DhcpFreeAllOptionDefs(&DhcpGlobalOptionDefList, &DhcpGlobalClassesList);
DhcpFreeAllClasses(&DhcpGlobalClassesList);
DhcpGlobalClientClassInfo = NULL;
DhcpGlobalDoRefresh = 0;
DhcpGlobalInit --;
}
DWORD
LockDhcpContext(
PDHCP_CONTEXT DhcpContext,
BOOL bCancelOngoingRequest
)
{
DWORD Error;
if (bCancelOngoingRequest) {
InterlockedIncrement(&DhcpContext->NumberOfWaitingThreads);
if (DhcpContext->CancelEvent != WSA_INVALID_EVENT) {
WSASetEvent(DhcpContext->CancelEvent);
}
}
Error = WaitForSingleObject(DhcpContext->RenewHandle,INFINITE);
//
// If CancelEvent is valid, reset it just in case no one waited on it.
// It is safe to do it here since we already locked the context
//
if (bCancelOngoingRequest && (0 == InterlockedDecrement(&DhcpContext->NumberOfWaitingThreads))) {
if (DhcpContext->CancelEvent != WSA_INVALID_EVENT) {
//
// There is a small chance that we reset the event
// before another thread set the event. In order to
// fully solve this problem, we need to protect the
// SetEvent/ResetEvent with a critical section. It
// doesn't worth the effort.
//
// The only harm of this problem is that that thread
// has to wait for up to 1 minutes to lock the context.
// This should be ok.
//
WSAResetEvent(DhcpContext->CancelEvent);
}
}
return Error;
}
BOOL
UnlockDhcpContext(
PDHCP_CONTEXT DhcpContext
)
{
return ReleaseSemaphore(DhcpContext->RenewHandle,1,NULL);
}
//================================================================================
// End of file
//================================================================================