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.

489 lines
15 KiB

  1. /*==========================================================================
  2. *
  3. * Copyright (C) 2000 Microsoft Corporation. All Rights Reserved.
  4. *
  5. * File: Disconnect.cpp
  6. * Content: DNET disconnection routines
  7. *@@BEGIN_MSINTERNAL
  8. * History:
  9. * Date By Reason
  10. * ==== == ======
  11. * 09/15/99 mjn Created
  12. * 12/23/99 mjn Hand all NameTable update sends from Host to worker thread
  13. * 12/23/99 mjn Fixed PlayerDisconnect to prevent user notification of
  14. * deletion from ALL_PLAYERS group
  15. * 12/23/99 mjn Added basic host migration functionality
  16. * 12/28/99 mjn Complete outstanding operations in DNPlayerDisconnectNew
  17. * 12/28/99 mjn Moved Async Op stuff to Async.h
  18. * 12/29/99 mjn Reformed DN_ASYNC_OP to use hParentOp instead of lpvUserContext
  19. * 01/03/00 mjn Added DNPrepareToDeletePlayer
  20. * 01/04/00 mjn Added code to allow outstanding ops to complete at host migration
  21. * 01/06/99 mjn Moved NameTable stuff to NameTable.h
  22. * 01/09/00 mjn Keep number of players in Application Description
  23. * 01/10/00 mjn Check AppDesc for host migration
  24. * 01/11/00 mjn Use CPackedBuffers instead of DN_ENUM_BUFFER_INFOs
  25. * 01/15/00 mjn Replaced DN_COUNT_BUFFER with CRefCountBuffer
  26. * 01/16/00 mjn Moved User callback stuff to User.h
  27. * 01/18/00 mjn Added DNAutoDestructGroups
  28. * 01/19/00 mjn Replaced DN_SYNC_EVENT with CSyncEvent
  29. * 01/20/00 mjn Fixed CBilink usage problem in DNLocalDisconnect
  30. * 01/22/00 mjn Added DNProcessHostDestroyPlayer
  31. * 01/23/00 mjn Update NameTable version for instructed disconnects
  32. * 01/24/00 mjn Use DNNTUpdateVersion to update NameTable version
  33. * 01/25/00 mjn Changed Host Migration to multi-step affair
  34. * 02/01/00 mjn Implement Player/Group context values
  35. * 04/05/00 mjn Updated DNProcessHostDestroyPlayer()
  36. * 04/12/00 mjn Removed DNAutoDestructGroups - covered in NameTable.DeletePlayer()
  37. * mjn Don't set DN_OBJECT_FLAG_DISCONNECTING in DNPlayerDisconnect()
  38. * 04/18/00 mjn Fixed player count problem
  39. * 04/19/00 mjn Update NameTable operations to use DN_WORKER_JOB_SEND_NAMETABLE_OPERATION
  40. * 05/16/00 mjn Do not take locks when clearing NameTable short-cut pointers
  41. * 06/06/00 mjn Fixed DNPlayerDisconnect to always check for host migration in peer-peer mode w/ host migration flag
  42. * 07/07/00 mjn Clear host migration status if new host disconnects during migration process
  43. * 07/20/00 mjn Use ClearHostWithDPNID() to clear HostPlayer in DNPlayerDisconnectNew()
  44. * 07/29/00 mjn Fix calls to DNUserConnectionTerminated()
  45. * 07/30/00 mjn Use DNUserTerminateSession() rather than DNUserConnectionTerminated()
  46. * 07/31/00 mjn Added hrReason to DNTerminateSession()
  47. * mjn Added dwDestroyReason to DNHostDisconnect()
  48. * mjn Removed DNProcessHostDestroyPlayer()
  49. * 07/31/00 mjn Change DN_MSG_INTERNAL_DELETE_PLAYER to DN_MSG_INTERNAL_DESTROY_PLAYER
  50. * 08/05/00 RichGr IA64: Use %p format specifier in DPFs for 32/64-bit pointers and handles.
  51. * 08/05/00 mjn Prevent DN_MSG_INTERNAL_DESTROY_PLAYER from being sent out in client/server
  52. * 08/06/00 mjn Added CWorkerJob
  53. * 08/07/00 mjn Added code to request peer-peer integrity checks and clean up afterwards
  54. * 09/04/00 mjn Added CApplicationDesc
  55. * 09/26/00 mjn Removed locking from CNameTable::SetVersion() and CNameTable::GetNewVersion()
  56. * 01/25/01 mjn Fixed 64-bit alignment problem in received messages
  57. * 02/12/01 mjn Fixed CConnection::GetEndPt() to track calling thread
  58. * 04/13/01 mjn Remove request for integrity check from request list in DNInstructedDisconnect()
  59. * 07/22/01 mjn Added DPNBUILD_NOHOSTMIGRATE compile flag
  60. *@@END_MSINTERNAL
  61. *
  62. ***************************************************************************/
  63. #include "dncorei.h"
  64. // DNPlayerDisconnectNew
  65. //
  66. // Another player has issued a disconnect with the local player.
  67. // - If the disconnecting player is still in the nametable
  68. // - prepare to delete player
  69. // - Save one refcount to be released by DELETE_PLAYER from host or Close
  70. // - check host migration
  71. #undef DPF_MODNAME
  72. #define DPF_MODNAME "DNPlayerDisconnectNew"
  73. HRESULT DNPlayerDisconnectNew(DIRECTNETOBJECT *const pdnObject,
  74. const DPNID dpnid)
  75. {
  76. CNameTableEntry *pNTEntry;
  77. CNameTableEntry *pLocalPlayer;
  78. HRESULT hResultCode;
  79. #ifndef DPNBUILD_NOHOSTMIGRATE
  80. DPNID dpnidNewHost;
  81. #endif // !DPNBUILD_NOHOSTMIGRATE
  82. BOOL fWasHost;
  83. BOOL fRequestIntegrityCheck;
  84. DPFX(DPFPREP, 4,"Parameters: dpnid [0x%lx]",dpnid);
  85. DNASSERT(pdnObject != NULL);
  86. pNTEntry = NULL;
  87. pLocalPlayer = NULL;
  88. if (pdnObject->dwFlags & DN_OBJECT_FLAG_CLIENT)
  89. {
  90. //
  91. // The Server has disconnected
  92. // We will indicate the connection terminated and shut down
  93. //
  94. DPFX(DPFPREP, 5,"Server has disconnected from this client");
  95. DNUserTerminateSession(pdnObject,DPNERR_CONNECTIONLOST,NULL,0);
  96. DNTerminateSession(pdnObject,DPNERR_CONNECTIONLOST);
  97. }
  98. else
  99. {
  100. //
  101. // Another peer has disconnected from this peer
  102. // We will delete this player from the NameTable
  103. // We may have to ask the host to perform an integrity check
  104. //
  105. DPFX(DPFPREP, 5,"Peer has disconnected from this peer");
  106. if ((hResultCode = pdnObject->NameTable.FindEntry(dpnid,&pNTEntry)) != DPN_OK)
  107. {
  108. DPFERR("Could not find disconnecting player in NameTable");
  109. DisplayDNError(0,hResultCode);
  110. goto Failure;
  111. }
  112. fRequestIntegrityCheck = FALSE;
  113. pNTEntry->Lock();
  114. if (pNTEntry->IsAvailable())
  115. {
  116. fRequestIntegrityCheck = TRUE;
  117. }
  118. pNTEntry->Unlock();
  119. if (fRequestIntegrityCheck)
  120. {
  121. DNRequestIntegrityCheck(pdnObject,dpnid);
  122. }
  123. pdnObject->NameTable.DeletePlayer(dpnid,0);
  124. //
  125. // If this was the Host, clear the short-cut pointer
  126. //
  127. fWasHost = pdnObject->NameTable.ClearHostWithDPNID( dpnid );
  128. //
  129. // May need to clear HOST_MIGRATING flag
  130. //
  131. DNEnterCriticalSection(&pdnObject->csDirectNetObject);
  132. if ((pdnObject->dwFlags & DN_OBJECT_FLAG_HOST_MIGRATING) && (pdnObject->pNewHost == pNTEntry))
  133. {
  134. pdnObject->dwFlags &= ~(DN_OBJECT_FLAG_HOST_MIGRATING);
  135. pdnObject->pNewHost->Release();
  136. pdnObject->pNewHost = NULL;
  137. }
  138. DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
  139. #ifndef DPNBUILD_NOHOSTMIGRATE
  140. //
  141. // If HostMigration flag is set, check to see if we are the new Host.
  142. // Otherwise, if the disconnecting player was the Host, the session is lost.
  143. //
  144. if (pdnObject->ApplicationDesc.AllowHostMigrate())
  145. {
  146. DPFX(DPFPREP, 5,"Host-Migration was set - check for new Host");
  147. dpnidNewHost = 0;
  148. if ((hResultCode = DNFindNewHost(pdnObject,&dpnidNewHost)) == DPN_OK)
  149. {
  150. DPFX(DPFPREP, 5,"New Host [0x%lx]",dpnidNewHost);
  151. if ((hResultCode = pdnObject->NameTable.GetLocalPlayerRef(&pLocalPlayer)) == DPN_OK)
  152. {
  153. if (pLocalPlayer->GetDPNID() == dpnidNewHost)
  154. {
  155. DPFX(DPFPREP, 5,"Local player is new Host");
  156. hResultCode = DNPerformHostMigration1(pdnObject,dpnid);
  157. }
  158. pLocalPlayer->Release();
  159. pLocalPlayer = NULL;
  160. }
  161. }
  162. }
  163. else
  164. #endif // DPNBUILD_NOHOSTMIGRATE
  165. {
  166. if (fWasHost)
  167. {
  168. DPFX(DPFPREP, 5,"Host-Migration was not set - terminating session");
  169. DNUserTerminateSession(pdnObject,DPNERR_CONNECTIONLOST,NULL,0);
  170. DNTerminateSession(pdnObject,DPNERR_CONNECTIONLOST);
  171. }
  172. }
  173. pNTEntry->Release();
  174. pNTEntry = NULL;
  175. }
  176. hResultCode = DPN_OK;
  177. Exit:
  178. DPFX(DPFPREP, 4,"Returning: [0x%lx]",hResultCode);
  179. return(hResultCode);
  180. Failure:
  181. if (pNTEntry)
  182. {
  183. pNTEntry->Release();
  184. pNTEntry = NULL;
  185. }
  186. if (pLocalPlayer)
  187. {
  188. pLocalPlayer->Release();
  189. pLocalPlayer = NULL;
  190. }
  191. goto Exit;
  192. }
  193. // DNHostDisconnect
  194. //
  195. // A player has initiated a disconnect with the host.
  196. // - Remove player from the name table
  197. // - Propegate DELETE_PLAYER messages to each player
  198. #pragma TODO(minara,"Use pConnection instead of dpnidDisconnecting ?")
  199. #undef DPF_MODNAME
  200. #define DPF_MODNAME "DNHostDisconnect"
  201. HRESULT DNHostDisconnect(DIRECTNETOBJECT *const pdnObject,
  202. const DPNID dpnidDisconnecting,
  203. const DWORD dwDestroyReason)
  204. {
  205. HRESULT hResultCode;
  206. CRefCountBuffer *pRefCountBuffer;
  207. CPendingDeletion *pPending;
  208. CWorkerJob *pWorkerJob;
  209. DN_INTERNAL_MESSAGE_DESTROY_PLAYER *pMsg;
  210. DPFX(DPFPREP, 4,"Parameters: pdnObject [0x%p], dpnidDisconnecting [0x%lx], dwDestroyReason [0x%lx]",
  211. pdnObject,dpnidDisconnecting,dwDestroyReason);
  212. DNASSERT(pdnObject != NULL);
  213. DNASSERT(dpnidDisconnecting != 0);
  214. pRefCountBuffer = NULL;
  215. pPending = NULL;
  216. pWorkerJob = NULL;
  217. // Remove entry from NameTable and inform other players, only if Host is NOT migrating
  218. // Otherwise, clean-up first and wait for migration to complete before informing others
  219. if (!(pdnObject->dwFlags & DN_OBJECT_FLAG_HOST_MIGRATING))
  220. {
  221. DWORD dwVersion;
  222. CNameTableEntry *pNTEntry;
  223. dwVersion = 0;
  224. pNTEntry = NULL;
  225. if ((hResultCode = pdnObject->NameTable.FindEntry(dpnidDisconnecting,&pNTEntry)) == DPN_OK)
  226. {
  227. pNTEntry->Lock();
  228. if (pNTEntry->GetDestroyReason() == 0)
  229. {
  230. pNTEntry->SetDestroyReason( dwDestroyReason );
  231. }
  232. pNTEntry->Unlock();
  233. pNTEntry->Release();
  234. pNTEntry = NULL;
  235. }
  236. hResultCode = pdnObject->NameTable.DeletePlayer(dpnidDisconnecting,&dwVersion);
  237. if (pdnObject->dwFlags & DN_OBJECT_FLAG_PEER)
  238. {
  239. //
  240. // Prepare internal message
  241. //
  242. hResultCode = RefCountBufferNew(pdnObject,
  243. sizeof(DN_INTERNAL_MESSAGE_DESTROY_PLAYER),
  244. MemoryBlockAlloc,
  245. MemoryBlockFree,
  246. &pRefCountBuffer);
  247. if (hResultCode != DPN_OK)
  248. {
  249. DPFERR("Could not allocate message buffer");
  250. DisplayDNError(0,hResultCode);
  251. goto Failure;
  252. }
  253. pMsg = reinterpret_cast<DN_INTERNAL_MESSAGE_DESTROY_PLAYER*>(pRefCountBuffer->GetBufferAddress());
  254. pMsg->dpnidLeaving = dpnidDisconnecting;
  255. pMsg->dwVersion = dwVersion;
  256. pMsg->dwVersionNotUsed = 0;
  257. pMsg->dwDestroyReason = dwDestroyReason;
  258. if ((hResultCode = WorkerJobNew(pdnObject,&pWorkerJob)) != DPN_OK)
  259. {
  260. DPFERR("Could not create worker job");
  261. DisplayDNError(0,hResultCode);
  262. goto Failure;
  263. }
  264. pWorkerJob->SetJobType( WORKER_JOB_SEND_NAMETABLE_OPERATION );
  265. pWorkerJob->SetSendNameTableOperationMsgId( DN_MSG_INTERNAL_DESTROY_PLAYER );
  266. pWorkerJob->SetSendNameTableOperationVersion( dwVersion );
  267. pWorkerJob->SetSendNameTableOperationDPNIDExclude( dpnidDisconnecting );
  268. pWorkerJob->SetRefCountBuffer( pRefCountBuffer );
  269. DNQueueWorkerJob(pdnObject,pWorkerJob);
  270. pWorkerJob = NULL;
  271. pRefCountBuffer->Release();
  272. pRefCountBuffer = NULL;
  273. }
  274. }
  275. else
  276. {
  277. //
  278. // Put this on the Outstanding operation list
  279. //
  280. if ((hResultCode = PendingDeletionNew(pdnObject,&pPending)) == DPN_OK)
  281. {
  282. pPending->SetDPNID( dpnidDisconnecting );
  283. DNEnterCriticalSection(&pdnObject->csNameTableOpList);
  284. pPending->m_bilinkPendingDeletions.InsertBefore(&pdnObject->m_bilinkPendingDeletions);
  285. DNLeaveCriticalSection(&pdnObject->csNameTableOpList);
  286. pPending = NULL;
  287. }
  288. #ifndef DPNBUILD_NOHOSTMIGRATE
  289. // See if we can continue with Host migration
  290. DNCheckReceivedAllVersions(pdnObject);
  291. #endif // DPNBUILD_NOHOSTMIGRATE
  292. }
  293. hResultCode = DPN_OK;
  294. Exit:
  295. DPFX(DPFPREP, 4,"Returning: [0x%lx]", hResultCode);
  296. return(hResultCode);
  297. Failure:
  298. if (pRefCountBuffer)
  299. {
  300. pRefCountBuffer->Release();
  301. pRefCountBuffer = NULL;
  302. }
  303. if (pPending)
  304. {
  305. pPending->ReturnSelfToPool();
  306. pPending = NULL;
  307. }
  308. goto Exit;
  309. }
  310. // DNInstructedDisconnect
  311. //
  312. // The host has instructed the local player to delete another player from the nametable
  313. // - If already closing
  314. // - ignore this message and return
  315. // - Prepare to delete player
  316. // - Release refcount of player
  317. #undef DPF_MODNAME
  318. #define DPF_MODNAME "DNInstructedDisconnect"
  319. HRESULT DNInstructedDisconnect(DIRECTNETOBJECT *const pdnObject,
  320. PVOID pv)
  321. {
  322. HRESULT hResultCode;
  323. DWORD dwVersion;
  324. CNameTableEntry *pNTEntry;
  325. UNALIGNED DN_INTERNAL_MESSAGE_DESTROY_PLAYER *pInfo;
  326. DPFX(DPFPREP, 4,"Parameters: pv [0x%p]",pv);
  327. DNASSERT(pdnObject != NULL);
  328. DNASSERT(pv != NULL);
  329. pNTEntry = NULL;
  330. pInfo = static_cast<DN_INTERNAL_MESSAGE_DESTROY_PLAYER*>(pv);
  331. DNASSERT(pInfo != NULL);
  332. DNASSERT(pInfo->dpnidLeaving != NULL);
  333. DNASSERT(pInfo->dwVersion != 0);
  334. DPFX(DPFPREP, 5,"Deleting player [0x%lx]",pInfo->dpnidLeaving);
  335. //
  336. // If the player is still in the NameTable, we will preset the destroy reason.
  337. // We will also use this "hint" to initiate a disconnect just in case the protocol
  338. //
  339. if ((hResultCode = pdnObject->NameTable.FindEntry(pInfo->dpnidLeaving,&pNTEntry)) == DPN_OK)
  340. {
  341. CConnection *pConnection;
  342. CCallbackThread CallbackThread;
  343. HANDLE hEndPt;
  344. pConnection = NULL;
  345. CallbackThread.Initialize();
  346. hEndPt = NULL;
  347. pNTEntry->Lock();
  348. if (pNTEntry->GetDestroyReason() == 0)
  349. {
  350. pNTEntry->SetDestroyReason( pInfo->dwDestroyReason );
  351. }
  352. pNTEntry->Unlock();
  353. //
  354. // Attempt a disconnect
  355. //
  356. if ((hResultCode = pNTEntry->GetConnectionRef( &pConnection )) == DPN_OK)
  357. {
  358. if ((hResultCode = pConnection->GetEndPt(&hEndPt,&CallbackThread)) == DPN_OK)
  359. {
  360. DNPerformDisconnect(pdnObject,pConnection,hEndPt,FALSE);
  361. pConnection->ReleaseEndPt(&CallbackThread);
  362. }
  363. pConnection->Release();
  364. pConnection = NULL;
  365. }
  366. pNTEntry->Release();
  367. pNTEntry = NULL;
  368. CallbackThread.Deinitialize();
  369. }
  370. else
  371. {
  372. //
  373. // Scan oustanding op list for integrity check request for this player.
  374. // If found, remove it from the request list and the handle table
  375. //
  376. CBilink *pBilink;
  377. CAsyncOp *pAsyncOp;
  378. DN_SEND_OP_DATA *pSendOpData;
  379. pAsyncOp = NULL;
  380. DNEnterCriticalSection(&pdnObject->csActiveList);
  381. pBilink = pdnObject->m_bilinkRequestList.GetNext();
  382. while (pBilink != &pdnObject->m_bilinkRequestList)
  383. {
  384. pAsyncOp = CONTAINING_OBJECT(pBilink,CAsyncOp,m_bilinkActiveList);
  385. DNASSERT(pAsyncOp->GetOpType() == ASYNC_OP_REQUEST);
  386. pSendOpData = pAsyncOp->GetLocalSendOpData();
  387. if (pSendOpData->dwMsgId == DN_MSG_INTERNAL_REQ_INTEGRITY_CHECK)
  388. {
  389. UNALIGNED DN_INTERNAL_MESSAGE_REQ_INTEGRITY_CHECK *pMsg;
  390. pMsg = reinterpret_cast<DN_INTERNAL_MESSAGE_REQ_INTEGRITY_CHECK*>
  391. (reinterpret_cast<UNALIGNED DN_INTERNAL_MESSAGE_REQ_PROCESS_COMPLETION*>(pSendOpData->BufferDesc[1].pBufferData) + 1);
  392. if (pMsg->dpnidTarget == pInfo->dpnidLeaving)
  393. {
  394. pAsyncOp->m_bilinkActiveList.RemoveFromList();
  395. pAsyncOp->AddRef();
  396. break;
  397. }
  398. }
  399. pSendOpData = NULL;
  400. pAsyncOp = NULL;
  401. pBilink = pBilink->GetNext();
  402. }
  403. DNLeaveCriticalSection(&pdnObject->csActiveList);
  404. if (pAsyncOp != NULL)
  405. {
  406. DNASSERT(pAsyncOp->GetHandle() != 0);
  407. if (SUCCEEDED(pdnObject->HandleTable.Destroy( pAsyncOp->GetHandle(), NULL )))
  408. {
  409. // Release the HandleTable reference
  410. pAsyncOp->Release();
  411. }
  412. pAsyncOp->Release();
  413. pAsyncOp = NULL;
  414. }
  415. DNASSERT(pAsyncOp == NULL);
  416. }
  417. dwVersion = pInfo->dwVersion;
  418. pdnObject->NameTable.DeletePlayer(pInfo->dpnidLeaving,&dwVersion);
  419. //
  420. // Update NameTable version
  421. //
  422. pdnObject->NameTable.WriteLock();
  423. pdnObject->NameTable.SetVersion(pInfo->dwVersion);
  424. pdnObject->NameTable.Unlock();
  425. hResultCode = DPN_OK;
  426. DPFX(DPFPREP, 4,"Returning: [0x%lx]",hResultCode);
  427. return(hResultCode);
  428. }