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

  1. //+-------------------------------------------------------------------
  2. //
  3. // File: addrrefresh.cxx
  4. //
  5. // Contents: Implements classes for handling dynamic TCP/IP address
  6. // changes
  7. //
  8. // Classes: CAddrRefreshMgr
  9. //
  10. // History: 26-Oct-00 jsimmons Created
  11. //
  12. //--------------------------------------------------------------------
  13. #include "act.hxx"
  14. // The single instance of this object
  15. CAddrRefreshMgr gAddrRefreshMgr;
  16. // Constructor
  17. CAddrRefreshMgr::CAddrRefreshMgr() :
  18. _bListenedOnTCP(FALSE),
  19. _bCheckedForIPV6(FALSE),
  20. _bIPV6Installed(FALSE)
  21. {
  22. InitACD(&_IPV4AddrChangeData, AF_INET);
  23. InitACD(&_IPV6AddrChangeData, AF_INET6);
  24. }
  25. //
  26. // InitACD
  27. //
  28. // Initializes a new ADDRESS_CHANGE_DATA structure for use.
  29. //
  30. void CAddrRefreshMgr::InitACD(ADDRESS_CHANGE_DATA* paddrchangedata, int addrfamily)
  31. {
  32. ASSERT(paddrchangedata);
  33. ZeroMemory(paddrchangedata, sizeof(ADDRESS_CHANGE_DATA));
  34. paddrchangedata->dwSig = ADDRCHANGEDATA_SIG;
  35. paddrchangedata->socket = INVALID_SOCKET;
  36. paddrchangedata->socket_af = addrfamily;
  37. paddrchangedata->pThis = this;
  38. }
  39. //
  40. // RegisterForAddressChanges
  41. //
  42. // Method to tell us to register with the network
  43. // stack to be notified of address changes. This is
  44. // a best-effort, if it fails we simply return; if
  45. // that happens, DCOM will not handle dynamic address
  46. // changes. Once this method succeeds, calling it
  47. // is a no-op until an address change notification
  48. // is signalled.
  49. //
  50. // Caller must be holding gpClientLock.
  51. //
  52. void CAddrRefreshMgr::RegisterForAddressChanges()
  53. {
  54. ASSERT(gpClientLock->HeldExclusive());
  55. // If we haven't yet listened on TCP, there is
  56. // no point in any of this
  57. if (!_bListenedOnTCP)
  58. return;
  59. // Always register for IPV4 changes (currently it's not possible
  60. // to install TCP/IP and not get IPV4)
  61. RegisterForAddrChangesHelper(&_IPV4AddrChangeData);
  62. // Register for IPV6 changes if it's installed
  63. if (IsIPV6Installed())
  64. {
  65. RegisterForAddrChangesHelper(&_IPV6AddrChangeData);
  66. }
  67. return;
  68. }
  69. //
  70. // RegisterForAddrChangesHelper
  71. //
  72. // Registers address change notifications for a specific ADDRESS_CHANGE_DATA
  73. // struct. This is a best-effort, if it fails we simply return; if that happens,
  74. // DCOM will not handle dynamic address changes. Once this method succeeds,
  75. // calling it again for the same ADDRESS_CHANGE_DATA is a no-op until an address
  76. // change notification is signalled.
  77. //
  78. void CAddrRefreshMgr::RegisterForAddrChangesHelper(ADDRESS_CHANGE_DATA* pAddrChangeData)
  79. {
  80. ASSERT(gpClientLock->HeldExclusive());
  81. ASSERT(_bListenedOnTCP);
  82. if (pAddrChangeData->hAddressChangeEvent == NULL)
  83. {
  84. pAddrChangeData->hAddressChangeEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  85. if (!pAddrChangeData->hAddressChangeEvent)
  86. return;
  87. }
  88. // We do not call WSAStartup, it is the responsibility of the caller
  89. // to make sure WSAStartup has already been been called successfully.
  90. // In practice, not calling this function until after we have
  91. // successfully listened on TCP satisfies this requirement.
  92. if (pAddrChangeData->socket == INVALID_SOCKET)
  93. {
  94. pAddrChangeData->socket = WSASocket(pAddrChangeData->socket_af,
  95. SOCK_STREAM,
  96. IPPROTO_TCP,
  97. NULL,
  98. 0,
  99. WSA_FLAG_OVERLAPPED
  100. );
  101. if (pAddrChangeData->socket == INVALID_SOCKET)
  102. {
  103. KdPrintEx((DPFLTR_DCOMSS_ID,
  104. DPFLTR_WARNING_LEVEL,
  105. "Failed to create notification socket\n"));
  106. return;
  107. }
  108. }
  109. // First make sure we have successfully registered for a
  110. // wait on the notification event with the NT thread pool
  111. if (!pAddrChangeData->bWaitRegistered)
  112. {
  113. pAddrChangeData->bWaitRegistered = RegisterWaitForSingleObject(
  114. &(pAddrChangeData->hWaitObject),
  115. pAddrChangeData->hAddressChangeEvent,
  116. CAddrRefreshMgr::TimerCallbackFn,
  117. pAddrChangeData,
  118. INFINITE,
  119. 0);
  120. if (!pAddrChangeData->bWaitRegistered)
  121. {
  122. KdPrintEx((DPFLTR_DCOMSS_ID,
  123. DPFLTR_WARNING_LEVEL,
  124. "RegisterWaitForSingleObject failed\n"));
  125. return;
  126. }
  127. }
  128. // Setup the notification again if we failed to register last time,
  129. // or if this is the first time we've ever been here
  130. if (!pAddrChangeData->bRegisteredForNotifications)
  131. {
  132. // Initialize overlapped structure
  133. ZeroMemory(&(pAddrChangeData->WSAOverlapped), sizeof(pAddrChangeData->WSAOverlapped));
  134. pAddrChangeData->WSAOverlapped.hEvent = pAddrChangeData->hAddressChangeEvent;
  135. int err;
  136. DWORD dwByteCnt;
  137. err = WSAIoctl(
  138. pAddrChangeData->socket,
  139. SIO_ADDRESS_LIST_CHANGE,
  140. NULL,
  141. 0,
  142. NULL,
  143. 0,
  144. &dwByteCnt,
  145. &(pAddrChangeData->WSAOverlapped),
  146. NULL
  147. );
  148. pAddrChangeData->bRegisteredForNotifications =
  149. (err == 0) || (WSAGetLastError() == WSA_IO_PENDING);
  150. if (!pAddrChangeData->bRegisteredForNotifications)
  151. {
  152. KdPrintEx((DPFLTR_DCOMSS_ID,
  153. DPFLTR_WARNING_LEVEL,
  154. "Failed to request ip change notification on socket (WSAGetLastError=%u)\n",
  155. WSAGetLastError()));
  156. return;
  157. }
  158. }
  159. // Success
  160. KdPrintEx((DPFLTR_DCOMSS_ID,
  161. DPFLTR_INFO_LEVEL,
  162. "DCOM: successfully registered for address change notifications\n"));
  163. return;
  164. }
  165. //
  166. // TimerCallbackFnHelper
  167. //
  168. // Helper function to handle address change notifications.
  169. //
  170. // Does the following tasks:
  171. // 1) re-registers for further address changes
  172. // 2) recomputes current resolver bindings
  173. // 3) pushes new bindings to currently running processes; note
  174. // that this is done async, so we don't tie up the thread.
  175. //
  176. void CAddrRefreshMgr::TimerCallbackFnHelper(ADDRESS_CHANGE_DATA* paddrchangedata)
  177. {
  178. RPC_STATUS status;
  179. gpClientLock->LockExclusive();
  180. ASSERT(gpClientLock->HeldExclusive());
  181. paddrchangedata->dwNotifications++;
  182. // The fact that we we got this callback means that our
  183. // previous registration has been consumed. Remember
  184. // that fact so we can re-register down below.
  185. paddrchangedata->bRegisteredForNotifications = FALSE;
  186. // re-register for address changes. The ordering of when
  187. // we do this and when we query for the new list is impt,
  188. // see docs for WSAIoctl that talk about proper ordering
  189. // of SIO_ADDRESS_LIST_CHANGE and SIO_ADDRESS_LIST_QUERY.
  190. RegisterForAddrChangesHelper(paddrchangedata);
  191. // Tell machine address object that addresses have changed
  192. gpMachineName->IPAddrsChanged(paddrchangedata->socket_af);
  193. // Compute new resolver bindings
  194. status = ComputeNewResolverBindings();
  195. // Release lock now, so we don't hold it across PushCurrentBindings
  196. ASSERT(gpClientLock->HeldExclusive());
  197. gpClientLock->UnlockExclusive();
  198. if (status == RPC_S_OK)
  199. {
  200. // Push new bindings to running processes
  201. PushCurrentBindings();
  202. }
  203. return;
  204. }
  205. //
  206. // TimerCallbackFn
  207. //
  208. // Static entry point that gets called by NT thread pool whenever
  209. // a notification event is signalled. pvParam points to the
  210. // ADDRESS_CHANGE_DATA for the changed notification.
  211. //
  212. void CALLBACK CAddrRefreshMgr::TimerCallbackFn(void* pvParam, BOOLEAN TimerOrWaitFired)
  213. {
  214. ASSERT(!TimerOrWaitFired); // should always be FALSE, ie event was signalled
  215. ADDRESS_CHANGE_DATA* paddrchangedata = (ADDRESS_CHANGE_DATA*)pvParam;
  216. ASSERT(paddrchangedata);
  217. ASSERT(paddrchangedata->dwSig == ADDRCHANGEDATA_SIG);
  218. ASSERT(paddrchangedata->pThis == &gAddrRefreshMgr);
  219. paddrchangedata->pThis->TimerCallbackFnHelper(paddrchangedata);
  220. return;
  221. }
  222. //
  223. // IsIPV6Installed
  224. //
  225. BOOL CAddrRefreshMgr::IsIPV6Installed()
  226. {
  227. if (_bCheckedForIPV6)
  228. return _bIPV6Installed;
  229. // don't do anything until we've listened on TCP
  230. if (!_bListenedOnTCP)
  231. return FALSE;
  232. // Try to check again
  233. CheckForIPV6Installed();
  234. // If check was successful return answer, otherwise say no
  235. return _bCheckedForIPV6 ? _bIPV6Installed : FALSE;
  236. }
  237. //
  238. // CheckForIPV6Installed
  239. //
  240. // Helper function that checks to see if IPV6 is installed;
  241. // sets as a side-effect, upon success, _bCheckedForIPV6 and
  242. // _bIPV6Installed. Note that like other configureable DCOM
  243. // protocols, we don't support dynamic configuration (ie, you
  244. // must reboot for DCOM to recognize that IPV6 is installed).
  245. //
  246. void CAddrRefreshMgr::CheckForIPV6Installed()
  247. {
  248. BYTE* pProtocolBuffer = NULL;
  249. WSAPROTOCOL_INFO* lpProtocolInfos = NULL;
  250. DWORD dwBufLen = 0;
  251. int iRet = 0;
  252. BOOL fIPV6Installed = FALSE;
  253. if (_bCheckedForIPV6)
  254. return;
  255. // Ask for buffer size
  256. dwBufLen = 0;
  257. iRet = WSAEnumProtocols(NULL, lpProtocolInfos, &dwBufLen);
  258. if (iRet != SOCKET_ERROR) goto done; // should not succeed
  259. if (WSAENOBUFS != WSAGetLastError()) goto done; // should fail for insuf. buffer
  260. // Allocate memory
  261. pProtocolBuffer = new BYTE[dwBufLen];
  262. if (!pProtocolBuffer) goto done;
  263. lpProtocolInfos = (WSAPROTOCOL_INFO*)pProtocolBuffer;
  264. // Make call for real this time
  265. iRet = WSAEnumProtocols(NULL, lpProtocolInfos, &dwBufLen);
  266. if (iRet == SOCKET_ERROR) goto done;
  267. // Enumerate thru the installed protocols, looking for IPV6
  268. for (int i = 0; i < iRet; i++)
  269. {
  270. if (lpProtocolInfos[i].iAddressFamily == AF_INET6)
  271. {
  272. fIPV6Installed = TRUE;
  273. break;
  274. }
  275. }
  276. // We're done
  277. _bCheckedForIPV6 = TRUE;
  278. _bIPV6Installed = fIPV6Installed;
  279. done:
  280. if (pProtocolBuffer)
  281. delete [] pProtocolBuffer;
  282. return;
  283. }