203 lines
5.4 KiB
C++
203 lines
5.4 KiB
C++
|
//+-------------------------------------------------------------------
|
||
|
//
|
||
|
// File: addrrefresh.cxx
|
||
|
//
|
||
|
// Contents: Implements classes for handling dynamic TCP/IP address
|
||
|
// changes
|
||
|
//
|
||
|
// Classes: CAddrRefreshMgr
|
||
|
//
|
||
|
// History: 26-Oct-00 jsimmons Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------
|
||
|
|
||
|
#include "act.hxx"
|
||
|
|
||
|
// The single instance of this object
|
||
|
CAddrRefreshMgr gAddrRefreshMgr;
|
||
|
|
||
|
// Constructor
|
||
|
CAddrRefreshMgr::CAddrRefreshMgr() :
|
||
|
_hEventIPAddressChange(NULL),
|
||
|
_hWaitObject(NULL),
|
||
|
_IPChangeNotificationSocket(INVALID_SOCKET),
|
||
|
_bWaitRegistered(FALSE),
|
||
|
_bListenedOnTCP(FALSE),
|
||
|
_bRegisteredForNotifications(FALSE)
|
||
|
{
|
||
|
ZeroMemory(&_WSAOverlapped, sizeof(_WSAOverlapped));
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// RegisterForAddressChanges
|
||
|
//
|
||
|
// Method to tell us to register with the network
|
||
|
// stack to be notified of address changes. This is
|
||
|
// a best-effort, if it fails we simply return; if
|
||
|
// that happens, DCOM will not handle dynamic address
|
||
|
// changes. Once this method succeeds, calling it
|
||
|
// is a no-op until an address change notification
|
||
|
// is signalled.
|
||
|
//
|
||
|
// Caller must be holding gpClientLock.
|
||
|
//
|
||
|
void CAddrRefreshMgr::RegisterForAddressChanges()
|
||
|
{
|
||
|
ASSERT(gpClientLock->HeldExclusive());
|
||
|
|
||
|
// If we haven't yet listened on TCP, there is
|
||
|
// no point in any of this
|
||
|
if (!_bListenedOnTCP)
|
||
|
return;
|
||
|
|
||
|
if (!_hEventIPAddressChange)
|
||
|
{
|
||
|
_hEventIPAddressChange = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||
|
if (!_hEventIPAddressChange)
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// We do not call WSAStartup, it is the responsibility of the caller
|
||
|
// to make sure WSAStartup has already been been called successfully.
|
||
|
// In practice, not calling this function until after we have
|
||
|
// successfully listened on TCP satisfies this requirement.
|
||
|
if (_IPChangeNotificationSocket == INVALID_SOCKET)
|
||
|
{
|
||
|
_IPChangeNotificationSocket = socket(AF_INET, SOCK_STREAM, 0);
|
||
|
if (_IPChangeNotificationSocket == INVALID_SOCKET)
|
||
|
{
|
||
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
||
|
DPFLTR_WARNING_LEVEL,
|
||
|
"Failed to create notification socket\n"));
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// First make sure we have successfully registered for a
|
||
|
// wait on the notification event with the NT thread pool
|
||
|
if (!_bWaitRegistered)
|
||
|
{
|
||
|
_bWaitRegistered = RegisterWaitForSingleObject(
|
||
|
&_hWaitObject,
|
||
|
_hEventIPAddressChange,
|
||
|
CAddrRefreshMgr::TimerCallbackFn,
|
||
|
this,
|
||
|
INFINITE,
|
||
|
WT_EXECUTEINWAITTHREAD);
|
||
|
if (!_bWaitRegistered)
|
||
|
{
|
||
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
||
|
DPFLTR_WARNING_LEVEL,
|
||
|
"RegisterWaitForSingleObject failed\n"));
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Setup the notification again if we failed to register last time,
|
||
|
// or if this is the first time we've ever been here
|
||
|
if (!_bRegisteredForNotifications)
|
||
|
{
|
||
|
// Initialize overlapped structure
|
||
|
ZeroMemory(&_WSAOverlapped, sizeof(_WSAOverlapped));
|
||
|
_WSAOverlapped.hEvent = _hEventIPAddressChange;
|
||
|
|
||
|
int err;
|
||
|
DWORD dwByteCnt;
|
||
|
err = WSAIoctl(
|
||
|
_IPChangeNotificationSocket,
|
||
|
SIO_ADDRESS_LIST_CHANGE,
|
||
|
NULL,
|
||
|
0,
|
||
|
NULL,
|
||
|
0,
|
||
|
&dwByteCnt,
|
||
|
&_WSAOverlapped,
|
||
|
NULL
|
||
|
);
|
||
|
_bRegisteredForNotifications = (err == 0) || (WSAGetLastError() == WSA_IO_PENDING);
|
||
|
if (!_bRegisteredForNotifications)
|
||
|
{
|
||
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
||
|
DPFLTR_WARNING_LEVEL,
|
||
|
"Failed to request ip change notification on socket (WSAGetLastError=%u)\n",
|
||
|
WSAGetLastError()));
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Success
|
||
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
||
|
DPFLTR_INFO_LEVEL,
|
||
|
"DCOM: successfully registered for address change notifications\n"));
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// TimerCallbackFnHelper
|
||
|
//
|
||
|
// Helper function to handle address change notifications.
|
||
|
//
|
||
|
// Does the following tasks:
|
||
|
// 1) re-registers for further address changes
|
||
|
// 2) recomputes current resolver bindings
|
||
|
// 3) pushes new bindings to currently running processes; note
|
||
|
// that this is done async, so we don't tie up the thread.
|
||
|
//
|
||
|
void CAddrRefreshMgr::TimerCallbackFnHelper()
|
||
|
{
|
||
|
RPC_STATUS status;
|
||
|
|
||
|
gpClientLock->LockExclusive();
|
||
|
ASSERT(gpClientLock->HeldExclusive());
|
||
|
|
||
|
// The fact that we we got this callback means that our
|
||
|
// previous registration has been consumed. Remember
|
||
|
// that fact so we can re-register down below.
|
||
|
_bRegisteredForNotifications = FALSE;
|
||
|
|
||
|
// re-register for address changes. The ordering of when
|
||
|
// we do this and when we query for the new list is impt,
|
||
|
// see docs for WSAIoctl that talk about proper ordering
|
||
|
// of SIO_ADDRESS_LIST_CHANGE and SIO_ADDRESS_LIST_QUERY.
|
||
|
RegisterForAddressChanges();
|
||
|
|
||
|
// Tell machine address object that addresses have changed
|
||
|
gpMachineName->IPAddrsChanged();
|
||
|
|
||
|
// Compute new resolver bindings
|
||
|
status = ComputeNewResolverBindings();
|
||
|
|
||
|
// Release lock now, so we don't hold it across PushCurrentBindings
|
||
|
ASSERT(gpClientLock->HeldExclusive());
|
||
|
gpClientLock->UnlockExclusive();
|
||
|
|
||
|
if (status == RPC_S_OK)
|
||
|
{
|
||
|
// Push new bindings to running processes
|
||
|
PushCurrentBindings();
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// TimerCallbackFn
|
||
|
//
|
||
|
// Static entry point that gets called by NT thread pool whenever
|
||
|
// our notification event is signalled.
|
||
|
//
|
||
|
void CALLBACK CAddrRefreshMgr::TimerCallbackFn(void* pvParam, BOOLEAN TimerOrWaitFired)
|
||
|
{
|
||
|
ASSERT(!TimerOrWaitFired); // should always be FALSE, ie event was signalled
|
||
|
|
||
|
CAddrRefreshMgr* pThis = (CAddrRefreshMgr*)pvParam;
|
||
|
|
||
|
ASSERT(pThis == &gAddrRefreshMgr); // should always be ptr to the singleton
|
||
|
|
||
|
pThis->TimerCallbackFnHelper();
|
||
|
}
|
||
|
|