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.

1286 lines
43 KiB

  1. /*==========================================================================
  2. *
  3. * Copyright (C) 1999 Microsoft Corporation. All Rights Reserved.
  4. *
  5. * File: Connect.cpp
  6. * Content: This file contains support for the CONNECT/DISCONNECT protocol in DirectNet.
  7. * It is organized with FrontEnd routines first (Connect, Listen),
  8. * and Backend handlers (timeouts, frame crackers) after.
  9. *
  10. * History:
  11. * Date By Reason
  12. * ==== == ======
  13. * 11/11/98 ejs Created
  14. * 07/01/2000 masonb Assumed Ownership
  15. *
  16. ****************************************************************************/
  17. #include "dnproti.h"
  18. /*** FRONT END ***/
  19. /*
  20. ** Connect
  21. **
  22. ** This function attempts to make a connection to a specified address.
  23. ** The function establishes the existance of a DirectNet entity and maps
  24. ** an EndPoint handle. Then we exchange CONNECT packets which allows each
  25. ** side to establish a baseline RTT.
  26. **
  27. */
  28. #undef DPF_MODNAME
  29. #define DPF_MODNAME "DNPConnect"
  30. HRESULT DNPConnect( PProtocolData pPData,
  31. IDirectPlay8Address *const lpaLocal,
  32. IDirectPlay8Address *const lpaRemote,
  33. const HANDLE hSPHandle,
  34. const ULONG ulFlags,
  35. void *const Context,
  36. PHANDLE phHandle)
  37. {
  38. PSPD pSPD; // Service Provider to handle this connect
  39. PMSD pMSD;
  40. SPCONNECTDATA ConnData; // Parameter Block
  41. HRESULT hr;
  42. // Determine which SP will take this call
  43. //
  44. DPFX(DPFPREP,DPF_CALLIN_LVL, "Parameters: pPData[%p], paLocal[%p], paRemote[%p], hSPHandle[%x], ulFlags[%x], Context[%p], phHandle[%p]", pPData, lpaLocal, lpaRemote, hSPHandle, ulFlags, Context, phHandle);
  45. pSPD = (PSPD) hSPHandle;
  46. ASSERT_SPD(pSPD);
  47. // Core should not call any Protocol APIs after calling DNPRemoveServiceProvider
  48. ASSERT(!(pSPD->ulSPFlags & SPFLAGS_TERMINATING));
  49. // We use an MSD to describe this op even though it isn't technically a message
  50. if((pMSD = static_cast<PMSD>( MSDPool->Get(MSDPool) )) == NULL)
  51. {
  52. DPFX(DPFPREP,0, "Returning DPNERR_OUTOFMEMORY - failed to create new MSD");
  53. return DPNERR_OUTOFMEMORY;
  54. }
  55. pMSD->CommandID = COMMAND_ID_CONNECT;
  56. pMSD->pSPD = pSPD;
  57. pMSD->Context = Context;
  58. ASSERT(pMSD->pEPD == NULL); // MSD_Get/Release ensures this, and IndicateConnect requires it.
  59. // Prepare to call SP to map the endpoint.
  60. ConnData.pAddressDeviceInfo = lpaLocal;
  61. ConnData.pAddressHost = lpaRemote;
  62. ConnData.dwReserved = 0; // Never used
  63. DNASSERT( ( ulFlags & ~( DN_CONNECTFLAGS_OKTOQUERYFORADDRESSING | DN_CONNECTFLAGS_ADDITIONALMULTIPLEXADAPTERS ) ) == 0 );
  64. ConnData.dwFlags = 0;
  65. if ( ( ulFlags & DN_CONNECTFLAGS_OKTOQUERYFORADDRESSING ) != 0 )
  66. {
  67. ConnData.dwFlags |= DPNSPF_OKTOQUERY;
  68. }
  69. if ( ( ulFlags & DN_CONNECTFLAGS_ADDITIONALMULTIPLEXADAPTERS ) != 0 )
  70. {
  71. ConnData.dwFlags |= DPNSPF_ADDITIONALMULTIPLEXADAPTERS;
  72. }
  73. ConnData.pvContext = pMSD;
  74. ConnData.hCommand = 0;
  75. pMSD->ulMsgFlags1 |= MFLAGS_ONE_IN_SERVICE_PROVIDER;
  76. #ifdef DEBUG
  77. // Hook up MSD before calling into SP
  78. Lock(&pSPD->SPLock);
  79. pMSD->blSPLinkage.InsertBefore( &pSPD->blMessageList); // Put this on cmd list
  80. pMSD->ulMsgFlags1 |= MFLAGS_ONE_ON_GLOBAL_LIST;
  81. Unlock(&pSPD->SPLock);
  82. #endif
  83. *phHandle = pMSD;
  84. // SP Connect call is guaranteed to return immediately
  85. LOCK_MSD(pMSD, "SP Ref"); // Add reference for call into SP
  86. LOCK_MSD(pMSD, "Temp Ref");
  87. DPFX(DPFPREP,DPF_CALLOUT_LVL, "Calling SP->Connect, pSPD[%p], pMSD[%p]", pSPD, pMSD);
  88. /**/hr = IDP8ServiceProvider_Connect(pSPD->IISPIntf, &ConnData); /** CALL SP **/
  89. if(hr != DPNERR_PENDING)
  90. {
  91. // SP Connect should always be asynchronous so if it isnt PENDING then it must have failed
  92. DPFX(DPFPREP,1, "SP->Connect did not return DPNERR_PENDING, assuming failure, hr[%x]", hr);
  93. ASSERT(hr != DPN_OK);
  94. Lock(&pMSD->CommandLock); // This will be unlocked by final RELEASE_MSD
  95. pMSD->ulMsgFlags1 &= ~(MFLAGS_ONE_IN_SERVICE_PROVIDER); // clear InSP flag
  96. #ifdef DEBUG
  97. Lock(&pSPD->SPLock);
  98. pMSD->blSPLinkage.RemoveFromList(); // knock this off the pending list
  99. pMSD->ulMsgFlags1 &= ~(MFLAGS_ONE_ON_GLOBAL_LIST);
  100. Unlock(&pSPD->SPLock);
  101. #endif
  102. DECREMENT_MSD(pMSD, "Temp Ref");
  103. DECREMENT_MSD(pMSD, "SP Ref"); // Remove one ref for SP call
  104. RELEASE_MSD(pMSD, "Release On Fail"); // Remove one ref to free resource
  105. DPFX(DPFPREP,1, "Returning hr[%x]", hr);
  106. return hr;
  107. }
  108. Lock(&pMSD->CommandLock);
  109. pMSD->hCommand = ConnData.hCommand; // retain SP command handle
  110. pMSD->dwCommandDesc = ConnData.dwCommandDescriptor;
  111. RELEASE_MSD(pMSD, "Temp Ref"); // Unlocks CommandLock
  112. DPFX(DPFPREP,DPF_CALLIN_LVL, "Returning DPNERR_PENDING, pMSD[%p]", pMSD);
  113. return DPNERR_PENDING;
  114. }
  115. /*
  116. ** Listen
  117. **
  118. ** This command tells DN that it should start to accept connection requests.
  119. ** This command will return pending, and will continue to indicate connections
  120. ** until it is explicitly cancelled. It may be desireable to establish a limit
  121. ** mechanism of some sort, but for the time being this will do.
  122. **
  123. ** Now it is desirable to Listen on multiple ports on a single adapter. This
  124. ** means that we need to accept multiple concurrent Listen commands on each adapter.
  125. ** Another fact of life is that we need to crack the Target address far enough to
  126. ** determine which SP to submit the Listen on.
  127. */
  128. #undef DPF_MODNAME
  129. #define DPF_MODNAME "DNPListen"
  130. HRESULT DNPListen( PProtocolData pPData,
  131. IDirectPlay8Address *const lpaTarget,
  132. // IDP8ServiceProvider* pISP,
  133. const HANDLE hSPHandle,
  134. ULONG ulFlags,
  135. PVOID Context,
  136. PHANDLE phHandle)
  137. {
  138. PSPD pSPD;
  139. PMSD pMSD;
  140. SPLISTENDATA ListenData;
  141. HRESULT hr;
  142. DPFX(DPFPREP,DPF_CALLIN_LVL, "Parameters: pPData[%p], paTarget[%p], hSPHandle[%x], ulFlags[%x], Context[%p], phHandle[%p]", pPData, lpaTarget, hSPHandle, ulFlags, Context, phHandle);
  143. pSPD = (PSPD) hSPHandle;
  144. ASSERT_SPD(pSPD);
  145. // Core should not call any Protocol APIs after calling DNPRemoveServiceProvider
  146. ASSERT(!(pSPD->ulSPFlags & SPFLAGS_TERMINATING));
  147. // We use an MSD to describe this op even though it isn't technically a message
  148. if((pMSD = static_cast<PMSD>( MSDPool->Get(MSDPool) )) == NULL)
  149. {
  150. DPFX(DPFPREP,0, "Returning DPNERR_OUTOFMEMORY - failed to create new MSD");
  151. return DPNERR_OUTOFMEMORY;
  152. }
  153. pMSD->CommandID = COMMAND_ID_LISTEN;
  154. pMSD->pSPD = pSPD;
  155. pMSD->Context = Context;
  156. ListenData.pAddressDeviceInfo = lpaTarget;
  157. DNASSERT( ( ulFlags & ~( DN_LISTENFLAGS_OKTOQUERYFORADDRESSING ) ) == 0 );
  158. ListenData.dwFlags = 0;
  159. if ( ( ulFlags & DN_LISTENFLAGS_OKTOQUERYFORADDRESSING ) != 0 )
  160. {
  161. ListenData.dwFlags |= DPNSPF_OKTOQUERY;
  162. }
  163. ListenData.pvContext = pMSD;
  164. ListenData.hCommand = 0;
  165. *phHandle = pMSD;
  166. // SP Listen call is guarenteed to return immediately
  167. pMSD->ulMsgFlags1 |= MFLAGS_ONE_IN_SERVICE_PROVIDER;
  168. #ifdef DEBUG
  169. Lock(&pSPD->SPLock);
  170. pMSD->blSPLinkage.InsertBefore( &pSPD->blMessageList); // Dont support timeouts for Listen
  171. pMSD->ulMsgFlags1 |= MFLAGS_ONE_ON_GLOBAL_LIST;
  172. Unlock(&pSPD->SPLock);
  173. #endif
  174. LOCK_MSD(pMSD, "SP Ref"); // AddRef for SP
  175. LOCK_MSD(pMSD, "Temp Ref");
  176. DPFX(DPFPREP,DPF_CALLOUT_LVL, "Calling SP->Listen, pSPD[%p], pMSD[%p]", pSPD, pMSD);
  177. /**/hr = IDP8ServiceProvider_Listen(pSPD->IISPIntf, &ListenData); /** CALL SP **/
  178. if(hr != DPNERR_PENDING)
  179. {
  180. // SP Listen should always be asynchronous so if it isnt PENDING then it must have failed
  181. DPFX(DPFPREP,1, "SP->Listen did not return DPNERR_PENDING, assuming failure, hr[%x]", hr);
  182. ASSERT(hr != DPN_OK);
  183. Lock(&pMSD->CommandLock);
  184. pMSD->ulMsgFlags1 &= ~(MFLAGS_ONE_IN_SERVICE_PROVIDER);
  185. #ifdef DEBUG
  186. Lock(&pSPD->SPLock);
  187. pMSD->blSPLinkage.RemoveFromList(); // knock this off the pending list
  188. pMSD->ulMsgFlags1 &= ~(MFLAGS_ONE_ON_GLOBAL_LIST);
  189. Unlock(&pSPD->SPLock);
  190. #endif
  191. DECREMENT_MSD(pMSD, "Temp Ref");
  192. DECREMENT_MSD(pMSD, "SP Ref"); // release once for SP
  193. RELEASE_MSD(pMSD, "Release On Fail"); // release again to return resource
  194. DPFX(DPFPREP,1, "Returning hr[%x]", hr);
  195. return hr;
  196. }
  197. Lock(&pMSD->CommandLock);
  198. pMSD->hCommand = ListenData.hCommand; // retail SP command handle
  199. pMSD->dwCommandDesc = ListenData.dwCommandDescriptor;
  200. RELEASE_MSD(pMSD, "Temp Ref"); // Unlocks CommandLock
  201. DPFX(DPFPREP,DPF_CALLIN_LVL, "Returning DPNERR_PENDING, pMSD[%p]", pMSD);
  202. return DPNERR_PENDING;
  203. }
  204. /*** BACKEND ROUTINES ***/
  205. /*
  206. ** Complete Connect
  207. **
  208. ** The user's Connect operation has completed. Clean everything up
  209. ** and signal the user.
  210. **
  211. ** THIS IS ALWAYS CALLED WITH THE COMMAND LOCK HELD IN MSD
  212. */
  213. #undef DPF_MODNAME
  214. #define DPF_MODNAME "CompleteConnect"
  215. VOID CompleteConnect(PMSD pMSD, PSPD pSPD, PEPD pEPD, HRESULT hr)
  216. {
  217. AssertCriticalSectionIsTakenByThisThread(&pMSD->CommandLock, TRUE);
  218. // We expect to never get here twice
  219. ASSERT(!(pMSD->ulMsgFlags1 & MFLAGS_ONE_COMPLETE));
  220. pMSD->ulMsgFlags1 |= MFLAGS_ONE_COMPLETE;
  221. // Connects cannot have timeout timers
  222. ASSERT(pMSD->TimeoutTimer == NULL);
  223. #ifdef DEBUG
  224. Lock(&pSPD->SPLock);
  225. if(pMSD->ulMsgFlags1 & MFLAGS_ONE_ON_GLOBAL_LIST)
  226. {
  227. pMSD->blSPLinkage.RemoveFromList(); // Remove MSD from master command list
  228. pMSD->ulMsgFlags1 &= ~(MFLAGS_ONE_ON_GLOBAL_LIST);
  229. }
  230. Unlock(&pSPD->SPLock);
  231. ASSERT(!(pMSD->ulMsgFlags1 & MFLAGS_ONE_COMPLETED_TO_CORE));
  232. pMSD->ulMsgFlags1 |= MFLAGS_ONE_COMPLETED_TO_CORE;
  233. pMSD->CallStackCoreCompletion.NoteCurrentCallStack();
  234. #endif
  235. pMSD->pEPD = NULL;
  236. Unlock(&pMSD->CommandLock);
  237. if(pEPD)
  238. {
  239. ASSERT(hr == DPN_OK);
  240. DPFX(DPFPREP,DPF_CALLOUT_LVL, "(%p) Calling Core->CompleteConnect, pMSD[%p], Core Context[%p], hr[%x], pEPD[%p]", pEPD, pMSD, pMSD->Context, hr, pEPD);
  241. pSPD->pPData->pfVtbl->CompleteConnect(pSPD->pPData->Parent, pMSD->Context, hr, (PHANDLE) pEPD, &pEPD->Context);
  242. Lock(&pEPD->EPLock);
  243. ReceiveComplete(pEPD); // Complete any, releases EPLock
  244. }
  245. else
  246. {
  247. ASSERT(hr != DPN_OK);
  248. DPFX(DPFPREP,DPF_CALLOUT_LVL, "Calling Core->CompleteConnect with NULL EPD, pMSD[%p], Core Context[%p], hr[%x]", pMSD, pMSD->Context, hr);
  249. pSPD->pPData->pfVtbl->CompleteConnect(pSPD->pPData->Parent, pMSD->Context, hr, NULL, NULL);
  250. }
  251. // Release the final reference on the MSD AFTER indicating to the Core
  252. Lock(&pMSD->CommandLock);
  253. RELEASE_MSD(pMSD, "Final Release On Complete"); // Finished with this one, releases CommandLock
  254. }
  255. /*
  256. ** Complete Disconnect
  257. **
  258. ** THIS IS ALWAYS CALLED WITH THE COMMAND LOCK HELD IN MSD
  259. */
  260. #undef DPF_MODNAME
  261. #define DPF_MODNAME "CompleteDisconnect"
  262. VOID CompleteDisconnect(PMSD pMSD, PSPD pSPD, PEPD pEPD)
  263. {
  264. AssertCriticalSectionIsTakenByThisThread(&pMSD->CommandLock, TRUE);
  265. // We expect to never get here twice
  266. ASSERT(!(pMSD->ulMsgFlags1 & MFLAGS_ONE_COMPLETE));
  267. pMSD->ulMsgFlags1 |= MFLAGS_ONE_COMPLETE;
  268. // Disconnects cannot have timeout timers
  269. ASSERT(pMSD->TimeoutTimer == NULL);
  270. #ifdef DEBUG
  271. Lock(&pSPD->SPLock);
  272. if(pMSD->ulMsgFlags1 & MFLAGS_ONE_ON_GLOBAL_LIST)
  273. {
  274. pMSD->blSPLinkage.RemoveFromList(); // Remove MSD from master command list
  275. pMSD->ulMsgFlags1 &= ~(MFLAGS_ONE_ON_GLOBAL_LIST);
  276. }
  277. Unlock(&pSPD->SPLock);
  278. ASSERT(!(pMSD->ulMsgFlags1 & MFLAGS_ONE_COMPLETED_TO_CORE));
  279. pMSD->ulMsgFlags1 |= MFLAGS_ONE_COMPLETED_TO_CORE;
  280. pMSD->CallStackCoreCompletion.NoteCurrentCallStack();
  281. #endif
  282. // No one else should use this
  283. pMSD->pEPD = NULL;
  284. if(pMSD->CommandID == COMMAND_ID_DISCONNECT)
  285. {
  286. Unlock(&pMSD->CommandLock);
  287. DPFX(DPFPREP,DPF_CALLOUT_LVL, "(%p) Calling Core->CompleteDisconnect, DPN_OK, pMSD[%p], Core Context[%p]", pEPD, pMSD, pMSD->Context);
  288. pSPD->pPData->pfVtbl->CompleteDisconnect(pSPD->pPData->Parent, pMSD->Context, DPN_OK);
  289. }
  290. else
  291. {
  292. Unlock(&pMSD->CommandLock);
  293. ASSERT(pMSD->CommandID == COMMAND_ID_DISC_RESPONSE);
  294. DPFX(DPFPREP,DPF_CALLOUT_LVL, "(%p) Calling Core->IndicateConnectionTerminated, DPN_OK, Core Context[%p]", pEPD, pEPD->Context);
  295. pSPD->pPData->pfVtbl->IndicateConnectionTerminated(pSPD->pPData->Parent, pEPD->Context, DPN_OK);
  296. }
  297. Lock(&pMSD->CommandLock);
  298. Lock(&pEPD->EPLock);
  299. RELEASE_EPD(pEPD, "UNLOCK (DISC COMMAND)"); // release hold on EPD, releases EPLock
  300. RELEASE_MSD(pMSD, "Final Release On Complete"); // Finished with this one, releases CommandLock
  301. }
  302. /*
  303. ** Complete SP Connect
  304. **
  305. ** A Connect Command has completed in the Service Provider. This does not mean our
  306. ** work is done... this means we now have a mapped EndPoint so we can exchange packets
  307. ** with this partner. We will now ping this partner to get an initial RTT and make sure
  308. ** there really is a protocol over there that will talk to us.
  309. **
  310. ** Of course, if SP does not return success then we can nip this whole thing in
  311. ** the proverbial bud.
  312. **
  313. ** ** COMMAND LOCK is held on entry **
  314. */
  315. #undef DPF_MODNAME
  316. #define DPF_MODNAME "CompleteSPConnect"
  317. VOID CompleteSPConnect(PMSD pMSD, PSPD pSPD, HRESULT hr)
  318. {
  319. PEPD pEPD;
  320. DPFX(DPFPREP,5, "SP Completes Connect, pMSD[%p])", pMSD);
  321. AssertCriticalSectionIsTakenByThisThread(&pMSD->CommandLock, TRUE);
  322. pEPD = pMSD->pEPD;
  323. ASSERT(!(pMSD->ulMsgFlags1 & MFLAGS_ONE_COMPLETE));
  324. if(hr != DPN_OK)
  325. {
  326. // This will only happen once since DoCancel will not have done it because the IN_SP flag was set, and
  327. // ConnectRetryTimeout has never yet been set.
  328. if (pEPD)
  329. {
  330. ASSERT_EPD(pEPD);
  331. Lock(&pEPD->EPLock);
  332. // Unlink EPD from MSD
  333. ASSERT(pEPD->pCommand == pMSD);
  334. pEPD->pCommand = NULL;
  335. DECREMENT_MSD(pMSD, "EPD Ref"); // Release Reference from EPD
  336. DropLink(pEPD); // This releases the EPLock
  337. }
  338. DPFX(DPFPREP,5, "SP failed Connect, completing Connect, pMSD[%p], hr[%x]", pMSD, hr);
  339. CompleteConnect(pMSD, pSPD, NULL, hr); // SP failed the connect call
  340. return;
  341. }
  342. // After a successful connect, we should have an endpoint
  343. pEPD = pMSD->pEPD;
  344. ASSERT_EPD(pEPD);
  345. Lock(&pEPD->EPLock);
  346. // The endpoint should have already been linked to this MSD
  347. ASSERT(pEPD->pCommand == pMSD);
  348. if(pMSD->ulMsgFlags1 & MFLAGS_ONE_CANCELLED)
  349. {
  350. // We get here when someone called DoCancel while we were still in the SP. As above:
  351. // This will only happen once since DoCancel will not have done it because the IN_SP flag was set, and
  352. // ConnectRetryTimeout has never yet been set.
  353. // Unlink EPD from MSD
  354. ASSERT(pEPD->pCommand == pMSD);
  355. pEPD->pCommand = NULL;
  356. DECREMENT_MSD(pMSD, "EPD Ref"); // Release Reference from EPD
  357. DropLink(pEPD); // This releases the EPLock
  358. DPFX(DPFPREP,5, "(%p) Command is cancelled or timed out, Complete Connect, pMSD[%p]", pEPD, pMSD);
  359. CompleteConnect(pMSD, pSPD, NULL, DPNERR_USERCANCEL);
  360. return;
  361. }
  362. // Set up End Point Data /////////////////////////////////////////////////////////////////
  363. // Transition state
  364. ASSERT(pEPD->ulEPFlags & EPFLAGS_STATE_DORMANT);
  365. pEPD->ulEPFlags &= ~(EPFLAGS_STATE_DORMANT);
  366. pEPD->ulEPFlags |= EPFLAGS_STATE_CONNECTING;
  367. // Send CONNECT
  368. pEPD->dwSessID = pSPD->pPData->dwNextSessID++;
  369. DPFX(DPFPREP,5, "(%p) Sending CONNECT Frame", pEPD);
  370. (void) SendCommandFrame(pEPD, FRAME_EXOPCODE_CONNECT, 0);
  371. // Set timer for reply, then wait for reply or TO.
  372. pEPD->uiRetryTimeout = pSPD->pPData->dwConnectTimeout;
  373. pEPD->uiRetryCount = pSPD->pPData->dwConnectRetries;
  374. LOCK_EPD(pEPD, "LOCK (CONN Retry Timer)"); // Create reference for timer
  375. DPFX(DPFPREP,5, "(%p) Setting Connect Retry Timer", pEPD);
  376. SetMyTimer(pEPD->uiRetryTimeout, 100, ConnectRetryTimeout, (PVOID) pEPD, &pEPD->ConnectTimer, &pEPD->ConnectTimerUnique);
  377. Unlock(&pEPD->EPLock);
  378. Unlock(&pMSD->CommandLock);
  379. }
  380. /*
  381. ** Connect Retry Timeout
  382. **
  383. ** Retry timer has expired on a Connect operation. This one function
  384. ** is shared by Calling and Listening partners. Complexity is due to the
  385. ** fact that cancel code cannot always ensure that this handler will not
  386. ** run, so there are flags to signal various edge conditions (cancel, abort,
  387. ** completion, high-level timeout).
  388. */
  389. #undef DPF_MODNAME
  390. #define DPF_MODNAME "ConnectRetryTimeout"
  391. VOID CALLBACK
  392. ConnectRetryTimeout(PVOID pvHandle, UINT uiUnique, PVOID pvUser)
  393. {
  394. PMSD pMSD;
  395. PEPD pEPD = (PEPD) pvUser;
  396. DPFX(DPFPREP,5, "ENTER Connect Retry Timeout pEPD=%p", pEPD);
  397. ASSERT_EPD(pEPD);
  398. Lock(&pEPD->EPLock);
  399. if((pEPD->ConnectTimer != pvHandle)||(pEPD->ConnectTimerUnique != uiUnique))
  400. {
  401. // Timer been reset! This is a spurious fire and should be ignored.
  402. RELEASE_EPD(pEPD, "UNLOCK: (Spurious (ie late) firing of CONNECT timer)"); // releases EPLock
  403. DPFX(DPFPREP,7, "(%p) Ignoring late CONNECT timer", pEPD);
  404. return;
  405. }
  406. pMSD = pEPD->pCommand;
  407. if(pMSD == NULL)
  408. {
  409. pEPD->ConnectTimer = 0;
  410. RELEASE_EPD(pEPD, "UNLOCK: (Conn retry timer - after completion)"); // releases EPLock
  411. return;
  412. }
  413. ASSERT_MSD(pMSD);
  414. // Make sure this doesn't go away when we leave the lock
  415. LOCK_MSD(pMSD, "Hold For Lock");
  416. Unlock(&pEPD->EPLock); // Release before taking higher level lock
  417. // Take both locks in the proper order
  418. Lock(&pMSD->CommandLock);
  419. Lock(&pEPD->EPLock);
  420. pEPD->ConnectTimer = 0;
  421. // This timer should only be running in a CONNECTING state
  422. if( (pMSD->ulMsgFlags1 & (MFLAGS_ONE_CANCELLED | MFLAGS_ONE_COMPLETE)) ||
  423. (!(pEPD->ulEPFlags & EPFLAGS_STATE_CONNECTING)))
  424. {
  425. RELEASE_MSD(pMSD, "Hold for lock"); // Remove temporary reference and release lock
  426. RELEASE_EPD(pEPD, "UNLOCK (Conn Retry Timer)"); // Remove reference for timer, releases EPLock
  427. return; // and thats all for now
  428. }
  429. // IF more retries are allowed and command is still active, send another CONNECT frame
  430. if(pEPD->uiRetryCount-- > 0)
  431. {
  432. pEPD->uiRetryTimeout = min(pEPD->uiRetryTimeout * 2, 5000); // exp backoff to a max of 5000ms until we establish our first RTT
  433. if(pMSD->CommandID == COMMAND_ID_CONNECT)
  434. {
  435. DPFX(DPFPREP,5, "(%p) Sending CONNECT Frame", pEPD);
  436. (void) SendCommandFrame(pEPD, FRAME_EXOPCODE_CONNECT, 0);
  437. }
  438. // Listen -- retry CONNECTED frame
  439. else
  440. {
  441. pEPD->ulEPFlags |= EPFLAGS_CHECKPOINT_INIT; // We will expect a reply to this frame
  442. DPFX(DPFPREP,5, "(%p) Sending CONNECTED Frame", pEPD);
  443. (void) SendCommandFrame(pEPD, FRAME_EXOPCODE_CONNECTED, 0);
  444. }
  445. // Send the next ping
  446. DPFX(DPFPREP,7, "(%p) Setting Connect Retry Timer", pEPD);
  447. SetMyTimer(pEPD->uiRetryTimeout, 100, ConnectRetryTimeout, (PVOID) pEPD, &pEPD->ConnectTimer, &pEPD->ConnectTimerUnique);
  448. Unlock(&pEPD->EPLock);
  449. RELEASE_MSD(pMSD, "Hold for lock"); // Remove temporary reference and release lock
  450. // Since we have re-started timer, we don't adjust refcount
  451. }
  452. else
  453. {
  454. // We got no response and timed out.
  455. if(pMSD->CommandID == COMMAND_ID_CONNECT)
  456. {
  457. DECREMENT_EPD(pEPD, "UNLOCK: (Connect Timer (Failure Path))");// Dec Ref for this timer, releases EPLock
  458. // This will only happen once since we know DoCancel has not been called due to our CANCELLED check above,
  459. // and if it is now called it will see our CANCELLED flag set below. We also know that the success case hasn't
  460. // happened or COMPLETE above would have been set. We also have not had two timers get here because of the
  461. // CANCELLED check above and set here.
  462. pMSD->ulMsgFlags1 |= MFLAGS_ONE_CANCELLED;
  463. // Unlink EPD from MSD
  464. ASSERT(pEPD->pCommand == pMSD);
  465. pEPD->pCommand = NULL;
  466. DECREMENT_MSD(pMSD, "EPD Ref"); // Release Reference from EPD
  467. DropLink(pEPD);// Releases EPLock
  468. DECREMENT_MSD(pMSD, "Hold for lock"); // Remove temporary reference
  469. DPFX(DPFPREP,1, "(%p) Connect retries exhausted, completing Connect, pMSD[%p]", pEPD, pMSD);
  470. CompleteConnect(pMSD, pMSD->pSPD, NULL, DPNERR_NORESPONSE); // releases CommandLock
  471. }
  472. // Listen - clean up associated state info, then blow away end point
  473. else
  474. {
  475. DPFX(DPFPREP,1, "(%p) Connect retries exhausted on Listen, Kill Connection, pMSD[%p]", pEPD, pMSD);
  476. if(pEPD->ulEPFlags & EPFLAGS_LINKED_TO_LISTEN)
  477. {
  478. pEPD->ulEPFlags &= ~(EPFLAGS_LINKED_TO_LISTEN);
  479. pEPD->blSPLinkage.RemoveFromList(); // Unlink EPD from Listen Queue
  480. }
  481. ASSERT(pEPD->pCommand != NULL);
  482. pEPD->pCommand = NULL; // Unlink listen from EPD
  483. DECREMENT_MSD(pMSD, "EPD Ref"); // release reference for link to EPD
  484. RELEASE_MSD(pMSD, "Hold for lock"); // Remove temporary reference and release lock
  485. DECREMENT_EPD(pEPD, "UNLOCK: (Connect Timer (Failure Path))");// Dec Ref for this timer, SPLock not already held
  486. DropLink(pEPD);
  487. }
  488. }
  489. }
  490. /*
  491. ** Process Connection Request
  492. **
  493. ** Somebody wants to connect to us. If we have a listen posted we will
  494. ** fill out a checkpoint structure to correlate his response and we will fire
  495. ** off a CONNECTED frame ourselves
  496. **
  497. ** Since our connection will not be up until we receive a CONNECTED response
  498. ** to our response we will need to set up a retry timer ourselves.
  499. */
  500. #undef DPF_MODNAME
  501. #define DPF_MODNAME "ProcessConnectRequest"
  502. VOID ProcessConnectRequest(PSPD pSPD, PEPD pEPD, PCFRAME pCFrame)
  503. {
  504. PMSD pMSD = NULL;
  505. DPFX(DPFPREP,DPF_CALLIN_LVL, "CONNECT REQUEST RECEIVED; EPD=%p SessID=%x", pEPD, pCFrame->dwSessID);
  506. Lock(&pEPD->EPLock);
  507. if((pMSD = pEPD->pCommand) == NULL)
  508. {
  509. // There are two cases: we are a connecting endpoint or we are a listening endpoint. In the connecting
  510. // case we will fail in the following 'if' because we do not allow connections on non-listening endpoints.
  511. // In the listening case, the fact that pMSD is NULL means that we have received the other side's
  512. // CONNECTED packet, which also tells us that they have seen our CONNECTED packet. The only reason we
  513. // would now be seeing a CONNECT is if a) there was a stale, late-delivered packet on the wire, or b)
  514. // some malicious user is spoofing packets to us. In both cases, ignoring the packet is the right
  515. // thing to do.
  516. // There is a third possibility for the listening endpoint case, and that is that the other side went down
  517. // and we didn't realize it, and they are now attempting to reconnect. The best we can do is wait the full
  518. // timeout until the link is torn down on our side, and let their retries make the connection for them at
  519. // that time. If we cut the timeout short, we have no way to know that a malicious user can't force
  520. // legitimate connections closed by spoofing CONNECT packets.
  521. DPFX(DPFPREP,1, "(%p) CONNECT Frame received on CONNECTED link, ignoring", pEPD);
  522. DNASSERTX(FALSE, 3);
  523. Unlock(&pEPD->EPLock);
  524. return;
  525. }
  526. ASSERT_MSD(pMSD);
  527. LOCK_MSD(pMSD, "LOCK: Hold For Lock"); // Place reference on Cmd until we can lock it
  528. Unlock(&pEPD->EPLock);
  529. Lock(&pMSD->CommandLock);
  530. Lock(&pEPD->EPLock); // Serialize access to EPD (this may not really be new sess)
  531. // Make sure this endpoint was listening for connections
  532. // This could be a Connect in which case this is a malicious packet. It could also be a Disonnect or Disconnect Response
  533. // if the endpoint is being disconnected. In any case we just need to ignore the connect packet.
  534. if(pMSD->CommandID != COMMAND_ID_LISTEN)
  535. {
  536. DPFX(DPFPREP,1, "(%p) PROTOCOL RECEIVED CONNECT REQUEST ON A NON-LISTENING ENDPOINT, IGNORING, pMSD[%p]", pEPD, pMSD);
  537. DNASSERTX(FALSE, 3);
  538. Unlock(&pEPD->EPLock);
  539. RELEASE_MSD(pMSD, "UNLOCK: Hold For Lock");
  540. return;
  541. }
  542. // Make sure we can work with this version
  543. if((pCFrame->dwVersion >> 16) != (DNET_VERSION_NUMBER >> 16))
  544. {
  545. DPFX(DPFPREP,1, "(%p) PROTOCOL RECEIVED CONNECT REQUEST FROM AN INCOMPATIBLE VERSION(theirs %x, ours %x), DROPPING LINK", pEPD, pCFrame->dwVersion, DNET_VERSION_NUMBER);
  546. DNASSERTX(FALSE, 2);
  547. RELEASE_MSD(pMSD, "UNLOCK: Hold For Lock");
  548. RejectInvalidPacket(pEPD, TRUE); // This releases the EPLock
  549. return;
  550. }
  551. // Make sure the listen command is still valid
  552. if(pMSD->ulMsgFlags1 & MFLAGS_ONE_CANCELLED)
  553. {
  554. DPFX(DPFPREP,1, "(%p) PROTOCOL RECEIVED CONNECT REQUEST ON A LISTEN THAT IS CANCELLED, DROPPING LINK, pMSD[%p]", pEPD, pMSD);
  555. RELEASE_MSD(pMSD, "UNLOCK: Hold For Lock");
  556. RejectInvalidPacket(pEPD, TRUE); // This releases the EPLock
  557. return;
  558. }
  559. // We shouldn't use the pMSD past here since this will unlock it
  560. RELEASE_MSD(pMSD, "UNLOCK: Hold For Lock");
  561. // Are we already connected?
  562. if(pEPD->ulEPFlags & (EPFLAGS_STATE_CONNECTED | EPFLAGS_STATE_TERMINATING))
  563. {
  564. DPFX(DPFPREP,1, "(%p) CONNECT Frame received on Connected or Terminating link, ignoring", pEPD);
  565. // If connection has been completed then we don't need to do more work
  566. // This can happen if we failed to cancel the connect timer in ProcessConnectedResponse and a CONNECT packet
  567. // comes in.
  568. Unlock(&pEPD->EPLock);
  569. return;
  570. }
  571. // If we are already in a CONNECTING state then this is not the first CONNECT frame we have seen. If the SessID's
  572. // match, then the partner probably didn't hear our first response, so we will resend it. If the SessID's don't
  573. // match, then either the partner aborted and is starting with a new SessID, or a malicious party is spoofing the
  574. // partner's address, and sending a bogus packet. In either of these cases we will ignore the connect. A partner
  575. // aborting a connect mid-way probably crashed anyway, and waiting for us to timeout the first connect attempt
  576. // will be the least of their worries.
  577. if(pEPD->ulEPFlags & EPFLAGS_STATE_CONNECTING)
  578. {
  579. if(pCFrame->dwSessID != pEPD->dwSessID)
  580. {
  581. DPFX(DPFPREP,1, "(%p) Received non-matching SessionID, ignoring CONNECT", pEPD);
  582. Unlock(&pEPD->EPLock);
  583. return;
  584. }
  585. // Unexpected CONNECT Frame has same Session ID. Partner probably lost our response. We will
  586. // respond again to this one.
  587. DPFX(DPFPREP,1, "(%p) Received duplicate CONNECT request. Sending another response...", pEPD);
  588. // Listen side must set this before sending CONNECTED
  589. pEPD->ulEPFlags |= EPFLAGS_CHECKPOINT_INIT; // We will expect a reply to this frame
  590. // If this fails they will be sending another CONNECT anyway, so we do nothing
  591. (void) SendCommandFrame(pEPD, FRAME_EXOPCODE_CONNECTED, pCFrame->bMsgID);
  592. Unlock(&pEPD->EPLock);
  593. return;
  594. }
  595. // Transition state
  596. ASSERT(pEPD->ulEPFlags & EPFLAGS_STATE_DORMANT);
  597. pEPD->ulEPFlags &= ~(EPFLAGS_STATE_DORMANT);
  598. pEPD->ulEPFlags |= EPFLAGS_STATE_CONNECTING;
  599. pEPD->dwSessID = pCFrame->dwSessID; // Use this SessID in all C-traffic
  600. pEPD->ulEPFlags |= EPFLAGS_CHECKPOINT_INIT; // We will expect a reply to this frame
  601. DPFX(DPFPREP,5, "(%p) Sending CONNECTED Frame", pEPD);
  602. (void) SendCommandFrame(pEPD, FRAME_EXOPCODE_CONNECTED, pCFrame->bMsgID);
  603. pEPD->uiRetryTimeout = pSPD->pPData->dwConnectTimeout; // Got to start somewhere
  604. pEPD->uiRetryCount = pSPD->pPData->dwConnectRetries; // w/exponential wait
  605. LOCK_EPD(pEPD, "LOCK: (CONNECT RETRY TIMER)"); // Create reference for timer
  606. DPFX(DPFPREP,5, "(%p) Setting Connect Timer", pEPD);
  607. SetMyTimer(pEPD->uiRetryTimeout, 100, ConnectRetryTimeout, (PVOID) pEPD, &pEPD->ConnectTimer, &pEPD->ConnectTimerUnique);
  608. if (pEPD->ConnectTimer == 0)
  609. {
  610. DPFX(DPFPREP,1, "(%p) Setting Connect Retry Timer failed", pEPD);
  611. // If we can't even set timers due to low memory, then the best we can do is
  612. // abandon this new connection and hope to give good service to our existing
  613. // connections.
  614. DECREMENT_EPD(pEPD, "UNLOCK: (CONNECT RETRY TIMER)");
  615. pEPD->ulEPFlags &= ~(EPFLAGS_LINKED_TO_LISTEN);
  616. pEPD->blSPLinkage.RemoveFromList(); // Unlink EPD from Listen Queue
  617. // Unlink the MSD from the EPD
  618. ASSERT(pEPD->pCommand == pMSD);
  619. pEPD->pCommand = NULL;
  620. DropLink(pEPD); // This releases EPLock
  621. Lock(&pMSD->CommandLock);
  622. RELEASE_MSD(pMSD, "EPD Ref");
  623. return;
  624. }
  625. Unlock(&pEPD->EPLock);
  626. }
  627. /*
  628. ** Process Connected Response
  629. **
  630. ** A response to a connection request has arrived (or a response to
  631. ** our connection response). Now the connection is officially up (on
  632. ** our end of the circuit). Set the link-state according to our first
  633. ** RTT sample and get ready to party.
  634. **
  635. ** If we are the originating party, we will want to send a
  636. ** CONNECTED frame to our partner, even though the connection is
  637. ** complete from our perspective. This will allow partner to establish
  638. ** his baseline RTT and clock bias as we can do here. In this case, he
  639. ** will have his POLL bit set in the frame we just received.
  640. **
  641. ** Now, we might get additional CONNECTED frames after the first one
  642. ** where we startup the link. This would most likely be due to our CONNECTED
  643. ** response getting lost. So if we get a CONNECTED frame with POLL set
  644. ** after our link is up, we will just go ahead and respond again without
  645. ** adjusting our state.
  646. **
  647. ** Note about Locks:
  648. **
  649. ** This code is complicated by the precedence of CritSec ownership. To simplify
  650. ** as much as possible we will take the Listen command lock at the very start of the
  651. ** procedure (when appropriate) because it has the highest level lock. This prevents
  652. ** us from completing the whole connection process and then finding that the Listen
  653. ** went away so we can't indicate it to the user.
  654. **
  655. ** We keep a RefCnt on the Listen so it won't go away while a new session
  656. ** is pending on it.
  657. */
  658. #undef DPF_MODNAME
  659. #define DPF_MODNAME "ProcessConnectedResponse"
  660. VOID ProcessConnectedResponse(PSPD pSPD, PEPD pEPD, PCFRAME pCFrame, DWORD tNow)
  661. {
  662. PCHKPT pCP;
  663. PMSD pMSD;
  664. DPFX(DPFPREP,DPF_CALLIN_LVL, "CONNECT RESPONSE RECEIVED (pEPD=0x%p)", pEPD);
  665. Lock(&pEPD->EPLock);
  666. // If the link has not seen a CONNECT or issued a CONNECT, we do not expect a CONNECTED.
  667. // Since this is the only reason this EPD was created, we will tear it down by rejecting
  668. // the connection.
  669. if (pEPD->ulEPFlags & EPFLAGS_STATE_DORMANT)
  670. {
  671. DPFX(DPFPREP,1, "(%p) CONNECTED response received on a dormant link, dropping link", pEPD);
  672. RejectInvalidPacket(pEPD, TRUE); // This will release the EPLock
  673. return;
  674. }
  675. // There is a possibility that the link is in the terminating state. If so, we don't
  676. // care about CONNECTED packets.
  677. if (pEPD->ulEPFlags & EPFLAGS_STATE_TERMINATING)
  678. {
  679. DPFX(DPFPREP,1, "(%p) CONNECTED response received on a terminating link, ignoring", pEPD);
  680. Unlock(&pEPD->EPLock);
  681. return;
  682. }
  683. // At this point either we are connecting and this is a response, or someone has connected to us, and this is
  684. // the reply to our response. Note that this could be a duplicate of one of these cases, so we may already
  685. // be in a connected state. There is also the possiblity that these are malicious packets.
  686. ASSERT(pEPD->ulEPFlags & (EPFLAGS_STATE_CONNECTING | EPFLAGS_STATE_CONNECTED));
  687. // If the SessID does not match, ignore the CONNECTED packet. This is either a malicious attempt at messing
  688. // up a legitimate connection, or our partner aborted and has come back in with a new session ID.
  689. if(pEPD->dwSessID != pCFrame->dwSessID)
  690. {
  691. DPFX(DPFPREP,1, "(%p) CONNECTED response has bad SessID, ignoring", pEPD);
  692. Unlock(&pEPD->EPLock);
  693. return;
  694. }
  695. // If we have completed our side of the connection then our only responsibility is to send responses
  696. // if our partner is still POLLING us.
  697. if(pEPD->ulEPFlags & EPFLAGS_STATE_CONNECTED)
  698. {
  699. // A Listen will set POLL on the CONNECTED packet, a Connect will not
  700. if(pCFrame->bCommand & PACKET_COMMAND_POLL)
  701. {
  702. // This side must be a listen unless this packet is malicious
  703. DPFX(DPFPREP,5, "(%p) Duplicate CONNECTED frame, sending another response...", pEPD);
  704. // If this fails we will let the partner's retry catch it.
  705. (void) SendCommandFrame(pEPD, FRAME_EXOPCODE_CONNECTED, pCFrame->bMsgID);
  706. }
  707. Unlock(&pEPD->EPLock);
  708. return;
  709. }
  710. // Since we are not CONNECTED yet, we must be in a CONNECTING state in order to receive a
  711. // CONNECTED response.
  712. ASSERT(pEPD->ulEPFlags & EPFLAGS_STATE_CONNECTING);
  713. // MSD should not be NULL if we are in the CONNECTING state
  714. pMSD = pEPD->pCommand;
  715. ASSERT_MSD(pMSD);
  716. LOCK_MSD(pMSD, "Hold For Lock"); // Place reference on Cmd until we can lock it
  717. Unlock(&pEPD->EPLock);
  718. Lock(&pMSD->CommandLock);
  719. Lock(&pEPD->EPLock);
  720. // Since we left the EPLock, we must verify that we are still in the connecting state
  721. if (!(pEPD->ulEPFlags & EPFLAGS_STATE_CONNECTING))
  722. {
  723. DPFX(DPFPREP,1, "(%p) EPD left the CONNECTING state while we were out of the lock, ignoring CONNECTED frame", pEPD);
  724. Unlock(&pEPD->EPLock);
  725. RELEASE_MSD(pMSD, "Hold For Lock"); // This releases the command lock
  726. return;
  727. }
  728. if(pMSD->ulMsgFlags1 & (MFLAGS_ONE_CANCELLED | MFLAGS_ONE_COMPLETE))
  729. {
  730. DPFX(DPFPREP,1, "(%p) Connect/Listen command cancelled or complete, ignoring CONNECTED frame", pEPD);
  731. // Whoever cancelled the Listen should be disconnecting this connection too
  732. // so all we have to do here is bail out.
  733. Unlock(&pEPD->EPLock);
  734. RELEASE_MSD(pMSD, "Hold For Lock"); // This releases the command lock
  735. return;
  736. }
  737. // Next, take care of this guy's reply if we still owe him one
  738. // A Listen will set POLL on the CONNECTED packet, a Connect will not
  739. if(pCFrame->bCommand & PACKET_COMMAND_POLL)
  740. {
  741. // This side must be a listen unless this packet is malicious
  742. DPFX(DPFPREP,5, "(%p) Sending CONNECTED Frame", pEPD);
  743. if(SendCommandFrame(pEPD, FRAME_EXOPCODE_CONNECTED, pCFrame->bMsgID) != DPN_OK)
  744. {
  745. DPFX(DPFPREP,5, "(%p) Sending CONNECTED Frame Failed", pEPD);
  746. // We cannot complete the connection... we will just let things time out
  747. RELEASE_MSD(pMSD, "Hold For Lock");
  748. Unlock(&pEPD->EPLock); // Protect the pCommand field
  749. return;
  750. }
  751. }
  752. // Now we can setup our new link, but only if this frame Correlates to a checkpoint we have outstanding
  753. // so we can seed our state variables.
  754. // Can we correlate resp?
  755. if((pCP = LookupCheckPoint(pEPD, pCFrame->bRspID)) != NULL)
  756. {
  757. // We are connected, so shut off retry timer
  758. if(pEPD->ConnectTimer != 0)
  759. {
  760. DPFX(DPFPREP,5, "(%p) Cancelling Connect Timer", pEPD);
  761. if(CancelMyTimer(pEPD->ConnectTimer, pEPD->ConnectTimerUnique) == DPN_OK)
  762. {
  763. DECREMENT_EPD(pEPD, "UNLOCK: (Conn Retry Timer - Connect Complete)"); // remove reference for timer, SPLock not already held
  764. }
  765. else
  766. {
  767. DPFX(DPFPREP,5, "(%p) Cancelling Connect Timer Failed", pEPD);
  768. }
  769. pEPD->ConnectTimer = 0; // This will prevent timer from trying to do any work if it couldn't cancel
  770. }
  771. // Transition state
  772. ASSERT(pEPD->ulEPFlags & EPFLAGS_STATE_CONNECTING);
  773. pEPD->ulEPFlags &= ~(EPFLAGS_STATE_CONNECTING);
  774. pEPD->ulEPFlags |= EPFLAGS_STATE_CONNECTED | EPFLAGS_STREAM_UNBLOCKED;// Link is open for business
  775. DPFX(DPFPREP,1, "(%p) Partner Reported Version: %x, Our Version: %x, Initial RTT %d - %d = %d", pEPD, pCFrame->dwVersion, DNET_VERSION_NUMBER, tNow, pCP->tTimestamp, tNow - pCP->tTimestamp);
  776. if(pEPD->ulEPFlags & EPFLAGS_LINKED_TO_LISTEN)
  777. {
  778. pEPD->ulEPFlags &= ~(EPFLAGS_LINKED_TO_LISTEN);
  779. pEPD->blSPLinkage.RemoveFromList(); // Unlink EPD from Listen Queue
  780. }
  781. InitLinkParameters(pEPD, (tNow - pCP->tTimestamp), sizeof(CFRAME), (pCP->tTimestamp - pCFrame->tTimestamp), tNow);
  782. ChkPtPool->Release(ChkPtPool, pCP);
  783. DPFX(DPFPREP,5, "(%p) N(R) = 0, N(S) = 0", pEPD);
  784. pEPD->bNextSend = 0;
  785. pEPD->bNextReceive = 0;
  786. FlushCheckPoints(pEPD); // Make sure we do this before the InitCheckPoint
  787. /*
  788. ** It turns out that the first RTT measurement is a very bad one (slow) because because
  789. ** it includes overhead for opening and binding a new socket, endpoint creation, etc.
  790. ** Therefore each side will take another quick sample right away. The initial calculations
  791. ** above will still serve as an initial RTT until this better sample is available
  792. */
  793. PerformCheckpoint(pEPD); // Take another RTT sample
  794. // Cleanup connect operation
  795. if(pMSD->CommandID == COMMAND_ID_CONNECT)
  796. {
  797. // There was a CONNECT Command issued that now must be completed
  798. DECREMENT_MSD(pMSD, "Hold For Lock"); // Remove temporary reference from above.
  799. // This will not happen twice because both COMPLETE and CANCELLED are checked above, and the
  800. // call to CompleteConnect will set COMPLETE.
  801. // Unlink the MSD from the EPD
  802. ASSERT(pEPD->pCommand == pMSD);
  803. pEPD->pCommand = NULL;
  804. DECREMENT_MSD(pMSD, "EPD Ref"); // Release reference for EPD link
  805. Unlock(&pEPD->EPLock);
  806. CompleteConnect(pMSD, pSPD, pEPD, DPN_OK); // This releases the MSD Lock
  807. }
  808. else
  809. { // LISTENING
  810. // We were the listener. We will indicate a Connect event on the listen
  811. // command w/o completing the Listen
  812. ASSERT(pMSD->CommandID == COMMAND_ID_LISTEN);
  813. // We know this will only happen once because the person who does it will transition us out
  814. // of the CONNECTING state, and we can't get here unless we are in that state.
  815. // Unlink the MSD from the EPD
  816. ASSERT(pEPD->pCommand == pMSD);
  817. pEPD->pCommand = NULL;
  818. DECREMENT_MSD(pMSD, "EPD Ref"); // Release reference for EPD link
  819. Unlock(&pEPD->EPLock);
  820. Unlock(&pMSD->CommandLock);
  821. DPFX(DPFPREP,DPF_CALLOUT_LVL, "(%p) Calling Core->IndicateConnect, pMSD[%p], Core Context[%p]", pEPD, pMSD, pMSD->Context);
  822. pSPD->pPData->pfVtbl->IndicateConnect(pSPD->pPData->Parent, pMSD->Context, (PHANDLE) pEPD, &pEPD->Context);
  823. // Complete any receives that queued while waiting for IndicateConnect
  824. Lock(&pEPD->EPLock);
  825. ReceiveComplete(pEPD); // releases EPLock
  826. // Release the final reference on the MSD AFTER indicating to the Core
  827. Lock(&pMSD->CommandLock);
  828. RELEASE_MSD(pMSD, "Hold For Lock"); // release temp MSD (releases lock)
  829. }
  830. }
  831. else
  832. {
  833. /*
  834. ** Uncorrelated CONNECTED frame. How can this happen? Parter's response must
  835. ** have been dropped, so he is retrying his CONN frame. Since we are trying to
  836. ** measure an accurate RTT we dont want to use his retry against our original
  837. ** request, so he zeros out his Resp correlator. We will eventually retry with
  838. ** new correlator and hopefully that frame will get through.
  839. */
  840. DPFX(DPFPREP,1, "(%p) Uncorrelated CONNECTED frame arrives", pEPD);
  841. Unlock(&pEPD->EPLock);
  842. RELEASE_MSD(pMSD, "Hold For Lock");
  843. }
  844. }
  845. /*
  846. ** Drop Link
  847. **
  848. ** For whatever reason we are dropping an active link. This requires us to
  849. ** Cancel any outstanding commands and give an indication to the user.
  850. **
  851. **
  852. ** ** CALLED WITH EPD->EPLOCK HELD; RETURNS WITH LOCK RELEASED **
  853. */
  854. #undef DPF_MODNAME
  855. #define DPF_MODNAME "DropLink"
  856. VOID DropLink(PEPD pEPD)
  857. {
  858. PRCD pRCD;
  859. PRCD pNext;
  860. CBilink *pLink;
  861. PSPRECEIVEDBUFFER pRcvBuff = NULL;
  862. PProtocolData pPData;
  863. BOOL fIndicateDisconnect;
  864. DPFX(DPFPREP,2, "Drop Link %p (refcnt=%d)", pEPD, pEPD->lRefCnt);
  865. ASSERT_EPD(pEPD);
  866. AssertCriticalSectionIsTakenByThisThread(&pEPD->EPLock, TRUE);
  867. PSPD pSPD = pEPD->pSPD;
  868. // First set/clear flags to prevent any new commands from issueing
  869. // We will not indicate disconnect if the Core never knew about the connetion
  870. fIndicateDisconnect = (pEPD->ulEPFlags & EPFLAGS_STATE_CONNECTED);
  871. // Transition state
  872. pEPD->ulEPFlags &= ~(EPFLAGS_STATE_CONNECTING | EPFLAGS_STATE_DORMANT | EPFLAGS_STATE_CONNECTED |
  873. EPFLAGS_SDATA_READY | EPFLAGS_STREAM_UNBLOCKED); // Link is now down
  874. pEPD->ulEPFlags |= EPFLAGS_STATE_TERMINATING; // Accept no new commands
  875. // I am creating a RefCnt bump for the send pipeline, which means we will no longer pull EPDs off
  876. // the pipeline here. The clearing of the flags above will cause the EPD to be dropped from the
  877. // pipeline the next time it is due to be serviced. We CAN still clean up all the frames'n'stuff
  878. // because the send loop doesnt need to actually DO anything with this EPD. This behavior allows
  879. // the SendThread to loop through the pipeline queue, surrendering locks, without having the queue
  880. // changing beneath it.
  881. // Stop all timers (there are five now)
  882. if(pEPD->RetryTimer)
  883. {
  884. if(CancelMyTimer(pEPD->RetryTimer, pEPD->RetryTimerUnique) == DPN_OK)
  885. {
  886. DECREMENT_EPD(pEPD, "UNLOCK (DROP RETRY)"); // SPLock not already held
  887. }
  888. pEPD->RetryTimer = 0;
  889. }
  890. if(pEPD->ConnectTimer)
  891. {
  892. if(CancelMyTimer(pEPD->ConnectTimer, pEPD->ConnectTimerUnique) == DPN_OK)
  893. {
  894. DECREMENT_EPD(pEPD, "UNLOCK (DROP CONNECT RETRY)"); // SPLock not already held
  895. }
  896. pEPD->ConnectTimer = 0;
  897. }
  898. if(pEPD->DelayedAckTimer)
  899. {
  900. if(CancelMyTimer(pEPD->DelayedAckTimer, pEPD->DelayedAckTimerUnique) == DPN_OK)
  901. {
  902. DECREMENT_EPD(pEPD, "UNLOCK (DROP DELAYEDACK)"); // SPLock not already held
  903. }
  904. pEPD->DelayedAckTimer = 0;
  905. }
  906. if(pEPD->DelayedMaskTimer)
  907. {
  908. if(CancelMyTimer(pEPD->DelayedMaskTimer, pEPD->DelayedMaskTimerUnique) == DPN_OK)
  909. {
  910. DECREMENT_EPD(pEPD, "UNLOCK (DROP DELAYED MASK)"); // SPLock not already held
  911. }
  912. pEPD->DelayedMaskTimer = 0;
  913. }
  914. if(pEPD->SendTimer)
  915. {
  916. if(CancelMyTimer(pEPD->SendTimer, pEPD->SendTimerUnique) == DPN_OK)
  917. {
  918. DECREMENT_EPD(pEPD, "UNLOCK (DROP SENDTIMER)"); // SPLock not already held
  919. pEPD->SendTimer = 0;
  920. }
  921. }
  922. if(pEPD->BGTimer)
  923. {
  924. if(CancelMyTimer(pEPD->BGTimer, pEPD->BGTimerUnique) == DPN_OK)
  925. {
  926. DECREMENT_EPD(pEPD, "UNLOCK (DROP BG TIMER)"); // SPLock not already held
  927. pEPD->BGTimer = 0;
  928. }
  929. }
  930. AbortSendsOnConnection(pEPD); // Cancel pending commands; releases EPLock
  931. Lock(&pEPD->EPLock);
  932. // Connects, Listens, and Disconnects are associated with an EPD through the pCommand member. AbortSends will
  933. // have removed any Disconnects, and no Connects or Listens should still be hanging on when we call DropLink.
  934. ASSERT(pEPD->pCommand == NULL);
  935. // Now we clean up any receives in progress. We throw away any partial or mis-ordered messages.
  936. while((pRCD = pEPD->pNewMessage) != NULL)
  937. {
  938. ASSERT_RCD(pRCD);
  939. pEPD->pNewMessage = pRCD->pMsgLink;
  940. if(pRCD->pRcvBuff == NULL)
  941. {
  942. ASSERT(pRCD->ulRFlags & (RFLAGS_FRAME_INDICATED_NONSEQ | RFLAGS_FRAME_LOST));
  943. }
  944. RELEASE_SP_BUFFER(pRCD->pRcvBuff);
  945. RELEASE_RCD(pRCD);
  946. }
  947. while(!pEPD->blOddFrameList.IsEmpty())
  948. {
  949. pLink = pEPD->blOddFrameList.GetNext();
  950. pRCD = CONTAINING_RECORD(pLink, RCD, blOddFrameLinkage);
  951. ASSERT_RCD(pRCD);
  952. pLink->RemoveFromList();
  953. if(pRCD->pRcvBuff == NULL)
  954. {
  955. ASSERT(pRCD->ulRFlags & (RFLAGS_FRAME_INDICATED_NONSEQ | RFLAGS_FRAME_LOST));
  956. }
  957. RELEASE_SP_BUFFER(pRCD->pRcvBuff);
  958. RELEASE_RCD(pRCD);
  959. }
  960. while(!pEPD->blCompleteList.IsEmpty())
  961. {
  962. pLink = pEPD->blCompleteList.GetNext();
  963. pRCD = CONTAINING_RECORD(pLink, RCD, blCompleteLinkage);
  964. ASSERT_RCD(pRCD);
  965. pLink->RemoveFromList();
  966. ASSERT(pEPD->uiCompleteMsgCount > 0);
  967. pEPD->uiCompleteMsgCount--;
  968. while(pRCD != NULL)
  969. {
  970. ASSERT_RCD(pRCD);
  971. pNext = pRCD->pMsgLink;
  972. RELEASE_SP_BUFFER(pRCD->pRcvBuff);
  973. RELEASE_RCD(pRCD);
  974. pRCD = pNext;
  975. }
  976. }
  977. pPData = pEPD->pSPD->pPData;
  978. IDP8ServiceProvider *pSPIntf = pEPD->pSPD->IISPIntf;
  979. if (!(pEPD->ulEPFlags & EPFLAGS_INDICATED_DISCONNECT))
  980. {
  981. if (fIndicateDisconnect)
  982. {
  983. // Make sure we are the only one that indicates the disconnect
  984. pEPD->ulEPFlags |= EPFLAGS_INDICATED_DISCONNECT;
  985. }
  986. }
  987. else
  988. {
  989. // Someone else beat us to it.
  990. fIndicateDisconnect = FALSE;
  991. }
  992. // We need to make sure that we don't allow DNPRemoveServiceProvider to complete until we are out of the Core
  993. // and SP. This reference will do that, and is released at the end of this function.
  994. LOCK_EPD(pEPD, "Drop Link Temp Ref");
  995. // Remove the base reference if it still exists
  996. if(!(pEPD->ulEPFlags & EPFLAGS_KILLED))
  997. {
  998. pEPD->ulEPFlags |= EPFLAGS_KILLED;
  999. RELEASE_EPD(pEPD, "UNLOCK (KILLCONN - Base Ref)"); // RELEASE the EPD, releases EPLock
  1000. }
  1001. else
  1002. {
  1003. Unlock(&pEPD->EPLock);
  1004. }
  1005. if(pRcvBuff)
  1006. {
  1007. DPFX(DPFPREP,DPF_CALLOUT_LVL, "(%p) Calling SP->ReturnReceiveBuffers, pSPD[%p], pRcvBuff[%p]", pEPD, pSPD, pRcvBuff);
  1008. IDP8ServiceProvider_ReturnReceiveBuffers(pSPIntf, pRcvBuff);
  1009. }
  1010. // Tell user that session is over
  1011. // If the Core previously knew about this endpoint, and we have not yet indicated disconnect to the
  1012. // Core, we need to do it now.
  1013. if(fIndicateDisconnect)
  1014. {
  1015. DPFX(DPFPREP,DPF_CALLOUT_LVL, "(%p) Calling Core->IndicateConnectionTerminated, DPNERR_CONNECTIONLOST, Core Context[%p]", pEPD, pEPD->Context);
  1016. pEPD->pSPD->pPData->pfVtbl->IndicateConnectionTerminated(pPData->Parent, pEPD->Context, DPNERR_CONNECTIONLOST);
  1017. }
  1018. Lock(&pEPD->EPLock);
  1019. RELEASE_EPD(pEPD, "Drop Link Temp Ref");
  1020. }