Source code of Windows XP (NT5)
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.

282 lines
6.3 KiB

  1. #include "stdafx.h"
  2. #include "caddress.h"
  3. #if !defined( BITS_V12_ON_NT4 )
  4. #include "caddress.tmh"
  5. #endif
  6. CIpAddressMonitor::CIpAddressMonitor() :
  7. m_AddressCount( -1 ),
  8. m_ListenSocket( INVALID_SOCKET ),
  9. m_CallbackFn ( NULL ),
  10. m_CallbackArg( NULL )
  11. {
  12. m_Overlapped.Internal = 0;
  13. WSADATA data;
  14. DWORD s = WSAStartup( MAKEWORD( 2, 0 ), &data );
  15. if (s)
  16. {
  17. THROW_HRESULT( HRESULT_FROM_WIN32( s ));
  18. }
  19. THROW_HRESULT( CreateListenSocket() );
  20. }
  21. CIpAddressMonitor::~CIpAddressMonitor()
  22. {
  23. CancelListen();
  24. if (m_ListenSocket != INVALID_SOCKET)
  25. {
  26. closesocket( m_ListenSocket );
  27. }
  28. WSACleanup();
  29. }
  30. HRESULT
  31. CIpAddressMonitor::CreateListenSocket()
  32. {
  33. //
  34. // Create an overlapped socket.
  35. //
  36. m_ListenSocket = WSASocket( AF_INET,
  37. SOCK_STREAM,
  38. IPPROTO_TCP,
  39. NULL, // no explicit protocol info
  40. NULL, // no group
  41. WSA_FLAG_OVERLAPPED
  42. );
  43. if (m_ListenSocket == INVALID_SOCKET)
  44. {
  45. return HRESULT_FROM_WIN32( WSAGetLastError() );
  46. }
  47. return S_OK;
  48. }
  49. bool
  50. CIpAddressMonitor::IsListening()
  51. {
  52. return (m_Overlapped.Internal == STATUS_PENDING);
  53. }
  54. long
  55. CIpAddressMonitor::GetAddressCount()
  56. {
  57. if (m_AddressCount == -1)
  58. {
  59. UpdateAddressCount();
  60. // if this failed, m_AddressCount may still be -1.
  61. }
  62. return m_AddressCount;
  63. }
  64. HRESULT
  65. CIpAddressMonitor::Listen(
  66. LISTEN_CALLBACK_FN fn,
  67. PVOID arg
  68. )
  69. {
  70. LogInfo("begin listen");
  71. m_Mutex.Enter();
  72. //
  73. // Only one listen at a time.
  74. //
  75. if (IsListening())
  76. {
  77. m_Mutex.Leave();
  78. LogInfo("already listening");
  79. return S_FALSE;
  80. }
  81. if (m_ListenSocket == INVALID_SOCKET)
  82. {
  83. HRESULT hr = CreateListenSocket();
  84. if (FAILED(hr))
  85. {
  86. m_Mutex.Leave();
  87. LogInfo("failed %x", hr);
  88. return hr;
  89. }
  90. }
  91. //
  92. // Listen for address list changes.
  93. //
  94. DWORD bytes;
  95. if (SOCKET_ERROR == WSAIoctl( m_ListenSocket,
  96. SIO_ADDRESS_LIST_CHANGE,
  97. NULL, // no in buffer
  98. 0, // no in buffer
  99. NULL, // no out buffer
  100. 0, // no out buffer,
  101. &bytes,
  102. &m_Overlapped,
  103. CIpAddressMonitor::ListenCompletionRoutine
  104. ))
  105. {
  106. if (WSAGetLastError() != ERROR_IO_PENDING)
  107. {
  108. HRESULT HrError = HRESULT_FROM_WIN32( WSAGetLastError() );
  109. m_Mutex.Leave();
  110. LogInfo("failed %x", HrError);
  111. return HrError;
  112. }
  113. }
  114. //
  115. // Note our success.
  116. //
  117. m_CallbackFn = fn;
  118. m_CallbackArg = arg;
  119. m_Mutex.Leave();
  120. LogInfo("end listen");
  121. return S_OK;
  122. }
  123. void CALLBACK
  124. CIpAddressMonitor::ListenCompletionRoutine(
  125. IN DWORD dwError,
  126. IN DWORD cbTransferred,
  127. IN LPWSAOVERLAPPED lpOverlapped,
  128. IN DWORD dwFlags
  129. )
  130. {
  131. CIpAddressMonitor * obj = CONTAINING_RECORD( lpOverlapped, CIpAddressMonitor, m_Overlapped );
  132. LogInfo("completion routine, object %p, err %d", obj, dwError );
  133. if (dwError == 0)
  134. {
  135. obj->m_Mutex.Enter();
  136. obj->UpdateAddressCount();
  137. PVOID arg = obj->m_CallbackArg;
  138. LISTEN_CALLBACK_FN fn = obj->m_CallbackFn;
  139. obj->m_Mutex.Leave();
  140. if (fn)
  141. {
  142. fn( arg );
  143. }
  144. }
  145. }
  146. void
  147. CIpAddressMonitor::CancelListen()
  148. {
  149. LogInfo("begin cancel");
  150. m_Mutex.Enter();
  151. if (!IsListening())
  152. {
  153. m_Mutex.Leave();
  154. LogInfo("no need to cancel");
  155. return;
  156. }
  157. //
  158. // Must wait for the I/O to be completed or aborted, since m_Overlapped
  159. // is written to in both cases.
  160. //
  161. CancelIo( HANDLE(m_ListenSocket) );
  162. long count = 0;
  163. while (m_Overlapped.Internal == STATUS_PENDING)
  164. {
  165. if (0 == (count % 100) )
  166. {
  167. LogInfo("waiting %d times...", count);
  168. }
  169. SleepEx( 1, TRUE );
  170. ++count;
  171. }
  172. closesocket( m_ListenSocket );
  173. m_ListenSocket = INVALID_SOCKET;
  174. m_Mutex.Leave();
  175. //
  176. // The overlapped operation is no longer pending, but the APC may still be queued.
  177. // Allow it to run.
  178. //
  179. SleepEx( 1, TRUE );
  180. LogInfo("end cancel");
  181. }
  182. HRESULT
  183. CIpAddressMonitor::UpdateAddressCount()
  184. {
  185. //
  186. // First call gets the required buffer size...
  187. //
  188. DWORD bytes;
  189. WSAIoctl( m_ListenSocket,
  190. SIO_ADDRESS_LIST_QUERY,
  191. NULL, // no in buffer
  192. 0, // no in buffer
  193. NULL, // no out buffer
  194. 0, // no out buffer,
  195. &bytes,
  196. NULL, // no OVERLAPPED
  197. NULL // no completion routine
  198. );
  199. if (WSAGetLastError() != WSAEFAULT)
  200. {
  201. m_AddressCount = -1;
  202. return HRESULT_FROM_WIN32( WSAGetLastError());
  203. }
  204. auto_ptr<char> Buffer;
  205. try
  206. {
  207. Buffer = auto_ptr<char>( new char[ bytes ] );
  208. }
  209. catch( ComError Error )
  210. {
  211. return Error.Error();
  212. }
  213. SOCKET_ADDRESS_LIST * List = reinterpret_cast<SOCKET_ADDRESS_LIST *>(Buffer.get());
  214. //
  215. // ...second call gets the data.
  216. //
  217. if (SOCKET_ERROR == WSAIoctl( m_ListenSocket,
  218. SIO_ADDRESS_LIST_QUERY,
  219. NULL,
  220. 0,
  221. List,
  222. bytes,
  223. &bytes,
  224. NULL,
  225. NULL
  226. ))
  227. {
  228. m_AddressCount = -1;
  229. return HRESULT_FROM_WIN32( WSAGetLastError());
  230. }
  231. m_AddressCount = List->iAddressCount;
  232. return S_OK;
  233. }