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