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.
334 lines
9.5 KiB
334 lines
9.5 KiB
//+-------------------------------------------------------------------
|
|
//
|
|
// 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() :
|
|
_bListenedOnTCP(FALSE),
|
|
_bCheckedForIPV6(FALSE),
|
|
_bIPV6Installed(FALSE)
|
|
{
|
|
InitACD(&_IPV4AddrChangeData, AF_INET);
|
|
InitACD(&_IPV6AddrChangeData, AF_INET6);
|
|
}
|
|
|
|
//
|
|
// InitACD
|
|
//
|
|
// Initializes a new ADDRESS_CHANGE_DATA structure for use.
|
|
//
|
|
void CAddrRefreshMgr::InitACD(ADDRESS_CHANGE_DATA* paddrchangedata, int addrfamily)
|
|
{
|
|
ASSERT(paddrchangedata);
|
|
|
|
ZeroMemory(paddrchangedata, sizeof(ADDRESS_CHANGE_DATA));
|
|
paddrchangedata->dwSig = ADDRCHANGEDATA_SIG;
|
|
paddrchangedata->socket = INVALID_SOCKET;
|
|
paddrchangedata->socket_af = addrfamily;
|
|
paddrchangedata->pThis = this;
|
|
}
|
|
|
|
|
|
//
|
|
// 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;
|
|
|
|
// Always register for IPV4 changes (currently it's not possible
|
|
// to install TCP/IP and not get IPV4)
|
|
RegisterForAddrChangesHelper(&_IPV4AddrChangeData);
|
|
|
|
// Register for IPV6 changes if it's installed
|
|
if (IsIPV6Installed())
|
|
{
|
|
RegisterForAddrChangesHelper(&_IPV6AddrChangeData);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// RegisterForAddrChangesHelper
|
|
//
|
|
// Registers address change notifications for a specific ADDRESS_CHANGE_DATA
|
|
// struct. 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 again for the same ADDRESS_CHANGE_DATA is a no-op until an address
|
|
// change notification is signalled.
|
|
//
|
|
void CAddrRefreshMgr::RegisterForAddrChangesHelper(ADDRESS_CHANGE_DATA* pAddrChangeData)
|
|
{
|
|
ASSERT(gpClientLock->HeldExclusive());
|
|
ASSERT(_bListenedOnTCP);
|
|
|
|
if (pAddrChangeData->hAddressChangeEvent == NULL)
|
|
{
|
|
pAddrChangeData->hAddressChangeEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (!pAddrChangeData->hAddressChangeEvent)
|
|
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 (pAddrChangeData->socket == INVALID_SOCKET)
|
|
{
|
|
pAddrChangeData->socket = WSASocket(pAddrChangeData->socket_af,
|
|
SOCK_STREAM,
|
|
IPPROTO_TCP,
|
|
NULL,
|
|
0,
|
|
WSA_FLAG_OVERLAPPED
|
|
);
|
|
if (pAddrChangeData->socket == 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 (!pAddrChangeData->bWaitRegistered)
|
|
{
|
|
pAddrChangeData->bWaitRegistered = RegisterWaitForSingleObject(
|
|
&(pAddrChangeData->hWaitObject),
|
|
pAddrChangeData->hAddressChangeEvent,
|
|
CAddrRefreshMgr::TimerCallbackFn,
|
|
pAddrChangeData,
|
|
INFINITE,
|
|
0);
|
|
if (!pAddrChangeData->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 (!pAddrChangeData->bRegisteredForNotifications)
|
|
{
|
|
// Initialize overlapped structure
|
|
ZeroMemory(&(pAddrChangeData->WSAOverlapped), sizeof(pAddrChangeData->WSAOverlapped));
|
|
pAddrChangeData->WSAOverlapped.hEvent = pAddrChangeData->hAddressChangeEvent;
|
|
|
|
int err;
|
|
DWORD dwByteCnt;
|
|
err = WSAIoctl(
|
|
pAddrChangeData->socket,
|
|
SIO_ADDRESS_LIST_CHANGE,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
&dwByteCnt,
|
|
&(pAddrChangeData->WSAOverlapped),
|
|
NULL
|
|
);
|
|
pAddrChangeData->bRegisteredForNotifications =
|
|
(err == 0) || (WSAGetLastError() == WSA_IO_PENDING);
|
|
if (!pAddrChangeData->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(ADDRESS_CHANGE_DATA* paddrchangedata)
|
|
{
|
|
RPC_STATUS status;
|
|
|
|
gpClientLock->LockExclusive();
|
|
ASSERT(gpClientLock->HeldExclusive());
|
|
|
|
paddrchangedata->dwNotifications++;
|
|
|
|
// 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.
|
|
paddrchangedata->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.
|
|
RegisterForAddrChangesHelper(paddrchangedata);
|
|
|
|
// Tell machine address object that addresses have changed
|
|
gpMachineName->IPAddrsChanged(paddrchangedata->socket_af);
|
|
|
|
// 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
|
|
// a notification event is signalled. pvParam points to the
|
|
// ADDRESS_CHANGE_DATA for the changed notification.
|
|
//
|
|
void CALLBACK CAddrRefreshMgr::TimerCallbackFn(void* pvParam, BOOLEAN TimerOrWaitFired)
|
|
{
|
|
ASSERT(!TimerOrWaitFired); // should always be FALSE, ie event was signalled
|
|
|
|
ADDRESS_CHANGE_DATA* paddrchangedata = (ADDRESS_CHANGE_DATA*)pvParam;
|
|
|
|
ASSERT(paddrchangedata);
|
|
ASSERT(paddrchangedata->dwSig == ADDRCHANGEDATA_SIG);
|
|
ASSERT(paddrchangedata->pThis == &gAddrRefreshMgr);
|
|
|
|
paddrchangedata->pThis->TimerCallbackFnHelper(paddrchangedata);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// IsIPV6Installed
|
|
//
|
|
BOOL CAddrRefreshMgr::IsIPV6Installed()
|
|
{
|
|
if (_bCheckedForIPV6)
|
|
return _bIPV6Installed;
|
|
|
|
// don't do anything until we've listened on TCP
|
|
if (!_bListenedOnTCP)
|
|
return FALSE;
|
|
|
|
// Try to check again
|
|
CheckForIPV6Installed();
|
|
|
|
// If check was successful return answer, otherwise say no
|
|
return _bCheckedForIPV6 ? _bIPV6Installed : FALSE;
|
|
}
|
|
|
|
//
|
|
// CheckForIPV6Installed
|
|
//
|
|
// Helper function that checks to see if IPV6 is installed;
|
|
// sets as a side-effect, upon success, _bCheckedForIPV6 and
|
|
// _bIPV6Installed. Note that like other configureable DCOM
|
|
// protocols, we don't support dynamic configuration (ie, you
|
|
// must reboot for DCOM to recognize that IPV6 is installed).
|
|
//
|
|
void CAddrRefreshMgr::CheckForIPV6Installed()
|
|
{
|
|
BYTE* pProtocolBuffer = NULL;
|
|
WSAPROTOCOL_INFO* lpProtocolInfos = NULL;
|
|
DWORD dwBufLen = 0;
|
|
int iRet = 0;
|
|
BOOL fIPV6Installed = FALSE;
|
|
|
|
if (_bCheckedForIPV6)
|
|
return;
|
|
|
|
// Ask for buffer size
|
|
dwBufLen = 0;
|
|
iRet = WSAEnumProtocols(NULL, lpProtocolInfos, &dwBufLen);
|
|
if (iRet != SOCKET_ERROR) goto done; // should not succeed
|
|
if (WSAENOBUFS != WSAGetLastError()) goto done; // should fail for insuf. buffer
|
|
|
|
// Allocate memory
|
|
pProtocolBuffer = new BYTE[dwBufLen];
|
|
if (!pProtocolBuffer) goto done;
|
|
|
|
lpProtocolInfos = (WSAPROTOCOL_INFO*)pProtocolBuffer;
|
|
|
|
// Make call for real this time
|
|
iRet = WSAEnumProtocols(NULL, lpProtocolInfos, &dwBufLen);
|
|
if (iRet == SOCKET_ERROR) goto done;
|
|
|
|
// Enumerate thru the installed protocols, looking for IPV6
|
|
for (int i = 0; i < iRet; i++)
|
|
{
|
|
if (lpProtocolInfos[i].iAddressFamily == AF_INET6)
|
|
{
|
|
fIPV6Installed = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// We're done
|
|
_bCheckedForIPV6 = TRUE;
|
|
_bIPV6Installed = fIPV6Installed;
|
|
|
|
done:
|
|
if (pProtocolBuffer)
|
|
delete [] pProtocolBuffer;
|
|
|
|
return;
|
|
}
|