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.
3509 lines
106 KiB
3509 lines
106 KiB
/*++
|
|
|
|
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[sizeof(WCHAR)*(MAX_COMPUTERNAME_LENGTH+300)];
|
|
WCHAR DhcpGlobalHostNameBufW[MAX_COMPUTERNAME_LENGTH+300];
|
|
|
|
VOID __inline
|
|
CancelRenew (
|
|
PDHCP_CONTEXT DhcpContext
|
|
)
|
|
{
|
|
if (DhcpContext->CancelEvent != WSA_INVALID_EVENT) {
|
|
WSASetEvent(DhcpContext->CancelEvent);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
CancelAllRenew (
|
|
VOID
|
|
)
|
|
{
|
|
PLIST_ENTRY listEntry = NULL;
|
|
PDHCP_CONTEXT dhcpContext = NULL;
|
|
|
|
listEntry = DhcpGlobalNICList.Flink;
|
|
while ( listEntry != &DhcpGlobalNICList ) {
|
|
dhcpContext = CONTAINING_RECORD( listEntry, DHCP_CONTEXT, NicListEntry );
|
|
CancelRenew (dhcpContext);
|
|
listEntry = listEntry->Flink;
|
|
}
|
|
}
|
|
|
|
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) {
|
|
|
|
DhcpGlobalMessageFileHandle = DllHandle;
|
|
|
|
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();
|
|
DhcpGlobalMessageFileHandle = NULL;
|
|
}
|
|
|
|
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_RUNNING) {
|
|
|
|
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 = ERROR_SUCCESS;
|
|
DWORD ReturnError = ERROR_SUCCESS;
|
|
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.
|
|
|
|
--*/
|
|
{
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
LPWSTR RegExpandedLocation = NULL;
|
|
HKEY ReturnKey = NULL;
|
|
|
|
if( NULL == AdapterName ) {
|
|
goto error;
|
|
}
|
|
|
|
dwError = DhcpRegExpandString( // expand each location into full string
|
|
DHCP_ADAPTER_PARAMETERS_KEY_OLD,
|
|
AdapterName,
|
|
&RegExpandedLocation,
|
|
NULL
|
|
);
|
|
if(ERROR_SUCCESS != dwError || NULL == RegExpandedLocation) {
|
|
goto error;
|
|
}
|
|
|
|
dwError = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
RegExpandedLocation,
|
|
0 /* Reserved */,
|
|
DHCP_CLIENT_KEY_ACCESS,
|
|
&ReturnKey
|
|
);
|
|
|
|
if( ERROR_SUCCESS != dwError ) {
|
|
goto error;
|
|
}
|
|
|
|
error:
|
|
if (RegExpandedLocation) {
|
|
DhcpFreeMemory(RegExpandedLocation);
|
|
}
|
|
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 = 0x7FFFFFFF;
|
|
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 DWORD PrevLeaseObtainedTime,
|
|
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);
|
|
if (PrevLeaseObtainedTime) {
|
|
LeaseObtainedTime = PrevLeaseObtainedTime;
|
|
} else {
|
|
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,
|
|
0,
|
|
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 ) );
|
|
|
|
//
|
|
// 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 = DnsDhcpRegisterInit();
|
|
|
|
// 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;
|
|
|
|
DhcpGlobalRecomputeTimerEvent = NULL;
|
|
DhcpGlobalTerminateEvent = NULL;
|
|
DhcpGlobalClientApiPipe = NULL;
|
|
DhcpGlobalClientApiPipeEvent = NULL;
|
|
UseMHAsyncDns = DEFAULT_USEMHASYNCDNS;
|
|
|
|
DhcpGlobalNewIpAddressNotifyEvent = NULL;
|
|
|
|
InitializeListHead( &DhcpGlobalNICList );
|
|
InitializeListHead( &DhcpGlobalRenewList );
|
|
|
|
|
|
DhcpGlobalMsgPopupThreadHandle = NULL;
|
|
DhcpGlobalDisplayPopup = TRUE;
|
|
|
|
DhcpGlobalParametersKey = NULL;
|
|
DhcpGlobalTcpipParametersKey = 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 dwErrorParam
|
|
)
|
|
/*++
|
|
|
|
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", dwErrorParam ));
|
|
|
|
if( dwErrorParam != ERROR_SUCCESS ) {
|
|
|
|
DhcpLogEvent( NULL, EVENT_DHCP_SHUTDOWN, dwErrorParam );
|
|
}
|
|
|
|
//
|
|
// 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 ) {
|
|
|
|
//
|
|
// Cancel all the ongoing renewal to speed up the termination of
|
|
// MediaSenseDetectionLoop thread which could be stuck in pinging
|
|
// gateway.
|
|
//
|
|
LOCK_RENEW_LIST();
|
|
CancelAllRenew();
|
|
UNLOCK_RENEW_LIST();
|
|
|
|
if( NULL != DhcpGlobalMediaSenseHandle ) {
|
|
// MediaSenseDetectionLoop can do discovers, etc. That has to die before
|
|
// any other data is killed
|
|
//
|
|
// BUG 415272: increase the wait time to 120 seconds from 3 seconds.
|
|
// 3 seconds is not enough since MediaSenseDetectionLoop is quite heavy.
|
|
// 1. It could be stucked in the pinging gateway which can takes 3 seconds.
|
|
// 2. It acquires critical sections.
|
|
// 3. It read/write registry.
|
|
//
|
|
// TBD: remove the TerminateThread since killing a thread could leave critical
|
|
// sections in locked state.
|
|
//
|
|
WaitStatus = WaitForSingleObject( DhcpGlobalMediaSenseHandle, 120 * 1000 );
|
|
if( WAIT_OBJECT_0 != WaitStatus ) {
|
|
// Should have completed by now. Since it did not, kill it!
|
|
// DhcpAssert (0);
|
|
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 LocalError;
|
|
int Retries;
|
|
|
|
|
|
NextEntry = RemoveHeadList(&DhcpGlobalNICList);
|
|
|
|
DhcpContext = CONTAINING_RECORD( NextEntry, DHCP_CONTEXT, NicListEntry );
|
|
InitializeListHead(&DhcpContext->NicListEntry);
|
|
RemoveEntryList( &DhcpContext->RenewalListEntry );
|
|
InitializeListHead( &DhcpContext->RenewalListEntry );
|
|
|
|
//
|
|
// Close the semaphore handle after first acquiring it
|
|
//
|
|
|
|
if( FALSE == DhcpGlobalIsShuttingDown ) {
|
|
|
|
UNLOCK_RENEW_LIST();
|
|
|
|
//
|
|
// BUG 415272
|
|
// Busy wait for the renewal thread to terminate.
|
|
//
|
|
// It is safe to do busy waiting. Once we reach this point,
|
|
// no new renewal thread will be launched since MediaSense
|
|
// and ProcessDhcpRequestForever threads has died.
|
|
//
|
|
Retries = 120;
|
|
while ((Retries-- > 0) && (1 != *((volatile LONG*)&(DhcpContext->RefCount)))) {
|
|
CancelRenew (DhcpContext);
|
|
Sleep (1000);
|
|
}
|
|
|
|
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);
|
|
} else {
|
|
DhcpAssert (0);
|
|
}
|
|
}
|
|
}
|
|
UNLOCK_RENEW_LIST();
|
|
|
|
if( FALSE == DhcpGlobalIsShuttingDown ) {
|
|
DhcpCleanupParamChangeRequests();
|
|
DhcpCleanupRegistry ();
|
|
}
|
|
|
|
if( DhcpGlobalMsgPopupThreadHandle != NULL ) {
|
|
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( 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 LocalError = DnsDhcpRegisterTerm();
|
|
if(ERROR_SUCCESS != LocalError) {
|
|
DhcpPrint((DEBUG_ERRORS, "DnsDhcpRegisterTerm: %ld\n", LocalError));
|
|
}
|
|
}
|
|
|
|
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 = dwErrorParam;
|
|
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;
|
|
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;
|
|
}
|
|
|
|
if(DhcpUnicodeToOemSize(DhcpGlobalHostNameW) >= sizeof(DhcpGlobalHostNameBuf)) {
|
|
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,
|
|
0,
|
|
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 success;
|
|
}
|
|
|
|
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
|
|
|
|
success:
|
|
|
|
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);
|
|
|
|
if (DhcpGlobalServiceStatus.dwCurrentState != 0 && DhcpGlobalServiceStatus.dwCurrentState != SERVICE_STOPPED) {
|
|
return;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
VOID
|
|
DhcpDeleteGlobalRegistrySettings(
|
|
VOID
|
|
)
|
|
{
|
|
DWORD dwError = 0;
|
|
|
|
dwError = DhcpDeleteRegistryOption(
|
|
NULL,
|
|
OPTION_NETBIOS_NODE_TYPE,
|
|
FALSE
|
|
);
|
|
|
|
dwError = DhcpDeleteRegistryOption(
|
|
NULL,
|
|
OPTION_NETBIOS_SCOPE_OPTION,
|
|
FALSE
|
|
);
|
|
}
|
|
|
|
VOID
|
|
DhcpDeletePerInterfaceRegistrySettings(
|
|
IN LPWSTR adapterName
|
|
)
|
|
{
|
|
DWORD dwError = 0;
|
|
|
|
dwError = DhcpDeleteRegistryOption(
|
|
adapterName,
|
|
OPTION_NETBIOS_NAME_SERVER,
|
|
FALSE
|
|
);
|
|
|
|
dwError = DhcpDeleteRegistryOption(
|
|
adapterName,
|
|
OPTION_NETBIOS_DATAGRAM_SERVER,
|
|
FALSE
|
|
);
|
|
|
|
dwError = DhcpDeleteRegistryOption(
|
|
adapterName,
|
|
OPTION_MSFT_VENDOR_NETBIOSLESS,
|
|
TRUE
|
|
);
|
|
|
|
dwError = DhcpDeleteRegistryOption(
|
|
adapterName,
|
|
OPTION_DOMAIN_NAME_SERVERS,
|
|
FALSE
|
|
);
|
|
}
|
|
|
|
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 ) {
|
|
if (IsListEmpty(&DhcpGlobalNICList)) {
|
|
//
|
|
// BUG 528718
|
|
// This is the first interface.
|
|
// Clean up the global settings.
|
|
//
|
|
DhcpDeleteGlobalRegistrySettings();
|
|
}
|
|
|
|
//
|
|
// Clean up the per-interface settings
|
|
// (If they are needed, we'll add them after the lease is renewed).
|
|
//
|
|
DhcpDeletePerInterfaceRegistrySettings(adapterName);
|
|
|
|
//
|
|
// 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);
|
|
CancelRenew (DhcpContext);
|
|
}
|
|
|
|
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
|
|
//================================================================================
|
|
|
|
|
|
|
|
|