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.
842 lines
16 KiB
842 lines
16 KiB
/*++
|
|
|
|
Copyright (c) 2000-2002 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
notify.c
|
|
|
|
Abstract:
|
|
|
|
DNS Resolver Service.
|
|
|
|
Notification thread
|
|
- host file changes
|
|
- registry config changes
|
|
|
|
Author:
|
|
|
|
Jim Gilroy (jamesg) November 2000
|
|
|
|
Revision History:
|
|
|
|
jamesg Nov 2001 -- IP6
|
|
|
|
--*/
|
|
|
|
|
|
#include "local.h"
|
|
|
|
//
|
|
// DHCP refresh call
|
|
//
|
|
|
|
extern
|
|
DWORD
|
|
DhcpStaticRefreshParams(
|
|
IN LPWSTR Adapter
|
|
);
|
|
|
|
//
|
|
// Host file directory
|
|
//
|
|
|
|
#define HOSTS_FILE_DIRECTORY L"\\drivers\\etc"
|
|
|
|
//
|
|
// Notify globals
|
|
//
|
|
|
|
DWORD g_NotifyThreadId = 0;
|
|
HANDLE g_hNotifyThread = NULL;
|
|
|
|
HANDLE g_hHostFileChange = NULL;
|
|
HANDLE g_hRegistryChange = NULL;
|
|
|
|
HKEY g_hCacheKey = NULL;
|
|
PSTR g_pmszAlternateNames = NULL;
|
|
|
|
|
|
//
|
|
// Private protos
|
|
//
|
|
|
|
VOID
|
|
CleanupRegistryMonitoring(
|
|
VOID
|
|
);
|
|
|
|
|
|
|
|
HANDLE
|
|
CreateHostsFileChangeHandle(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create hosts file change handle.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
HANDLE changeHandle;
|
|
PWSTR psystemDirectory = NULL;
|
|
UINT len;
|
|
WCHAR hostDirectory[ MAX_PATH*2 ];
|
|
|
|
DNSDBG( INIT, ( "CreateHostsFileChangeHandle\n" ));
|
|
|
|
//
|
|
// build host file name
|
|
//
|
|
|
|
len = GetSystemDirectory( hostDirectory, MAX_PATH );
|
|
if ( !len || len>MAX_PATH )
|
|
{
|
|
DNSLOG_F1( "Error: Failed to get system directory" );
|
|
DNSLOG_F1( "NotifyThread exiting." );
|
|
return( NULL );
|
|
}
|
|
|
|
wcscat( hostDirectory, HOSTS_FILE_DIRECTORY );
|
|
|
|
//
|
|
// drop change notify on host file directory
|
|
//
|
|
|
|
changeHandle = FindFirstChangeNotification(
|
|
hostDirectory,
|
|
FALSE,
|
|
FILE_NOTIFY_CHANGE_FILE_NAME |
|
|
FILE_NOTIFY_CHANGE_LAST_WRITE );
|
|
|
|
if ( changeHandle == INVALID_HANDLE_VALUE )
|
|
{
|
|
DNSLOG_F1( "NotifyThread failed to get handle from" );
|
|
DNSLOG_F2(
|
|
"Failed to get hosts file change handle.\n"
|
|
"Error code: <0x%.8X>",
|
|
GetLastError() );
|
|
return( NULL );
|
|
}
|
|
|
|
return( changeHandle );
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Registry change monitoring
|
|
//
|
|
|
|
DNS_STATUS
|
|
InitializeRegistryMonitoring(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Setup registry change monitoring.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Globals:
|
|
|
|
g_pmszAlternateNames -- set with current alternate names value
|
|
|
|
g_hCacheKey -- cache reg key is opened
|
|
|
|
g_hRegistryChange -- creates event to be signalled on change notify
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
Error code on failure.
|
|
|
|
--*/
|
|
{
|
|
DNS_STATUS status;
|
|
|
|
DNSDBG( TRACE, (
|
|
"InitializeRegistryMonitoring()\n" ));
|
|
|
|
//
|
|
// open monitoring regkey at DnsCache\Parameters
|
|
// set on parameters key which always exists rather than
|
|
// explicitly on alternate names key, which may not
|
|
//
|
|
|
|
status = RegOpenKeyExW(
|
|
HKEY_LOCAL_MACHINE,
|
|
DNS_CACHE_KEY,
|
|
0,
|
|
KEY_READ,
|
|
& g_hCacheKey );
|
|
if ( status != NO_ERROR )
|
|
{
|
|
goto Failed;
|
|
}
|
|
|
|
g_hRegistryChange = CreateEvent(
|
|
NULL, // no security
|
|
FALSE, // auto-reset
|
|
FALSE, // start non-signalled
|
|
NULL // no name
|
|
);
|
|
if ( !g_hRegistryChange )
|
|
{
|
|
status = GetLastError();
|
|
goto Failed;
|
|
}
|
|
|
|
//
|
|
// set change notify
|
|
//
|
|
|
|
status = RegNotifyChangeKeyValue(
|
|
g_hCacheKey,
|
|
TRUE, // watch subtree
|
|
REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET,
|
|
g_hRegistryChange,
|
|
TRUE // async, func doesn't block
|
|
);
|
|
if ( status != NO_ERROR )
|
|
{
|
|
goto Failed;
|
|
}
|
|
|
|
//
|
|
// read alternate computer names
|
|
// - need value to compare when we get a hit on change-notify
|
|
// - read can fail -- value stays NULL
|
|
//
|
|
|
|
Reg_GetValue(
|
|
NULL, // no session
|
|
g_hCacheKey, // cache key
|
|
RegIdAlternateNames,
|
|
REGTYPE_ALTERNATE_NAMES,
|
|
& g_pmszAlternateNames
|
|
);
|
|
|
|
goto Done;
|
|
|
|
Failed:
|
|
|
|
//
|
|
// cleanup
|
|
//
|
|
|
|
CleanupRegistryMonitoring();
|
|
|
|
Done:
|
|
|
|
DNSDBG( TRACE, (
|
|
"Leave InitializeRegistryMonitoring() => %d\n"
|
|
"\tpAlternateNames = %p\n"
|
|
"\thChangeEvent = %p\n"
|
|
"\thCacheKey = %p\n",
|
|
status,
|
|
g_pmszAlternateNames,
|
|
g_hRegistryChange,
|
|
g_hCacheKey
|
|
));
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
DNS_STATUS
|
|
RestartRegistryMonitoring(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check for change in alternate names.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Globals:
|
|
|
|
g_pmszAlternateNames -- read
|
|
|
|
g_hCacheKey -- used for read
|
|
|
|
g_hRegistryChange -- used to restart change-notify
|
|
|
|
Return Value:
|
|
|
|
TRUE if alternate names has changed.
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
DNS_STATUS status;
|
|
|
|
DNSDBG( TRACE, (
|
|
"RestartRegistryMonitoring()\n" ));
|
|
|
|
//
|
|
// sanity check
|
|
//
|
|
|
|
if ( !g_hCacheKey || !g_hRegistryChange )
|
|
{
|
|
ASSERT( g_hCacheKey && g_hRegistryChange );
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// restart change notify
|
|
//
|
|
|
|
status = RegNotifyChangeKeyValue(
|
|
g_hCacheKey,
|
|
TRUE, // watch subtree
|
|
REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET,
|
|
g_hRegistryChange,
|
|
TRUE // async, func doesn't block
|
|
);
|
|
if ( status != NO_ERROR )
|
|
{
|
|
DNSDBG( ANY, (
|
|
"RegChangeNotify failed! %d\n",
|
|
status ));
|
|
ASSERT( FALSE );
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
CleanupRegistryMonitoring(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cleanup registry monitoring.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Globals:
|
|
|
|
g_pmszAlternateNames -- is freed
|
|
|
|
g_hCacheKey -- cache reg key is closed
|
|
|
|
g_hRegistryChange -- is closed
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
DNS_STATUS status;
|
|
|
|
DNSDBG( TRACE, (
|
|
"CleanupRegistryMonitoring()\n" ));
|
|
|
|
if ( g_hHostFileChange )
|
|
{
|
|
CloseHandle( g_hHostFileChange );
|
|
g_hHostFileChange = NULL;
|
|
}
|
|
|
|
// cleanup registry change stuff
|
|
|
|
DnsApiFree( g_pmszAlternateNames );
|
|
g_pmszAlternateNames = NULL;
|
|
|
|
RegCloseKey( g_hCacheKey );
|
|
g_hCacheKey = NULL;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
CheckForAlternateNamesChange(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check for change in alternate names.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Globals:
|
|
|
|
g_pmszAlternateNames -- read
|
|
|
|
g_hCacheKey -- used for read
|
|
|
|
Return Value:
|
|
|
|
TRUE if alternate names has changed.
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
DNS_STATUS status;
|
|
BOOL fcheck = TRUE;
|
|
PCHAR palternateNames = NULL;
|
|
|
|
DNSDBG( TRACE, (
|
|
"CheckForAlternateNamesChange()\n" ));
|
|
|
|
//
|
|
// sanity check
|
|
//
|
|
|
|
if ( !g_hCacheKey || !g_hRegistryChange )
|
|
{
|
|
ASSERT( g_hCacheKey && g_hRegistryChange );
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// read alternate computer names
|
|
// - need value to compare when we get a hit on change-notify
|
|
// - read can fail -- value stays NULL
|
|
//
|
|
|
|
Reg_GetValue(
|
|
NULL, // no session
|
|
g_hCacheKey, // cache key
|
|
RegIdAlternateNames,
|
|
REGTYPE_ALTERNATE_NAMES,
|
|
& palternateNames
|
|
);
|
|
|
|
//
|
|
// detect alternate names change
|
|
//
|
|
|
|
if ( palternateNames || g_pmszAlternateNames )
|
|
{
|
|
if ( !palternateNames || !g_pmszAlternateNames )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
if ( !MultiSz_Equal_A(
|
|
palternateNames,
|
|
g_pmszAlternateNames ) )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
fcheck = FALSE;
|
|
|
|
|
|
Cleanup:
|
|
|
|
DnsApiFree( palternateNames );
|
|
|
|
DNSDBG( TRACE, (
|
|
"Leave CheckForAlternateNamesChange() => %d\n",
|
|
fcheck ));
|
|
|
|
return fcheck;
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// Notify thread routines
|
|
//
|
|
|
|
VOID
|
|
ThreadShutdownWait(
|
|
IN HANDLE hThread
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Wait on thread shutdown.
|
|
|
|
Arguments:
|
|
|
|
hThread -- thread handle that is shutting down
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
DWORD waitResult;
|
|
|
|
if ( !hThread )
|
|
{
|
|
return;
|
|
}
|
|
|
|
DNSDBG( ANY, (
|
|
"Waiting on shutdown of thread %d (%p)\n",
|
|
hThread, hThread ));
|
|
|
|
waitResult = WaitForSingleObject(
|
|
hThread,
|
|
10000 );
|
|
|
|
switch( waitResult )
|
|
{
|
|
case WAIT_OBJECT_0:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// thread didn't stop -- need to kill it
|
|
|
|
ASSERT( waitResult == WAIT_TIMEOUT );
|
|
|
|
DNSLOG_F2( "Shutdown: thread %d not stopped, terminating", hThread );
|
|
TerminateThread( hThread, 1 );
|
|
break;
|
|
}
|
|
|
|
// close thread handle
|
|
|
|
CloseHandle( hThread );
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
NotifyThread(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Main notify thread.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Globals:
|
|
|
|
g_hStopEvent -- waits on shutdown even
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
DWORD handleCount;
|
|
DWORD waitResult;
|
|
HANDLE handleArray[3];
|
|
|
|
DNSDBG( INIT, (
|
|
"\nStart NotifyThread\n" ));
|
|
|
|
//
|
|
// get file change handle
|
|
//
|
|
|
|
g_hHostFileChange = CreateHostsFileChangeHandle();
|
|
|
|
//
|
|
// init registry change-notify
|
|
//
|
|
|
|
InitializeRegistryMonitoring();
|
|
|
|
//
|
|
// wait on
|
|
// - host file change => flush+rebuild cache
|
|
// - registry change => reread config info
|
|
// - shutdown => exit
|
|
//
|
|
|
|
handleArray[0] = g_hStopEvent;
|
|
handleCount = 1;
|
|
|
|
if ( g_hHostFileChange )
|
|
{
|
|
handleArray[handleCount++] = g_hHostFileChange;
|
|
}
|
|
if ( g_hRegistryChange )
|
|
{
|
|
handleArray[handleCount++] = g_hRegistryChange;
|
|
}
|
|
|
|
if ( handleCount == 1 )
|
|
{
|
|
DNSDBG( ANY, (
|
|
"No change handles -- exit notify thread.\n" ));
|
|
goto ThreadExit;
|
|
}
|
|
|
|
//
|
|
// DCR: notify init failure handling
|
|
// right now this loop is toast if eventing fails during any cycle
|
|
//
|
|
// should handle notify init failures
|
|
// - have check-n-reinit (for each notification) in the loop
|
|
// - when one fails, loop goes to timed wait (10m) and
|
|
// then retries init in next cycle; timeout just cycles
|
|
// loop
|
|
//
|
|
|
|
while( 1 )
|
|
{
|
|
waitResult = WaitForMultipleObjects(
|
|
handleCount,
|
|
handleArray,
|
|
FALSE,
|
|
INFINITE );
|
|
|
|
switch( waitResult )
|
|
{
|
|
case WAIT_OBJECT_0:
|
|
|
|
// shutdown event
|
|
// - if stopping exit
|
|
// - do garbage collection if required
|
|
// - otherwise short wait to avoid spin if screwup
|
|
// and not get thrashed by failed garbage collection
|
|
|
|
DNSLOG_F1( "NotifyThread: Shutdown Event" );
|
|
if ( g_StopFlag )
|
|
{
|
|
goto ThreadExit;
|
|
}
|
|
else if ( g_GarbageCollectFlag )
|
|
{
|
|
Cache_GarbageCollect( 0 );
|
|
}
|
|
ELSE_ASSERT_FALSE;
|
|
|
|
Sleep( 1000 );
|
|
if ( g_StopFlag )
|
|
{
|
|
goto ThreadExit;
|
|
}
|
|
continue;
|
|
|
|
case WAIT_OBJECT_0 + 1:
|
|
|
|
// host file change -- flush cache
|
|
|
|
DNSLOG_F1( "NotifyThread: Host file change event" );
|
|
|
|
// reset notification -- BEFORE reload
|
|
|
|
if ( !FindNextChangeNotification( g_hHostFileChange ) )
|
|
{
|
|
DNSLOG_F1( "NotifyThread failed to get handle" );
|
|
DNSLOG_F1( "from FindNextChangeNotification." );
|
|
DNSLOG_F2( "Error code: <0x%.8X>", GetLastError() );
|
|
goto ThreadExit;
|
|
}
|
|
|
|
Cache_Flush();
|
|
break;
|
|
|
|
case WAIT_OBJECT_0 + 2:
|
|
|
|
// registry change notification -- flush cache and reload
|
|
|
|
DNSLOG_F1( "NotifyThread: Registry change event" );
|
|
|
|
// restart notification -- BEFORE reload
|
|
|
|
RestartRegistryMonitoring();
|
|
|
|
// rebuild config
|
|
|
|
DNSDBG( ANY, ( "\nRegistry notification, rebuilding config.\n" ));
|
|
HandleConfigChange(
|
|
"Registry-notification",
|
|
FALSE // no cache flush required
|
|
);
|
|
|
|
// check if alternate name change (to notify registrations)
|
|
//
|
|
// DCR: should notify on ordinary name change
|
|
// - save old netinfo, get new, compare
|
|
// - could wrap this into HandleConfigChange
|
|
|
|
if ( CheckForAlternateNamesChange() )
|
|
{
|
|
DNSDBG( ANY, ( "\nAlternate name change, notify for reregistration!\n" ));
|
|
DhcpStaticRefreshParams(
|
|
NULL // global refresh, no particular adapter
|
|
);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
|
|
ASSERT( g_StopFlag );
|
|
if ( g_StopFlag )
|
|
{
|
|
goto ThreadExit;
|
|
}
|
|
Sleep( 5000 );
|
|
continue;
|
|
}
|
|
}
|
|
|
|
ThreadExit:
|
|
|
|
DNSDBG( INIT, (
|
|
"NotifyThread exit\n" ));
|
|
DNSLOG_F1( "NotifyThread exiting." );
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
StartNotify(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Start notify thread.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
ErrorCode on failure.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// clear
|
|
//
|
|
|
|
g_NotifyThreadId = 0;
|
|
g_hNotifyThread = NULL;
|
|
|
|
g_hHostFileChange = NULL;
|
|
g_hRegistryChange = NULL;
|
|
|
|
|
|
//
|
|
// host file write monitor thread
|
|
// keeps cache in sync when write made to host file
|
|
//
|
|
|
|
g_hNotifyThread = CreateThread(
|
|
NULL,
|
|
0,
|
|
(LPTHREAD_START_ROUTINE) NotifyThread,
|
|
NULL,
|
|
0,
|
|
&g_NotifyThreadId );
|
|
if ( !g_hNotifyThread )
|
|
{
|
|
DNS_STATUS status = GetLastError();
|
|
|
|
DNSLOG_F1( "ERROR: InitializeCache function failed to create" );
|
|
DNSLOG_F1( " HOSTS file monitor thread." );
|
|
DNSLOG_F2( " Error code: <0x%.8X>", status );
|
|
DNSLOG_F1( " NOTE: Resolver service will continue to run." );
|
|
|
|
DNSDBG( ANY, (
|
|
"FAILED Notify thread start!\n"
|
|
"\tstatus = %d\n",
|
|
status ));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
ShutdownNotify(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Shutdown notify thread.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
ErrorCode on failure.
|
|
|
|
--*/
|
|
{
|
|
DWORD waitResult;
|
|
|
|
DNSDBG( INIT, ( "NotifyShutdown()\n" ));
|
|
|
|
//
|
|
// wait for notify thread to stop
|
|
//
|
|
|
|
ThreadShutdownWait( g_hNotifyThread );
|
|
g_hNotifyThread = NULL;
|
|
|
|
//
|
|
// close notification handles
|
|
//
|
|
|
|
if ( g_hRegistryChange )
|
|
{
|
|
CloseHandle( g_hRegistryChange );
|
|
g_hRegistryChange = NULL;
|
|
}
|
|
|
|
// registry monitoring cleanup
|
|
|
|
CleanupRegistryMonitoring();
|
|
|
|
// clear globals
|
|
|
|
g_NotifyThreadId = 0;
|
|
g_hNotifyThread = NULL;
|
|
}
|
|
|
|
//
|
|
// End notify.c
|
|
//
|
|
|