Leaked source code of windows server 2003
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

//+-------------------------------------------------------------------
//
// 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;
}