windows-nt/Source/XPSP1/NT/com/ole32/dcomss/olescm/addrrefresh.cxx
2020-09-26 16:20:57 +08:00

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();
}