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.

1898 lines
69 KiB

  1. /*==========================================================================
  2. *
  3. * Copyright (C) 1998-2002 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
  31. DNPConnect(HANDLE hProtocolData, IDirectPlay8Address* paLocal, IDirectPlay8Address* paRemote, HANDLE hSPHandle, ULONG ulFlags, VOID* pvContext, VOID* pvSessionData, DWORD dwSessionDataSize, PHANDLE phConnectHandle)
  32. {
  33. ProtocolData* pPData;
  34. PSPD pSPD; // Service Provider to handle this connect
  35. PMSD pMSD;
  36. SPCONNECTDATA ConnData; // Parameter Block
  37. HRESULT hr;
  38. #ifdef DBG
  39. ULONG ulAllowedFlags;
  40. #endif // DBG
  41. // Determine which SP will take this call
  42. //
  43. DPFX(DPFPREP,DPF_CALLIN_LVL, "Parameters: hProtocolData[%p], paLocal[%p], paRemote[%p], hSPHandle[%x], ulFlags[%x], pvContext[%p], pvSessionData[%p], dwSessionDataSize[%u], phConnectHandle[%p]", hProtocolData, paLocal, paRemote, hSPHandle, ulFlags, pvContext, pvSessionData, dwSessionDataSize, phConnectHandle);
  44. hr = DPNERR_PENDING;
  45. pPData = (ProtocolData*)hProtocolData;
  46. ASSERT_PPD(pPData);
  47. pSPD = (PSPD) hSPHandle;
  48. ASSERT_SPD(pSPD);
  49. // Core should not call any Protocol APIs after calling DNPRemoveServiceProvider
  50. ASSERT(!(pSPD->ulSPFlags & SPFLAGS_TERMINATING));
  51. // We use an MSD to describe this op even though it isn't technically a message
  52. if((pMSD = (PMSD)POOLALLOC(MEMID_CONNECT_MSD, &MSDPool)) == NULL)
  53. {
  54. DPFX(DPFPREP,0, "Returning DPNERR_OUTOFMEMORY - failed to create new MSD");
  55. hr = DPNERR_OUTOFMEMORY;
  56. goto Exit;
  57. }
  58. pMSD->CommandID = COMMAND_ID_CONNECT;
  59. pMSD->pSPD = pSPD;
  60. pMSD->Context = pvContext;
  61. ASSERT(pMSD->pEPD == NULL); // MSD_Get/Release ensures this, and IndicateConnect requires it.
  62. // Prepare to call SP to map the endpoint.
  63. ConnData.pAddressDeviceInfo = paLocal;
  64. ConnData.pAddressHost = paRemote;
  65. ConnData.dwReserved = 0; // Never used
  66. #ifdef DBG
  67. ulAllowedFlags = DN_CONNECTFLAGS_SESSIONDATA;
  68. #ifndef DPNBUILD_NOSPUI
  69. ulAllowedFlags |= DN_CONNECTFLAGS_OKTOQUERYFORADDRESSING;
  70. #endif // ! DPNBUILD_NOSPUI
  71. #ifndef DPNBUILD_ONLYONEADAPTER
  72. ulAllowedFlags |= DN_CONNECTFLAGS_ADDITIONALMULTIPLEXADAPTERS;
  73. #endif // ! DPNBUILD_ONLYONEADAPTER
  74. #ifndef DPNBUILD_NOMULTICAST
  75. ulAllowedFlags |= DN_CONNECTFLAGS_MULTICAST_SEND | DN_CONNECTFLAGS_MULTICAST_RECEIVE;
  76. #endif // ! DPNBUILD_NOMULTICAST
  77. DNASSERT( ( ulFlags & ~(ulAllowedFlags) ) == 0 );
  78. #ifndef DPNBUILD_NOMULTICAST
  79. DNASSERT( ! ( ( ulFlags & DN_CONNECTFLAGS_MULTICAST_SEND ) && ( ulFlags & DN_CONNECTFLAGS_MULTICAST_RECEIVE ) ) );
  80. #endif // ! DPNBUILD_NOMULTICAST
  81. #endif // DBG
  82. ConnData.dwFlags = 0;
  83. #ifndef DPNBUILD_NOSPUI
  84. if ( ( ulFlags & DN_CONNECTFLAGS_OKTOQUERYFORADDRESSING ) != 0 )
  85. {
  86. ConnData.dwFlags |= DPNSPF_OKTOQUERY;
  87. }
  88. #endif // ! DPNBUILD_NOSPUI
  89. #ifndef DPNBUILD_ONLYONEADAPTER
  90. if ( ( ulFlags & DN_CONNECTFLAGS_ADDITIONALMULTIPLEXADAPTERS ) != 0 )
  91. {
  92. ConnData.dwFlags |= DPNSPF_ADDITIONALMULTIPLEXADAPTERS;
  93. }
  94. #endif // ! DPNBUILD_ONLYONEADAPTER
  95. #ifndef DPNBUILD_NOMULTICAST
  96. if ( ( ulFlags & DN_CONNECTFLAGS_MULTICAST_SEND ) != 0 )
  97. {
  98. pMSD->CommandID = COMMAND_ID_CONNECT_MULTICAST_SEND;
  99. ConnData.dwFlags |= DPNSPF_CONNECT_MULTICAST_SEND;
  100. }
  101. if ( ( ulFlags & DN_CONNECTFLAGS_MULTICAST_RECEIVE ) != 0 )
  102. {
  103. pMSD->CommandID = COMMAND_ID_CONNECT_MULTICAST_RECEIVE;
  104. ConnData.dwFlags |= DPNSPF_CONNECT_MULTICAST_RECEIVE;
  105. }
  106. #endif // DPNBUILD_NOMULTICAST
  107. if ( ( ulFlags & DN_CONNECTFLAGS_SESSIONDATA ) != 0 )
  108. {
  109. ConnData.dwFlags |= DPNSPF_SESSIONDATA;
  110. ConnData.pvSessionData = pvSessionData;
  111. ConnData.dwSessionDataSize = dwSessionDataSize;
  112. }
  113. ConnData.pvContext = pMSD;
  114. ConnData.hCommand = 0;
  115. pMSD->ulMsgFlags1 |= MFLAGS_ONE_IN_SERVICE_PROVIDER;
  116. #ifdef DBG
  117. // Hook up MSD before calling into SP
  118. Lock(&pSPD->SPLock);
  119. pMSD->blSPLinkage.InsertBefore( &pSPD->blMessageList); // Put this on cmd list
  120. pMSD->ulMsgFlags1 |= MFLAGS_ONE_ON_GLOBAL_LIST;
  121. Unlock(&pSPD->SPLock);
  122. #endif // DBG
  123. *phConnectHandle = pMSD;
  124. // SP Connect call is guaranteed to return immediately
  125. LOCK_MSD(pMSD, "SP Ref"); // Add reference for call into SP
  126. LOCK_MSD(pMSD, "Temp Ref");
  127. AssertNoCriticalSectionsFromGroupTakenByThisThread(&g_blProtocolCritSecsHeld);
  128. DPFX(DPFPREP,DPF_CALLOUT_LVL, "Calling SP->Connect, pSPD[%p], pMSD[%p]", pSPD, pMSD);
  129. /**/hr = IDP8ServiceProvider_Connect(pSPD->IISPIntf, &ConnData); /** CALL SP **/
  130. if(hr != DPNERR_PENDING)
  131. {
  132. // SP Connect should always be asynchronous so if it isnt PENDING then it must have failed
  133. DPFX(DPFPREP,1, "SP->Connect did not return DPNERR_PENDING, assuming failure, hr[%x]", hr);
  134. // DPNERR_PENDING is the only success code we accept
  135. ASSERT(FAILED(hr));
  136. Lock(&pMSD->CommandLock); // This will be unlocked by final RELEASE_MSD
  137. pMSD->ulMsgFlags1 &= ~(MFLAGS_ONE_IN_SERVICE_PROVIDER); // clear InSP flag
  138. #ifdef DBG
  139. Lock(&pSPD->SPLock);
  140. pMSD->blSPLinkage.RemoveFromList(); // knock this off the pending list
  141. pMSD->ulMsgFlags1 &= ~(MFLAGS_ONE_ON_GLOBAL_LIST);
  142. Unlock(&pSPD->SPLock);
  143. #endif // DBG
  144. DECREMENT_MSD(pMSD, "Temp Ref");
  145. DECREMENT_MSD(pMSD, "SP Ref"); // Remove one ref for SP call
  146. RELEASE_MSD(pMSD, "Release On Fail"); // Remove one ref to free resource
  147. goto Exit;
  148. }
  149. Lock(&pMSD->CommandLock);
  150. pMSD->hCommand = ConnData.hCommand; // retain SP command handle
  151. pMSD->dwCommandDesc = ConnData.dwCommandDescriptor;
  152. RELEASE_MSD(pMSD, "Temp Ref"); // Unlocks CommandLock
  153. Exit:
  154. DPFX(DPFPREP,DPF_CALLIN_LVL, "Returning hr[%x], pMSD[%p]", hr, pMSD);
  155. AssertNoCriticalSectionsFromGroupTakenByThisThread(&g_blProtocolCritSecsHeld);
  156. return hr;
  157. }
  158. /*
  159. ** Listen
  160. **
  161. ** This command tells DN that it should start to accept connection requests.
  162. ** This command will return pending, and will continue to indicate connections
  163. ** until it is explicitly cancelled. It may be desireable to establish a limit
  164. ** mechanism of some sort, but for the time being this will do.
  165. **
  166. ** Now it is desirable to Listen on multiple ports on a single adapter. This
  167. ** means that we need to accept multiple concurrent Listen commands on each adapter.
  168. ** Another fact of life is that we need to crack the Target address far enough to
  169. ** determine which SP to submit the Listen on.
  170. */
  171. #undef DPF_MODNAME
  172. #define DPF_MODNAME "DNPListen"
  173. HRESULT
  174. DNPListen(HANDLE hProtocolData, IDirectPlay8Address* paTarget, HANDLE hSPHandle, ULONG ulFlags, VOID* pvContext, VOID* pvSessionData, DWORD dwSessionDataSize, HANDLE* phListenHandle)
  175. {
  176. ProtocolData* pPData;
  177. PSPD pSPD;
  178. PMSD pMSD;
  179. SPLISTENDATA ListenData;
  180. HRESULT hr;
  181. #ifdef DBG
  182. ULONG ulAllowedFlags;
  183. #endif // DBG
  184. DPFX(DPFPREP,DPF_CALLIN_LVL, "Parameters: hProtocolData[%p], paTarget[%p], hSPHandle[%p], ulFlags[%x], pvContext[%p], pvSessionData[%p], dwSessionDataSize[%u], phListenHandle[%p]", hProtocolData, paTarget, hSPHandle, ulFlags, pvContext, pvSessionData, dwSessionDataSize, phListenHandle);
  185. hr = DPNERR_PENDING;
  186. pPData = (ProtocolData*)hProtocolData;
  187. ASSERT_PPD(pPData);
  188. pSPD = (PSPD) hSPHandle;
  189. ASSERT_SPD(pSPD);
  190. // Core should not call any Protocol APIs after calling DNPRemoveServiceProvider
  191. ASSERT(!(pSPD->ulSPFlags & SPFLAGS_TERMINATING));
  192. // We use an MSD to describe this op even though it isn't technically a message
  193. if((pMSD = (PMSD)POOLALLOC(MEMID_LISTEN_MSD, &MSDPool)) == NULL)
  194. {
  195. DPFX(DPFPREP,0, "Returning DPNERR_OUTOFMEMORY - failed to create new MSD");
  196. hr = DPNERR_OUTOFMEMORY;
  197. goto Exit;
  198. }
  199. pMSD->CommandID = COMMAND_ID_LISTEN;
  200. pMSD->pSPD = pSPD;
  201. pMSD->Context = pvContext;
  202. ListenData.pAddressDeviceInfo = paTarget;
  203. #ifdef DBG
  204. ulAllowedFlags = DN_LISTENFLAGS_DISALLOWENUMS | DN_LISTENFLAGS_SESSIONDATA |
  205. DN_LISTENFLAGS_FASTSIGNED | DN_LISTENFLAGS_FULLSIGNED;
  206. #ifndef DPNBUILD_NOSPUI
  207. ulAllowedFlags |= DN_LISTENFLAGS_OKTOQUERYFORADDRESSING;
  208. #endif // ! DPNBUILD_NOSPUI
  209. #ifndef DPNBUILD_NOMULTICAST
  210. ulAllowedFlags |= DN_LISTENFLAGS_MULTICAST | DN_LISTENFLAGS_ALLOWUNKNOWNSENDERS;
  211. #endif // ! DPNBUILD_NOMULTICAST
  212. DNASSERT( ( ulFlags & ~(ulAllowedFlags) ) == 0 );
  213. DNASSERT(((ulFlags & DN_LISTENFLAGS_FASTSIGNED) && (ulFlags & DN_LISTENFLAGS_FULLSIGNED))==0);
  214. #endif // DBG
  215. //if we've got signing on pick initial connect secrets, otherwise zero out the secrets
  216. pMSD->ullCurrentConnectSecret=0;
  217. if (ulFlags & DN_LISTENFLAGS_FASTSIGNED)
  218. {
  219. pMSD->ulMsgFlags1|=MFLAGS_ONE_FAST_SIGNED;
  220. DNGetGoodRandomData(&pMSD->ullCurrentConnectSecret, sizeof(pMSD->ullCurrentConnectSecret));
  221. }
  222. else if (ulFlags & DN_LISTENFLAGS_FULLSIGNED)
  223. {
  224. pMSD->ulMsgFlags1|=MFLAGS_ONE_FULL_SIGNED;
  225. DNGetGoodRandomData(&pMSD->ullCurrentConnectSecret, sizeof(pMSD->ullCurrentConnectSecret));
  226. }
  227. pMSD->ullLastConnectSecret=pMSD->ullCurrentConnectSecret;
  228. pMSD->dwTimeConnectSecretChanged=GETTIMESTAMP();
  229. ListenData.dwFlags = 0;
  230. #ifndef DPNBUILD_NOSPUI
  231. if ( ( ulFlags & DN_LISTENFLAGS_OKTOQUERYFORADDRESSING ) != 0 )
  232. {
  233. ListenData.dwFlags |= DPNSPF_OKTOQUERY;
  234. }
  235. #endif // ! DPNBUILD_NOSPUI
  236. #ifndef DPNBUILD_NOMULTICAST
  237. if ( ( ulFlags & DN_LISTENFLAGS_MULTICAST ) != 0 )
  238. {
  239. pMSD->CommandID = COMMAND_ID_LISTEN_MULTICAST;
  240. ListenData.dwFlags |= DPNSPF_LISTEN_MULTICAST;
  241. if ( ( ulFlags & DN_LISTENFLAGS_ALLOWUNKNOWNSENDERS ) != 0 )
  242. {
  243. ListenData.dwFlags |= DPNSPF_LISTEN_ALLOWUNKNOWNSENDERS;
  244. }
  245. }
  246. #endif // ! DPNBUILD_NOMULTICAST
  247. if ( ( ulFlags & DN_LISTENFLAGS_SESSIONDATA ) != 0 )
  248. {
  249. ListenData.dwFlags |= DPNSPF_SESSIONDATA;
  250. ListenData.pvSessionData = pvSessionData;
  251. ListenData.dwSessionDataSize = dwSessionDataSize;
  252. }
  253. if ( (ulFlags & DN_LISTENFLAGS_DISALLOWENUMS ) != 0 )
  254. {
  255. ListenData.dwFlags |= DPNSPF_LISTEN_DISALLOWENUMS;
  256. }
  257. ListenData.pvContext = pMSD;
  258. ListenData.hCommand = 0;
  259. *phListenHandle = pMSD;
  260. // SP Listen call is guarenteed to return immediately
  261. pMSD->ulMsgFlags1 |= MFLAGS_ONE_IN_SERVICE_PROVIDER;
  262. #ifdef DBG
  263. Lock(&pSPD->SPLock);
  264. pMSD->blSPLinkage.InsertBefore( &pSPD->blMessageList); // Dont support timeouts for Listen
  265. pMSD->ulMsgFlags1 |= MFLAGS_ONE_ON_GLOBAL_LIST;
  266. Unlock(&pSPD->SPLock);
  267. #endif // DBG
  268. LOCK_MSD(pMSD, "SP Ref"); // AddRef for SP
  269. LOCK_MSD(pMSD, "Temp Ref");
  270. AssertNoCriticalSectionsFromGroupTakenByThisThread(&g_blProtocolCritSecsHeld);
  271. DPFX(DPFPREP,DPF_CALLOUT_LVL, "Calling SP->Listen, pSPD[%p], pMSD[%p]", pSPD, pMSD);
  272. /**/hr = IDP8ServiceProvider_Listen(pSPD->IISPIntf, &ListenData); /** CALL SP **/
  273. if(hr != DPNERR_PENDING)
  274. {
  275. // SP Listen should always be asynchronous so if it isnt PENDING then it must have failed
  276. DPFX(DPFPREP,1, "SP->Listen did not return DPNERR_PENDING, assuming failure, hr[%x]", hr);
  277. // DPNERR_PENDING is the only success code we accept
  278. ASSERT(FAILED(hr));
  279. Lock(&pMSD->CommandLock);
  280. pMSD->ulMsgFlags1 &= ~(MFLAGS_ONE_IN_SERVICE_PROVIDER);
  281. #ifdef DBG
  282. Lock(&pSPD->SPLock);
  283. pMSD->blSPLinkage.RemoveFromList(); // knock this off the pending list
  284. pMSD->ulMsgFlags1 &= ~(MFLAGS_ONE_ON_GLOBAL_LIST);
  285. Unlock(&pSPD->SPLock);
  286. #endif // DBG
  287. DECREMENT_MSD(pMSD, "Temp Ref");
  288. DECREMENT_MSD(pMSD, "SP Ref"); // release once for SP
  289. RELEASE_MSD(pMSD, "Release On Fail"); // release again to return resource
  290. goto Exit;
  291. }
  292. Lock(&pMSD->CommandLock);
  293. pMSD->hCommand = ListenData.hCommand; // retail SP command handle
  294. pMSD->dwCommandDesc = ListenData.dwCommandDescriptor;
  295. RELEASE_MSD(pMSD, "Temp Ref"); // Unlocks CommandLock
  296. Exit:
  297. DPFX(DPFPREP,DPF_CALLIN_LVL, "Returning hr[%x], pMSD[%p]", hr, pMSD);
  298. AssertNoCriticalSectionsFromGroupTakenByThisThread(&g_blProtocolCritSecsHeld);
  299. return hr;
  300. }
  301. /*** BACKEND ROUTINES ***/
  302. /*
  303. ** Complete Connect
  304. **
  305. ** The user's Connect operation has completed. Clean everything up
  306. ** and signal the user.
  307. **
  308. ** THIS IS ALWAYS CALLED WITH THE COMMAND LOCK HELD IN MSD, LEAVES WITH IT RELEASED
  309. */
  310. #undef DPF_MODNAME
  311. #define DPF_MODNAME "CompleteConnect"
  312. VOID CompleteConnect(PMSD pMSD, PSPD pSPD, PEPD pEPD, HRESULT hr)
  313. {
  314. AssertCriticalSectionIsTakenByThisThread(&pMSD->CommandLock, TRUE);
  315. // We expect to never get here twice
  316. ASSERT(!(pMSD->ulMsgFlags1 & MFLAGS_ONE_COMPLETE));
  317. pMSD->ulMsgFlags1 |= MFLAGS_ONE_COMPLETE;
  318. // Connects cannot have timeout timers
  319. ASSERT(pMSD->TimeoutTimer == NULL);
  320. #ifdef DBG
  321. Lock(&pSPD->SPLock);
  322. if(pMSD->ulMsgFlags1 & MFLAGS_ONE_ON_GLOBAL_LIST)
  323. {
  324. pMSD->blSPLinkage.RemoveFromList(); // Remove MSD from master command list
  325. pMSD->ulMsgFlags1 &= ~(MFLAGS_ONE_ON_GLOBAL_LIST);
  326. }
  327. Unlock(&pSPD->SPLock);
  328. ASSERT(!(pMSD->ulMsgFlags1 & MFLAGS_ONE_COMPLETED_TO_CORE));
  329. pMSD->ulMsgFlags1 |= MFLAGS_ONE_COMPLETED_TO_CORE;
  330. pMSD->CallStackCoreCompletion.NoteCurrentCallStack();
  331. #endif // DBG
  332. pMSD->pEPD = NULL;
  333. Unlock(&pMSD->CommandLock);
  334. AssertNoCriticalSectionsFromGroupTakenByThisThread(&g_blProtocolCritSecsHeld);
  335. if(pEPD)
  336. {
  337. ASSERT(hr == DPN_OK);
  338. DPFX(DPFPREP,DPF_CALLOUT_LVL, "(%p) Calling Core->CompleteConnect, pMSD[%p], Core Context[%p], hr[%x], pEPD[%p]", pEPD, pMSD, pMSD->Context, hr, pEPD);
  339. pSPD->pPData->pfVtbl->CompleteConnect(pSPD->pPData->Parent, pMSD->Context, hr, (PHANDLE) pEPD, &pEPD->Context);
  340. Lock(&pEPD->EPLock);
  341. ReceiveComplete(pEPD); // Complete any, releases EPLock
  342. }
  343. else
  344. {
  345. ASSERT(hr != DPN_OK);
  346. DPFX(DPFPREP,DPF_CALLOUT_LVL, "Calling Core->CompleteConnect with NULL EPD, pMSD[%p], Core Context[%p], hr[%x]", pMSD, pMSD->Context, hr);
  347. pSPD->pPData->pfVtbl->CompleteConnect(pSPD->pPData->Parent, pMSD->Context, hr, NULL, NULL);
  348. }
  349. // Release the final reference on the MSD AFTER indicating to the Core
  350. Lock(&pMSD->CommandLock);
  351. RELEASE_MSD(pMSD, "Final Release On Complete"); // Finished with this one, releases CommandLock
  352. }
  353. /*
  354. ** Complete Disconnect
  355. **
  356. ** THIS IS ALWAYS CALLED WITH THE COMMAND LOCK HELD IN MSD
  357. */
  358. #undef DPF_MODNAME
  359. #define DPF_MODNAME "CompleteDisconnect"
  360. VOID CompleteDisconnect(PMSD pMSD, PSPD pSPD, PEPD pEPD)
  361. {
  362. AssertCriticalSectionIsTakenByThisThread(&pMSD->CommandLock, TRUE);
  363. // We expect to never get here twice
  364. ASSERT(!(pMSD->ulMsgFlags1 & MFLAGS_ONE_COMPLETE));
  365. pMSD->ulMsgFlags1 |= MFLAGS_ONE_COMPLETE;
  366. // Disconnects cannot have timeout timers
  367. ASSERT(pMSD->TimeoutTimer == NULL);
  368. #ifdef DBG
  369. Lock(&pSPD->SPLock);
  370. if(pMSD->ulMsgFlags1 & MFLAGS_ONE_ON_GLOBAL_LIST)
  371. {
  372. pMSD->blSPLinkage.RemoveFromList(); // Remove MSD from master command list
  373. pMSD->ulMsgFlags1 &= ~(MFLAGS_ONE_ON_GLOBAL_LIST);
  374. }
  375. Unlock(&pSPD->SPLock);
  376. ASSERT(!(pMSD->ulMsgFlags1 & MFLAGS_ONE_COMPLETED_TO_CORE));
  377. pMSD->ulMsgFlags1 |= MFLAGS_ONE_COMPLETED_TO_CORE;
  378. pMSD->CallStackCoreCompletion.NoteCurrentCallStack();
  379. #endif // DBG
  380. // No one else should use this
  381. pMSD->pEPD = NULL;
  382. if(pMSD->CommandID == COMMAND_ID_DISCONNECT)
  383. {
  384. Unlock(&pMSD->CommandLock);
  385. AssertNoCriticalSectionsFromGroupTakenByThisThread(&g_blProtocolCritSecsHeld);
  386. DPFX(DPFPREP,DPF_CALLOUT_LVL, "(%p) Calling Core->CompleteDisconnect, DPN_OK, pMSD[%p], Core Context[%p]", pEPD, pMSD, pMSD->Context);
  387. pSPD->pPData->pfVtbl->CompleteDisconnect(pSPD->pPData->Parent, pMSD->Context, DPN_OK);
  388. }
  389. else
  390. {
  391. Unlock(&pMSD->CommandLock);
  392. ASSERT(pMSD->CommandID == COMMAND_ID_DISC_RESPONSE);
  393. AssertNoCriticalSectionsFromGroupTakenByThisThread(&g_blProtocolCritSecsHeld);
  394. DPFX(DPFPREP,DPF_CALLOUT_LVL, "(%p) Calling Core->IndicateConnectionTerminated, DPN_OK, Core Context[%p]", pEPD, pEPD->Context);
  395. pSPD->pPData->pfVtbl->IndicateConnectionTerminated(pSPD->pPData->Parent, pEPD->Context, DPN_OK);
  396. }
  397. Lock(&pMSD->CommandLock);
  398. Lock(&pEPD->EPLock);
  399. // Release MSD before EPD since final EPD will call out to SP and we don't want any locks held
  400. RELEASE_MSD(pMSD, "Final Release On Complete"); // Finished with this one, releases CommandLock
  401. RELEASE_EPD(pEPD, "UNLOCK (DISC COMMAND)"); // release hold on EPD, releases EPLock
  402. }
  403. /*
  404. ** Complete Hard Disconnect
  405. **
  406. ** CALLED WITH EP LOCK HELD. RETURNS WITH EP LOCK RELEASED
  407. */
  408. #undef DPF_MODNAME
  409. #define DPF_MODNAME "CompleteHardDisconnect"
  410. VOID CompleteHardDisconnect(PEPD pEPD)
  411. {
  412. AssertCriticalSectionIsTakenByThisThread(&pEPD->EPLock, TRUE);
  413. DNASSERT(pEPD->ulEPFlags & (EPFLAGS_HARD_DISCONNECT_SOURCE | EPFLAGS_HARD_DISCONNECT_TARGET));
  414. //potentially multiple threads can try and complete the hard disconnect
  415. //e.g. We might get a hard disconnect response at the same time we complete the send
  416. //of our final hard disconnect frame. This flag+guard ensures we only complete it once
  417. if (pEPD->ulEPFlags2 & EPFLAGS2_HARD_DISCONNECT_COMPLETE)
  418. {
  419. DPFX(DPFPREP, 7, "(%p) Ignoring. Hard disconnect already completed", pEPD);
  420. Unlock(&pEPD->EPLock);
  421. return;
  422. }
  423. pEPD->ulEPFlags2|=EPFLAGS2_HARD_DISCONNECT_COMPLETE;
  424. //if we've got a timer running to send more hard disconnect frames then cancel it
  425. if (pEPD->LinkTimer)
  426. {
  427. if (CancelProtocolTimer(pEPD->pSPD, pEPD->LinkTimer, pEPD->LinkTimerUnique)==DPN_OK)
  428. {
  429. DECREMENT_EPD(pEPD, "UNLOCK (DROP LINK RETRY)");
  430. }
  431. pEPD->LinkTimer=NULL;
  432. pEPD->LinkTimerUnique=0;
  433. }
  434. DPFX(DPFPREP, 7, "(%p) Completed hard disconnect sequence, dropping link.", pEPD);
  435. //actually completing/indicating the disconnection to core is handled in the drop link function
  436. DropLink(pEPD);
  437. //ep lock released by above call
  438. //finally drop the reference placed on the ep either when disconnect was called or when
  439. //it received its first hard disconnect frame
  440. Lock(&pEPD->EPLock);
  441. RELEASE_EPD(pEPD, "UNLOCK (HARD DISCONNECT)"); // release hold on EPD, releases EPLock
  442. }
  443. /*
  444. ** Complete SP Connect
  445. **
  446. ** A Connect Command has completed in the Service Provider. This does not mean our
  447. ** work is done... this means we now have a mapped EndPoint so we can exchange packets
  448. ** with this partner. We will now ping this partner to get an initial RTT and make sure
  449. ** there really is a protocol over there that will talk to us.
  450. **
  451. ** Of course, if SP does not return success then we can nip this whole thing in
  452. ** the proverbial bud.
  453. **
  454. */
  455. #undef DPF_MODNAME
  456. #define DPF_MODNAME "CompleteSPConnect"
  457. VOID CompleteSPConnect(PMSD pMSD, PSPD pSPD, HRESULT hr)
  458. {
  459. PEPD pEPD;
  460. DPFX(DPFPREP,5, "SP Completes Connect, pMSD[%p])", pMSD);
  461. pEPD = pMSD->pEPD;
  462. ASSERT(!(pMSD->ulMsgFlags1 & MFLAGS_ONE_COMPLETE));
  463. if(hr != DPN_OK)
  464. {
  465. // This will only happen once since DoCancel will not have done it because the IN_SP flag was set, and
  466. // ConnectRetryTimeout has never yet been set.
  467. if (pEPD)
  468. {
  469. ASSERT_EPD(pEPD);
  470. Lock(&pEPD->EPLock);
  471. // Unlink EPD from MSD
  472. ASSERT(pEPD->pCommand == pMSD);
  473. pEPD->pCommand = NULL;
  474. DECREMENT_MSD(pMSD, "EPD Ref"); // Release Reference from EPD
  475. DropLink(pEPD); // This releases the EPLock
  476. }
  477. Lock(&pMSD->CommandLock); // must do this before clearing IN_SP flag
  478. pMSD->ulMsgFlags1 &= ~(MFLAGS_ONE_IN_SERVICE_PROVIDER); // clear InSP flag
  479. DECREMENT_MSD(pMSD, "SP Ref"); // Dec ref count w/o release lock
  480. DPFX(DPFPREP,5, "SP failed Connect, completing Connect, pMSD[%p], hr[%x]", pMSD, hr);
  481. CompleteConnect(pMSD, pSPD, NULL, hr); // SP failed the connect call, unlocks CommandLock
  482. return;
  483. }
  484. // After a successful connect, we should have an endpoint
  485. ASSERT_EPD(pEPD);
  486. Lock(&pMSD->CommandLock); // must do this before clearing IN_SP flag
  487. Lock(&pEPD->EPLock);
  488. // The endpoint should have already been linked to this MSD
  489. ASSERT(pEPD->pCommand == pMSD);
  490. if(pMSD->ulMsgFlags1 & MFLAGS_ONE_CANCELLED)
  491. {
  492. // We get here when someone called DoCancel while we were still in the SP. As above:
  493. // This will only happen once since DoCancel will not have done it because the IN_SP flag was set, and
  494. // ConnectRetryTimeout has never yet been set.
  495. // Unlink EPD from MSD
  496. ASSERT(pEPD->pCommand == pMSD);
  497. pEPD->pCommand = NULL;
  498. DECREMENT_MSD(pMSD, "EPD Ref"); // Release Reference from EPD
  499. Unlock(&pMSD->CommandLock); // DropLink may call into the SP
  500. DropLink(pEPD); // This releases the EPLock
  501. Lock(&pMSD->CommandLock); // must do this before clearing IN_SP flag
  502. pMSD->ulMsgFlags1 &= ~(MFLAGS_ONE_IN_SERVICE_PROVIDER); // clear InSP flag
  503. DECREMENT_MSD(pMSD, "SP Ref"); // Dec ref count w/o release lock
  504. DPFX(DPFPREP,5, "(%p) Command is cancelled or timed out, Complete Connect, pMSD[%p]", pEPD, pMSD);
  505. CompleteConnect(pMSD, pSPD, NULL, DPNERR_USERCANCEL);
  506. return;
  507. }
  508. pMSD->ulMsgFlags1 &= ~(MFLAGS_ONE_IN_SERVICE_PROVIDER); // clear InSP flag
  509. DECREMENT_MSD(pMSD, "SP Ref"); // Dec ref count w/o release lock
  510. // Set up End Point Data /////////////////////////////////////////////////////////////////
  511. // Transition state
  512. ASSERT(pEPD->ulEPFlags & EPFLAGS_STATE_DORMANT);
  513. pEPD->ulEPFlags &= ~(EPFLAGS_STATE_DORMANT);
  514. pEPD->ulEPFlags |= EPFLAGS_STATE_CONNECTING;
  515. // Send CONNECT
  516. do
  517. {
  518. pEPD->dwSessID = DNGetGoodRandomNumber();
  519. } while (pEPD->dwSessID==0);
  520. DPFX(DPFPREP,5, "(%p) Sending CONNECT Frame, SessionID = %x", pEPD, pEPD->dwSessID);
  521. (void) SendCommandFrame(pEPD, FRAME_EXOPCODE_CONNECT, 0, 0, FALSE);
  522. // Set timer for reply, then wait for reply or TO.
  523. pEPD->uiRetryTimeout = pSPD->pPData->dwConnectTimeout;
  524. pEPD->uiNumRetriesRemaining = pSPD->pPData->dwConnectRetries;
  525. LOCK_EPD(pEPD, "LOCK (CONN Retry Timer)"); // Create reference for timer
  526. DPFX(DPFPREP,5, "(%p) Setting Connect Retry Timer", pEPD);
  527. ScheduleProtocolTimer(pSPD, pEPD->uiRetryTimeout, 100, ConnectRetryTimeout, (PVOID) pEPD,
  528. &pEPD->LinkTimer, &pEPD->LinkTimerUnique);
  529. Unlock(&pEPD->EPLock);
  530. Unlock(&pMSD->CommandLock);
  531. }
  532. /*
  533. ** Connect Retry Timeout
  534. **
  535. ** Retry timer has expired on a Connect operation. This one function
  536. ** is shared by Calling and Listening partners. Complexity is due to the
  537. ** fact that cancel code cannot always ensure that this handler will not
  538. ** run, so there are flags to signal various edge conditions (cancel, abort,
  539. ** completion, high-level timeout).
  540. */
  541. #undef DPF_MODNAME
  542. #define DPF_MODNAME "ConnectRetryTimeout"
  543. VOID CALLBACK
  544. ConnectRetryTimeout(void * const pvUser, void * const pvHandle, const UINT uiUnique)
  545. {
  546. PMSD pMSD;
  547. PEPD pEPD = (PEPD) pvUser;
  548. PSPD pSPD = pEPD->pSPD;
  549. DPFX(DPFPREP,5, "ENTER Connect Retry Timeout pEPD=%p", pEPD);
  550. ASSERT_EPD(pEPD);
  551. Lock(&pEPD->EPLock);
  552. if((pEPD->LinkTimer != pvHandle)||(pEPD->LinkTimerUnique != uiUnique))
  553. {
  554. // Timer been reset! This is a spurious fire and should be ignored.
  555. RELEASE_EPD(pEPD, "UNLOCK: (Spurious (ie late) firing of CONNECT timer)"); // releases EPLock
  556. DPFX(DPFPREP,7, "(%p) Ignoring late CONNECT timer", pEPD);
  557. return;
  558. }
  559. pMSD = pEPD->pCommand;
  560. if(pMSD == NULL)
  561. {
  562. pEPD->LinkTimer = 0;
  563. RELEASE_EPD(pEPD, "UNLOCK: (Conn retry timer - after completion)"); // releases EPLock
  564. return;
  565. }
  566. ASSERT_MSD(pMSD);
  567. // Make sure this doesn't go away when we leave the lock
  568. LOCK_MSD(pMSD, "Hold For Lock");
  569. Unlock(&pEPD->EPLock); // Release before taking higher level lock
  570. // Take both locks in the proper order
  571. Lock(&pMSD->CommandLock);
  572. Lock(&pEPD->EPLock);
  573. pEPD->LinkTimer = 0;
  574. // This timer should only be running in a CONNECTING state
  575. if( (pMSD->ulMsgFlags1 & (MFLAGS_ONE_CANCELLED | MFLAGS_ONE_COMPLETE)) ||
  576. (!(pEPD->ulEPFlags & EPFLAGS_STATE_CONNECTING)))
  577. {
  578. // Release MSD before EPD since final EPD will call out to SP and we don't want any locks held
  579. RELEASE_MSD(pMSD, "Hold for lock"); // Remove temporary reference and release lock
  580. RELEASE_EPD(pEPD, "UNLOCK (Conn Retry Timer)"); // Remove reference for timer, releases EPLock
  581. return; // and thats all for now
  582. }
  583. // IF more retries are allowed and command is still active, send another CONNECT frame
  584. if(pEPD->uiNumRetriesRemaining-- > 0)
  585. {
  586. pEPD->uiRetryTimeout = _MIN(pEPD->uiRetryTimeout * 2, 5000); // exp backoff to a max of 5000ms until we establish our first RTT
  587. if(pMSD->CommandID == COMMAND_ID_CONNECT)
  588. {
  589. DPFX(DPFPREP,5, "(%p) Sending CONNECT Frame", pEPD);
  590. (void) SendCommandFrame(pEPD, FRAME_EXOPCODE_CONNECT, 0, 0, FALSE);
  591. }
  592. // Listen -- retry CONNECTED frame
  593. else
  594. {
  595. pEPD->ulEPFlags |= EPFLAGS_CHECKPOINT_INIT; // We will expect a reply to this frame
  596. DPFX(DPFPREP,5, "(%p) Sending CONNECTED Frame", pEPD);
  597. (void) SendCommandFrame(pEPD, FRAME_EXOPCODE_CONNECTED, 0, 0, FALSE);
  598. }
  599. // Send the next ping
  600. DPFX(DPFPREP,7, "(%p) Setting Connect Retry Timer", pEPD);
  601. pEPD->LinkTimer = pvHandle;
  602. RescheduleProtocolTimer(pSPD, pvHandle, pEPD->uiRetryTimeout, 100,
  603. ConnectRetryTimeout, (PVOID) pEPD, &pEPD->LinkTimerUnique);
  604. Unlock(&pEPD->EPLock);
  605. RELEASE_MSD(pMSD, "Hold for lock"); // Remove temporary reference and release lock
  606. // Since we have re-started timer, we don't adjust refcount
  607. }
  608. else
  609. {
  610. // We got no response and timed out.
  611. if(pMSD->CommandID == COMMAND_ID_CONNECT)
  612. {
  613. DECREMENT_EPD(pEPD, "UNLOCK: (Connect Timer (Failure Path))");// Dec Ref for this timer, releases EPLock
  614. // This will only happen once since we know DoCancel has not been called due to our CANCELLED check above,
  615. // and if it is now called it will see our CANCELLED flag set below. We also know that the success case hasn't
  616. // happened or COMPLETE above would have been set. We also have not had two timers get here because of the
  617. // CANCELLED check above and set here.
  618. pMSD->ulMsgFlags1 |= MFLAGS_ONE_CANCELLED;
  619. // Unlink EPD from MSD
  620. ASSERT(pEPD->pCommand == pMSD);
  621. pEPD->pCommand = NULL;
  622. DECREMENT_MSD(pMSD, "EPD Ref"); // Release Reference from EPD
  623. Unlock(&pMSD->CommandLock); // DropLink may call into the SP.
  624. DropLink(pEPD);// Releases EPLock
  625. DECREMENT_MSD(pMSD, "Hold for lock"); // Remove temporary reference
  626. Lock(&pMSD->CommandLock);
  627. DPFX(DPFPREP,1, "(%p) Connect retries exhausted, completing Connect, pMSD[%p]", pEPD, pMSD);
  628. CompleteConnect(pMSD, pMSD->pSPD, NULL, DPNERR_NORESPONSE); // releases CommandLock
  629. }
  630. // Listen - clean up associated state info, then blow away end point
  631. else
  632. {
  633. DPFX(DPFPREP,1, "(%p) Connect retries exhausted on Listen, Kill Connection, pMSD[%p]", pEPD, pMSD);
  634. if(pEPD->ulEPFlags & EPFLAGS_LINKED_TO_LISTEN)
  635. {
  636. pEPD->ulEPFlags &= ~(EPFLAGS_LINKED_TO_LISTEN);
  637. pEPD->blSPLinkage.RemoveFromList(); // Unlink EPD from Listen Queue
  638. }
  639. ASSERT(pEPD->pCommand != NULL);
  640. pEPD->pCommand = NULL; // Unlink listen from EPD
  641. DECREMENT_MSD(pMSD, "EPD Ref"); // release reference for link to EPD
  642. RELEASE_MSD(pMSD, "Hold for lock"); // Remove temporary reference and release lock
  643. DECREMENT_EPD(pEPD, "UNLOCK: (Connect Timer (Failure Path))");// Dec Ref for this timer, SPLock not already held
  644. DropLink(pEPD);
  645. }
  646. }
  647. }
  648. /*
  649. ** Process Connection Request
  650. **
  651. ** Somebody wants to connect to us. If we have a listen posted we will
  652. ** fill out a checkpoint structure to correlate his response and we will fire
  653. ** off a CONNECTED frame ourselves
  654. **
  655. ** Since our connection will not be up until we receive a CONNECTED response
  656. ** to our response we will need to set up a retry timer ourselves.
  657. **
  658. ** Called with ep lock held. Returns with ep lock released
  659. */
  660. #undef DPF_MODNAME
  661. #define DPF_MODNAME "ProcessConnectRequest"
  662. VOID ProcessConnectRequest(PSPD pSPD, PEPD pEPD, PCFRAME pCFrame)
  663. {
  664. PMSD pMSD = NULL;
  665. DPFX(DPFPREP,DPF_CALLIN_LVL, "CONNECT REQUEST RECEIVED; EPD=%p SessID=%x", pEPD, pCFrame->dwSessID);
  666. AssertCriticalSectionIsTakenByThisThread(&pEPD->EPLock, TRUE);
  667. if((pMSD = pEPD->pCommand) == NULL)
  668. {
  669. // There are two cases: we are a connecting endpoint or we are a listening endpoint. In the connecting
  670. // case we will fail in the following 'if' because we do not allow connections on non-listening endpoints.
  671. // In the listening case, the fact that pMSD is NULL means that we have received the other side's
  672. // CONNECTED packet, which also tells us that they have seen our CONNECTED packet. The only reason we
  673. // would now be seeing a CONNECT is if a) there was a stale, late-delivered packet on the wire, or b)
  674. // some malicious user is spoofing packets to us. In both cases, ignoring the packet is the right
  675. // thing to do.
  676. // There is a third possibility for the listening endpoint case, and that is that the other side went down
  677. // and we didn't realize it, and they are now attempting to reconnect. The best we can do is wait the full
  678. // timeout until the link is torn down on our side, and let their retries make the connection for them at
  679. // that time. If we cut the timeout short, we have no way to know that a malicious user can't force
  680. // legitimate connections closed by spoofing CONNECT packets.
  681. DPFX(DPFPREP,1, "(%p) CONNECT Frame received on CONNECTED link, ignoring", pEPD);
  682. DNASSERTX(FALSE, 3);
  683. Unlock(&pEPD->EPLock);
  684. return;
  685. }
  686. ASSERT_MSD(pMSD);
  687. LOCK_MSD(pMSD, "LOCK: Hold For Lock"); // Place reference on Cmd until we can lock it
  688. Unlock(&pEPD->EPLock);
  689. Lock(&pMSD->CommandLock);
  690. Lock(&pEPD->EPLock); // Serialize access to EPD (this may not really be new sess)
  691. // Make sure this endpoint was listening for connections
  692. // This could be a Connect in which case this is a malicious packet. It could also be a Disonnect or Disconnect Response
  693. // if the endpoint is being disconnected. In any case we just need to ignore the connect packet.
  694. if(pMSD->CommandID != COMMAND_ID_LISTEN)
  695. {
  696. DPFX(DPFPREP,1, "(%p) PROTOCOL RECEIVED CONNECT REQUEST ON A NON-LISTENING ENDPOINT, IGNORING, pMSD[%p]", pEPD, pMSD);
  697. DNASSERTX(FALSE, 3);
  698. Unlock(&pEPD->EPLock);
  699. RELEASE_MSD(pMSD, "UNLOCK: Hold For Lock");
  700. return;
  701. }
  702. // Make sure we can work with this version
  703. if((pCFrame->dwVersion >> 16) != (DNET_VERSION_NUMBER >> 16))
  704. {
  705. DPFX(DPFPREP,1, "(%p) PROTOCOL RECEIVED CONNECT REQUEST FROM AN INCOMPATIBLE VERSION(theirs %x, ours %x), DROPPING LINK", pEPD, pCFrame->dwVersion, DNET_VERSION_NUMBER);
  706. DNASSERTX(FALSE, 2);
  707. RELEASE_MSD(pMSD, "UNLOCK: Hold For Lock");
  708. RejectInvalidPacket(pEPD);
  709. //above call will release ep lock
  710. return;
  711. }
  712. // Make sure we've been sent a valid session identity
  713. // N.B. We only added the fact that session ids couldn't be zero when signing was added
  714. if (VersionSupportsSigning(pCFrame->dwVersion) && pCFrame->dwSessID==0)
  715. {
  716. DPFX(DPFPREP,1, "(%p) Protocol received an invalid session identity", pEPD);
  717. DNASSERTX(FALSE, 2);
  718. RELEASE_MSD(pMSD, "UNLOCK: Hold For Lock");
  719. RejectInvalidPacket(pEPD);
  720. //above call will release ep lock
  721. return;
  722. }
  723. // Make sure the listen command is still valid
  724. if(pMSD->ulMsgFlags1 & MFLAGS_ONE_CANCELLED)
  725. {
  726. DPFX(DPFPREP,1, "(%p) PROTOCOL RECEIVED CONNECT REQUEST ON A LISTEN THAT IS CANCELLED, DROPPING LINK, pMSD[%p]", pEPD, pMSD);
  727. RELEASE_MSD(pMSD, "UNLOCK: Hold For Lock");
  728. RejectInvalidPacket(pEPD); // This releases the EPLock
  729. return;
  730. }
  731. // We shouldn't use the pMSD past here since this will unlock it
  732. RELEASE_MSD(pMSD, "UNLOCK: Hold For Lock");
  733. // Are we already connected?
  734. if(pEPD->ulEPFlags & (EPFLAGS_STATE_CONNECTED | EPFLAGS_STATE_TERMINATING))
  735. {
  736. DPFX(DPFPREP,1, "(%p) CONNECT Frame received on Connected or Terminating link, ignoring", pEPD);
  737. // If connection has been completed then we don't need to do more work
  738. // This can happen if we failed to cancel the connect timer in ProcessConnectedResponse and a CONNECT packet
  739. // comes in.
  740. Unlock(&pEPD->EPLock);
  741. return;
  742. }
  743. // If we are already in a CONNECTING state then this is not the first CONNECT frame we have seen. If the SessID's
  744. // match, then the partner probably didn't hear our first response, so we will resend it. If the SessID's don't
  745. // match, then either the partner aborted and is starting with a new SessID, or a malicious party is spoofing the
  746. // partner's address, and sending a bogus packet. In either of these cases we will ignore the connect. A partner
  747. // aborting a connect mid-way probably crashed anyway, and waiting for us to timeout the first connect attempt
  748. // will be the least of their worries.
  749. if(pEPD->ulEPFlags & EPFLAGS_STATE_CONNECTING)
  750. {
  751. if(pCFrame->dwSessID != pEPD->dwSessID)
  752. {
  753. DPFX(DPFPREP,1, "(%p) Received non-matching SessionID, ignoring CONNECT", pEPD);
  754. Unlock(&pEPD->EPLock);
  755. return;
  756. }
  757. // Unexpected CONNECT Frame has same Session ID. Partner probably lost our response. We will
  758. // respond again to this one.
  759. DPFX(DPFPREP,1, "(%p) Received duplicate CONNECT request. Sending another response...", pEPD);
  760. // Listen side must set this before sending CONNECTED
  761. pEPD->ulEPFlags |= EPFLAGS_CHECKPOINT_INIT; // We will expect a reply to this frame
  762. // If this fails they will be sending another CONNECT anyway, so we do nothing
  763. (void) SendCommandFrame(pEPD, FRAME_EXOPCODE_CONNECTED, pCFrame->bMsgID, 0, FALSE);
  764. Unlock(&pEPD->EPLock);
  765. return;
  766. }
  767. // Transition state
  768. ASSERT(pEPD->ulEPFlags & EPFLAGS_STATE_DORMANT);
  769. pEPD->ulEPFlags &= ~(EPFLAGS_STATE_DORMANT);
  770. pEPD->ulEPFlags |= EPFLAGS_STATE_CONNECTING;
  771. // If version number is high enough then mark the fact that this link could potentially use signing
  772. // The fact we're handling a CONNECT here (rather than in unconnected data) indicates that the session
  773. // is actually unsigned, but flicking this bit ensure we'll use the new style keep alives
  774. if (VersionSupportsSigning(pCFrame->dwVersion) && VersionSupportsSigning(DNET_VERSION_NUMBER))
  775. {
  776. pEPD->ulEPFlags2|=EPFLAGS2_SUPPORTS_SIGNING;
  777. }
  778. pEPD->dwSessID = pCFrame->dwSessID; // Use this SessID in all C-traffic
  779. pEPD->ulEPFlags |= EPFLAGS_CHECKPOINT_INIT; // We will expect a reply to this frame
  780. DPFX(DPFPREP,5, "(%p) Sending CONNECTED Frame", pEPD);
  781. (void) SendCommandFrame(pEPD, FRAME_EXOPCODE_CONNECTED, pCFrame->bMsgID, 0, FALSE);
  782. pEPD->uiRetryTimeout = pSPD->pPData->dwConnectTimeout; // Got to start somewhere
  783. pEPD->uiNumRetriesRemaining = pSPD->pPData->dwConnectRetries; // w/exponential wait
  784. LOCK_EPD(pEPD, "LOCK: (CONNECT RETRY TIMER)"); // Create reference for timer
  785. DPFX(DPFPREP,5, "(%p) Setting Connect Timer", pEPD);
  786. ScheduleProtocolTimer(pSPD, pEPD->uiRetryTimeout, 100, ConnectRetryTimeout, (PVOID) pEPD,
  787. &pEPD->LinkTimer, &pEPD->LinkTimerUnique);
  788. if (pEPD->LinkTimer == 0)
  789. {
  790. DPFX(DPFPREP,1, "(%p) Setting Connect Retry Timer failed", pEPD);
  791. // If we can't even set timers due to low memory, then the best we can do is
  792. // abandon this new connection and hope to give good service to our existing
  793. // connections.
  794. DECREMENT_EPD(pEPD, "UNLOCK: (CONNECT RETRY TIMER)");
  795. pEPD->ulEPFlags &= ~(EPFLAGS_LINKED_TO_LISTEN);
  796. pEPD->blSPLinkage.RemoveFromList(); // Unlink EPD from Listen Queue
  797. // Unlink the MSD from the EPD
  798. ASSERT(pEPD->pCommand == pMSD);
  799. pEPD->pCommand = NULL;
  800. DropLink(pEPD); // This releases EPLock
  801. Lock(&pMSD->CommandLock);
  802. RELEASE_MSD(pMSD, "EPD Ref");
  803. return;
  804. }
  805. Unlock(&pEPD->EPLock);
  806. }
  807. /*
  808. ** CompleteConnectedResponse
  809. **
  810. ** This is completes a connection response: marking the link as in use, initialize the send/recv parameters and
  811. ** indicates the connection to the core. Its called from both ProcessConnectedResponse and
  812. ** ProcessConnnectedSignedResponse, performing the link setup that common to both
  813. **
  814. ** Called with both EP Lock and MSD Command Lock held. Returns with both released.
  815. **/
  816. #undef DPF_MODNAME
  817. #define DPF_MODNAME "CompleteConnectedResponse"
  818. void CompleteConnectedResponse(PProtocolData pPData, PEPD pEPD, PMSD pMSD, PCFRAME pCFrame, DWORD dwInitialRTT, DWORD tNow)
  819. {
  820. DNASSERT((pEPD->ulEPFlags & (EPFLAGS_STATE_DORMANT | EPFLAGS_STATE_CONNECTING | EPFLAGS_STATE_TERMINATING))==0);
  821. AssertCriticalSectionIsTakenByThisThread(&pEPD->EPLock, TRUE);
  822. AssertCriticalSectionIsTakenByThisThread(&pMSD->CommandLock, TRUE);
  823. PSPD pSPD=pEPD->pSPD;
  824. //mark that the link is connected and ready to send
  825. pEPD->ulEPFlags |= EPFLAGS_STATE_CONNECTED | EPFLAGS_STREAM_UNBLOCKED;
  826. //clip the measured RTT to within a sensible range
  827. if (((int) dwInitialRTT) <= 0)
  828. {
  829. DPFX(DPFPREP,1, "(%p) dwInitialRTT measured as %d, using 1ms instead", pEPD, dwInitialRTT);
  830. dwInitialRTT = 1;
  831. }
  832. else if (dwInitialRTT>pPData->dwSendRetryIntervalLimit/2)
  833. {
  834. dwInitialRTT=pPData->dwSendRetryIntervalLimit/2;
  835. }
  836. if (VersionSupportsCoalescence(pCFrame->dwVersion)==FALSE)
  837. {
  838. DPFX(DPFPREP,1, "(%p) Partner does not support coalescence", pEPD);
  839. pEPD->ulEPFlags2 |= EPFLAGS2_NOCOALESCENCE;
  840. }
  841. #ifdef DPNBUILD_COALESCENEVER
  842. else
  843. {
  844. DPFX(DPFPREP,7, "(%p) Disabling coalescence sends even though partner supports them.", pEPD);
  845. pEPD->ulEPFlags2 |= EPFLAGS2_NOCOALESCENCE;
  846. }
  847. #endif // DPNBUILD_COALESCENEVER
  848. if(pEPD->ulEPFlags & EPFLAGS_LINKED_TO_LISTEN)
  849. {
  850. pEPD->ulEPFlags &= ~(EPFLAGS_LINKED_TO_LISTEN);
  851. pEPD->blSPLinkage.RemoveFromList(); // Unlink EPD from Listen Queue
  852. }
  853. DPFX(DPFPREP,1, "(%p) Partner Reported Version: %x, Our Version: %x, tNow %u Initial RTT %u",
  854. pEPD, pCFrame->dwVersion, DNET_VERSION_NUMBER, tNow, dwInitialRTT);
  855. //set up EP ready to received and send with some sensible initial state
  856. InitLinkParameters(pEPD, dwInitialRTT, tNow);
  857. DPFX(DPFPREP,5, "(%p) N(R) = 0, N(S) = 0", pEPD);
  858. pEPD->bNextSend = 0;
  859. pEPD->bNextReceive = 0;
  860. pEPD->bHighestAck = 0xFF;
  861. /*
  862. ** It turns out that the first RTT measurement is a very bad one (slow) because because
  863. ** it includes overhead for opening and binding a new socket, endpoint creation, etc.
  864. ** Therefore each side will take another quick sample right away. The initial calculations
  865. ** above will still serve as an initial RTT until this better sample is available
  866. */
  867. // Take another RTT sample
  868. DPFX(DPFPREP,7,"(%p) Performing Checkpoint", pEPD);
  869. SendKeepAlive(pEPD);
  870. // Cleanup connect operation
  871. if(pMSD->CommandID == COMMAND_ID_CONNECT)
  872. {
  873. // There was a CONNECT Command issued that now must be completed
  874. DECREMENT_MSD(pMSD, "Hold For Lock"); // Remove temporary reference from above.
  875. // This will not happen twice because both COMPLETE and CANCELLED are checked above, and the
  876. // call to CompleteConnect will set COMPLETE.
  877. // Unlink the MSD from the EPD
  878. ASSERT(pEPD->pCommand == pMSD);
  879. pEPD->pCommand = NULL;
  880. DECREMENT_MSD(pMSD, "EPD Ref"); // Release reference for EPD link
  881. Unlock(&pEPD->EPLock);
  882. CompleteConnect(pMSD, pSPD, pEPD, DPN_OK); // This releases the MSD Lock
  883. }
  884. else
  885. { // LISTENING
  886. // We were the listener. We will indicate a Connect event on the listen
  887. // command w/o completing the Listen
  888. ASSERT(pMSD->CommandID == COMMAND_ID_LISTEN);
  889. // We know this will only happen once because the person who does it will transition us out
  890. // of the CONNECTING state, and we can't get here unless we are in that state.
  891. // Unlink the MSD from the EPD
  892. ASSERT(pEPD->pCommand == pMSD);
  893. pEPD->pCommand = NULL;
  894. DECREMENT_MSD(pMSD, "EPD Ref"); // Release reference for EPD link
  895. Unlock(&pEPD->EPLock);
  896. Unlock(&pMSD->CommandLock);
  897. AssertNoCriticalSectionsFromGroupTakenByThisThread(&g_blProtocolCritSecsHeld);
  898. DPFX(DPFPREP,DPF_CALLOUT_LVL, "(%p) Calling Core->IndicateConnect, pMSD[%p], Core Context[%p]", pEPD, pMSD, pMSD->Context);
  899. pSPD->pPData->pfVtbl->IndicateConnect(pSPD->pPData->Parent, pMSD->Context, (PHANDLE) pEPD, &pEPD->Context);
  900. // Complete any receives that queued while waiting for IndicateConnect
  901. Lock(&pEPD->EPLock);
  902. ReceiveComplete(pEPD); // releases EPLock
  903. // Release the final reference on the MSD AFTER indicating to the Core
  904. Lock(&pMSD->CommandLock);
  905. RELEASE_MSD(pMSD, "Hold For Lock"); // release temp MSD (releases lock)
  906. }
  907. }
  908. /*
  909. ** Process Connected Response
  910. **
  911. ** A response to a connection request has arrived (or a response to
  912. ** our connection response). Now the connection is officially up (on
  913. ** our end of the circuit). Set the link-state according to our first
  914. ** RTT sample and get ready to party.
  915. **
  916. ** If we are the originating party, we will want to send a
  917. ** CONNECTED frame to our partner, even though the connection is
  918. ** complete from our perspective. This will allow partner to establish
  919. ** his baseline RTT and clock bias as we can do here. In this case, he
  920. ** will have his POLL bit set in the frame we just received.
  921. **
  922. ** Now, we might get additional CONNECTED frames after the first one
  923. ** where we startup the link. This would most likely be due to our CONNECTED
  924. ** response getting lost. So if we get a CONNECTED frame with POLL set
  925. ** after our link is up, we will just go ahead and respond again without
  926. ** adjusting our state.
  927. **
  928. ** Note about Locks:
  929. **
  930. ** This code is complicated by the precedence of CritSec ownership. To simplify
  931. ** as much as possible we will take the Listen command lock at the very start of the
  932. ** procedure (when appropriate) because it has the highest level lock. This prevents
  933. ** us from completing the whole connection process and then finding that the Listen
  934. ** went away so we can't indicate it to the user.
  935. **
  936. ** We keep a RefCnt on the Listen so it won't go away while a new session
  937. ** is pending on it.
  938. */
  939. #undef DPF_MODNAME
  940. #define DPF_MODNAME "ProcessConnectedResponse"
  941. VOID ProcessConnectedResponse(PSPD pSPD, PEPD pEPD, PCFRAME pCFrame, DWORD tNow)
  942. {
  943. PCHKPT pCP;
  944. PMSD pMSD;
  945. DWORD dwInitialRTT;
  946. DPFX(DPFPREP,DPF_CALLIN_LVL, "CONNECT RESPONSE RECEIVED (pEPD=0x%p)", pEPD);
  947. AssertCriticalSectionIsTakenByThisThread(&pEPD->EPLock, TRUE);
  948. // If the link has not seen a CONNECT or issued a CONNECT, we do not expect a CONNECTED.
  949. // Since this is the only reason this EPD was created, we will tear it down by rejecting
  950. // the connection.
  951. if (pEPD->ulEPFlags & EPFLAGS_STATE_DORMANT)
  952. {
  953. DPFX(DPFPREP,1, "(%p) CONNECTED response received on a dormant link, dropping link", pEPD);
  954. RejectInvalidPacket(pEPD);
  955. //ep lock will be released by above function
  956. return;
  957. }
  958. // There is a possibility that the link is in the terminating state. If so, we don't
  959. // care about CONNECTED packets.
  960. if (pEPD->ulEPFlags & EPFLAGS_STATE_TERMINATING)
  961. {
  962. DPFX(DPFPREP,1, "(%p) CONNECTED response received on a terminating link, ignoring", pEPD);
  963. Unlock(&pEPD->EPLock);
  964. return;
  965. }
  966. // At this point either we are connecting and this is a response, or someone has connected to us, and this is
  967. // the reply to our response. Note that this could be a duplicate of one of these cases, so we may already
  968. // be in a connected state. There is also the possiblity that these are malicious packets.
  969. ASSERT(pEPD->ulEPFlags & (EPFLAGS_STATE_CONNECTING | EPFLAGS_STATE_CONNECTED));
  970. // If the SessID does not match, ignore the CONNECTED packet. This is either a malicious attempt at messing
  971. // up a legitimate connection, or our partner aborted and has come back in with a new session ID.
  972. if(pEPD->dwSessID != pCFrame->dwSessID)
  973. {
  974. DPFX(DPFPREP,1, "(%p) CONNECTED response has bad SessID, ignoring", pEPD);
  975. Unlock(&pEPD->EPLock);
  976. return;
  977. }
  978. // If we have completed our side of the connection then our only responsibility is to send responses
  979. // if our partner is still POLLING us.
  980. if(pEPD->ulEPFlags & EPFLAGS_STATE_CONNECTED)
  981. {
  982. // A Listen will set POLL on the CONNECTED packet, a Connect will not
  983. // If we're a signed link then this is a bogus packet, since we should only get CONNECTEDSIGNED responses
  984. // hence we do nothing in that case
  985. if ((pCFrame->bCommand & PACKET_COMMAND_POLL) && ((pEPD->ulEPFlags2 & EPFLAGS2_SIGNED_LINK)==0))
  986. {
  987. DPFX(DPFPREP,5, "(%p) Duplicate CONNECTED frame, sending another response...", pEPD);
  988. // If this fails we will let the partner's retry catch it.
  989. (void) SendCommandFrame(pEPD, FRAME_EXOPCODE_CONNECTED, pCFrame->bMsgID, 0, FALSE);
  990. }
  991. Unlock(&pEPD->EPLock);
  992. return;
  993. }
  994. // Since we are not CONNECTED yet, we must be in a CONNECTING state in order to receive a
  995. // CONNECTED response.
  996. ASSERT(pEPD->ulEPFlags & EPFLAGS_STATE_CONNECTING);
  997. // MSD should not be NULL if we are in the CONNECTING state
  998. pMSD = pEPD->pCommand;
  999. ASSERT_MSD(pMSD);
  1000. LOCK_MSD(pMSD, "Hold For Lock"); // Place reference on Cmd until we can lock it
  1001. Unlock(&pEPD->EPLock);
  1002. Lock(&pMSD->CommandLock);
  1003. Lock(&pEPD->EPLock);
  1004. // Since we left the EPLock, we must verify that we are still in the connecting state
  1005. if (!(pEPD->ulEPFlags & EPFLAGS_STATE_CONNECTING))
  1006. {
  1007. DPFX(DPFPREP,1, "(%p) EPD left the CONNECTING state while we were out of the lock, ignoring CONNECTED frame", pEPD);
  1008. Unlock(&pEPD->EPLock);
  1009. RELEASE_MSD(pMSD, "Hold For Lock"); // This releases the command lock
  1010. return;
  1011. }
  1012. if(pMSD->ulMsgFlags1 & (MFLAGS_ONE_CANCELLED | MFLAGS_ONE_COMPLETE))
  1013. {
  1014. DPFX(DPFPREP,1, "(%p) Connect/Listen command cancelled or complete, ignoring CONNECTED frame", pEPD);
  1015. // Whoever cancelled the Listen should be disconnecting this connection too
  1016. // so all we have to do here is bail out.
  1017. Unlock(&pEPD->EPLock);
  1018. RELEASE_MSD(pMSD, "Hold For Lock"); // This releases the command lock
  1019. return;
  1020. }
  1021. // Next, take care of this guy's reply if we still owe him one
  1022. // A Listen will set POLL on the CONNECTED packet, a Connect will not
  1023. if(pCFrame->bCommand & PACKET_COMMAND_POLL)
  1024. {
  1025. DPFX(DPFPREP,5, "(%p) Sending CONNECTED Frame", pEPD);
  1026. if(SendCommandFrame(pEPD, FRAME_EXOPCODE_CONNECTED, pCFrame->bMsgID, 0, FALSE) != DPN_OK)
  1027. {
  1028. DPFX(DPFPREP,5, "(%p) Sending CONNECTED Frame Failed", pEPD);
  1029. // We cannot complete the connection... we will just let things time out
  1030. RELEASE_MSD(pMSD, "Hold For Lock");
  1031. Unlock(&pEPD->EPLock); // Protect the pCommand field
  1032. return;
  1033. }
  1034. }
  1035. // Now we can setup our new link, but only if this frame Correlates to a checkpoint we have outstanding
  1036. // so we can seed our state variables.
  1037. // Can we correlate resp?
  1038. pCP = LookupCheckPoint(pEPD, pCFrame->bRspID);
  1039. if (pCP==NULL)
  1040. {
  1041. /*
  1042. ** Uncorrelated CONNECTED frame. How can this happen? Parter's response must
  1043. ** have been dropped, so he is retrying his CONN frame. Since we are trying to
  1044. ** measure an accurate RTT we dont want to use his retry against our original
  1045. ** request, so he zeros out his Resp correlator. We will eventually retry with
  1046. ** new correlator and hopefully that frame will get through.
  1047. */
  1048. DPFX(DPFPREP,1, "(%p) Uncorrelated CONNECTED frame arrives", pEPD);
  1049. Unlock(&pEPD->EPLock);
  1050. RELEASE_MSD(pMSD, "Hold For Lock");
  1051. return;
  1052. }
  1053. // We are connected, so shut off retry timer
  1054. if(pEPD->LinkTimer != 0)
  1055. {
  1056. DPFX(DPFPREP,5, "(%p) Cancelling Connect Timer", pEPD);
  1057. if(CancelProtocolTimer(pSPD, pEPD->LinkTimer, pEPD->LinkTimerUnique) == DPN_OK)
  1058. {
  1059. DECREMENT_EPD(pEPD, "UNLOCK: (Conn Retry Timer - Connect Complete)"); // remove reference for timer, SPLock not already held
  1060. }
  1061. else
  1062. {
  1063. DPFX(DPFPREP,5, "(%p) Cancelling Connect Timer Failed", pEPD);
  1064. }
  1065. pEPD->LinkTimer = 0; // This will prevent timer from trying to do any work if it couldn't cancel
  1066. }
  1067. //twiddle the connecting bit off. We'll set the CONNECTED bit when we complete the connected response
  1068. ASSERT(pEPD->ulEPFlags & EPFLAGS_STATE_CONNECTING);
  1069. pEPD->ulEPFlags &= ~(EPFLAGS_STATE_CONNECTING);
  1070. // If version number is high enough then mark the fact that this link could potentially use signing
  1071. // We've obviously got an unsigned link (because we got a CONNECT rather than a CONNECTSIGNED), but
  1072. // this'll ensure we use the new style Keep Alives rather than the old
  1073. // N.B. The listening side may have already flicked this bit when it got the initial CONNECT. So this
  1074. // is only really necessary for the connecting side. But there's no point doing an extra check for a harmless or op.
  1075. if (VersionSupportsSigning(pCFrame->dwVersion) && VersionSupportsSigning(DNET_VERSION_NUMBER))
  1076. {
  1077. pEPD->ulEPFlags2|=EPFLAGS2_SUPPORTS_SIGNING;
  1078. }
  1079. //compute the initial RTT based on the checkpoint and then clean the checkpoint state up
  1080. dwInitialRTT = tNow - pCP->tTimestamp;
  1081. ChkPtPool.Release(pCP);
  1082. FlushCheckPoints(pEPD); // Make sure we do this before the InitCheckPoint
  1083. //Finally set link up and indicate connect to the core
  1084. //This runs the code path common to both CONNECTED and CONNECTEDSIGNED responses
  1085. CompleteConnectedResponse(pSPD->pPData, pEPD, pMSD, pCFrame, dwInitialRTT, tNow);
  1086. //above call releases both EP lock and MSD command lock
  1087. }
  1088. /*
  1089. ** ProcessConnectedSignedResponse
  1090. **
  1091. ** This is called when a CONNECTEDSIGNED cframe is received and we're either already connected/connecting
  1092. ** or we've just completed the 3 way handshake using unconnected data and are now ready to complete
  1093. ** the connection.
  1094. **
  1095. ** Called with the EP Lock held. Returns with it released.
  1096. **/
  1097. #undef DPF_MODNAME
  1098. #define DPF_MODNAME "ProcessConnectedSignedResponse"
  1099. VOID ProcessConnectedSignedResponse(PSPD pSPD, PEPD pEPD, CFRAME_CONNECTEDSIGNED * pCFrame, DWORD tNow)
  1100. {
  1101. PMSD pMSD;
  1102. DPFX(DPFPREP,DPF_CALLIN_LVL, "CONNECT SIGNED response received (pEPD=0x%p)", pEPD);
  1103. AssertCriticalSectionIsTakenByThisThread(&pEPD->EPLock, TRUE);
  1104. //If the link is already being terminated (possibly this is a repeat reponse from a listener,
  1105. //so we've already indicated that the link is up and then torn it down) then ignore this frame
  1106. if (pEPD->ulEPFlags & EPFLAGS_STATE_TERMINATING)
  1107. {
  1108. DPFX(DPFPREP,1, "(%p) CONNECTED SIGNED response received on a terminating link, ignoring", pEPD);
  1109. Unlock(&pEPD->EPLock);
  1110. return;
  1111. }
  1112. //If endpoint has already got a session identity it must match the one presented in this frame
  1113. //anything else indicates a malicious packet. Also the supplied sesion identity must be none zero
  1114. if ((pCFrame->dwSessID==0) || (pEPD->dwSessID && pEPD->dwSessID!=pCFrame->dwSessID))
  1115. {
  1116. DPFX(DPFPREP,1, "(%p) CONNECTED SIGNED response received with invalid session identity "
  1117. "pCFrame->dwSessID[%u] pEPD->dwSessID[%u]", pEPD, pCFrame->dwSessID, pEPD->dwSessID);
  1118. RejectInvalidPacket(pEPD);
  1119. //above call will release EP lock
  1120. return;
  1121. }
  1122. //there are basically 3 conditions we care about.
  1123. //1. Endpoint is already connected. In this case we're probably the connector and our original
  1124. //connected signed frame to the listener was dropped. We just need to resend that response
  1125. //2. Endpoint is connecting. In this case we're the connector and we've just got our first response
  1126. //to our initial CONNECT frame. Time to complete the connection and make endpoint connected
  1127. //3. Endpoint is dormant. In this case we're the listener and we've just completed the three way
  1128. //handshake for a valid connection. Time to complete the connection and make endpoint connected
  1129. //connected case is simplest to handle
  1130. if(pEPD->ulEPFlags & EPFLAGS_STATE_CONNECTED)
  1131. {
  1132. //if the poll bit is set it means the frame originated from a listening side, so we should
  1133. //send a response back to them. If endpoint isn't marked as already being in a signed session
  1134. //then something is screwy and we should ignore this frame
  1135. if((pCFrame->bCommand & PACKET_COMMAND_POLL) &&
  1136. (pEPD->ulEPFlags2 & EPFLAGS2_SIGNED_LINK))
  1137. {
  1138. DPFX(DPFPREP,5, "(%p) Duplicate CONNECTED SIGNED frame, sending another response...", pEPD);
  1139. SendConnectedSignedFrame(pEPD, pCFrame, tNow);
  1140. }
  1141. Unlock(&pEPD->EPLock);
  1142. return;
  1143. }
  1144. //we're going to need to complete the connection as we must have either just got a response to our CONNECT
  1145. //(CONNECTING case) or we're completed the 3 way initial connect handshake (DORMANT case)
  1146. DNASSERT(pEPD->ulEPFlags & (EPFLAGS_STATE_DORMANT | EPFLAGS_STATE_CONNECTING));
  1147. //The MSD will either refer to a listen or a connect. We have to fiddle around with locking order
  1148. //at this point, to ensure we take the MSD lock before the EP lock
  1149. pMSD = pEPD->pCommand;
  1150. ASSERT_MSD(pMSD);
  1151. LOCK_MSD(pMSD, "Hold For Lock"); // Place reference on Cmd until we can lock it
  1152. Unlock(&pEPD->EPLock);
  1153. Lock(&pMSD->CommandLock);
  1154. Lock(&pEPD->EPLock);
  1155. // Since we left the EPLock, we must verify that we are still good to process this frame
  1156. if ((pEPD->ulEPFlags & (EPFLAGS_STATE_DORMANT | EPFLAGS_STATE_CONNECTING))==0)
  1157. {
  1158. DPFX(DPFPREP,1, "(%p) EPD now in invalid state to process CONNECTED SIGNED frame", pEPD);
  1159. Unlock(&pEPD->EPLock);
  1160. RELEASE_MSD(pMSD, "Hold For Lock"); // This releases the command lock
  1161. return;
  1162. }
  1163. if(pMSD->ulMsgFlags1 & (MFLAGS_ONE_CANCELLED | MFLAGS_ONE_COMPLETE))
  1164. {
  1165. DPFX(DPFPREP,1, "(%p) Connect/Listen command cancelled or complete, ignoring CONNECTED SIGNED frame", pEPD);
  1166. // Whoever cancelled the Connect/Listen should be disconnecting this connection too
  1167. // so all we have to do here is bail out.
  1168. Unlock(&pEPD->EPLock);
  1169. RELEASE_MSD(pMSD, "Hold For Lock"); // This releases the command lock
  1170. return;
  1171. }
  1172. DWORD dwInitialRTT;
  1173. //if we're making the connection then we need to send a response back to the listener to confirm we're
  1174. //a valid host, compute what the RTT we just saw was and shut off the timer we were using to send CONNECT frames
  1175. if (pEPD->ulEPFlags & EPFLAGS_STATE_CONNECTING)
  1176. {
  1177. //check the signing options we've been sent from the listener make sense
  1178. //we should have a single signing type specified (and not both)
  1179. //and we're responsible for picking the secrets, so the values specified in the frame should be zero
  1180. if (((pCFrame->dwSigningOpts & (PACKET_SIGNING_FAST | PACKET_SIGNING_FULL))==0) ||
  1181. ((pCFrame->dwSigningOpts & (PACKET_SIGNING_FAST | PACKET_SIGNING_FULL))==
  1182. (PACKET_SIGNING_FAST | PACKET_SIGNING_FULL)) ||
  1183. pCFrame->ullSenderSecret!=0 || pCFrame->ullReceiverSecret!=0)
  1184. {
  1185. DPFX(DPFPREP, 0, "Ignoring CONNECTED_SIGNED cframe with invalid signing options");
  1186. RELEASE_MSD(pMSD, "Hold For Lock");
  1187. Unlock(&pEPD->EPLock);
  1188. return;
  1189. }
  1190. //find the check point associated with the initial connect so we can assess RTT
  1191. //if we can't find one something is screwy so we'll just ignore this frame
  1192. PCHKPT pCP = LookupCheckPoint(pEPD, pCFrame->bRspID);
  1193. if (pCP==NULL)
  1194. {
  1195. DPFX(DPFPREP,5, "(%p) Failed to find checkpoint. Ignoring frame", pEPD);
  1196. RELEASE_MSD(pMSD, "Hold For Lock");
  1197. Unlock(&pEPD->EPLock);
  1198. return;
  1199. }
  1200. //listener should always respond with the poll bit set. If its not set this is some bogus packet and we should ignore it
  1201. if ((pCFrame->bCommand & PACKET_COMMAND_POLL)==0)
  1202. {
  1203. DPFX(DPFPREP,5, "(%p) Ignoring CONNECTED SIGNED response with clear POLL bit", pEPD);
  1204. RELEASE_MSD(pMSD, "Hold For Lock");
  1205. Unlock(&pEPD->EPLock);
  1206. return;
  1207. }
  1208. //generate both the local and the remote secret. These'll both be sent back to the listener in the CONNECTED SIGNED
  1209. //response we're going to send out immediately after this. Note we only generate these values once and they
  1210. //can never be zero
  1211. while (pEPD->ullCurrentLocalSecret==0)
  1212. {
  1213. DNGetGoodRandomData(&pEPD->ullCurrentLocalSecret, sizeof(pEPD->ullCurrentLocalSecret));
  1214. }
  1215. while (pEPD->ullCurrentRemoteSecret==0)
  1216. {
  1217. DNGetGoodRandomData(&pEPD->ullCurrentRemoteSecret, sizeof(pEPD->ullCurrentRemoteSecret));
  1218. }
  1219. pEPD->ullOldLocalSecret=pEPD->ullCurrentLocalSecret;
  1220. pEPD->ullOldRemoteSecret=pEPD->ullCurrentRemoteSecret;
  1221. pEPD->ullLocalSecretModifier=pEPD->ullCurrentLocalSecret;
  1222. pEPD->ullRemoteSecretModifier=pEPD->ullCurrentRemoteSecret;
  1223. //Also if we fail to send the response frame then we shouldn't evolve our state. We'll retransmit a new CONNECT
  1224. //at some point and we can try the whole process again then
  1225. DPFX(DPFPREP,5, "(%p) Sending CONNECTED SIGNED Frame", pEPD);
  1226. if (SendConnectedSignedFrame(pEPD, pCFrame, tNow)!=DPN_OK)
  1227. {
  1228. DPFX(DPFPREP,5, "(%p) Failed to send CONNECTED SIGNED response", pEPD);
  1229. RELEASE_MSD(pMSD, "Hold For Lock");
  1230. Unlock(&pEPD->EPLock);
  1231. return;
  1232. }
  1233. //connect has succeeded so clean up the link timer
  1234. if(pEPD->LinkTimer != 0)
  1235. {
  1236. DPFX(DPFPREP,5, "(%p) Cancelling Connect Timer", pEPD);
  1237. if(CancelProtocolTimer(pSPD, pEPD->LinkTimer, pEPD->LinkTimerUnique) == DPN_OK)
  1238. {
  1239. // remove reference for timer, SPLock not already held
  1240. DECREMENT_EPD(pEPD, "UNLOCK: (Conn Retry Timer - Connect Complete)");
  1241. }
  1242. else
  1243. {
  1244. DPFX(DPFPREP,5, "(%p) Cancelling Connect Timer Failed", pEPD);
  1245. }
  1246. pEPD->LinkTimer = 0; // This will prevent timer from trying to do any work if it couldn't cancel
  1247. }
  1248. //compute the initial RTT, and then clear up the check point objects
  1249. dwInitialRTT = tNow - pCP->tTimestamp;
  1250. ChkPtPool.Release(pCP);
  1251. FlushCheckPoints(pEPD);
  1252. //twiddle the connecting bit off. We'll flick the connected bit on in CompleteConnectedResponse
  1253. pEPD->ulEPFlags &= ~(EPFLAGS_STATE_CONNECTING);
  1254. }
  1255. //else if we've got a dormant endpoint then this endpoint has been created as a result of completing a
  1256. //3 way handshake with unconnected data. We can now complete the connection
  1257. else if (pEPD->ulEPFlags & EPFLAGS_STATE_DORMANT)
  1258. {
  1259. //N.B. By the time we get here we'll have already checked the version number, the signing options,
  1260. //the connect signature and the session identity for validity
  1261. //twiddle the dormant bit off. We'll flick the connected bit on in CompleteConnectedResponse
  1262. pEPD->ulEPFlags &= ~(EPFLAGS_STATE_DORMANT);
  1263. //compute the RTT based on our original timestamp thats been echoed back to us
  1264. dwInitialRTT=tNow-pCFrame->dwEchoTimestamp;
  1265. //store the session identity indicated by the frame we just received
  1266. pEPD->dwSessID=pCFrame->dwSessID;
  1267. //and store the secrets we should be using to sign the link
  1268. pEPD->ullCurrentLocalSecret=pCFrame->ullReceiverSecret;
  1269. pEPD->ullOldLocalSecret=pCFrame->ullReceiverSecret;
  1270. pEPD->ullLocalSecretModifier=pCFrame->ullReceiverSecret;
  1271. pEPD->ullCurrentRemoteSecret=pCFrame->ullSenderSecret;
  1272. pEPD->ullOldRemoteSecret=pCFrame->ullSenderSecret;
  1273. pEPD->ullRemoteSecretModifier=pCFrame->ullSenderSecret;
  1274. }
  1275. //else endpoint is in some weird undetermined condition
  1276. else
  1277. {
  1278. DPFX(DPFPREP, 0, "(%p) In unknown state when processing CONNECTED SIGNED", pEPD);
  1279. DNASSERT(0);
  1280. RELEASE_MSD(pMSD, "Hold For Lock");
  1281. Unlock(&pEPD->EPLock);
  1282. return;
  1283. }
  1284. //mark the endpoint with the relevant signing flags
  1285. pEPD->ulEPFlags2|=EPFLAGS2_SUPPORTS_SIGNING;
  1286. if (pCFrame->dwSigningOpts & PACKET_SIGNING_FAST)
  1287. {
  1288. DPFX(DPFPREP, 7, "(%p) Marking endpoint as fast signed local secret %x-%x remote secret %x-%x",
  1289. pEPD, DPFX_OUTPUT_ULL(pEPD->ullCurrentLocalSecret), DPFX_OUTPUT_ULL(pEPD->ullCurrentRemoteSecret));
  1290. pEPD->ulEPFlags2|=EPFLAGS2_FAST_SIGNED_LINK;
  1291. }
  1292. else
  1293. {
  1294. DPFX(DPFPREP, 7, "(%p) Marking endpoint as full signed local current secret %x-%x old secret %x-%x modifier %x-%x remote current secret %x-%x old secret %x-%x modifier %x-%x",
  1295. pEPD, DPFX_OUTPUT_ULL(pEPD->ullCurrentLocalSecret), DPFX_OUTPUT_ULL(pEPD->ullOldLocalSecret),
  1296. DPFX_OUTPUT_ULL(pEPD->ullLocalSecretModifier), DPFX_OUTPUT_ULL(pEPD->ullCurrentRemoteSecret),
  1297. DPFX_OUTPUT_ULL(pEPD->ullOldRemoteSecret), DPFX_OUTPUT_ULL(pEPD->ullRemoteSecretModifier));
  1298. pEPD->ulEPFlags2|=EPFLAGS2_FULL_SIGNED_LINK;
  1299. //start looking for frames in the first 3/4's of the sequence space we can use to modify the secrets
  1300. pEPD->byRemoteSecretModifierSeqNum=SEQ_WINDOW_3Q;
  1301. pEPD->byLocalSecretModifierSeqNum=SEQ_WINDOW_3Q;
  1302. }
  1303. //and complete the connection setup processing
  1304. //this runs the code common to both CONNECTED and CONNECTEDSIGNED paths
  1305. CompleteConnectedResponse(pSPD->pPData, pEPD, pMSD, (PCFRAME ) pCFrame, dwInitialRTT, tNow);
  1306. //above call releases both EP lock and MSD command lock
  1307. }
  1308. /*
  1309. ** Drop Link
  1310. **
  1311. ** For whatever reason we are dropping an active link. This requires us to
  1312. ** Cancel any outstanding commands and give an indication to the user.
  1313. **
  1314. **
  1315. ** ** CALLED WITH EPD->EPLOCK HELD; RETURNS WITH LOCK RELEASED **
  1316. */
  1317. #undef DPF_MODNAME
  1318. #define DPF_MODNAME "DropLink"
  1319. VOID DropLink(PEPD pEPD)
  1320. {
  1321. DPFX(DPFPREP,2, "Drop Link %p (refcnt=%d)", pEPD, pEPD->lRefCnt);
  1322. ASSERT_EPD(pEPD);
  1323. AssertCriticalSectionIsTakenByThisThread(&pEPD->EPLock, TRUE);
  1324. PSPD pSPD = pEPD->pSPD;
  1325. // First set/clear flags to prevent any new commands from issueing
  1326. // We will not indicate disconnect if the Core never knew about the connetion
  1327. BOOL fIndicateDisconnect = (pEPD->ulEPFlags & EPFLAGS_STATE_CONNECTED);
  1328. // By default if we indicate disconnection from this function it'll be the connection terminated type
  1329. //the only exception is if ep is the source of a hard disconnect
  1330. BOOL fDisconnectTypeTermination=TRUE;
  1331. // Transition state
  1332. pEPD->ulEPFlags &= ~(EPFLAGS_STATE_CONNECTING | EPFLAGS_STATE_DORMANT | EPFLAGS_STATE_CONNECTED |
  1333. EPFLAGS_SDATA_READY | EPFLAGS_STREAM_UNBLOCKED); // Link is now down
  1334. pEPD->ulEPFlags |= EPFLAGS_STATE_TERMINATING; // Accept no new commands
  1335. // I am creating a RefCnt bump for the send pipeline, which means we will no longer pull EPDs off
  1336. // the pipeline here. The clearing of the flags above will cause the EPD to be dropped from the
  1337. // pipeline the next time it is due to be serviced. We CAN still clean up all the frames'n'stuff
  1338. // because the send loop doesnt need to actually DO anything with this EPD. This behavior allows
  1339. // the SendThread to loop through the pipeline queue, surrendering locks, without having the queue
  1340. // changing beneath it.
  1341. //cancel all the timers on an endpoint
  1342. CancelEpdTimers(pEPD);
  1343. //EPLock is still held when this returns
  1344. //cancel all pending sends
  1345. AbortSendsOnConnection(pEPD);
  1346. //EPLock is released when this returns
  1347. Lock(&pEPD->EPLock);
  1348. // Connects, Listens, and Disconnects are associated with an EPD through the pCommand member. AbortSends will
  1349. // have removed any Disconnects, and no Connects or Listens should still be hanging on when we call DropLink.
  1350. ASSERT(pEPD->pCommand == NULL);
  1351. // Now we clean up any receives in progress. We throw away any partial or mis-ordered messages.
  1352. //This returns all the recv buffers (if any) on a single list we can pass back to the SP once we unlock the EPD
  1353. SPRECEIVEDBUFFER * pRcvBuff = AbortRecvsOnConnection(pEPD);
  1354. //EPLock is still held when this returns
  1355. IDP8ServiceProvider *pSPIntf = pSPD->IISPIntf;
  1356. //if we had a COMMAND_ID_DISCONNECT or COMMAND_ID_DISC_RESPONSE msg held in EPD::pCommand then
  1357. //during AbortSendsOnConnection we'll have handled completing the connection. Alternatively if this is a hard disconnect
  1358. //or the link has dropped in an untidy way, we'll have to indicate the disconnect here.
  1359. //N.B. Its possible if we started a soft disconnect, and the remote end started a hard disconnect, then although we'll
  1360. //actually drop due to the hard disconnect, we'll do the disconect completition using the soft disconnect msg
  1361. //i.e. Indicating completition in the AbortSendsOnConnection using the COMMAND_ID_DISCONNECT msg
  1362. if (!(pEPD->ulEPFlags & EPFLAGS_INDICATED_DISCONNECT))
  1363. {
  1364. if (fIndicateDisconnect)
  1365. {
  1366. // Make sure we are the only one that indicates the disconnect
  1367. pEPD->ulEPFlags |= EPFLAGS_INDICATED_DISCONNECT;
  1368. // If this endpoint was the source of a hard disconnect we should indicate disconection as a completition
  1369. // rather than a termination
  1370. if (pEPD->ulEPFlags & EPFLAGS_HARD_DISCONNECT_SOURCE)
  1371. {
  1372. fDisconnectTypeTermination=FALSE;
  1373. }
  1374. }
  1375. }
  1376. else
  1377. {
  1378. // Someone else beat us to it.
  1379. fIndicateDisconnect = FALSE;
  1380. }
  1381. // We need to make sure that we don't allow DNPRemoveServiceProvider to complete until we are out of the Core
  1382. // and SP. This reference will do that, and is released at the end of this function.
  1383. LOCK_EPD(pEPD, "Drop Link Temp Ref");
  1384. // Remove the base reference if it still exists
  1385. if(!(pEPD->ulEPFlags2 & EPFLAGS2_KILLED))
  1386. {
  1387. pEPD->ulEPFlags2 |= EPFLAGS2_KILLED;
  1388. RELEASE_EPD(pEPD, "UNLOCK (KILLCONN - Base Ref)"); // RELEASE the EPD, releases EPLock
  1389. }
  1390. else
  1391. {
  1392. Unlock(&pEPD->EPLock);
  1393. }
  1394. if(pRcvBuff)
  1395. {
  1396. AssertNoCriticalSectionsFromGroupTakenByThisThread(&g_blProtocolCritSecsHeld);
  1397. DPFX(DPFPREP,DPF_CALLOUT_LVL, "(%p) Calling SP->ReturnReceiveBuffers, pSPD[%p], pRcvBuff[%p]", pEPD, pSPD, pRcvBuff);
  1398. IDP8ServiceProvider_ReturnReceiveBuffers(pSPIntf, pRcvBuff);
  1399. }
  1400. // Tell user that session is over
  1401. // If the Core previously knew about this endpoint, and we have not yet indicated disconnect to the
  1402. // Core, we need to do it now.
  1403. //N.B. For hard disconnects this is always where the disconnection is indicated, since we don't have a disconnect MSG
  1404. //store in the pCommand member which might have been picked up elsewhere
  1405. if(fIndicateDisconnect)
  1406. {
  1407. AssertNoCriticalSectionsFromGroupTakenByThisThread(&g_blProtocolCritSecsHeld);
  1408. if (fDisconnectTypeTermination)
  1409. {
  1410. DPFX(DPFPREP,DPF_CALLOUT_LVL, "(%p) Calling Core->IndicateConnectionTerminated, DPNERR_CONNECTIONLOST, Core Context[%p]", pEPD, pEPD->Context);
  1411. pSPD->pPData->pfVtbl->IndicateConnectionTerminated(pSPD->pPData->Parent, pEPD->Context, DPNERR_CONNECTIONLOST);
  1412. }
  1413. else
  1414. {
  1415. DPFX(DPFPREP,DPF_CALLOUT_LVL, "(%p) Calling Core->CompleteDisconnect, DPN_OK, Core Context[%p]", pEPD, pEPD->pvHardDisconnectContext);
  1416. pSPD->pPData->pfVtbl->CompleteDisconnect(pSPD->pPData->Parent, pEPD->pvHardDisconnectContext, DPN_OK);
  1417. }
  1418. }
  1419. Lock(&pEPD->EPLock);
  1420. RELEASE_EPD(pEPD, "Drop Link Temp Ref");
  1421. }
  1422. /*
  1423. ** Cancel Epd Timers
  1424. **
  1425. ** This clears all timers that an endpoint potentially creates. Called either when
  1426. ** we're dropping the link or hard disconnecting the link. Its possible a timer won't
  1427. ** by cancellable, in which case we assume it'll fire and then pick up the new endpoint
  1428. ** state (i.e. Its been terminated).
  1429. **
  1430. ** ** CALLED WITH EPD->EPLOCK HELD; RETURNS WITH EPD->EPLOCK HELD **
  1431. */
  1432. #undef DPF_MODNAME
  1433. #define DPF_MODNAME "CancelEpdTimers"
  1434. VOID CancelEpdTimers(PEPD pEPD)
  1435. {
  1436. PSPD pSPD=pEPD->pSPD;
  1437. if(pEPD->RetryTimer)
  1438. {
  1439. if(CancelProtocolTimer(pSPD, pEPD->RetryTimer, pEPD->RetryTimerUnique) == DPN_OK)
  1440. {
  1441. DECREMENT_EPD(pEPD, "UNLOCK (DROP RETRY)"); // SPLock not already held
  1442. }
  1443. pEPD->RetryTimer = 0;
  1444. }
  1445. if(pEPD->LinkTimer)
  1446. {
  1447. if(CancelProtocolTimer(pSPD, pEPD->LinkTimer, pEPD->LinkTimerUnique) == DPN_OK)
  1448. {
  1449. DECREMENT_EPD(pEPD, "UNLOCK (DROP LINK RETRY)"); // SPLock not already held
  1450. }
  1451. pEPD->LinkTimer = 0;
  1452. }
  1453. if(pEPD->DelayedAckTimer)
  1454. {
  1455. if(CancelProtocolTimer(pSPD, pEPD->DelayedAckTimer, pEPD->DelayedAckTimerUnique) == DPN_OK)
  1456. {
  1457. DECREMENT_EPD(pEPD, "UNLOCK (DROP DELAYEDACK)"); // SPLock not already held
  1458. }
  1459. pEPD->DelayedAckTimer = 0;
  1460. }
  1461. if(pEPD->DelayedMaskTimer)
  1462. {
  1463. if(CancelProtocolTimer(pSPD, pEPD->DelayedMaskTimer, pEPD->DelayedMaskTimerUnique) == DPN_OK)
  1464. {
  1465. DECREMENT_EPD(pEPD, "UNLOCK (DROP DELAYED MASK)"); // SPLock not already held
  1466. }
  1467. pEPD->DelayedMaskTimer = 0;
  1468. }
  1469. if(pEPD->SendTimer)
  1470. {
  1471. if(CancelProtocolTimer(pSPD, pEPD->SendTimer, pEPD->SendTimerUnique) == DPN_OK)
  1472. {
  1473. DECREMENT_EPD(pEPD, "UNLOCK (DROP SENDTIMER)"); // SPLock not already held
  1474. pEPD->SendTimer = 0;
  1475. }
  1476. }
  1477. if(pEPD->BGTimer)
  1478. {
  1479. if(CancelProtocolTimer(pSPD, pEPD->BGTimer, pEPD->BGTimerUnique) == DPN_OK)
  1480. {
  1481. DECREMENT_EPD(pEPD, "UNLOCK (DROP BG TIMER)"); // SPLock not already held
  1482. pEPD->BGTimer = 0;
  1483. }
  1484. }
  1485. }
  1486. /*
  1487. ** Abort Recvs on Connection
  1488. **
  1489. ** This messages we've queued on a connection (misordered, partial, complete, whatever)
  1490. ** Called when we're dropping the link or when we're hard disconnecting it
  1491. **
  1492. ** ** CALLED WITH EPD->EPLOCK HELD; RETURNS WITH EPD->EPLOCK HELD **
  1493. */
  1494. #undef DPF_MODNAME
  1495. #define DPF_MODNAME "AbortRecvsOnConnection"
  1496. SPRECEIVEDBUFFER * AbortRecvsOnConnection(PEPD pEPD)
  1497. {
  1498. PRCD pRCD, pNext;
  1499. CBilink * pLink;
  1500. SPRECEIVEDBUFFER * pRcvBuff=NULL;
  1501. while((pRCD = pEPD->pNewMessage) != NULL)
  1502. {
  1503. ASSERT_RCD(pRCD);
  1504. pEPD->pNewMessage = pRCD->pMsgLink;
  1505. if(pRCD->pRcvBuff == NULL)
  1506. {
  1507. ASSERT(pRCD->ulRFlags & (RFLAGS_FRAME_INDICATED_NONSEQ | RFLAGS_FRAME_LOST));
  1508. }
  1509. RELEASE_SP_BUFFER(pRcvBuff, pRCD->pRcvBuff);
  1510. RELEASE_RCD(pRCD);
  1511. }
  1512. while(!pEPD->blOddFrameList.IsEmpty())
  1513. {
  1514. pLink = pEPD->blOddFrameList.GetNext();
  1515. pRCD = CONTAINING_OBJECT(pLink, RCD, blOddFrameLinkage);
  1516. ASSERT_RCD(pRCD);
  1517. pLink->RemoveFromList();
  1518. if(pRCD->pRcvBuff == NULL)
  1519. {
  1520. ASSERT(pRCD->ulRFlags & (RFLAGS_FRAME_INDICATED_NONSEQ | RFLAGS_FRAME_LOST));
  1521. }
  1522. RELEASE_SP_BUFFER(pRcvBuff, pRCD->pRcvBuff);
  1523. RELEASE_RCD(pRCD);
  1524. }
  1525. while(!pEPD->blCompleteList.IsEmpty())
  1526. {
  1527. pLink = pEPD->blCompleteList.GetNext();
  1528. pRCD = CONTAINING_OBJECT(pLink, RCD, blCompleteLinkage);
  1529. ASSERT_RCD(pRCD);
  1530. pLink->RemoveFromList();
  1531. ASSERT(pEPD->uiCompleteMsgCount > 0);
  1532. pEPD->uiCompleteMsgCount--;
  1533. while(pRCD != NULL)
  1534. {
  1535. ASSERT_RCD(pRCD);
  1536. pNext = pRCD->pMsgLink;
  1537. RELEASE_SP_BUFFER(pRcvBuff, pRCD->pRcvBuff);
  1538. RELEASE_RCD(pRCD);
  1539. pRCD = pNext;
  1540. }
  1541. }
  1542. return pRcvBuff;
  1543. }