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.

4737 lines
142 KiB

  1. /*++
  2. Copyright (c) 2000-2002 Microsoft Corporation
  3. Module Name:
  4. clientconn.c
  5. Abstract:
  6. Contains the code for the client-side HTTP connection stuff.
  7. Author:
  8. Henry Sanders (henrysa) 14-Aug-2000
  9. Rajesh Sundaram (rajeshsu) 01-Oct-2000
  10. Revision History:
  11. --*/
  12. #include "precomp.h"
  13. #ifdef ALLOC_PRAGMA
  14. #pragma alloc_text( INIT, UcInitializeClientConnections )
  15. #pragma alloc_text( PAGE, UcTerminateClientConnections )
  16. #pragma alloc_text( PAGE, UcpTerminateClientConnectionsHelper )
  17. #pragma alloc_text( PAGE, UcOpenClientConnection )
  18. #pragma alloc_text( PAGEUC, UcSendRequestOnConnection )
  19. #pragma alloc_text( PAGEUC, UcCancelSentRequest )
  20. #pragma alloc_text( PAGEUC, UcRestartMdlSend )
  21. #pragma alloc_text( PAGEUC, UcpRestartEntityMdlSend )
  22. #pragma alloc_text( PAGEUC, UcIssueRequests )
  23. #pragma alloc_text( PAGEUC, UcIssueEntities )
  24. #pragma alloc_text( PAGEUC, UcpCleanupConnection )
  25. #pragma alloc_text( PAGEUC, UcRestartClientConnect )
  26. #pragma alloc_text( PAGEUC, UcpCancelPendingRequest )
  27. #pragma alloc_text( PAGEUC, UcSendEntityBody )
  28. #pragma alloc_text( PAGEUC, UcReferenceClientConnection )
  29. #pragma alloc_text( PAGEUC, UcDereferenceClientConnection )
  30. #pragma alloc_text( PAGEUC, UcpConnectionStateMachineWorker )
  31. #pragma alloc_text( PAGEUC, UcKickOffConnectionStateMachine )
  32. #pragma alloc_text( PAGEUC, UcComputeHttpRawConnectionLength )
  33. #pragma alloc_text( PAGEUC, UcGenerateHttpRawConnectionInfo )
  34. #pragma alloc_text( PAGEUC, UcServerCertificateInstalled )
  35. #pragma alloc_text( PAGEUC, UcConnectionStateMachine )
  36. #pragma alloc_text( PAGEUC, UcpInitializeConnection )
  37. #pragma alloc_text( PAGEUC, UcpOpenTdiObjects )
  38. #pragma alloc_text( PAGEUC, UcpFreeTdiObject )
  39. #pragma alloc_text( PAGEUC, UcpAllocateTdiObject )
  40. #pragma alloc_text( PAGEUC, UcpPopTdiObject )
  41. #pragma alloc_text( PAGEUC, UcpPushTdiObject )
  42. #pragma alloc_text( PAGEUC, UcpCheckForPipelining )
  43. #pragma alloc_text( PAGEUC, UcClearConnectionBusyFlag )
  44. #pragma alloc_text( PAGEUC, UcAddServerCertInfoToConnection )
  45. #pragma alloc_text( PAGEUC, UcpCompareServerCert )
  46. #pragma alloc_text( PAGEUC, UcpFindRequestToFail )
  47. #endif
  48. //
  49. // Cache of UC_CLIENT_CONNECTIONS
  50. //
  51. #define NUM_ADDRESS_TYPES 2
  52. #define ADDRESS_TYPE_TO_INDEX(addrtype) ((addrtype) == TDI_ADDRESS_TYPE_IP6)
  53. LIST_ENTRY g_ClientTdiConnectionSListHead[NUM_ADDRESS_TYPES];
  54. #define G_CLIENT_TDI_CONNECTION_SLIST_HEAD(addrtype) \
  55. (g_ClientTdiConnectionSListHead[ADDRESS_TYPE_TO_INDEX(addrtype)])
  56. UL_SPIN_LOCK g_ClientConnSpinLock[NUM_ADDRESS_TYPES];
  57. #define G_CLIENT_CONN_SPIN_LOCK(addrtype) \
  58. (g_ClientConnSpinLock[ADDRESS_TYPE_TO_INDEX(addrtype)])
  59. USHORT g_ClientConnListCount[NUM_ADDRESS_TYPES];
  60. #define G_CLIENT_CONN_LIST_COUNT(addrtype) \
  61. (&g_ClientConnListCount[ADDRESS_TYPE_TO_INDEX(addrtype)])
  62. TA_IP_ADDRESS g_LocalAddressIP;
  63. TA_IP6_ADDRESS g_LocalAddressIP6;
  64. PTRANSPORT_ADDRESS g_LocalAddresses[NUM_ADDRESS_TYPES];
  65. #define G_LOCAL_ADDRESS(addrtype) \
  66. (g_LocalAddresses[ADDRESS_TYPE_TO_INDEX(addrtype)])
  67. ULONG g_LocalAddressLengths[NUM_ADDRESS_TYPES];
  68. #define G_LOCAL_ADDRESS_LENGTH(addrtype) \
  69. (g_LocalAddressLengths[ADDRESS_TYPE_TO_INDEX(addrtype)])
  70. NPAGED_LOOKASIDE_LIST g_ClientConnectionLookaside;
  71. BOOLEAN g_ClientConnectionInitialized;
  72. /***************************************************************************++
  73. Routine Description:
  74. Performs global initialization of this module.
  75. Return Value:
  76. NTSTATUS - Completion status.
  77. --***************************************************************************/
  78. NTSTATUS
  79. UcInitializeClientConnections(
  80. VOID
  81. )
  82. {
  83. //
  84. // Sanity check.
  85. //
  86. PAGED_CODE();
  87. //
  88. // Initialize our various free lists etc.
  89. //
  90. InitializeListHead(
  91. &G_CLIENT_TDI_CONNECTION_SLIST_HEAD(TDI_ADDRESS_TYPE_IP)
  92. );
  93. InitializeListHead(
  94. &G_CLIENT_TDI_CONNECTION_SLIST_HEAD(TDI_ADDRESS_TYPE_IP6)
  95. );
  96. UlInitializeSpinLock(
  97. &G_CLIENT_CONN_SPIN_LOCK(TDI_ADDRESS_TYPE_IP),
  98. "g_ClientConnSpinLock"
  99. );
  100. UlInitializeSpinLock(
  101. &G_CLIENT_CONN_SPIN_LOCK(TDI_ADDRESS_TYPE_IP6),
  102. "g_ClientConnSpinLock"
  103. );
  104. //
  105. // Initialize the client lookaside list.
  106. //
  107. ExInitializeNPagedLookasideList(
  108. &g_ClientConnectionLookaside,
  109. NULL,
  110. NULL,
  111. 0,
  112. sizeof(UC_CLIENT_CONNECTION),
  113. UC_CLIENT_CONNECTION_POOL_TAG,
  114. 0
  115. );
  116. g_ClientConnectionInitialized = TRUE;
  117. //
  118. // Initialize our local address object. This is an addrss object with
  119. // a wildcard address (0 IP, 0 port) that we use for outgoing requests.
  120. //
  121. g_LocalAddressIP.TAAddressCount = 1;
  122. g_LocalAddressIP.Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP;
  123. g_LocalAddressIP.Address[0].AddressType = TDI_ADDRESS_TYPE_IP;
  124. g_LocalAddressIP.Address[0].Address[0].sin_port = 0;
  125. g_LocalAddressIP.Address[0].Address[0].in_addr = 0;
  126. g_LocalAddressIP6.TAAddressCount = 1;
  127. g_LocalAddressIP6.Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP6;
  128. g_LocalAddressIP6.Address[0].AddressType = TDI_ADDRESS_TYPE_IP6;
  129. RtlZeroMemory(&g_LocalAddressIP6.Address[0].Address[0],
  130. sizeof(TDI_ADDRESS_IP6));
  131. G_LOCAL_ADDRESS(TDI_ADDRESS_TYPE_IP) =
  132. (PTRANSPORT_ADDRESS)&g_LocalAddressIP;
  133. G_LOCAL_ADDRESS(TDI_ADDRESS_TYPE_IP6) =
  134. (PTRANSPORT_ADDRESS)&g_LocalAddressIP6;
  135. G_LOCAL_ADDRESS_LENGTH(TDI_ADDRESS_TYPE_IP) = sizeof(g_LocalAddressIP);
  136. G_LOCAL_ADDRESS_LENGTH(TDI_ADDRESS_TYPE_IP6) = sizeof(g_LocalAddressIP6);
  137. return STATUS_SUCCESS;
  138. }
  139. /***************************************************************************++
  140. Routine Description:
  141. Performs termination of a Client TDI connections slist.
  142. Return Value:
  143. None.
  144. --***************************************************************************/
  145. VOID
  146. UcpTerminateClientConnectionsHelper(
  147. IN USHORT AddressType
  148. )
  149. {
  150. PLIST_ENTRY pListHead;
  151. PUC_TDI_OBJECTS pTdiObject;
  152. PLIST_ENTRY pListEntry;
  153. pListHead = &G_CLIENT_TDI_CONNECTION_SLIST_HEAD(AddressType);
  154. //
  155. // Sanity check.
  156. //
  157. PAGED_CODE();
  158. //
  159. // Since this is called from the unload thread, there can't be any other
  160. // thread, so we don't have to take the spin lock.
  161. //
  162. while(!IsListEmpty(pListHead))
  163. {
  164. pListEntry = RemoveHeadList(pListHead);
  165. pTdiObject = CONTAINING_RECORD(
  166. pListEntry,
  167. UC_TDI_OBJECTS,
  168. Linkage
  169. );
  170. (*G_CLIENT_CONN_LIST_COUNT(AddressType))--;
  171. UcpFreeTdiObject(pTdiObject);
  172. }
  173. ASSERT(*G_CLIENT_CONN_LIST_COUNT(AddressType) == 0);
  174. }
  175. /***************************************************************************++
  176. Routine Description:
  177. Performs global termination of this module.
  178. Return Value:
  179. None.
  180. --***************************************************************************/
  181. VOID
  182. UcTerminateClientConnections(
  183. VOID
  184. )
  185. {
  186. //
  187. // Sanity check.
  188. //
  189. PAGED_CODE();
  190. if(g_ClientConnectionInitialized)
  191. {
  192. UcpTerminateClientConnectionsHelper(
  193. TDI_ADDRESS_TYPE_IP
  194. );
  195. UcpTerminateClientConnectionsHelper(
  196. TDI_ADDRESS_TYPE_IP6
  197. );
  198. ExDeleteNPagedLookasideList(&g_ClientConnectionLookaside);
  199. }
  200. }
  201. /***************************************************************************++
  202. Routine Description:
  203. Opens an HTTP connection. The HTTP connection will have a TDI connection
  204. object associated with it and that object will itself be associated
  205. with our address object.
  206. Arguments:
  207. pHttpConnection - Receives a pointer to the HTTP connection object.
  208. Return Value:
  209. NTSTATUS - Completion status.
  210. --***************************************************************************/
  211. NTSTATUS
  212. UcOpenClientConnection(
  213. IN PUC_PROCESS_SERVER_INFORMATION pInfo,
  214. OUT PUC_CLIENT_CONNECTION *pUcConnection
  215. )
  216. {
  217. NTSTATUS Status;
  218. PUC_CLIENT_CONNECTION pConnection;
  219. //
  220. // Sanity check.
  221. //
  222. PAGED_CODE();
  223. *pUcConnection = NULL;
  224. //
  225. // Try to snag a connection from the global pool.
  226. //
  227. pConnection = (PUC_CLIENT_CONNECTION)
  228. ExAllocateFromNPagedLookasideList(
  229. &g_ClientConnectionLookaside
  230. );
  231. if(pConnection)
  232. {
  233. //
  234. // One time connection initialization.
  235. //
  236. pConnection->Signature = UC_CLIENT_CONNECTION_SIGNATURE;
  237. UlInitializeSpinLock(&pConnection->SpinLock, "Uc Connection Spinlock");
  238. InitializeListHead(&pConnection->PendingRequestList);
  239. InitializeListHead(&pConnection->SentRequestList);
  240. InitializeListHead(&pConnection->ProcessedRequestList);
  241. UlInitializeWorkItem(&pConnection->WorkItem);
  242. pConnection->bWorkItemQueued = 0;
  243. pConnection->RefCount = 0;
  244. pConnection->Flags = 0;
  245. pConnection->pEvent = NULL;
  246. // Server Cert Info initialization
  247. RtlZeroMemory(&pConnection->ServerCertInfo,
  248. sizeof(pConnection->ServerCertInfo));
  249. CREATE_REF_TRACE_LOG( pConnection->pTraceLog,
  250. 96 - REF_TRACE_OVERHEAD,
  251. 0,
  252. TRACELOG_LOW_PRIORITY,
  253. UL_REF_TRACE_LOG_POOL_TAG );
  254. //
  255. // Initialize this connection.
  256. //
  257. Status = UcpInitializeConnection(pConnection, pInfo);
  258. if(!NT_SUCCESS(Status))
  259. {
  260. DESTROY_REF_TRACE_LOG( pConnection->pTraceLog,
  261. UL_REF_TRACE_LOG_POOL_TAG);
  262. ExFreeToNPagedLookasideList(
  263. &g_ClientConnectionLookaside,
  264. pConnection
  265. );
  266. return Status;
  267. }
  268. pConnection->ConnectionState = UcConnectStateConnectIdle;
  269. pConnection->SslState = UcSslStateNoSslState;
  270. pConnection->pTdiObjects = NULL;
  271. REFERENCE_CLIENT_CONNECTION(pConnection);
  272. }
  273. else
  274. {
  275. Status = STATUS_INSUFFICIENT_RESOURCES;
  276. }
  277. *pUcConnection = pConnection;
  278. return Status;
  279. }
  280. /***************************************************************************++
  281. Routine Description:
  282. Checks if we can pipeline.
  283. Arguments:
  284. pConnection - Receives a pointer to the HTTP connection object.
  285. Return Value:
  286. TRUE - Yes, we can send the next request.
  287. FALSE - No, cannot.
  288. --***************************************************************************/
  289. BOOLEAN
  290. UcpCheckForPipelining(
  291. IN PUC_CLIENT_CONNECTION pConnection
  292. )
  293. {
  294. PUC_HTTP_REQUEST pPendingRequest, pSentRequest;
  295. PLIST_ENTRY pSentEntry, pPendingEntry;
  296. ASSERT( UC_IS_VALID_CLIENT_CONNECTION(pConnection) );
  297. ASSERT( UlDbgSpinLockOwned(&pConnection->SpinLock) );
  298. ASSERT( !IsListEmpty(&pConnection->PendingRequestList) );
  299. // If the remote server supports pipeling and this request does also
  300. // or the sent request list is empty, go ahead and send it.
  301. if( IsListEmpty(&pConnection->SentRequestList) )
  302. {
  303. // Sent List is empty, we can send.
  304. return TRUE;
  305. }
  306. else
  307. {
  308. pPendingEntry = pConnection->PendingRequestList.Flink;
  309. pPendingRequest = CONTAINING_RECORD(pPendingEntry,
  310. UC_HTTP_REQUEST,
  311. Linkage
  312. );
  313. pSentEntry = pConnection->SentRequestList.Flink;
  314. pSentRequest = CONTAINING_RECORD(pSentEntry,
  315. UC_HTTP_REQUEST,
  316. Linkage
  317. );
  318. ASSERT(UC_IS_VALID_HTTP_REQUEST(pPendingRequest));
  319. ASSERT(UC_IS_VALID_HTTP_REQUEST(pSentRequest));
  320. if(pConnection->pServerInfo->pNextHopInfo->Version11 &&
  321. pPendingRequest->RequestFlags.PipeliningAllowed &&
  322. pSentRequest->RequestFlags.PipeliningAllowed
  323. )
  324. {
  325. ASSERT(pSentRequest->RequestFlags.NoRequestEntityBodies);
  326. ASSERT(pPendingRequest->RequestFlags.NoRequestEntityBodies);
  327. ASSERT(pSentRequest->DontFreeMdls == 0);
  328. return TRUE;
  329. }
  330. }
  331. return FALSE;
  332. }
  333. /***************************************************************************++
  334. Routine Description:
  335. Send a request on a client connection. We've given a connection (which
  336. must be referenced when we're called) and a request. The connection may
  337. or may not be established. We'll queue the request to the connection, then
  338. figure out the state of the connection. If it's not connected we'll get
  339. a connection going. If it is connected then we determine if it's ok to
  340. send the request now or not.
  341. Arguments:
  342. pConnection - Pointer to the connection structure on which we're
  343. sending.
  344. pRequest - Pointer to the request we're sending.
  345. Return Value:
  346. NTSTATUS - Status of attempt to send the request.
  347. --***************************************************************************/
  348. NTSTATUS
  349. UcSendRequestOnConnection(
  350. PUC_CLIENT_CONNECTION pConnection,
  351. PUC_HTTP_REQUEST pRequest,
  352. KIRQL OldIrql)
  353. {
  354. BOOLEAN RequestCancelled;
  355. PUC_HTTP_REQUEST pHeadRequest;
  356. PLIST_ENTRY pEntry;
  357. ASSERT( UC_IS_VALID_CLIENT_CONNECTION(pConnection) );
  358. pEntry = pConnection->PendingRequestList.Flink;
  359. pHeadRequest = CONTAINING_RECORD(pEntry,
  360. UC_HTTP_REQUEST,
  361. Linkage);
  362. ASSERT(UC_IS_VALID_HTTP_REQUEST(pHeadRequest));
  363. //
  364. // We will call UcSendRequestOnConnection only if
  365. // a. The request is not buffered OR
  366. // b. The request is buffered & we've seen the last entity.
  367. //
  368. // For case a, we might not have seen all the entity body.
  369. // but we still want to send since we know the content-length.
  370. ASSERT(!pRequest->RequestFlags.RequestBuffered ||
  371. (pRequest->RequestFlags.RequestBuffered &&
  372. pRequest->RequestFlags.LastEntitySeen));
  373. // Check the state. If it's connected, we may be able to send the request
  374. // right now. We can send only if we are the "head" request on the list.
  375. if (
  376. // Connection is still active
  377. pConnection->ConnectionState == UcConnectStateConnectReady
  378. &&
  379. // No one else is sending
  380. !(pConnection->Flags & CLIENT_CONN_FLAG_SEND_BUSY)
  381. &&
  382. // We are the head request on this list
  383. (pRequest == pHeadRequest)
  384. &&
  385. // pipelining is OK
  386. UcpCheckForPipelining(pConnection)
  387. )
  388. {
  389. // It's OK to send now.
  390. IoMarkIrpPending(pRequest->RequestIRP);
  391. UcIssueRequests(pConnection, OldIrql);
  392. return STATUS_PENDING;
  393. }
  394. //
  395. // We can't send now, so leave it queued. Since we're leaving this request
  396. // queued set it up for cancelling now.
  397. //
  398. RequestCancelled = UcSetRequestCancelRoutine(
  399. pRequest,
  400. UcpCancelPendingRequest
  401. );
  402. if (RequestCancelled)
  403. {
  404. UC_WRITE_TRACE_LOG(
  405. g_pUcTraceLog,
  406. UC_ACTION_REQUEST_CANCELLED,
  407. pConnection,
  408. pRequest,
  409. pRequest->RequestIRP,
  410. UlongToPtr((ULONG)STATUS_CANCELLED)
  411. );
  412. IoMarkIrpPending(pRequest->RequestIRP);
  413. pRequest->RequestIRP = NULL;
  414. //
  415. // Remove this request from the pending list, so that other threads
  416. // don't land up sending it.
  417. //
  418. RemoveEntryList(&pRequest->Linkage);
  419. InitializeListHead(&pRequest->Linkage);
  420. //
  421. // Make sure that any new API calls for this request ID are failed.
  422. //
  423. UcSetFlag(&pRequest->RequestFlags.Value, UcMakeRequestCancelledFlag());
  424. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  425. return STATUS_PENDING;
  426. }
  427. IoMarkIrpPending(pRequest->RequestIRP);
  428. UC_WRITE_TRACE_LOG(
  429. g_pUcTraceLog,
  430. UC_ACTION_REQUEST_QUEUED,
  431. pConnection,
  432. pRequest,
  433. pRequest->RequestIRP,
  434. UlongToPtr((ULONG)STATUS_PENDING)
  435. );
  436. //
  437. // If connection is not ready & if we are the "head" request, then
  438. // we kick off the connection state machine.
  439. //
  440. if (pConnection->ConnectionState != UcConnectStateConnectReady &&
  441. pRequest == pHeadRequest
  442. )
  443. {
  444. UcConnectionStateMachine(pConnection, OldIrql);
  445. }
  446. else
  447. {
  448. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  449. }
  450. return STATUS_PENDING;
  451. }
  452. /***************************************************************************++
  453. Routine Description:
  454. Cancel a pending request that caused a connection. This routine is called
  455. when we're canceling a request that's on the pending list and we've got a
  456. connect IRP outstanding.
  457. Arguments:
  458. pDeviceObject - Pointer to device object.
  459. Irp - Pointer to IRP being canceled.
  460. Return Value:
  461. --***************************************************************************/
  462. VOID
  463. UcCancelSentRequest(
  464. PDEVICE_OBJECT pDeviceObject,
  465. PIRP Irp
  466. )
  467. {
  468. PUC_HTTP_REQUEST pRequest;
  469. PUC_CLIENT_CONNECTION pConnection;
  470. KIRQL OldIrql;
  471. LONG OldReceiveBusy;
  472. UNREFERENCED_PARAMETER(pDeviceObject);
  473. // Release the cancel spin lock, since we're not using it.
  474. IoReleaseCancelSpinLock(Irp->CancelIrql);
  475. // Retrieve the pointers we need. The request pointer is stored inthe
  476. // driver context array, and a back pointer to the connection is stored
  477. // in the request. Whoever set the cancel routine is responsible for
  478. // referencing the connection for us.
  479. pRequest = (PUC_HTTP_REQUEST) Irp->Tail.Overlay.DriverContext[0];
  480. pConnection = pRequest->pConnection;
  481. ASSERT(UC_IS_VALID_CLIENT_CONNECTION(pConnection));
  482. OldReceiveBusy = InterlockedExchange(
  483. &pRequest->ReceiveBusy,
  484. UC_REQUEST_RECEIVE_CANCELLED
  485. );
  486. if(OldReceiveBusy == UC_REQUEST_RECEIVE_BUSY ||
  487. OldReceiveBusy == UC_REQUEST_RECEIVE_CANCELLED)
  488. {
  489. // The cancel routine fired when the request was being parsed. We'll
  490. // just postpone the cancel - The receive thread will pick it up
  491. // later.
  492. pRequest->RequestIRP = Irp;
  493. UC_WRITE_TRACE_LOG(
  494. g_pUcTraceLog,
  495. UC_ACTION_REQUEST_CLEAN_PENDED,
  496. pConnection,
  497. pRequest,
  498. Irp,
  499. UlongToPtr((ULONG)STATUS_CANCELLED)
  500. );
  501. return;
  502. }
  503. UC_WRITE_TRACE_LOG(
  504. g_pUcTraceLog,
  505. UC_ACTION_REQUEST_CANCELLED,
  506. pConnection,
  507. pRequest,
  508. Irp,
  509. UlongToPtr((ULONG)STATUS_CANCELLED)
  510. );
  511. UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
  512. //
  513. // IF we are here, then we are guaranteed that our send has completed.
  514. // But, the app could have asked us not to free the MDL chain (because of
  515. // SSPI auth). In such cases, we have to free it here.
  516. //
  517. // If the SSPI worker thread kicks in, then we'll just fail it.
  518. //
  519. UcFreeSendMdls(pRequest->pMdlHead);
  520. pRequest->pMdlHead = NULL;
  521. pRequest->RequestIRP = NULL;
  522. //
  523. // Note: We cannot just call UcFailRequest from here. UcFailRequest
  524. // is supposed to be called when a request is failed (e.g. parseer
  525. // error) or canceled (HttpCancelRequest API) & hence has code to
  526. // not double complete the IRP if the cancel routine kicked in.
  527. //
  528. // Since we are the IRP cancel routine, we have to manually
  529. // complete the IRP. An IRP in this state has not hit the wire.
  530. // so, we just free send MDLs & cancel it. Note that we call
  531. // UcFailRequest to handle common IRP cleanup.
  532. //
  533. Irp->IoStatus.Status = STATUS_CANCELLED;
  534. Irp->RequestorMode = pRequest->AppRequestorMode;
  535. Irp->MdlAddress = pRequest->AppMdl;
  536. UcSetFlag(&pRequest->RequestFlags.Value, UcMakeRequestCancelledFlag());
  537. UcFailRequest(pRequest, STATUS_CANCELLED, OldIrql);
  538. // For the IRP
  539. UC_DEREFERENCE_REQUEST(pRequest);
  540. UlCompleteRequest(Irp, IO_NO_INCREMENT);
  541. }
  542. /*********************************************************************++
  543. Routine Description:
  544. This is our send request complete routine.
  545. Arguments:
  546. pDeviceObject - The device object we called.
  547. pIrp - The IRP that is completing.
  548. Context - Our context value, a pointer to a request
  549. structure.
  550. Return Value:
  551. NTSTATUS - MORE_PROCESSING_REQUIRED if this isn't to be completed now,
  552. SUCCESS otherwise.
  553. --*********************************************************************/
  554. VOID
  555. UcRestartMdlSend(
  556. IN PVOID pCompletionContext,
  557. IN NTSTATUS Status,
  558. IN ULONG_PTR Information
  559. )
  560. {
  561. PUC_HTTP_REQUEST pRequest;
  562. PUC_CLIENT_CONNECTION pConnection;
  563. KIRQL OldIrql;
  564. BOOLEAN RequestCancelled;
  565. PIRP pIrp;
  566. UNREFERENCED_PARAMETER(Information);
  567. pRequest = (PUC_HTTP_REQUEST) pCompletionContext;
  568. pConnection = pRequest->pConnection;
  569. ASSERT(UC_IS_VALID_CLIENT_CONNECTION(pConnection));
  570. UC_WRITE_TRACE_LOG(
  571. g_pUcTraceLog,
  572. UC_ACTION_REQUEST_SEND_COMPLETE,
  573. pConnection,
  574. pRequest,
  575. pRequest->RequestIRP,
  576. UlongToPtr(Status)
  577. );
  578. UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
  579. //
  580. // If the send complete failed, or if we had pended a request cleanup
  581. // we'll pick them up now.
  582. //
  583. if(!NT_SUCCESS(Status) || pRequest->RequestFlags.CleanPended)
  584. {
  585. if(!NT_SUCCESS(Status))
  586. {
  587. pRequest->RequestStatus = Status;
  588. pRequest->RequestState = UcRequestStateDone;
  589. }
  590. else
  591. {
  592. switch(pRequest->RequestState)
  593. {
  594. case UcRequestStateSent:
  595. pRequest->RequestState = UcRequestStateSendCompleteNoData;
  596. break;
  597. case UcRequestStateNoSendCompletePartialData:
  598. pRequest->RequestState =
  599. UcRequestStateSendCompletePartialData;
  600. break;
  601. case UcRequestStateNoSendCompleteFullData:
  602. pRequest->RequestState = UcRequestStateResponseParsed;
  603. break;
  604. }
  605. Status = pRequest->RequestStatus;
  606. }
  607. UC_WRITE_TRACE_LOG(
  608. g_pUcTraceLog,
  609. UC_ACTION_REQUEST_CLEAN_RESUMED,
  610. pConnection,
  611. pRequest,
  612. UlongToPtr(pRequest->RequestState),
  613. UlongToPtr(pConnection->ConnectionState)
  614. );
  615. UcCompleteParsedRequest(pRequest,
  616. Status,
  617. TRUE,
  618. OldIrql
  619. );
  620. return;
  621. }
  622. switch(pRequest->RequestState)
  623. {
  624. case UcRequestStateSent:
  625. pRequest->RequestState = UcRequestStateSendCompleteNoData;
  626. if(!pRequest->RequestFlags.ReceiveBufferSpecified)
  627. {
  628. if(!pRequest->DontFreeMdls)
  629. {
  630. // The app has not supplied any receive buffers. If
  631. // we are not doing SSPI auth (that requires a re-negotiate)
  632. // we can complete the IRP.
  633. pIrp = UcPrepareRequestIrp(pRequest, Status);
  634. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  635. if(pIrp)
  636. {
  637. UlCompleteRequest(pIrp, IO_NETWORK_INCREMENT);
  638. }
  639. }
  640. else
  641. {
  642. // The app has not passed any receive buffers, but we are
  643. // doing SSPI auth. We cannot free the MDL chain or complete
  644. // the IRP, because we might have to re-negotiate.
  645. //
  646. // We'll insert a cancel routine in the IRP.
  647. if(pRequest->RequestIRP != NULL)
  648. {
  649. UC_WRITE_TRACE_LOG(
  650. g_pUcTraceLog,
  651. UC_ACTION_REQUEST_SET_CANCEL_ROUTINE,
  652. pConnection,
  653. pRequest,
  654. pRequest->RequestIRP,
  655. 0
  656. );
  657. RequestCancelled = UcSetRequestCancelRoutine(
  658. pRequest,
  659. UcCancelSentRequest
  660. );
  661. if(RequestCancelled)
  662. {
  663. // Make sure that any new API calls for this
  664. // request ID are failed.
  665. UcSetFlag(&pRequest->RequestFlags.Value,
  666. UcMakeRequestCancelledFlag());
  667. UC_WRITE_TRACE_LOG(
  668. g_pUcTraceLog,
  669. UC_ACTION_REQUEST_CANCELLED,
  670. pConnection,
  671. pRequest,
  672. pRequest->RequestIRP,
  673. UlongToPtr((ULONG)STATUS_CANCELLED)
  674. );
  675. pRequest->RequestIRP = NULL;
  676. }
  677. }
  678. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  679. }
  680. }
  681. else
  682. {
  683. // The app has specified a receive buffer. We will not complete
  684. // the IRP from here, as we have to wait till the receive
  685. // buffer gets filled up.
  686. if(!pRequest->DontFreeMdls)
  687. {
  688. // If we are not doing SSPI auth, we can free the MDL
  689. // chain.
  690. UcFreeSendMdls(pRequest->pMdlHead);
  691. pRequest->pMdlHead = NULL;
  692. }
  693. if(pRequest->RequestIRP != NULL)
  694. {
  695. UC_WRITE_TRACE_LOG(
  696. g_pUcTraceLog,
  697. UC_ACTION_REQUEST_SET_CANCEL_ROUTINE,
  698. pConnection,
  699. pRequest,
  700. pRequest->RequestIRP,
  701. 0
  702. );
  703. RequestCancelled = UcSetRequestCancelRoutine(
  704. pRequest,
  705. UcCancelSentRequest
  706. );
  707. if(RequestCancelled)
  708. {
  709. // Make sure that any new API calls for this
  710. // request ID are failed.
  711. UcSetFlag(&pRequest->RequestFlags.Value,
  712. UcMakeRequestCancelledFlag());
  713. UC_WRITE_TRACE_LOG(
  714. g_pUcTraceLog,
  715. UC_ACTION_REQUEST_CANCELLED,
  716. pConnection,
  717. pRequest,
  718. pRequest->RequestIRP,
  719. UlongToPtr((ULONG)STATUS_CANCELLED)
  720. );
  721. pRequest->RequestIRP = NULL;
  722. }
  723. }
  724. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  725. }
  726. break;
  727. case UcRequestStateNoSendCompletePartialData:
  728. //
  729. // We got a send complete after receiving some response.
  730. //
  731. pRequest->RequestState = UcRequestStateSendCompletePartialData;
  732. if(!pRequest->RequestFlags.ReceiveBufferSpecified)
  733. {
  734. if(!pRequest->DontFreeMdls)
  735. {
  736. // The app has not supplied any receive buffers. If
  737. // we are not doing SSPI auth (that requires a re-negotiate)
  738. // we can complete the IRP.
  739. pIrp = UcPrepareRequestIrp(pRequest, Status);
  740. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  741. if(pIrp)
  742. {
  743. UlCompleteRequest(pIrp, IO_NETWORK_INCREMENT);
  744. }
  745. }
  746. else
  747. {
  748. // The app has not passed any receive buffers, but we are
  749. // doing SSPI auth. We cannot free the MDL chain or complete
  750. // the IRP, because we might have to re-negotiate.
  751. //
  752. // We'll insert a cancel routine in the IRP.
  753. if(pRequest->RequestIRP != NULL)
  754. {
  755. UC_WRITE_TRACE_LOG(
  756. g_pUcTraceLog,
  757. UC_ACTION_REQUEST_SET_CANCEL_ROUTINE,
  758. pConnection,
  759. pRequest,
  760. pRequest->RequestIRP,
  761. 0
  762. );
  763. RequestCancelled = UcSetRequestCancelRoutine(
  764. pRequest,
  765. UcCancelSentRequest
  766. );
  767. if(RequestCancelled)
  768. {
  769. // Make sure that any new API calls for this
  770. // request ID are failed.
  771. UcSetFlag(&pRequest->RequestFlags.Value,
  772. UcMakeRequestCancelledFlag());
  773. UC_WRITE_TRACE_LOG(
  774. g_pUcTraceLog,
  775. UC_ACTION_REQUEST_CANCELLED,
  776. pConnection,
  777. pRequest,
  778. pRequest->RequestIRP,
  779. UlongToPtr((ULONG)STATUS_CANCELLED)
  780. );
  781. pRequest->RequestIRP = NULL;
  782. }
  783. }
  784. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  785. }
  786. }
  787. else
  788. {
  789. // The app has specified a receive buffer. If it's been
  790. // fully written, we can complete the IRP.
  791. if(pRequest->RequestIRPBytesWritten)
  792. {
  793. pIrp = UcPrepareRequestIrp(pRequest, Status);
  794. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  795. if(pIrp)
  796. {
  797. UlCompleteRequest(pIrp, IO_NETWORK_INCREMENT);
  798. }
  799. break;
  800. }
  801. if(!pRequest->DontFreeMdls)
  802. {
  803. // If we are not doing SSPI auth, we can free the MDL
  804. // chain.
  805. UcFreeSendMdls(pRequest->pMdlHead);
  806. pRequest->pMdlHead = NULL;
  807. }
  808. if(pRequest->RequestIRP != NULL)
  809. {
  810. UC_WRITE_TRACE_LOG(
  811. g_pUcTraceLog,
  812. UC_ACTION_REQUEST_SET_CANCEL_ROUTINE,
  813. pConnection,
  814. pRequest,
  815. pRequest->RequestIRP,
  816. 0
  817. );
  818. RequestCancelled = UcSetRequestCancelRoutine(
  819. pRequest,
  820. UcCancelSentRequest
  821. );
  822. if(RequestCancelled)
  823. {
  824. // Make sure that any new API calls for this
  825. // request ID are failed.
  826. UcSetFlag(&pRequest->RequestFlags.Value,
  827. UcMakeRequestCancelledFlag());
  828. UC_WRITE_TRACE_LOG(
  829. g_pUcTraceLog,
  830. UC_ACTION_REQUEST_CANCELLED,
  831. pConnection,
  832. pRequest,
  833. pRequest->RequestIRP,
  834. UlongToPtr((ULONG)STATUS_CANCELLED)
  835. );
  836. pRequest->RequestIRP = NULL;
  837. }
  838. }
  839. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  840. }
  841. break;
  842. case UcRequestStateNoSendCompleteFullData:
  843. // The send complete happened after the response was parsed.
  844. // We don't have to free the MDLs here or complete the IRP,
  845. // as these will be handled by UcCompleteParsedRequest.
  846. //
  847. pRequest->RequestState = UcRequestStateResponseParsed;
  848. UcCompleteParsedRequest(pRequest, Status, TRUE, OldIrql);
  849. break;
  850. default:
  851. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  852. ASSERT(0);
  853. break;
  854. }
  855. }
  856. /*********************************************************************++
  857. Routine Description:
  858. This is our send request complete routine for entity bodies.
  859. Arguments:
  860. pDeviceObject - The device object we called.
  861. pIrp - The IRP that is completing.
  862. Context - Our context value, a pointer to a request
  863. structure.
  864. Return Value:
  865. NTSTATUS - MORE_PROCESSING_REQUIRED if this isn't to be completed now,
  866. SUCCESS otherwise.
  867. --*********************************************************************/
  868. VOID
  869. UcpRestartEntityMdlSend(
  870. IN PVOID pCompletionContext,
  871. IN NTSTATUS Status,
  872. IN ULONG_PTR Information
  873. )
  874. {
  875. PUC_HTTP_SEND_ENTITY_BODY pEntity;
  876. PUC_HTTP_REQUEST pRequest;
  877. PUC_CLIENT_CONNECTION pConnection;
  878. KIRQL OldIrql;
  879. BOOLEAN bCancelRoutineCalled;
  880. PIRP pIrp;
  881. UNREFERENCED_PARAMETER(Information);
  882. pEntity = (PUC_HTTP_SEND_ENTITY_BODY) pCompletionContext;
  883. pRequest = pEntity->pRequest;
  884. pConnection = pRequest->pConnection;
  885. pIrp = pEntity->pIrp;
  886. ASSERT(UC_IS_VALID_HTTP_REQUEST(pRequest));
  887. ASSERT(UC_IS_VALID_CLIENT_CONNECTION(pConnection));
  888. //
  889. // Free the send MDLs. We want to do this as soon as we can, so we
  890. // do it when the send-completes.
  891. //
  892. ASSERT(pRequest->DontFreeMdls == 0);
  893. ASSERT(pRequest->RequestFlags.RequestBuffered == 0);
  894. UcFreeSendMdls(pEntity->pMdlHead);
  895. //
  896. // If the send complete failed, we have to fail this send, even though
  897. // it might have succeeded
  898. //
  899. if(!NT_SUCCESS(pRequest->RequestStatus))
  900. {
  901. Status = pRequest->RequestStatus;
  902. }
  903. UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
  904. RemoveEntryList(&pEntity->Linkage);
  905. //
  906. // try to remove the cancel routine in the IRP
  907. //
  908. bCancelRoutineCalled = UcRemoveEntityCancelRoutine(pEntity);
  909. //
  910. // If we are pending a cleanup, now's the time to complete it.
  911. //
  912. if(pEntity->pRequest->RequestFlags.CleanPended &&
  913. IsListEmpty(&pRequest->SentEntityList))
  914. {
  915. UC_WRITE_TRACE_LOG(
  916. g_pUcTraceLog,
  917. UC_ACTION_REQUEST_CLEAN_RESUMED,
  918. pConnection,
  919. pRequest,
  920. UlongToPtr(pRequest->RequestState),
  921. UlongToPtr(pConnection->ConnectionState)
  922. );
  923. UcCompleteParsedRequest(pRequest,
  924. pRequest->RequestStatus,
  925. TRUE,
  926. OldIrql
  927. );
  928. }
  929. else
  930. {
  931. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  932. }
  933. if(!bCancelRoutineCalled)
  934. {
  935. pIrp->RequestorMode = pEntity->AppRequestorMode;
  936. pIrp->MdlAddress = pEntity->AppMdl;
  937. pIrp->IoStatus.Status = Status;
  938. pIrp->IoStatus.Information = 0;
  939. UlCompleteRequest(pIrp, IO_NETWORK_INCREMENT);
  940. }
  941. UL_FREE_POOL_WITH_QUOTA(
  942. pEntity,
  943. UC_ENTITY_POOL_TAG,
  944. NonPagedPool,
  945. pEntity->BytesAllocated,
  946. pRequest->pServerInfo->pProcess
  947. );
  948. UC_DEREFERENCE_REQUEST(pRequest);
  949. }
  950. /***************************************************************************++
  951. Routine Description:
  952. Issue requests on a connection. This routine is called when another routine
  953. determines that requests need to be issued on the connection. We'll loop,
  954. issuing requests as long as we can. The connection we're given must be both
  955. referenced and have the spin lock held when we're called. Also, the send
  956. busy flag must be set.
  957. Arguments:
  958. pConnection - Pointer to the connection structure on which requests
  959. are to be issued.
  960. OldIrql - The IRQL to be restored when the lock is freed.
  961. Return Value:
  962. --***************************************************************************/
  963. VOID
  964. UcIssueRequests(
  965. PUC_CLIENT_CONNECTION pConnection,
  966. KIRQL OldIrql
  967. )
  968. {
  969. PLIST_ENTRY pEntry;
  970. PUC_HTTP_REQUEST pRequest;
  971. NTSTATUS Status;
  972. BOOLEAN bCloseConnection = FALSE;
  973. ASSERT( UlDbgSpinLockOwned(&pConnection->SpinLock) );
  974. ASSERT(UC_IS_VALID_CLIENT_CONNECTION(pConnection));
  975. //
  976. // We cannot cleanup the connection when we are still sending requests.
  977. // So, we pend connection cleanup when our Send thread is active.
  978. //
  979. // Such pended cleanups will get picked up at the end of this routine.
  980. //
  981. ASSERT(!(pConnection->Flags & CLIENT_CONN_FLAG_SEND_BUSY));
  982. pConnection->Flags |= CLIENT_CONN_FLAG_SEND_BUSY;
  983. // We know it's OK to send when we're first called or we wouldn't have
  984. // been called. Get a pointer to the first entry on the pending list
  985. // and send it. Then we'll keep looping while there's still stuff
  986. // on the pending list that can be sent and the connection is still
  987. // alive.
  988. //
  989. ASSERT(!IsListEmpty(&pConnection->PendingRequestList));
  990. pEntry = pConnection->PendingRequestList.Flink;
  991. for (;;)
  992. {
  993. BOOLEAN bCancelled;
  994. // Remove the current entry from the list, and get a pointer to the
  995. // containing request. We know we're going to send this one if we're
  996. // here, so remove a cancel routine if there is one. If the request
  997. // is already cancelled, skip it, otherwise move it to the sent
  998. // list.
  999. pRequest = CONTAINING_RECORD(
  1000. pEntry,
  1001. UC_HTTP_REQUEST,
  1002. Linkage);
  1003. ASSERT( UC_IS_VALID_HTTP_REQUEST(pRequest) );
  1004. //
  1005. // Can't send something that is still buffered.
  1006. //
  1007. if(pRequest->RequestFlags.RequestBuffered &&
  1008. !pRequest->RequestFlags.LastEntitySeen)
  1009. {
  1010. UC_WRITE_TRACE_LOG(
  1011. g_pUcTraceLog,
  1012. UC_ACTION_REQUEST_BUFFERED,
  1013. pConnection,
  1014. pRequest,
  1015. pRequest->RequestIRP,
  1016. 0
  1017. );
  1018. break;
  1019. }
  1020. RemoveEntryList(pEntry);
  1021. // See if there was a cancel routine set on this request, and if there
  1022. // was remove it. If it's already gone, the request is cancelled.
  1023. bCancelled = UcRemoveRequestCancelRoutine(pRequest);
  1024. if (bCancelled)
  1025. {
  1026. ASSERT(pRequest->RequestState == UcRequestStateCaptured);
  1027. // If the cancel routine was already null, this request is in the
  1028. // process of being cancelled. In that case just initialize the
  1029. // linkage on the request (so the cancel routine can't pull it
  1030. // from the list again), and continue on.
  1031. UC_WRITE_TRACE_LOG(
  1032. g_pUcTraceLog,
  1033. UC_ACTION_REQUEST_CANCELLED,
  1034. pConnection,
  1035. pRequest,
  1036. pRequest->RequestIRP,
  1037. UlongToPtr((ULONG)STATUS_CANCELLED)
  1038. );
  1039. InitializeListHead(pEntry);
  1040. }
  1041. else
  1042. {
  1043. UC_REFERENCE_REQUEST(pRequest);
  1044. // Either there wasn't a cancel routine or it was removed
  1045. // successfully. In either case put this request on the sent
  1046. // list and send it.
  1047. InsertTailList(&pConnection->SentRequestList, pEntry);
  1048. pRequest->RequestState = UcRequestStateSent;
  1049. UC_WRITE_TRACE_LOG(
  1050. g_pUcTraceLog,
  1051. UC_ACTION_REQUEST_SENT,
  1052. pConnection,
  1053. pRequest,
  1054. pRequest->RequestIRP,
  1055. 0
  1056. );
  1057. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  1058. // Send it, saving the status for later return.
  1059. Status = UcSendData(pConnection,
  1060. pRequest->pMdlHead,
  1061. pRequest->BytesBuffered,
  1062. &UcRestartMdlSend,
  1063. (PVOID)pRequest,
  1064. pRequest->RequestIRP,
  1065. FALSE
  1066. );
  1067. if(STATUS_PENDING != Status)
  1068. {
  1069. UcRestartMdlSend(pRequest, Status, 0);
  1070. }
  1071. // Acquire the spinlock so we can check again.
  1072. UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
  1073. if(pRequest->RequestStatus == STATUS_SUCCESS &&
  1074. pConnection->ConnectionState == UcConnectStateConnectReady)
  1075. {
  1076. if(UcIssueEntities(pRequest, pConnection, &OldIrql) == FALSE)
  1077. {
  1078. // We have not sent all of the data for this request.
  1079. // Additional requests will be blocked from being sent out
  1080. // because there will be a request on the SentRequestList
  1081. // that still hasn't sent out all of its entities.
  1082. //
  1083. UC_WRITE_TRACE_LOG(
  1084. g_pUcTraceLog,
  1085. UC_ACTION_REQUEST_MORE_ENTITY_NEEDED,
  1086. pConnection,
  1087. pRequest,
  1088. pRequest->RequestIRP,
  1089. 0
  1090. );
  1091. UC_DEREFERENCE_REQUEST(pRequest);
  1092. break;
  1093. }
  1094. //
  1095. // If we have sent out all the entity for this request, see
  1096. // if we have to close the connection.
  1097. //
  1098. if(pRequest->RequestConnectionClose)
  1099. {
  1100. bCloseConnection = TRUE;
  1101. UC_DEREFERENCE_REQUEST(pRequest);
  1102. break;
  1103. }
  1104. }
  1105. else
  1106. {
  1107. //
  1108. // The send failed or the connection is torn down.
  1109. // If the send failed, we don't necessarily have to tear the
  1110. // connection down. If the connection is torn down, we'll
  1111. // exit (see below).
  1112. }
  1113. UC_DEREFERENCE_REQUEST(pRequest);
  1114. }
  1115. //
  1116. // If the pending list is empty or the connection is not active, we
  1117. // can't send.
  1118. //
  1119. if (IsListEmpty(&pConnection->PendingRequestList) ||
  1120. pConnection->ConnectionState != UcConnectStateConnectReady
  1121. )
  1122. {
  1123. break;
  1124. }
  1125. //
  1126. // We have at least one request to send out. See if we can pipeline.
  1127. //
  1128. if(UcpCheckForPipelining(pConnection) == FALSE)
  1129. {
  1130. break;
  1131. }
  1132. // We still have something on the list and we might be able to send it.
  1133. // Look at it to see if it's OK.
  1134. pEntry = pConnection->PendingRequestList.Flink;
  1135. }
  1136. UcClearConnectionBusyFlag(
  1137. pConnection,
  1138. CLIENT_CONN_FLAG_SEND_BUSY,
  1139. OldIrql,
  1140. bCloseConnection
  1141. );
  1142. }
  1143. /***************************************************************************++
  1144. Routine Description:
  1145. Issue entities on a connection. This routine is called after we send the
  1146. original request, or from the context of the send-entity IOCTL handler.
  1147. Arguments:
  1148. pRequest - pointer to the request that got sent out.
  1149. pConnection - Pointer to the connection structure
  1150. OldIrql - The IRQL to be restored when the lock is freed.
  1151. Return Value:
  1152. TRUE - We are done with this request & all of it's entities.
  1153. FALSE - More entities to come.
  1154. --***************************************************************************/
  1155. BOOLEAN
  1156. UcIssueEntities(
  1157. PUC_HTTP_REQUEST pRequest,
  1158. PUC_CLIENT_CONNECTION pConnection,
  1159. PKIRQL OldIrql
  1160. )
  1161. {
  1162. PLIST_ENTRY pEntry;
  1163. PUC_HTTP_SEND_ENTITY_BODY pEntity;
  1164. NTSTATUS Status;
  1165. BOOLEAN bLast;
  1166. bLast = (0 == pRequest->RequestFlags.LastEntitySeen) ? FALSE : TRUE;
  1167. ASSERT(UC_IS_VALID_CLIENT_CONNECTION(pConnection));
  1168. ASSERT(pConnection->Flags & CLIENT_CONN_FLAG_SEND_BUSY);
  1169. // We know it's OK to send when we're first called or we wouldn't have
  1170. // been called. Get a pointer to the first entry on the pending list
  1171. // and send it. Then we'll keep looping while there's still stuff
  1172. // on the pending list that can be sent and the connection is still
  1173. // alive.
  1174. //
  1175. while(!IsListEmpty(&pRequest->PendingEntityList))
  1176. {
  1177. BOOLEAN bCancelled;
  1178. //
  1179. // We don't add buffered entities to the PendingEntityList
  1180. //
  1181. ASSERT(!pRequest->RequestFlags.RequestBuffered);
  1182. // Remove the current entry from the list, and get a pointer to the
  1183. // containing request. We know we're going to send this one if we're
  1184. // here, so remove a cancel routine if there is one. If the request
  1185. // is already cancelled, skip it, otherwise move it to the sent
  1186. // list.
  1187. pEntry = RemoveHeadList(&pRequest->PendingEntityList);
  1188. pEntity = CONTAINING_RECORD(pEntry,
  1189. UC_HTTP_SEND_ENTITY_BODY,
  1190. Linkage);
  1191. bLast = pEntity->Last;
  1192. if(pEntity->pIrp)
  1193. {
  1194. // See if there was a cancel routine set on this request, and if
  1195. // there was remove it. If it's already gone, the request is
  1196. // cancelled.
  1197. bCancelled = UcRemoveEntityCancelRoutine(pEntity);
  1198. if (bCancelled)
  1199. {
  1200. // If the cancel routine was already null, this request is in
  1201. // the process of being cancelled. In that case just
  1202. // initialize the linkage on the request (so the cancel routine
  1203. // can't pull it from the list again), and continue on.
  1204. InitializeListHead(pEntry);
  1205. }
  1206. else
  1207. {
  1208. // Either there wasn't a cancel routine or it was removed
  1209. // successfully. In either case put this request on the sent
  1210. // list and send it.
  1211. InsertTailList(&pRequest->SentEntityList, &pEntity->Linkage);
  1212. // Send it, saving the status for later return.
  1213. UC_WRITE_TRACE_LOG(
  1214. g_pUcTraceLog,
  1215. UC_ACTION_ENTITY_SENT,
  1216. pConnection,
  1217. pRequest,
  1218. pEntity,
  1219. pEntity->pIrp
  1220. );
  1221. UlReleaseSpinLock(&pConnection->SpinLock, *OldIrql);
  1222. Status = UcSendData(pConnection,
  1223. pEntity->pMdlHead,
  1224. pEntity->BytesBuffered,
  1225. &UcpRestartEntityMdlSend,
  1226. (PVOID)pEntity,
  1227. pEntity->pIrp,
  1228. FALSE);
  1229. if(STATUS_PENDING != Status)
  1230. {
  1231. UcpRestartEntityMdlSend(pRequest, Status, 0);
  1232. }
  1233. // Acquire the spinlock so we can check again.
  1234. UlAcquireSpinLock(&pConnection->SpinLock, OldIrql);
  1235. }
  1236. }
  1237. }
  1238. return bLast;
  1239. }
  1240. /***************************************************************************++
  1241. Routine Description:
  1242. Free a client connection structure after the reference count has gone to
  1243. zero. Freeing the structure means putting it back onto our free list.
  1244. Arguments:
  1245. pConnection - Pointer to the connection structure to be freed.
  1246. Return Value:
  1247. --***************************************************************************/
  1248. NTSTATUS
  1249. UcpCleanupConnection(
  1250. IN PUC_CLIENT_CONNECTION pConnection,
  1251. IN KIRQL OldIrql,
  1252. IN BOOLEAN Final
  1253. )
  1254. {
  1255. PLIST_ENTRY pList;
  1256. PUC_HTTP_REQUEST pRequest;
  1257. USHORT FreeAddressType;
  1258. PUC_TDI_OBJECTS pTdiObject;
  1259. ASSERT( UC_IS_VALID_CLIENT_CONNECTION(pConnection) );
  1260. //
  1261. // First, we flag this connection to make sure that no new requests
  1262. // The connect state is used to do this. We don't have to explicitly
  1263. // do it here, but we'll just make sure that this is the case.
  1264. //
  1265. ASSERT(!(pConnection->Flags & CLIENT_CONN_FLAG_CLEANUP_PENDED));
  1266. //
  1267. // If there's a thread that is issuing requests, we'll resume cleanup
  1268. // when it's done.
  1269. //
  1270. if(pConnection->Flags & CLIENT_CONN_FLAG_SEND_BUSY ||
  1271. pConnection->Flags & CLIENT_CONN_FLAG_RECV_BUSY)
  1272. {
  1273. UC_WRITE_TRACE_LOG(
  1274. g_pUcTraceLog,
  1275. UC_ACTION_CONNECTION_CLEAN_PENDED,
  1276. pConnection,
  1277. NULL,
  1278. UlongToPtr(pConnection->ConnectionState),
  1279. UlongToPtr(pConnection->Flags)
  1280. );
  1281. pConnection->Flags |= CLIENT_CONN_FLAG_CLEANUP_PENDED;
  1282. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  1283. return STATUS_PENDING;
  1284. }
  1285. //
  1286. // Walk the sent-request list to pick of the requests that got submitted.
  1287. //
  1288. while(!IsListEmpty(&pConnection->SentRequestList))
  1289. {
  1290. pList = pConnection->SentRequestList.Flink;
  1291. pRequest = CONTAINING_RECORD(pList,
  1292. UC_HTTP_REQUEST,
  1293. Linkage);
  1294. ASSERT( UC_IS_VALID_HTTP_REQUEST(pRequest) );
  1295. pRequest->RequestStatus = pConnection->ConnectionStatus;
  1296. if(UcCompleteParsedRequest(pRequest,
  1297. pRequest->RequestStatus,
  1298. FALSE,
  1299. OldIrql
  1300. ) == STATUS_PENDING)
  1301. {
  1302. UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
  1303. //
  1304. // There could be a window where the request gets cleaned up
  1305. // after the lock gets released & before we acquire it again.
  1306. // So, before we actually pend connection cleanup, we'll make
  1307. // sure that the request is still on the list.
  1308. //
  1309. if(pList == pConnection->SentRequestList.Flink)
  1310. {
  1311. UC_WRITE_TRACE_LOG(
  1312. g_pUcTraceLog,
  1313. UC_ACTION_CONNECTION_CLEAN_PENDED,
  1314. pConnection,
  1315. pRequest,
  1316. UlongToPtr(pRequest->RequestState),
  1317. UlongToPtr(pConnection->Flags)
  1318. );
  1319. pConnection->Flags |= CLIENT_CONN_FLAG_CLEANUP_PENDED;
  1320. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  1321. return STATUS_PENDING;
  1322. }
  1323. else
  1324. {
  1325. // This request really got cleaned, so we'll move on to the
  1326. // next.
  1327. }
  1328. }
  1329. else
  1330. {
  1331. UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
  1332. }
  1333. }
  1334. if(Final ||
  1335. (!IsListEmpty(&pConnection->PendingRequestList) &&
  1336. !(pConnection->Flags & CLIENT_CONN_FLAG_CONNECT_READY)))
  1337. {
  1338. //
  1339. // If we are doing final cleanup, then ideally these lists should
  1340. // be empty, since we would have kicked these guys off in the
  1341. // Cleanup Handler. However, it does not hurt us to check these
  1342. // here & clean if there are some entries.
  1343. //
  1344. // A pended request initiates a connection setup, which can go through
  1345. // various phases (e.g. connect failures, wait for server cert,
  1346. // proxy SSL, etc), before it's actually ready to be used.
  1347. //
  1348. // If we get called in the cleanup handler before we move to the
  1349. // ready state, then our connection setup has failed and we are
  1350. // required to fail all pended requests. If we don't fail the pended
  1351. // requests, we'll get into a infinite loop, where we'll constantly
  1352. // try to set up a connection (which could fail again).
  1353. //
  1354. if(Final)
  1355. {
  1356. while(!IsListEmpty(&pConnection->ProcessedRequestList))
  1357. {
  1358. pList = RemoveHeadList(&pConnection->ProcessedRequestList);
  1359. pRequest = CONTAINING_RECORD(pList,
  1360. UC_HTTP_REQUEST,
  1361. Linkage);
  1362. InitializeListHead(pList);
  1363. pRequest->RequestStatus = STATUS_CANCELLED;
  1364. ASSERT(pRequest->RequestState == UcRequestStateResponseParsed);
  1365. if(UcRemoveRequestCancelRoutine(pRequest))
  1366. {
  1367. UC_WRITE_TRACE_LOG(
  1368. g_pUcTraceLog,
  1369. UC_ACTION_REQUEST_CANCELLED,
  1370. pConnection,
  1371. pRequest,
  1372. pRequest->RequestIRP,
  1373. UlongToPtr((ULONG)STATUS_CANCELLED)
  1374. );
  1375. }
  1376. else
  1377. {
  1378. pRequest->RequestState = UcRequestStateDone;
  1379. UcCompleteParsedRequest(pRequest,
  1380. pRequest->RequestStatus,
  1381. FALSE,
  1382. OldIrql
  1383. );
  1384. UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
  1385. }
  1386. }
  1387. }
  1388. while(!IsListEmpty(&pConnection->PendingRequestList))
  1389. {
  1390. pList = RemoveHeadList(&pConnection->PendingRequestList);
  1391. pRequest = CONTAINING_RECORD(pList,
  1392. UC_HTTP_REQUEST,
  1393. Linkage);
  1394. InitializeListHead(pList);
  1395. pRequest->RequestStatus = pConnection->ConnectionStatus;
  1396. ASSERT(pRequest->RequestState == UcRequestStateCaptured);
  1397. if(UcRemoveRequestCancelRoutine(pRequest))
  1398. {
  1399. UC_WRITE_TRACE_LOG(
  1400. g_pUcTraceLog,
  1401. UC_ACTION_REQUEST_CANCELLED,
  1402. pConnection,
  1403. pRequest,
  1404. pRequest->RequestIRP,
  1405. UlongToPtr((ULONG)STATUS_CANCELLED)
  1406. );
  1407. }
  1408. else
  1409. {
  1410. pRequest->RequestState = UcRequestStateDone;
  1411. UcCompleteParsedRequest(pRequest,
  1412. pRequest->RequestStatus,
  1413. FALSE,
  1414. OldIrql
  1415. );
  1416. UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
  1417. }
  1418. }
  1419. }
  1420. //
  1421. // Reset the flags.
  1422. //
  1423. pConnection->Flags = 0;
  1424. UC_WRITE_TRACE_LOG(
  1425. g_pUcTraceLog,
  1426. UC_ACTION_CONNECTION_CLEANED,
  1427. pConnection,
  1428. UlongToPtr(pConnection->ConnectionStatus),
  1429. UlongToPtr(pConnection->ConnectionState),
  1430. UlongToPtr(pConnection->Flags)
  1431. );
  1432. pTdiObject = pConnection->pTdiObjects;
  1433. pConnection->pTdiObjects = NULL;
  1434. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  1435. //
  1436. // Let's assume that we did a active close & push this object back
  1437. // on the list.
  1438. //
  1439. if(pTdiObject)
  1440. {
  1441. FreeAddressType = pTdiObject->ConnectionType;
  1442. pTdiObject->pConnection = NULL;
  1443. UcpPushTdiObject(pTdiObject, FreeAddressType);
  1444. }
  1445. //
  1446. // Get rid of our opaque id if we're a filtered connection.
  1447. // Also make sure we stop delivering AppWrite data to the parser.
  1448. //
  1449. if (pConnection->FilterInfo.pFilterChannel)
  1450. {
  1451. HTTP_RAW_CONNECTION_ID ConnectionId;
  1452. UlUnbindConnectionFromFilter(&pConnection->FilterInfo);
  1453. ConnectionId = pConnection->FilterInfo.ConnectionId;
  1454. HTTP_SET_NULL_ID( &pConnection->FilterInfo.ConnectionId );
  1455. if (!HTTP_IS_NULL_ID( &ConnectionId ))
  1456. {
  1457. UlFreeOpaqueId(ConnectionId, UlOpaqueIdTypeRawConnection);
  1458. DEREFERENCE_CLIENT_CONNECTION(pConnection);
  1459. }
  1460. UlDestroyFilterConnection(&pConnection->FilterInfo);
  1461. DEREFERENCE_FILTER_CHANNEL(pConnection->FilterInfo.pFilterChannel);
  1462. pConnection->FilterInfo.pFilterChannel = NULL;
  1463. }
  1464. //
  1465. // Get rid of any buffers we allocated for
  1466. // certificate information.
  1467. //
  1468. if (pConnection->FilterInfo.SslInfo.pServerCertData)
  1469. {
  1470. UL_FREE_POOL(
  1471. pConnection->FilterInfo.SslInfo.pServerCertData,
  1472. UL_SSL_CERT_DATA_POOL_TAG
  1473. );
  1474. pConnection->FilterInfo.SslInfo.pServerCertData = NULL;
  1475. }
  1476. if (pConnection->FilterInfo.SslInfo.pCertEncoded)
  1477. {
  1478. UL_FREE_POOL(
  1479. pConnection->FilterInfo.SslInfo.pCertEncoded,
  1480. UL_SSL_CERT_DATA_POOL_TAG
  1481. );
  1482. pConnection->FilterInfo.SslInfo.pCertEncoded = NULL;
  1483. }
  1484. if (pConnection->FilterInfo.SslInfo.Token)
  1485. {
  1486. HANDLE Token;
  1487. Token = (HANDLE) pConnection->FilterInfo.SslInfo.Token;
  1488. //
  1489. // If we are not running under the system process. And if the
  1490. // thread we are running under has some APCs queued currently
  1491. // KeAttachProcess won't allow us to attach to another process
  1492. // and will bugcheck 5. We have to be queued as a work item and
  1493. // should be running on the passive IRQL.
  1494. //
  1495. ASSERT( PsGetCurrentProcess() == (PEPROCESS) g_pUlSystemProcess );
  1496. ZwClose(Token);
  1497. }
  1498. //
  1499. // Free any allocated memory
  1500. //
  1501. if(pConnection->MergeIndication.pBuffer)
  1502. {
  1503. UL_FREE_POOL_WITH_QUOTA(
  1504. pConnection->MergeIndication.pBuffer,
  1505. UC_RESPONSE_TDI_BUFFER_POOL_TAG,
  1506. NonPagedPool,
  1507. pConnection->MergeIndication.BytesAllocated,
  1508. pConnection->pServerInfo->pProcess
  1509. );
  1510. pConnection->MergeIndication.pBuffer = NULL;
  1511. }
  1512. // Free serialized server certificate, if any
  1513. UC_FREE_SERIALIZED_CERT(&pConnection->ServerCertInfo,
  1514. pConnection->pServerInfo->pProcess);
  1515. // Free certificate issuer list, if any
  1516. UC_FREE_CERT_ISSUER_LIST(&pConnection->ServerCertInfo,
  1517. pConnection->pServerInfo->pProcess);
  1518. return STATUS_SUCCESS;
  1519. }
  1520. /***************************************************************************++
  1521. Routine Description:
  1522. The common connect complete routine. Called from the TDI UcpConnectComplete
  1523. handler.
  1524. Arguments:
  1525. pConnection - Pointer to the connection structure to be freed.
  1526. Status - Connection Status.
  1527. Return Value:
  1528. --***************************************************************************/
  1529. VOID
  1530. UcRestartClientConnect(
  1531. IN PUC_CLIENT_CONNECTION pConnection,
  1532. IN NTSTATUS Status
  1533. )
  1534. {
  1535. KIRQL OldIrql;
  1536. BOOLEAN bCloseConnection;
  1537. bCloseConnection = FALSE;
  1538. UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
  1539. pConnection->ConnectionStatus = Status;
  1540. UC_WRITE_TRACE_LOG(
  1541. g_pUcTraceLog,
  1542. UC_ACTION_CONNECTION_RESTART_CONNECT,
  1543. pConnection,
  1544. UlongToPtr(pConnection->ConnectionStatus),
  1545. UlongToPtr(pConnection->ConnectionState),
  1546. UlongToPtr(pConnection->Flags)
  1547. );
  1548. //
  1549. // First, we check to see if we have received a Cleanup/Close IRP.
  1550. // If that's the case, we proceed directly to cleanup, regardless of
  1551. // the status.
  1552. //
  1553. // Now, if the connection was successfully established, we have to tear
  1554. // it down. This will eventually cleanup the connection & complete the
  1555. // pended Cleanup/Close IRP.
  1556. //
  1557. if(pConnection->pEvent)
  1558. {
  1559. pConnection->Flags &= ~CLIENT_CONN_FLAG_TDI_ALLOCATE;
  1560. if(Status == STATUS_SUCCESS)
  1561. {
  1562. pConnection->ConnectionState = UcConnectStateConnectComplete;
  1563. bCloseConnection = TRUE;
  1564. }
  1565. else
  1566. {
  1567. UC_WRITE_TRACE_LOG(
  1568. g_pUcTraceLog,
  1569. UC_ACTION_CONNECTION_CLEANUP,
  1570. pConnection,
  1571. UlongToPtr(pConnection->ConnectionStatus),
  1572. UlongToPtr(pConnection->ConnectionState),
  1573. UlongToPtr(pConnection->Flags)
  1574. );
  1575. pConnection->ConnectionState = UcConnectStateConnectCleanup;
  1576. pConnection->ConnectionStatus = STATUS_CANCELLED;
  1577. }
  1578. }
  1579. else if (Status == STATUS_SUCCESS)
  1580. {
  1581. // It did, we connected. Now make sure we're still in the connecting
  1582. // state, and get pending requests going.
  1583. ASSERT(pConnection->ConnectionState == UcConnectStateConnectPending);
  1584. pConnection->Flags &= ~CLIENT_CONN_FLAG_TDI_ALLOCATE;
  1585. pConnection->ConnectionState = UcConnectStateConnectComplete;
  1586. }
  1587. else if (Status == STATUS_ADDRESS_ALREADY_EXISTS)
  1588. {
  1589. // If a connection attempt fails with STATUS_ADDRESS_ALREADY_EXISTS
  1590. // it means that the TCB that is represented by the AO+CO object
  1591. // is in TIME_WAIT. We'll put this AO+CO back on our list &
  1592. // proceed to allocate a new one from TCP.
  1593. //
  1594. // If a newly allocated AO+CO also fails with TIME_WAIT, then we'll
  1595. // just give up & show the error to the application.
  1596. //
  1597. if(pConnection->Flags & CLIENT_CONN_FLAG_TDI_ALLOCATE)
  1598. {
  1599. // Darn. An newly allocated TDI object also failed with TIME_WAIT.
  1600. // we'll have to fail the connection attempt. This is our
  1601. // recursion breaking condition.
  1602. pConnection->Flags &= ~CLIENT_CONN_FLAG_TDI_ALLOCATE;
  1603. goto ConnectFailure;
  1604. }
  1605. else
  1606. {
  1607. // The actual free of the AO+CO will happen in the
  1608. // Connection State Machine.
  1609. pConnection->ConnectionState = UcConnectStateConnectIdle;
  1610. }
  1611. }
  1612. else
  1613. {
  1614. // This connect attempt failed. See if there are any more addresses.
  1615. // getaddrinfo can pass back a list of addresses & we'll try all those
  1616. // addresses before giving up & bouncing the error to the app.
  1617. // Some of these maybe IPv4 address & some others maybe IPv6 addresses.
  1618. //
  1619. // We use pConnection->NextAddressCount to make sure that we don't
  1620. // overflow the address-list that's stored in the ServInfo structure.
  1621. //
  1622. pConnection->NextAddressCount =
  1623. (pConnection->NextAddressCount + 1) %
  1624. pConnection->pServerInfo->pTransportAddress->TAAddressCount;
  1625. //
  1626. // pConnection->pNextAddress points to a TA_ADDRESS structure in the
  1627. // TRANSPORT_ADDRESS list that is stored off the ServInfo structure.
  1628. // This will be used for the "next" connect attempt.
  1629. //
  1630. pConnection->pNextAddress = (PTA_ADDRESS)
  1631. ((PCHAR) pConnection->pNextAddress +
  1632. FIELD_OFFSET(TA_ADDRESS, Address) +
  1633. pConnection->pNextAddress->AddressLength);
  1634. if(pConnection->NextAddressCount == 0)
  1635. {
  1636. // We've rolled back (i.e we've cycled through all the IP addresses
  1637. // Let's treat this as a real failure & propogate the error to the
  1638. // application.
  1639. //
  1640. ConnectFailure:
  1641. // The connect failed. We need to fail any pending requests.
  1642. pConnection->Flags &= ~CLIENT_CONN_FLAG_TDI_ALLOCATE;
  1643. UC_WRITE_TRACE_LOG(
  1644. g_pUcTraceLog,
  1645. UC_ACTION_CONNECTION_CLEANUP,
  1646. pConnection,
  1647. UlongToPtr(Status),
  1648. UlongToPtr(pConnection->ConnectionState),
  1649. UlongToPtr(pConnection->Flags)
  1650. );
  1651. pConnection->ConnectionState = UcConnectStateConnectCleanup;
  1652. }
  1653. else
  1654. {
  1655. // Set the state to IDLE, so that we use the next address to
  1656. // connect.
  1657. pConnection->Flags &= ~CLIENT_CONN_FLAG_TDI_ALLOCATE;
  1658. pConnection->ConnectionState = UcConnectStateConnectIdle;
  1659. }
  1660. }
  1661. //
  1662. // We can't be sending any reqeusts when a connection attempt is in
  1663. // progress.
  1664. //
  1665. ASSERT(IsListEmpty(&pConnection->SentRequestList));
  1666. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  1667. if(bCloseConnection)
  1668. {
  1669. UC_CLOSE_CONNECTION(pConnection, TRUE, STATUS_CANCELLED);
  1670. }
  1671. }
  1672. /***************************************************************************++
  1673. Routine Description:
  1674. Cancel a pending request. This routine is called when we're canceling
  1675. a request that's on the pending list, hasn't been sent and hasn't caused
  1676. a connect request.
  1677. Arguments:
  1678. pDeviceObject - Pointer to device object.
  1679. Irp - Pointer to IRP being canceled.
  1680. Return Value:
  1681. --***************************************************************************/
  1682. VOID
  1683. UcpCancelPendingRequest(
  1684. PDEVICE_OBJECT pDeviceObject,
  1685. PIRP Irp
  1686. )
  1687. {
  1688. PUC_HTTP_REQUEST pRequest;
  1689. PUC_CLIENT_CONNECTION pConnection;
  1690. KIRQL OldIrql;
  1691. UNREFERENCED_PARAMETER(pDeviceObject);
  1692. // Release the cancel spin lock, since we're not using it.
  1693. IoReleaseCancelSpinLock(Irp->CancelIrql);
  1694. // Retrieve the pointers we need. The request pointer is stored inthe
  1695. // driver context array, and a back pointer to the connection is stored
  1696. // in the request. Whoever set the cancel routine is responsible for
  1697. // referencing the connection for us.
  1698. pRequest = (PUC_HTTP_REQUEST)Irp->Tail.Overlay.DriverContext[0];
  1699. pConnection = pRequest->pConnection;
  1700. ASSERT(UC_IS_VALID_CLIENT_CONNECTION(pConnection));
  1701. UC_WRITE_TRACE_LOG(
  1702. g_pUcTraceLog,
  1703. UC_ACTION_REQUEST_CANCELLED,
  1704. pConnection,
  1705. pRequest,
  1706. Irp,
  1707. UlongToPtr((ULONG)STATUS_CANCELLED)
  1708. );
  1709. //
  1710. // Note: We cannot just call UcFailRequest from here. UcFailRequest
  1711. // is supposed to be called when a request is failed (e.g. parseer
  1712. // error) or canceled (HttpCancelRequest API) & hence has code to
  1713. // not double complete the IRP if the cancel routine kicked in.
  1714. //
  1715. // Since we are the IRP cancel routine, we have to manually
  1716. // complete the IRP. An IRP in this state has not hit the wire.
  1717. // so, we just free send MDLs & cancel it. Note that we call
  1718. // UcFailRequest to handle common IRP cleanup.
  1719. //
  1720. UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
  1721. UcFreeSendMdls(pRequest->pMdlHead);
  1722. pRequest->pMdlHead = NULL;
  1723. pRequest->RequestIRP = NULL;
  1724. Irp->IoStatus.Status = STATUS_CANCELLED;
  1725. Irp->RequestorMode = pRequest->AppRequestorMode;
  1726. Irp->MdlAddress = pRequest->AppMdl;
  1727. UcSetFlag(&pRequest->RequestFlags.Value, UcMakeRequestCancelledFlag());
  1728. UcFailRequest(pRequest, STATUS_CANCELLED, OldIrql);
  1729. // For the IRP
  1730. UC_DEREFERENCE_REQUEST(pRequest);
  1731. UlCompleteRequest(Irp, IO_NO_INCREMENT);
  1732. }
  1733. /***************************************************************************++
  1734. Routine Description:
  1735. Send an entity body on a connection.
  1736. Arguments:
  1737. Return Value:
  1738. --***************************************************************************/
  1739. NTSTATUS
  1740. UcSendEntityBody(
  1741. IN PUC_HTTP_REQUEST pRequest,
  1742. IN PUC_HTTP_SEND_ENTITY_BODY pEntity,
  1743. IN PIRP pIrp,
  1744. IN PIO_STACK_LOCATION pIrpSp,
  1745. OUT PBOOLEAN bDontFail,
  1746. IN BOOLEAN bLast
  1747. )
  1748. {
  1749. NTSTATUS Status;
  1750. PUC_CLIENT_CONNECTION pConnection;
  1751. KIRQL OldIrql;
  1752. BOOLEAN RequestCancelled;
  1753. BOOLEAN bCloseConnection = FALSE;
  1754. pConnection = pRequest->pConnection;
  1755. ASSERT(UC_IS_VALID_CLIENT_CONNECTION(pConnection));
  1756. ASSERT(UC_IS_VALID_HTTP_REQUEST(pRequest));
  1757. UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
  1758. if(pRequest->RequestState == UcRequestStateDone ||
  1759. pRequest->RequestFlags.Cancelled == TRUE ||
  1760. pRequest->RequestFlags.LastEntitySeen
  1761. )
  1762. {
  1763. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  1764. return STATUS_INVALID_PARAMETER;
  1765. }
  1766. if(bLast)
  1767. {
  1768. UcSetFlag(&pRequest->RequestFlags.Value,
  1769. UcMakeRequestLastEntitySeenFlag());
  1770. }
  1771. if(pRequest->RequestFlags.RequestBuffered)
  1772. {
  1773. if(pRequest->RequestFlags.LastEntitySeen)
  1774. {
  1775. //
  1776. // We have seen the last entity for this request. We had already
  1777. // pinned the request on a connection (by inserting it in the
  1778. // pending list), we just need to issue the request.
  1779. //
  1780. UC_WRITE_TRACE_LOG(
  1781. g_pUcTraceLog,
  1782. UC_ACTION_ENTITY_LAST,
  1783. pConnection,
  1784. pRequest,
  1785. pEntity,
  1786. pIrp
  1787. );
  1788. InsertTailList(&pRequest->SentEntityList, &pEntity->Linkage);
  1789. //
  1790. // If the request IRP is around, we'll just use it. Otherwise,
  1791. // we'll use the entity IRP.
  1792. //
  1793. if(pRequest->RequestIRP)
  1794. {
  1795. // If the request IRP is around, it means that the app has
  1796. // passed a receive buffer. In such cases, we will use the
  1797. // request IRP to call TDI.
  1798. //
  1799. // We can complete the entity IRP with status_success since
  1800. // we don't need it. If this thread return something that is
  1801. // not STATUS_PENDING, then the IOCTL handler will complete
  1802. // the IRP.
  1803. ASSERT(pRequest->RequestFlags.ReceiveBufferSpecified == TRUE);
  1804. pEntity->pIrp = 0;
  1805. Status = UcSendRequestOnConnection(pConnection,
  1806. pRequest,
  1807. OldIrql);
  1808. // If we get STATUS_PENDING, we want the IOCTL handler to
  1809. // complete the entity IRP with success, since we used the
  1810. // request IRP. However, if we get any other status, then
  1811. // we want to propogate it to the IOCTL handler. This will
  1812. // fail the request, which will complete the request IRP.
  1813. return ((Status == STATUS_PENDING)?STATUS_SUCCESS:Status);
  1814. }
  1815. else
  1816. {
  1817. pEntity->pIrp = 0;
  1818. pRequest->AppRequestorMode = pIrp->RequestorMode;
  1819. pRequest->AppMdl = pIrp->MdlAddress;
  1820. pRequest->RequestIRP = pIrp;
  1821. pRequest->RequestIRPSp = pIrpSp;
  1822. ASSERT(pRequest->pFileObject == pIrpSp->FileObject);
  1823. // Take a ref for the IRP.
  1824. UC_REFERENCE_REQUEST(pRequest);
  1825. return UcSendRequestOnConnection(pConnection,
  1826. pRequest,
  1827. OldIrql);
  1828. }
  1829. }
  1830. else
  1831. {
  1832. //
  1833. // We have buffered the request & hence we have completed it early.
  1834. // Let's do the same with the entity body also.
  1835. //
  1836. UC_WRITE_TRACE_LOG(
  1837. g_pUcTraceLog,
  1838. UC_ACTION_ENTITY_BUFFERED,
  1839. pConnection,
  1840. pRequest,
  1841. pEntity,
  1842. pIrp
  1843. );
  1844. InsertTailList(&pRequest->SentEntityList, &pEntity->Linkage);
  1845. pEntity->pIrp->IoStatus.Status = STATUS_SUCCESS;
  1846. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  1847. return STATUS_SUCCESS;
  1848. }
  1849. }
  1850. //
  1851. // If the request has not been buffered earlier, we can send right
  1852. // away.
  1853. //
  1854. UC_WRITE_TRACE_LOG(
  1855. g_pUcTraceLog,
  1856. UC_ACTION_ENTITY_READY_TO_SEND,
  1857. pConnection,
  1858. pRequest,
  1859. pEntity,
  1860. pIrp
  1861. );
  1862. if(pConnection->Flags & CLIENT_CONN_FLAG_SEND_BUSY ||
  1863. (pRequest->RequestState == UcRequestStateCaptured) ||
  1864. (!IsListEmpty(&pRequest->PendingEntityList))
  1865. )
  1866. {
  1867. //
  1868. // We can't send this request now. Either
  1869. // a. This request itself has not been sent to TDI.
  1870. // b. Other send-entities are ahead of us.
  1871. // c. This request has not seen all of it's entity bodies.
  1872. //
  1873. UC_WRITE_TRACE_LOG(
  1874. g_pUcTraceLog,
  1875. UC_ACTION_ENTITY_QUEUED,
  1876. pConnection,
  1877. pRequest,
  1878. pEntity,
  1879. pIrp
  1880. );
  1881. InsertTailList(&pRequest->PendingEntityList, &pEntity->Linkage);
  1882. IoMarkIrpPending(pEntity->pIrp);
  1883. RequestCancelled = UcSetEntityCancelRoutine(
  1884. pEntity,
  1885. UcpCancelSendEntity
  1886. );
  1887. if(RequestCancelled)
  1888. {
  1889. UC_WRITE_TRACE_LOG(
  1890. g_pUcTraceLog,
  1891. UC_ACTION_ENTITY_CANCELLED,
  1892. pConnection,
  1893. pRequest,
  1894. pEntity,
  1895. pIrp
  1896. );
  1897. pEntity->pIrp = NULL;
  1898. }
  1899. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  1900. return STATUS_PENDING;
  1901. }
  1902. if(pConnection->ConnectionState == UcConnectStateConnectReady &&
  1903. pRequest->RequestState != UcRequestStateResponseParsed &&
  1904. pRequest->RequestState != UcRequestStateNoSendCompleteFullData)
  1905. {
  1906. // We can send now as we are doing chunked sends. Rather than
  1907. // calling UcSendData directly, we'll call UcIssueEntities.
  1908. // We could get multiple send-entities, and we don't want them
  1909. // to go out of order.
  1910. pConnection->Flags |= CLIENT_CONN_FLAG_SEND_BUSY;
  1911. Status = STATUS_PENDING;
  1912. InsertTailList(&pRequest->PendingEntityList, &pEntity->Linkage);
  1913. IoMarkIrpPending(pEntity->pIrp);
  1914. UC_REFERENCE_REQUEST(pRequest);
  1915. if(UcIssueEntities(pRequest, pConnection, &OldIrql))
  1916. {
  1917. // We have sent the last entity. Now, we can see if we want to
  1918. // send the next request or clear the flag.
  1919. if(pRequest->RequestConnectionClose)
  1920. {
  1921. //
  1922. // Remember that we've to close the connection. We'll do this
  1923. // after we release the spin lock.
  1924. //
  1925. bCloseConnection = TRUE;
  1926. }
  1927. else if(
  1928. pConnection->ConnectionState == UcConnectStateConnectReady &&
  1929. !IsListEmpty(&pConnection->PendingRequestList) &&
  1930. IsListEmpty(&pConnection->SentRequestList)
  1931. )
  1932. {
  1933. UC_DEREFERENCE_REQUEST(pRequest);
  1934. ASSERT(pConnection->Flags & CLIENT_CONN_FLAG_SEND_BUSY);
  1935. pConnection->Flags &= ~CLIENT_CONN_FLAG_SEND_BUSY;
  1936. //
  1937. // Connection is still ready, see if we can send any more
  1938. // requests. Note that we have to check the state again, because
  1939. // we are releasing the lock above.
  1940. //
  1941. UcIssueRequests(pConnection, OldIrql);
  1942. return STATUS_PENDING;
  1943. }
  1944. }
  1945. UC_DEREFERENCE_REQUEST(pRequest);
  1946. UcClearConnectionBusyFlag(
  1947. pConnection,
  1948. CLIENT_CONN_FLAG_SEND_BUSY,
  1949. OldIrql,
  1950. bCloseConnection
  1951. );
  1952. }
  1953. else
  1954. {
  1955. // It appears as if the connection was torn down for some reason.
  1956. // let's propogate this error to the app.
  1957. //
  1958. // We don't want to fail the request because of this error code.
  1959. // The connection could be torn down because of a 401, and we want to
  1960. // give the app a chance to read the response buffer. If we fail the
  1961. // request, we are preventing the app from reading the response.
  1962. //
  1963. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  1964. *bDontFail = TRUE;
  1965. Status = STATUS_CONNECTION_DISCONNECTED;
  1966. }
  1967. return Status;
  1968. }
  1969. /***************************************************************************++
  1970. Routine Description:
  1971. Reference a client connection structure.
  1972. Arguments:
  1973. pConnection - Pointer to the connection structure to be referenced.
  1974. Return Value:
  1975. --***************************************************************************/
  1976. VOID
  1977. UcReferenceClientConnection(
  1978. PVOID pObject
  1979. REFERENCE_DEBUG_FORMAL_PARAMS
  1980. )
  1981. {
  1982. LONG RefCount;
  1983. PUC_CLIENT_CONNECTION pConnection;
  1984. pConnection = (PUC_CLIENT_CONNECTION) pObject;
  1985. ASSERT( UC_IS_VALID_CLIENT_CONNECTION(pConnection) );
  1986. RefCount = InterlockedIncrement(&pConnection->RefCount);
  1987. WRITE_REF_TRACE_LOG(
  1988. g_pTdiTraceLog,
  1989. REF_ACTION_REFERENCE_UL_CONNECTION,
  1990. RefCount,
  1991. pConnection,
  1992. pFileName,
  1993. LineNumber
  1994. );
  1995. ASSERT( RefCount > 0 );
  1996. }
  1997. /***************************************************************************++
  1998. Routine Description:
  1999. Dereference a client connection structure. If the reference count goes
  2000. to 0, we'll free the structure.
  2001. Arguments:
  2002. pConnection - Pointer to the connection structure to be
  2003. dereferenced.
  2004. Return Value:
  2005. --***************************************************************************/
  2006. VOID
  2007. UcDereferenceClientConnection(
  2008. PVOID pObject
  2009. REFERENCE_DEBUG_FORMAL_PARAMS
  2010. )
  2011. {
  2012. LONG RefCount;
  2013. PUC_CLIENT_CONNECTION pConnection;
  2014. pConnection = (PUC_CLIENT_CONNECTION) pObject;
  2015. ASSERT( UC_IS_VALID_CLIENT_CONNECTION(pConnection) );
  2016. RefCount = InterlockedDecrement(&pConnection->RefCount);
  2017. WRITE_REF_TRACE_LOG(
  2018. g_pTdiTraceLog,
  2019. REF_ACTION_DEREFERENCE_UL_CONNECTION,
  2020. RefCount,
  2021. pConnection,
  2022. pFileName,
  2023. LineNumber
  2024. );
  2025. ASSERT(RefCount >= 0);
  2026. if (RefCount == 0)
  2027. {
  2028. ASSERT(pConnection->pTdiObjects == NULL);
  2029. DESTROY_REF_TRACE_LOG(pConnection->pTraceLog,
  2030. UL_REF_TRACE_LOG_POOL_TAG);
  2031. if(pConnection->pEvent)
  2032. {
  2033. KeSetEvent(pConnection->pEvent, 0, FALSE);
  2034. pConnection->pEvent = NULL;
  2035. }
  2036. ExFreeToNPagedLookasideList(
  2037. &g_ClientConnectionLookaside,
  2038. pConnection
  2039. );
  2040. }
  2041. }
  2042. /***************************************************************************++
  2043. Routine Description:
  2044. The worker thread that calls the connection state machine.
  2045. Arguments:
  2046. pWorkItem - Pointer to the work-item
  2047. Return Value:
  2048. None
  2049. --***************************************************************************/
  2050. VOID
  2051. UcpConnectionStateMachineWorker(
  2052. IN PUL_WORK_ITEM pWorkItem
  2053. )
  2054. {
  2055. KIRQL OldIrql;
  2056. PUC_CLIENT_CONNECTION pConnection;
  2057. //
  2058. // Sanity check.
  2059. //
  2060. ASSERT( KeGetCurrentIrql() == PASSIVE_LEVEL );
  2061. //
  2062. // Grab the connection.
  2063. //
  2064. pConnection = CONTAINING_RECORD(
  2065. pWorkItem,
  2066. UC_CLIENT_CONNECTION,
  2067. WorkItem
  2068. );
  2069. ASSERT( UC_IS_VALID_CLIENT_CONNECTION(pConnection) );
  2070. UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
  2071. ASSERT(pConnection->bWorkItemQueued);
  2072. pConnection->bWorkItemQueued = FALSE;
  2073. UcConnectionStateMachine(pConnection, OldIrql);
  2074. DEREFERENCE_CLIENT_CONNECTION(pConnection);
  2075. }
  2076. /***************************************************************************++
  2077. Routine Description:
  2078. This routine kicks off the worker thread that fires the connection state
  2079. machine. This is always called with the connection spin lock held. If
  2080. the worker has already been issued but not fired, we don't do anything.
  2081. Arguments:
  2082. pConnection - Pointer to the connection structure
  2083. OldIrql - The IRQL that we have to use when calling UlReleaseSpinLock.
  2084. Return Value:
  2085. None
  2086. --***************************************************************************/
  2087. VOID
  2088. UcKickOffConnectionStateMachine(
  2089. IN PUC_CLIENT_CONNECTION pConnection,
  2090. IN KIRQL OldIrql,
  2091. IN UC_CONNECTION_WORKER_TYPE WorkerType
  2092. )
  2093. {
  2094. if(!pConnection->bWorkItemQueued)
  2095. {
  2096. pConnection->bWorkItemQueued = TRUE;
  2097. REFERENCE_CLIENT_CONNECTION(pConnection);
  2098. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  2099. if(UcConnectionPassive == WorkerType)
  2100. {
  2101. // If we are already at PASSIVE, UL_CALL_PASSIVE calls the callback
  2102. // in the context of the same thread.
  2103. UL_CALL_PASSIVE(&pConnection->WorkItem,
  2104. &UcpConnectionStateMachineWorker);
  2105. }
  2106. else
  2107. {
  2108. ASSERT(UcConnectionWorkItem == WorkerType);
  2109. UL_QUEUE_WORK_ITEM(&pConnection->WorkItem,
  2110. &UcpConnectionStateMachineWorker);
  2111. }
  2112. }
  2113. else
  2114. {
  2115. //
  2116. // Someone else has already done this.
  2117. //
  2118. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  2119. }
  2120. }
  2121. /***************************************************************************++
  2122. Routine Description:
  2123. This routine computes the size required to store HTTP_RAW_CONNECTION_INFO
  2124. structure. The computation takes into account the space needed to store
  2125. embedded pointers to structures. See the diagram below.
  2126. Arguments:
  2127. pConnectionContext - Pointer to UC_CLIENT_CONNECTION.
  2128. Return Value:
  2129. Length (in bytes) needed to generate raw connection info structure.
  2130. --***************************************************************************/
  2131. ULONG
  2132. UcComputeHttpRawConnectionLength(
  2133. IN PVOID pConnectionContext
  2134. )
  2135. {
  2136. PUC_CLIENT_CONNECTION pConnection;
  2137. ULONG ReturnLength;
  2138. pConnection = (PUC_CLIENT_CONNECTION) pConnectionContext;
  2139. ASSERT(UC_IS_VALID_CLIENT_CONNECTION(pConnection));
  2140. //
  2141. // Memory layout: (must be in sync with UcGenerateHttpRawConnectionInfo)
  2142. //
  2143. // +---------------------------------------------------------------+
  2144. // | H_R_C_I |\\\| T_A_L_I |\\| T_A_L_I |\\| H_C_S_C | Server Name |
  2145. // +---------------------------------------------------------------+
  2146. //
  2147. ReturnLength = ALIGN_UP(sizeof(HTTP_RAW_CONNECTION_INFO), PVOID);
  2148. ReturnLength += 2 * ALIGN_UP(TDI_ADDRESS_LENGTH_IP6, PVOID);
  2149. ReturnLength += sizeof(HTTP_CLIENT_SSL_CONTEXT);
  2150. ReturnLength += pConnection->pServerInfo->pServerInfo->ServerNameLength;
  2151. return ReturnLength;
  2152. }
  2153. /***************************************************************************++
  2154. Routine Description:
  2155. Builds the HTTP_RAW_CONNECTION structure
  2156. Arguments:
  2157. pContext - Pointer to the UL_CONNECTION
  2158. pKernelBuffer - Pointer to kernel buffer
  2159. pUserBuffer - Pointer to user buffer
  2160. OutputBufferLength - Length of output buffer
  2161. pBuffer - Buffer for holding any data
  2162. InitialLength - Size of input data.
  2163. --***************************************************************************/
  2164. ULONG
  2165. UcGenerateHttpRawConnectionInfo(
  2166. IN PVOID pContext,
  2167. IN PUCHAR pKernelBuffer,
  2168. IN PVOID pUserBuffer,
  2169. IN ULONG OutputBufferLength,
  2170. IN PUCHAR pBuffer,
  2171. IN ULONG InitialLength
  2172. )
  2173. {
  2174. PHTTP_RAW_CONNECTION_INFO pConnInfo;
  2175. PTDI_ADDRESS_IP6 pLocalAddress;
  2176. PTDI_ADDRESS_IP6 pRemoteAddress;
  2177. PHTTP_TRANSPORT_ADDRESS pAddress;
  2178. PUC_CLIENT_CONNECTION pConnection;
  2179. ULONG BytesCopied = 0;
  2180. PUCHAR pInitialData;
  2181. PUCHAR pCurr;
  2182. PWSTR pServerName;
  2183. USHORT ServerNameLength = 0;
  2184. PHTTP_CLIENT_SSL_CONTEXT pClientSSLContext;
  2185. pConnection = (PUC_CLIENT_CONNECTION) pContext;
  2186. ASSERT( UC_IS_VALID_CLIENT_CONNECTION(pConnection) );
  2187. //
  2188. // We'll assume that the kernel buffer is PVOID aligned. Based on this,
  2189. // pointer, other pointers must be aligned.
  2190. //
  2191. ASSERT(pKernelBuffer == ALIGN_UP_POINTER(pKernelBuffer, PVOID));
  2192. //
  2193. // N.B. pCurr must always be PVOID aligned.
  2194. //
  2195. pCurr = pKernelBuffer;
  2196. //
  2197. // Create HTTP_RAW_CONNECTION_INFO structure.
  2198. //
  2199. pConnInfo = (PHTTP_RAW_CONNECTION_INFO)pCurr;
  2200. pCurr += ALIGN_UP(sizeof(HTTP_RAW_CONNECTION_INFO), PVOID);
  2201. //
  2202. // Create Local TDI_ADDRESS_IP6 structure.
  2203. //
  2204. pLocalAddress = (PTDI_ADDRESS_IP6)pCurr;
  2205. pCurr += ALIGN_UP(sizeof(TDI_ADDRESS_IP6), PVOID);
  2206. //
  2207. // Create Remote TDI_ADDRESS_IP6 structure.
  2208. //
  2209. pRemoteAddress = (PTDI_ADDRESS_IP6)pCurr;
  2210. pCurr += ALIGN_UP(sizeof(TDI_ADDRESS_IP6), PVOID);
  2211. //
  2212. // Create HTTP_CLIENT_SSL_CONTEXT structure.
  2213. //
  2214. pClientSSLContext = (PHTTP_CLIENT_SSL_CONTEXT)pCurr;
  2215. //
  2216. // The rest of the space is used to store the server name followed by
  2217. // initialize data.
  2218. //
  2219. pServerName = &pClientSSLContext->ServerName[0];
  2220. ServerNameLength = pConnection->pServerInfo->pServerInfo->ServerNameLength;
  2221. pInitialData = (PUCHAR) ((PUCHAR)pServerName + ServerNameLength);
  2222. pConnInfo->pClientSSLContext = (PHTTP_CLIENT_SSL_CONTEXT)
  2223. FIXUP_PTR(
  2224. PVOID,
  2225. pUserBuffer,
  2226. pKernelBuffer,
  2227. pClientSSLContext,
  2228. OutputBufferLength
  2229. );
  2230. //
  2231. // The last element of HTTP_CLIENT_SSL_CONTEXT is an array WCHAR[1].
  2232. // The following calculation takes that WCHAR into account.
  2233. //
  2234. pConnInfo->ClientSSLContextLength = sizeof(HTTP_CLIENT_SSL_CONTEXT)
  2235. - sizeof(WCHAR)
  2236. + ServerNameLength;
  2237. // Ssl protocol version to be used for this connection
  2238. pClientSSLContext->SslProtocolVersion =
  2239. pConnection->pServerInfo->SslProtocolVersion;
  2240. // Client certificate to be used for this connection
  2241. pClientSSLContext->pClientCertContext =
  2242. pConnection->pServerInfo->pClientCert;
  2243. // Copy the server certificate validation mode.
  2244. pClientSSLContext->ServerCertValidation =
  2245. pConnection->pServerInfo->ServerCertValidation;
  2246. pClientSSLContext->ServerNameLength = ServerNameLength;
  2247. RtlCopyMemory(
  2248. pServerName,
  2249. pConnection->pServerInfo->pServerInfo->pServerName,
  2250. ServerNameLength
  2251. );
  2252. //
  2253. // Now fill in the raw connection data structure.
  2254. //
  2255. pConnInfo->ConnectionId = pConnection->FilterInfo.ConnectionId;
  2256. pAddress = &pConnInfo->Address;
  2257. pAddress->pRemoteAddress = FIXUP_PTR(
  2258. PVOID,
  2259. pUserBuffer,
  2260. pKernelBuffer,
  2261. pRemoteAddress,
  2262. OutputBufferLength
  2263. );
  2264. pAddress->pLocalAddress = FIXUP_PTR(
  2265. PVOID,
  2266. pUserBuffer,
  2267. pKernelBuffer,
  2268. pLocalAddress,
  2269. OutputBufferLength
  2270. );
  2271. RtlZeroMemory(pRemoteAddress, sizeof(TDI_ADDRESS_IP6));
  2272. RtlZeroMemory(pLocalAddress, sizeof(TDI_ADDRESS_IP6));
  2273. //
  2274. // Copy any initial data.
  2275. //
  2276. if (InitialLength)
  2277. {
  2278. ASSERT(pBuffer);
  2279. pConnInfo->InitialDataSize = InitialLength;
  2280. pConnInfo->pInitialData = FIXUP_PTR(
  2281. PVOID, // Type
  2282. pUserBuffer, // pUserPtr
  2283. pKernelBuffer, // pKernelPtr
  2284. pInitialData, // pOffsetPtr
  2285. OutputBufferLength // BufferLength
  2286. );
  2287. RtlCopyMemory(
  2288. pInitialData,
  2289. pBuffer,
  2290. InitialLength
  2291. );
  2292. BytesCopied += InitialLength;
  2293. }
  2294. return BytesCopied;
  2295. }
  2296. /****************************************************************************++
  2297. Routine Description:
  2298. Triggers the connection state machine
  2299. Called when a server certificate is received from the filter.
  2300. Arguments:
  2301. None.
  2302. Return Value:
  2303. None.
  2304. --****************************************************************************/
  2305. VOID
  2306. UcServerCertificateInstalled(
  2307. IN PVOID pConnectionContext,
  2308. IN NTSTATUS Status
  2309. )
  2310. {
  2311. PUC_CLIENT_CONNECTION pConnection;
  2312. KIRQL OldIrql;
  2313. pConnection = (PUC_CLIENT_CONNECTION) pConnectionContext;
  2314. ASSERT( UC_IS_VALID_CLIENT_CONNECTION(pConnection) );
  2315. if (!NT_SUCCESS(Status))
  2316. {
  2317. UC_CLOSE_CONNECTION(pConnection, TRUE, Status);
  2318. }
  2319. else
  2320. {
  2321. UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
  2322. ASSERT(pConnection->ConnectionState ==
  2323. UcConnectStatePerformingSslHandshake);
  2324. ASSERT(pConnection->SslState == UcSslStateServerCertReceived);
  2325. UcKickOffConnectionStateMachine(
  2326. pConnection,
  2327. OldIrql,
  2328. UcConnectionWorkItem
  2329. );
  2330. }
  2331. }
  2332. /***************************************************************************++
  2333. Routine Description:
  2334. This routine is the connection state machine
  2335. Arguments:
  2336. pConnection - Pointer to the connection structure
  2337. OldIrql - The IRQL that we have to use when calling UlReleaseSpinLock.
  2338. Return Value:
  2339. None
  2340. --***************************************************************************/
  2341. VOID
  2342. UcConnectionStateMachine(
  2343. IN PUC_CLIENT_CONNECTION pConnection,
  2344. IN KIRQL OldIrql
  2345. )
  2346. {
  2347. ULONG TakenLength;
  2348. NTSTATUS Status;
  2349. PUC_HTTP_REQUEST pHeadRequest;
  2350. PLIST_ENTRY pEntry;
  2351. PUC_PROCESS_SERVER_INFORMATION pServInfo;
  2352. USHORT AddressType, FreeAddressType;
  2353. PUC_TDI_OBJECTS pTdiObjects;
  2354. // Sanity check.
  2355. ASSERT(UlDbgSpinLockOwned(&pConnection->SpinLock));
  2356. UC_WRITE_TRACE_LOG(
  2357. g_pUcTraceLog,
  2358. UC_ACTION_CONNECTION_STATE_ENTER,
  2359. pConnection,
  2360. UlongToPtr(pConnection->Flags),
  2361. UlongToPtr(pConnection->ConnectionState),
  2362. 0
  2363. );
  2364. pServInfo = pConnection->pServerInfo;
  2365. Begin:
  2366. ASSERT( UlDbgSpinLockOwned(&pConnection->SpinLock) );
  2367. switch(pConnection->ConnectionState)
  2368. {
  2369. case UcConnectStateConnectCleanup:
  2370. Cleanup:
  2371. ASSERT(pConnection->ConnectionState == UcConnectStateConnectCleanup);
  2372. pConnection->ConnectionState = UcConnectStateConnectCleanupBegin;
  2373. if(pConnection->pEvent)
  2374. {
  2375. //
  2376. // We have been called in the cleanup handler, so we just clean
  2377. // the connection & not initialize it again. This connection will
  2378. // make its' way into the SLIST & will be there for re-use.
  2379. //
  2380. UcpCleanupConnection(pConnection, OldIrql, TRUE);
  2381. break;
  2382. }
  2383. else
  2384. {
  2385. //
  2386. // We have come here from a Disconnect handler or a Abort handler.
  2387. // We have to clean up the connection & then re-initialize it. In
  2388. // the process of cleaning up the connection, we will release the
  2389. // lock. So we move to an interim state, so that we dont' cleanup
  2390. // twice.
  2391. //
  2392. Status = UcpCleanupConnection(pConnection, OldIrql, FALSE);
  2393. if(Status == STATUS_PENDING)
  2394. {
  2395. // Our cleanup is pended because we are waiting for sends
  2396. // to complete.
  2397. break;
  2398. }
  2399. Status = UcpInitializeConnection(pConnection,
  2400. pServInfo);
  2401. if(!NT_SUCCESS(Status))
  2402. {
  2403. LIST_ENTRY TempList;
  2404. PUC_HTTP_REQUEST pRequest;
  2405. InitializeListHead(&TempList);
  2406. //
  2407. // What do we do here ? This connection is not usable but
  2408. // we have not been called in the cleanup handler. We'll remove
  2409. // this connection from the active list, which will prevent it
  2410. // from being used by any new requests.
  2411. //
  2412. UlAcquirePushLockExclusive(&pServInfo->PushLock);
  2413. // Make the connection unaccessible from ServInfo
  2414. ASSERT(pConnection->ConnectionIndex <
  2415. pServInfo->MaxConnectionCount);
  2416. ASSERT(pServInfo->Connections[pConnection->ConnectionIndex]
  2417. == pConnection);
  2418. pServInfo->Connections[pConnection->ConnectionIndex] = NULL;
  2419. // Invalidate connection index
  2420. pConnection->ConnectionIndex = HTTP_REQUEST_ON_CONNECTION_ANY;
  2421. pServInfo->CurrentConnectionCount--;
  2422. UlReleasePushLock(&pServInfo->PushLock);
  2423. UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
  2424. //
  2425. // Get rid of all requests from the processed & pended lists.
  2426. //
  2427. while(!IsListEmpty(&pConnection->ProcessedRequestList))
  2428. {
  2429. pEntry = RemoveHeadList(&pConnection->ProcessedRequestList);
  2430. pRequest = CONTAINING_RECORD(
  2431. pEntry,
  2432. UC_HTTP_REQUEST,
  2433. Linkage
  2434. );
  2435. ASSERT( UC_IS_VALID_HTTP_REQUEST(pRequest) );
  2436. UC_REFERENCE_REQUEST(pRequest);
  2437. InsertHeadList(&TempList, &pRequest->Linkage);
  2438. }
  2439. while(!IsListEmpty(&pConnection->PendingRequestList))
  2440. {
  2441. pEntry = RemoveHeadList(&pConnection->PendingRequestList);
  2442. InsertHeadList(&TempList, pEntry);
  2443. }
  2444. ASSERT(IsListEmpty(&pConnection->SentRequestList));
  2445. //
  2446. // Dereference for the ServInfo.
  2447. //
  2448. DEREFERENCE_CLIENT_CONNECTION(pConnection);
  2449. UC_WRITE_TRACE_LOG(
  2450. g_pUcTraceLog,
  2451. UC_ACTION_CONNECTION_CLEANUP,
  2452. pConnection,
  2453. UlongToPtr(pConnection->ConnectionStatus),
  2454. UlongToPtr(pConnection->ConnectionState),
  2455. UlongToPtr(pConnection->Flags)
  2456. );
  2457. pConnection->ConnectionState = UcConnectStateConnectCleanup;
  2458. while(!IsListEmpty(&TempList))
  2459. {
  2460. pEntry = TempList.Flink;
  2461. pRequest = CONTAINING_RECORD(pEntry,
  2462. UC_HTTP_REQUEST,
  2463. Linkage);
  2464. UcFailRequest(pRequest, Status, OldIrql);
  2465. UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
  2466. }
  2467. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  2468. break;
  2469. }
  2470. else
  2471. {
  2472. UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
  2473. if(pConnection->pEvent)
  2474. {
  2475. pConnection->ConnectionState = UcConnectStateConnectCleanup;
  2476. UcpCleanupConnection(pConnection, OldIrql, TRUE);
  2477. break;
  2478. }
  2479. else
  2480. {
  2481. pConnection->ConnectionState = UcConnectStateConnectIdle;
  2482. //
  2483. // FALL Through!
  2484. //
  2485. }
  2486. }
  2487. }
  2488. case UcConnectStateConnectIdle:
  2489. ASSERT(pConnection->ConnectionState == UcConnectStateConnectIdle);
  2490. if(!IsListEmpty(&pConnection->PendingRequestList))
  2491. {
  2492. pConnection->ConnectionState = UcConnectStateConnectPending;
  2493. AddressType = pConnection->pNextAddress->AddressType;
  2494. if(NULL == pConnection->pTdiObjects)
  2495. {
  2496. //
  2497. // If there is no TDI object, then grab one for this connect
  2498. // attempt
  2499. //
  2500. pTdiObjects = UcpPopTdiObject(AddressType);
  2501. }
  2502. else
  2503. {
  2504. //
  2505. // If we are here, we have a old TDI object that failed
  2506. // a connection attempt. This could be because the old one
  2507. // was in TIME_WAIT (Failed with STATUS_ADDRESS_ALREADY_EXISTS)
  2508. //
  2509. // We have to free the old one & allocate a new one. Since
  2510. // both free and allocate have to happen at Passive IRQL,
  2511. // we'll do it after we release the lock (below).
  2512. pTdiObjects = NULL;
  2513. }
  2514. if(NULL == pTdiObjects)
  2515. {
  2516. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  2517. //
  2518. // If there is an old one, free it.
  2519. //
  2520. if(NULL != pConnection->pTdiObjects)
  2521. {
  2522. FreeAddressType = pConnection->pTdiObjects->ConnectionType;
  2523. pConnection->pTdiObjects->pConnection = NULL;
  2524. UcpPushTdiObject(pConnection->pTdiObjects, FreeAddressType);
  2525. pConnection->pTdiObjects = NULL;
  2526. }
  2527. //
  2528. // Allocate a new one.
  2529. //
  2530. Status = UcpAllocateTdiObject(
  2531. &pTdiObjects,
  2532. AddressType
  2533. );
  2534. UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
  2535. ASSERT(pConnection->ConnectionState ==
  2536. UcConnectStateConnectPending);
  2537. if(!NT_SUCCESS(Status))
  2538. {
  2539. ASSERT(Status != STATUS_ADDRESS_ALREADY_EXISTS);
  2540. pConnection->ConnectionState = UcConnectStateConnectCleanup;
  2541. goto Begin;
  2542. }
  2543. pConnection->Flags |= CLIENT_CONN_FLAG_TDI_ALLOCATE;
  2544. }
  2545. pConnection->pTdiObjects = pTdiObjects;
  2546. pTdiObjects->pConnection = pConnection;
  2547. //
  2548. // The address information in the connection has been filled out.
  2549. // Reference the connection, and call the appropriate connect
  2550. // routine.
  2551. //
  2552. REFERENCE_CLIENT_CONNECTION(pConnection);
  2553. UC_WRITE_TRACE_LOG(
  2554. g_pUcTraceLog,
  2555. UC_ACTION_CONNECTION_BEGIN_CONNECT,
  2556. pConnection,
  2557. UlongToPtr(pConnection->ConnectionStatus),
  2558. UlongToPtr(pConnection->ConnectionState),
  2559. UlongToPtr(pConnection->Flags)
  2560. );
  2561. //
  2562. // Fill out the current TDI address & go to the next one.
  2563. //
  2564. ASSERT(pConnection->NextAddressCount <
  2565. pServInfo->pTransportAddress->TAAddressCount);
  2566. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  2567. Status = UcClientConnect(
  2568. pConnection,
  2569. pConnection->pTdiObjects->pIrp
  2570. );
  2571. if (STATUS_PENDING != Status)
  2572. {
  2573. UcRestartClientConnect(pConnection, Status);
  2574. UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
  2575. //
  2576. // In the normal case, UcpConnectComplete calls
  2577. // UcRestartClientConnect, does the de-ref & kicks off
  2578. // the connection state machine.
  2579. //
  2580. // Since we are calling UcRestartClientConnect directly,
  2581. // we should deref.
  2582. DEREFERENCE_CLIENT_CONNECTION(pConnection);
  2583. goto Begin;
  2584. }
  2585. }
  2586. else
  2587. {
  2588. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  2589. }
  2590. break;
  2591. case UcConnectStateConnectPending:
  2592. case UcConnectStateProxySslConnect:
  2593. case UcConnectStateConnectCleanupBegin:
  2594. case UcConnectStateDisconnectIndicatedPending:
  2595. case UcConnectStateDisconnectPending:
  2596. case UcConnectStateDisconnectComplete:
  2597. case UcConnectStateAbortPending:
  2598. //
  2599. // We have already issued a connect, we don't have to do anything
  2600. // here.
  2601. //
  2602. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  2603. break;
  2604. case UcConnectStateConnectComplete:
  2605. //
  2606. // The TCP has connected.
  2607. //
  2608. if(!IsListEmpty(&pConnection->PendingRequestList))
  2609. {
  2610. if(pConnection->Flags & CLIENT_CONN_FLAG_PROXY_SSL_CONNECTION)
  2611. {
  2612. PUC_HTTP_REQUEST pConnectRequest;
  2613. //
  2614. // We are going through a proxy. We need to send
  2615. // a CONNECT verb.
  2616. //
  2617. pEntry = pConnection->PendingRequestList.Flink;
  2618. pHeadRequest = CONTAINING_RECORD(pEntry,
  2619. UC_HTTP_REQUEST,
  2620. Linkage);
  2621. pConnection->ConnectionState = UcConnectStateProxySslConnect;
  2622. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  2623. pConnectRequest =
  2624. UcBuildConnectVerbRequest(pConnection, pHeadRequest);
  2625. if(pConnectRequest == NULL)
  2626. {
  2627. //
  2628. // The CONNECT verb failed, we can't do much so we'll
  2629. // acquire the lock & fail it.
  2630. //
  2631. UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
  2632. pConnection->ConnectionState = UcConnectStateConnectCleanup;
  2633. goto Cleanup;
  2634. }
  2635. else
  2636. {
  2637. UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
  2638. REFERENCE_CLIENT_CONNECTION(pConnection);
  2639. InsertHeadList(&pConnection->SentRequestList,
  2640. &pConnectRequest->Linkage);
  2641. ASSERT(pConnection->ConnectionState ==
  2642. UcConnectStateProxySslConnect);
  2643. pConnectRequest->RequestState = UcRequestStateSent;
  2644. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  2645. //
  2646. // go ahead & issue the request.
  2647. //
  2648. ASSERT(pConnectRequest->RequestConnectionClose == FALSE);
  2649. Status = UcSendData(pConnection,
  2650. pConnectRequest->pMdlHead,
  2651. pConnectRequest->BytesBuffered,
  2652. &UcRestartMdlSend,
  2653. (PVOID) pConnectRequest,
  2654. pConnectRequest->RequestIRP,
  2655. TRUE);
  2656. if(STATUS_PENDING != Status)
  2657. {
  2658. UcRestartMdlSend(pConnectRequest, Status, 0);
  2659. }
  2660. }
  2661. }
  2662. else if(pConnection->FilterInfo.pFilterChannel)
  2663. {
  2664. pConnection->ConnectionState =
  2665. UcConnectStatePerformingSslHandshake;
  2666. pConnection->SslState = UcSslStateConnectionDelivered;
  2667. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  2668. UlDeliverConnectionToFilter(
  2669. &pConnection->FilterInfo,
  2670. NULL,
  2671. 0,
  2672. &TakenLength
  2673. );
  2674. ASSERT(TakenLength == 0);
  2675. }
  2676. else
  2677. {
  2678. pConnection->ConnectionState = UcConnectStateConnectReady;
  2679. goto IssueRequests;
  2680. }
  2681. }
  2682. else
  2683. {
  2684. //
  2685. // There are no requests that need to be sent out, let's
  2686. // remain in this state.
  2687. //
  2688. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  2689. }
  2690. break;
  2691. case UcConnectStateProxySslConnectComplete:
  2692. if(!IsListEmpty(&pConnection->PendingRequestList))
  2693. {
  2694. pConnection->ConnectionState =
  2695. UcConnectStatePerformingSslHandshake;
  2696. pConnection->SslState = UcSslStateConnectionDelivered;
  2697. ASSERT(pConnection->FilterInfo.pFilterChannel);
  2698. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  2699. UlDeliverConnectionToFilter(
  2700. &pConnection->FilterInfo,
  2701. NULL,
  2702. 0,
  2703. &TakenLength
  2704. );
  2705. ASSERT(TakenLength == 0);
  2706. }
  2707. else
  2708. {
  2709. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  2710. }
  2711. break;
  2712. case UcConnectStateConnectReady:
  2713. IssueRequests:
  2714. ASSERT(pConnection->ConnectionState == UcConnectStateConnectReady);
  2715. // It's connected. If no one is using the connection to write right
  2716. // now, and either the remote server supports pipeling and this
  2717. // request does also or the sent request list is empty, go ahead and
  2718. // send it.
  2719. pConnection->Flags |= CLIENT_CONN_FLAG_CONNECT_READY;
  2720. if ( !IsListEmpty(&pConnection->PendingRequestList) &&
  2721. !(pConnection->Flags & CLIENT_CONN_FLAG_SEND_BUSY) &&
  2722. UcpCheckForPipelining(pConnection)
  2723. )
  2724. {
  2725. // It's OK to send now.
  2726. UcIssueRequests(pConnection, OldIrql);
  2727. }
  2728. else
  2729. {
  2730. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  2731. }
  2732. break;
  2733. case UcConnectStateIssueFilterClose:
  2734. pConnection->ConnectionState = UcConnectStateDisconnectPending;
  2735. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  2736. UlFilterCloseHandler(
  2737. &pConnection->FilterInfo,
  2738. NULL,
  2739. NULL
  2740. );
  2741. break;
  2742. case UcConnectStateIssueFilterDisconnect:
  2743. pConnection->ConnectionState = UcConnectStateDisconnectIndicatedPending;
  2744. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  2745. UlFilterDisconnectHandler(&pConnection->FilterInfo);
  2746. break;
  2747. case UcConnectStatePerformingSslHandshake:
  2748. //
  2749. // Perform Ssl handshake.
  2750. //
  2751. if (pConnection->SslState == UcSslStateServerCertReceived)
  2752. {
  2753. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  2754. if(UcpCompareServerCert(pConnection))
  2755. {
  2756. // Okay to move the connection state machine forward.
  2757. UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
  2758. goto Begin;
  2759. }
  2760. }
  2761. else
  2762. {
  2763. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  2764. }
  2765. break;
  2766. default:
  2767. ASSERT(!"Invalid Connection state");
  2768. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  2769. break;
  2770. }
  2771. ASSERT(!UlDbgSpinLockOwned(&pConnection->SpinLock));
  2772. UC_WRITE_TRACE_LOG(
  2773. g_pUcTraceLog,
  2774. UC_ACTION_CONNECTION_STATE_LEAVE,
  2775. pConnection,
  2776. UlongToPtr(pConnection->Flags),
  2777. UlongToPtr(pConnection->ConnectionState),
  2778. 0
  2779. );
  2780. }
  2781. /***************************************************************************++
  2782. Routine Description:
  2783. Initializes a UC_CLIENT_CONNECTION for use.
  2784. Arguments:
  2785. pConnection - Pointer to the UL_CONNECTION to initialize.
  2786. SecureConnection - TRUE if this connection is for a secure endpoint.
  2787. --***************************************************************************/
  2788. NTSTATUS
  2789. UcpInitializeConnection(
  2790. IN PUC_CLIENT_CONNECTION pConnection,
  2791. IN PUC_PROCESS_SERVER_INFORMATION pInfo
  2792. )
  2793. {
  2794. NTSTATUS Status;
  2795. PUL_FILTER_CHANNEL pChannel;
  2796. //
  2797. // Initialization.
  2798. //
  2799. pConnection->MergeIndication.pBuffer = NULL;
  2800. pConnection->MergeIndication.BytesWritten = 0;
  2801. pConnection->MergeIndication.BytesAvailable = 0;
  2802. pConnection->MergeIndication.BytesAllocated = 0;
  2803. pConnection->NextAddressCount = 0;
  2804. pConnection->pNextAddress = pInfo->pTransportAddress->Address;
  2805. if(pInfo->bSecure)
  2806. {
  2807. pChannel = UxRetrieveClientFilterChannel(pInfo->pProcess);
  2808. if(!pChannel)
  2809. {
  2810. return STATUS_NO_TRACKING_SERVICE;
  2811. }
  2812. if(pInfo->bProxy)
  2813. {
  2814. pConnection->Flags |= CLIENT_CONN_FLAG_PROXY_SSL_CONNECTION;
  2815. }
  2816. else
  2817. {
  2818. pConnection->Flags &= ~CLIENT_CONN_FLAG_PROXY_SSL_CONNECTION;
  2819. }
  2820. }
  2821. else
  2822. {
  2823. pChannel = NULL;
  2824. pConnection->Flags &= ~CLIENT_CONN_FLAG_PROXY_SSL_CONNECTION;
  2825. }
  2826. Status = UxInitializeFilterConnection(
  2827. &pConnection->FilterInfo,
  2828. pChannel,
  2829. pInfo->bSecure,
  2830. &UcReferenceClientConnection,
  2831. &UcDereferenceClientConnection,
  2832. &UcCloseRawFilterConnection,
  2833. &UcpSendRawData,
  2834. &UcpReceiveRawData,
  2835. &UcHandleResponse,
  2836. &UcComputeHttpRawConnectionLength,
  2837. &UcGenerateHttpRawConnectionInfo,
  2838. &UcServerCertificateInstalled,
  2839. &UcDisconnectRawFilterConnection,
  2840. NULL, // Listen Context
  2841. pConnection
  2842. );
  2843. if(Status != STATUS_SUCCESS)
  2844. {
  2845. if(pChannel)
  2846. {
  2847. //
  2848. // Undo the Retrieve
  2849. //
  2850. DEREFERENCE_FILTER_CHANNEL(pChannel);
  2851. }
  2852. }
  2853. return Status;
  2854. }
  2855. /***************************************************************************++
  2856. Routine Description:
  2857. Opens the TDI connection & address objects, called from connection init
  2858. code.
  2859. Arguments:
  2860. pTdi - a pointer to TDI Objects
  2861. Return Value:
  2862. NTSTATUS - STATUS_SUCCESS if IO should continue processing this
  2863. IRP, STATUS_MORE_PROCESSING_REQUIRED if IO should stop processing
  2864. this IRP.
  2865. --***************************************************************************/
  2866. NTSTATUS
  2867. UcpOpenTdiObjects(
  2868. IN PUC_TDI_OBJECTS pTdi
  2869. )
  2870. {
  2871. USHORT AddressType;
  2872. NTSTATUS status;
  2873. AddressType = pTdi->ConnectionType;
  2874. //
  2875. // First, open the TDI connection object for this connection.
  2876. //
  2877. status = UxOpenTdiConnectionObject(
  2878. AddressType,
  2879. (CONNECTION_CONTEXT)pTdi,
  2880. &pTdi->ConnectionObject
  2881. );
  2882. if (!NT_SUCCESS(status))
  2883. {
  2884. return status;
  2885. }
  2886. //
  2887. // Now open an address object for this connection.
  2888. //
  2889. status = UxOpenTdiAddressObject(
  2890. G_LOCAL_ADDRESS(AddressType),
  2891. G_LOCAL_ADDRESS_LENGTH(AddressType),
  2892. &pTdi->AddressObject
  2893. );
  2894. if (!NT_SUCCESS(status))
  2895. {
  2896. UxCloseTdiObject(&pTdi->ConnectionObject);
  2897. return status;
  2898. }
  2899. else
  2900. {
  2901. //
  2902. // Hook up a receive handler.
  2903. //
  2904. status = UxSetEventHandler(
  2905. &pTdi->AddressObject,
  2906. TDI_EVENT_RECEIVE,
  2907. (ULONG_PTR) &UcpTdiReceiveHandler,
  2908. pTdi
  2909. );
  2910. }
  2911. if(!NT_SUCCESS(status))
  2912. {
  2913. UxCloseTdiObject(&pTdi->ConnectionObject);
  2914. UxCloseTdiObject(&pTdi->AddressObject);
  2915. return status;
  2916. }
  2917. else
  2918. {
  2919. //
  2920. // Hook up a Disconnect handler.
  2921. //
  2922. status = UxSetEventHandler(
  2923. &pTdi->AddressObject,
  2924. TDI_EVENT_DISCONNECT,
  2925. (ULONG_PTR) &UcpTdiDisconnectHandler,
  2926. pTdi
  2927. );
  2928. }
  2929. if(!NT_SUCCESS(status))
  2930. {
  2931. UxCloseTdiObject(&pTdi->ConnectionObject);
  2932. UxCloseTdiObject(&pTdi->AddressObject);
  2933. return status;
  2934. }
  2935. return status;
  2936. }
  2937. /***************************************************************************++
  2938. Routine Description:
  2939. Allocates a TDI object, which contains a AO & CO. This routine is called
  2940. when we don't have any TDI objects in the pool. associate it with a
  2941. local address.
  2942. Arguments:
  2943. ppTdiObjects - A pointer to the TDI object.
  2944. AddressType - IPv4 or IPv6
  2945. Return Value:
  2946. NTSTATUS - Completion status.
  2947. --***************************************************************************/
  2948. NTSTATUS
  2949. UcpAllocateTdiObject(
  2950. OUT PUC_TDI_OBJECTS *ppTdiObjects,
  2951. IN USHORT AddressType
  2952. )
  2953. {
  2954. PUC_TDI_OBJECTS pTdiObjects;
  2955. NTSTATUS status;
  2956. PUX_TDI_OBJECT pTdiObject;
  2957. KEVENT Event;
  2958. PIRP pIrp;
  2959. IO_STATUS_BLOCK ioStatusBlock;
  2960. PAGED_CODE();
  2961. *ppTdiObjects = NULL;
  2962. //
  2963. // Allocate the pool for the connection structure.
  2964. //
  2965. pTdiObjects = UL_ALLOCATE_STRUCT(
  2966. NonPagedPool,
  2967. UC_TDI_OBJECTS,
  2968. UC_TDI_OBJECTS_POOL_TAG
  2969. );
  2970. if (pTdiObjects == NULL)
  2971. {
  2972. return STATUS_INSUFFICIENT_RESOURCES;
  2973. }
  2974. pTdiObjects->ConnectionType = AddressType;
  2975. pTdiObjects->TdiInfo.UserDataLength = 0;
  2976. pTdiObjects->TdiInfo.UserData = NULL;
  2977. pTdiObjects->TdiInfo.OptionsLength = 0;
  2978. pTdiObjects->TdiInfo.Options = NULL;
  2979. pTdiObjects->pConnection = NULL;
  2980. //
  2981. // Open the TDI address & connection objects. We need one AO per connection
  2982. // as we will have to open multiple TCP connections to the same server.
  2983. //
  2984. if((status = UcpOpenTdiObjects(pTdiObjects)) != STATUS_SUCCESS)
  2985. {
  2986. UL_FREE_POOL(pTdiObjects, UC_TDI_OBJECTS_POOL_TAG);
  2987. return status;
  2988. }
  2989. //
  2990. // Allocate an IRP for calling into TDI (e.g. Disconnects, Connects, etc)
  2991. //
  2992. pTdiObject = &pTdiObjects->ConnectionObject;
  2993. pTdiObjects->pIrp = UlAllocateIrp(
  2994. pTdiObject->pDeviceObject->StackSize,
  2995. FALSE
  2996. );
  2997. if(!pTdiObjects->pIrp)
  2998. {
  2999. UcpFreeTdiObject(pTdiObjects);
  3000. return STATUS_INSUFFICIENT_RESOURCES;
  3001. }
  3002. //
  3003. // Init the IrpContext.
  3004. //
  3005. pTdiObjects->IrpContext.Signature = UL_IRP_CONTEXT_SIGNATURE;
  3006. //
  3007. // Now, associate the Address Object with the Connection Object.
  3008. //
  3009. KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
  3010. pIrp = TdiBuildInternalDeviceControlIrp(
  3011. TDI_ASSOCIATE_ADDRESS,
  3012. pTdiObjects->ConnectionObject.pDeviceObject,
  3013. pTdiObjects->ConnectionObject.pFileObject,
  3014. &Event,
  3015. &ioStatusBlock
  3016. );
  3017. if (pIrp != NULL)
  3018. {
  3019. TdiBuildAssociateAddress(
  3020. pIrp, // IRP
  3021. pTdiObjects->ConnectionObject.pDeviceObject, // Conn. device object.
  3022. pTdiObjects->ConnectionObject.pFileObject, // Conn. File object.
  3023. NULL, // Completion routine
  3024. NULL, // Context
  3025. pTdiObjects->AddressObject.Handle // Address obj handle.
  3026. );
  3027. //
  3028. // We don't want to call UlCallDriver, since we did not allocate this
  3029. // IRP using UL.
  3030. //
  3031. status = IoCallDriver(
  3032. pTdiObjects->ConnectionObject.pDeviceObject,
  3033. pIrp
  3034. );
  3035. // If it didn't complete, wait for it.
  3036. if (status == STATUS_PENDING)
  3037. {
  3038. status = KeWaitForSingleObject(
  3039. &Event,
  3040. Executive,
  3041. KernelMode,
  3042. FALSE,
  3043. NULL
  3044. );
  3045. ASSERT( status == STATUS_SUCCESS);
  3046. status = ioStatusBlock.Status;
  3047. }
  3048. }
  3049. else
  3050. {
  3051. status = STATUS_INSUFFICIENT_RESOURCES;
  3052. }
  3053. if(!NT_SUCCESS(status))
  3054. {
  3055. UcpFreeTdiObject(pTdiObjects);
  3056. return status;
  3057. }
  3058. *ppTdiObjects = pTdiObjects;
  3059. return STATUS_SUCCESS;
  3060. }
  3061. /***************************************************************************++
  3062. Routine Description:
  3063. Free's the TDI object to the list.
  3064. Arguments:
  3065. pTdiObjects - A pointer to the TDI object.
  3066. AddressType - IPv4 or IPv6
  3067. Return Value:
  3068. NTSTATUS - Completion status.
  3069. --***************************************************************************/
  3070. VOID
  3071. UcpFreeTdiObject(
  3072. IN PUC_TDI_OBJECTS pTdiObjects
  3073. )
  3074. {
  3075. PAGED_CODE();
  3076. if(pTdiObjects->pIrp)
  3077. {
  3078. UlFreeIrp(pTdiObjects->pIrp);
  3079. }
  3080. UxCloseTdiObject(&pTdiObjects->ConnectionObject);
  3081. UxCloseTdiObject(&pTdiObjects->AddressObject);
  3082. UL_FREE_POOL(pTdiObjects, UC_TDI_OBJECTS_POOL_TAG);
  3083. }
  3084. /***************************************************************************++
  3085. Routine Description:
  3086. Retrieves a TDI object from a list. IF not found, allocates a new one.
  3087. Arguments:
  3088. ppTdiObjects - A pointer to the TDI object.
  3089. AddressType - IPv4 or IPv6
  3090. Return Value:
  3091. NTSTATUS - Completion status.
  3092. --***************************************************************************/
  3093. PUC_TDI_OBJECTS
  3094. UcpPopTdiObject(
  3095. IN USHORT AddressType
  3096. )
  3097. {
  3098. PLIST_ENTRY pListEntry;
  3099. PUC_TDI_OBJECTS pTdiObjects;
  3100. KIRQL OldIrql;
  3101. //
  3102. // Get a AO/CO pair from the address object list.
  3103. //
  3104. UlAcquireSpinLock(&G_CLIENT_CONN_SPIN_LOCK(AddressType), &OldIrql);
  3105. if(IsListEmpty(&G_CLIENT_TDI_CONNECTION_SLIST_HEAD(AddressType)))
  3106. {
  3107. pTdiObjects = NULL;
  3108. }
  3109. else
  3110. {
  3111. (*G_CLIENT_CONN_LIST_COUNT(AddressType)) --;
  3112. pListEntry = RemoveHeadList(
  3113. &G_CLIENT_TDI_CONNECTION_SLIST_HEAD(AddressType)
  3114. );
  3115. pTdiObjects = CONTAINING_RECORD(
  3116. pListEntry,
  3117. UC_TDI_OBJECTS,
  3118. Linkage
  3119. );
  3120. ASSERT(pTdiObjects->pConnection == NULL);
  3121. }
  3122. UlReleaseSpinLock(&G_CLIENT_CONN_SPIN_LOCK(AddressType), OldIrql);
  3123. return pTdiObjects;
  3124. }
  3125. /***************************************************************************++
  3126. Routine Description:
  3127. Free's the TDI object to the list.
  3128. Arguments:
  3129. pTdiObjects - A pointer to the TDI object.
  3130. AddressType - IPv4 or IPv6
  3131. Return Value:
  3132. NTSTATUS - Completion status.
  3133. --***************************************************************************/
  3134. VOID
  3135. UcpPushTdiObject(
  3136. IN PUC_TDI_OBJECTS pTdiObjects,
  3137. IN USHORT AddressType
  3138. )
  3139. {
  3140. KIRQL OldIrql;
  3141. ASSERT(pTdiObjects->pConnection == NULL);
  3142. PAGED_CODE();
  3143. UlAcquireSpinLock(&G_CLIENT_CONN_SPIN_LOCK(AddressType), &OldIrql);
  3144. if((*G_CLIENT_CONN_LIST_COUNT(AddressType)) < CLIENT_CONN_TDI_LIST_MAX)
  3145. {
  3146. (*G_CLIENT_CONN_LIST_COUNT(AddressType))++;
  3147. InsertTailList(
  3148. &G_CLIENT_TDI_CONNECTION_SLIST_HEAD(AddressType),
  3149. &pTdiObjects->Linkage
  3150. );
  3151. UlReleaseSpinLock(&G_CLIENT_CONN_SPIN_LOCK(AddressType), OldIrql);
  3152. }
  3153. else
  3154. {
  3155. UlReleaseSpinLock(&G_CLIENT_CONN_SPIN_LOCK(AddressType), OldIrql);
  3156. UcpFreeTdiObject(pTdiObjects);
  3157. }
  3158. }
  3159. /***************************************************************************++
  3160. Routine Description:
  3161. Clears the send or receive busy flag & re-kicks the connection state machine
  3162. Arguments:
  3163. pConnection - The UC_CLIENT_CONNECTION structure.
  3164. Flag - CLIENT_CONN_FLAG_SEND_BUSY or CLIENT_CONN_FLAG_RECV_BUSY
  3165. OldIrql - Irql at which lock was acquired.
  3166. bCloseConnection - Whether we shoudl close the connection after releasing
  3167. lock.
  3168. Return Value:
  3169. None
  3170. --***************************************************************************/
  3171. VOID
  3172. UcClearConnectionBusyFlag(
  3173. IN PUC_CLIENT_CONNECTION pConnection,
  3174. IN ULONG Flags,
  3175. IN KIRQL OldIrql,
  3176. IN BOOLEAN bCloseConnection
  3177. )
  3178. {
  3179. ASSERT( UlDbgSpinLockOwned(&pConnection->SpinLock) );
  3180. ASSERT((Flags & CLIENT_CONN_FLAG_SEND_BUSY) ||
  3181. (Flags & CLIENT_CONN_FLAG_RECV_BUSY));
  3182. ASSERT(pConnection->Flags & Flags);
  3183. pConnection->Flags &= ~Flags;
  3184. if(pConnection->Flags & CLIENT_CONN_FLAG_CLEANUP_PENDED)
  3185. {
  3186. //
  3187. // The connection got torn down in between. We've pended the
  3188. // cleanup let's resume it now.
  3189. //
  3190. UC_WRITE_TRACE_LOG(
  3191. g_pUcTraceLog,
  3192. UC_ACTION_CONNECTION_CLEAN_RESUMED,
  3193. pConnection,
  3194. UlongToPtr(pConnection->ConnectionStatus),
  3195. UlongToPtr(pConnection->ConnectionState),
  3196. UlongToPtr(pConnection->Flags)
  3197. );
  3198. ASSERT(pConnection->ConnectionState ==
  3199. UcConnectStateConnectCleanupBegin);
  3200. pConnection->ConnectionState = UcConnectStateConnectCleanup;
  3201. pConnection->Flags &= ~CLIENT_CONN_FLAG_CLEANUP_PENDED;
  3202. UcKickOffConnectionStateMachine(
  3203. pConnection,
  3204. OldIrql,
  3205. UcConnectionWorkItem
  3206. );
  3207. }
  3208. else
  3209. {
  3210. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  3211. if(bCloseConnection)
  3212. {
  3213. UC_CLOSE_CONNECTION(pConnection,
  3214. FALSE,
  3215. STATUS_CONNECTION_DISCONNECTED);
  3216. }
  3217. }
  3218. return;
  3219. }
  3220. /***************************************************************************++
  3221. Routine Description:
  3222. Attaches captured SSL server certificate to a connection.
  3223. Called with the pConnection->FilterConnLock held. The connection is
  3224. assumed to be in the connected state.
  3225. Arguments:
  3226. pConnection - the connection that gets the info
  3227. pServerCertInfo - input server cert info
  3228. --***************************************************************************/
  3229. NTSTATUS
  3230. UcAddServerCertInfoToConnection(
  3231. IN PUX_FILTER_CONNECTION pConnection,
  3232. IN PHTTP_SSL_SERVER_CERT_INFO pServerCertInfo
  3233. )
  3234. {
  3235. NTSTATUS Status;
  3236. PUC_CLIENT_CONNECTION pClientConn;
  3237. //
  3238. // Sanity check.
  3239. //
  3240. ASSERT(IS_VALID_FILTER_CONNECTION(pConnection));
  3241. ASSERT(pServerCertInfo);
  3242. ASSERT(UlDbgSpinLockOwned(&pConnection->FilterConnLock));
  3243. ASSERT(pConnection->ConnState == UlFilterConnStateConnected);
  3244. //
  3245. // Initialize local variables
  3246. //
  3247. Status = STATUS_INVALID_PARAMETER;
  3248. //
  3249. // Get client connection
  3250. //
  3251. pClientConn = (PUC_CLIENT_CONNECTION)pConnection->pConnectionContext;
  3252. ASSERT(UC_IS_VALID_CLIENT_CONNECTION(pClientConn));
  3253. //
  3254. // We are already at DPC, so aquire spin lock at DPC
  3255. // BUGBUG: deadlock? (acquiring filter lock followed by connection lock)
  3256. // (Is there a place we acquire connection lock before filter lock?
  3257. //
  3258. UlAcquireSpinLockAtDpcLevel(&pClientConn->SpinLock);
  3259. //
  3260. // The server certinfo can be passed only during initial handshake or
  3261. // after receiving ssl renegotiate from the server - in which case
  3262. // the connection must be ready (for a request to be sent out on it)
  3263. //
  3264. if (pServerCertInfo->Status == SEC_E_OK)
  3265. {
  3266. // Did a renegotiation happen?
  3267. if (pClientConn->ConnectionState == UcConnectStateConnectReady)
  3268. {
  3269. // Renegotiation must yield the same server certificate
  3270. if (!UC_COMPARE_CERT_HASH(pServerCertInfo,
  3271. &pClientConn->ServerCertInfo))
  3272. {
  3273. goto quit;
  3274. }
  3275. }
  3276. else if (pClientConn->ConnectionState !=
  3277. UcConnectStatePerformingSslHandshake ||
  3278. pClientConn->SslState != UcSslStateConnectionDelivered)
  3279. {
  3280. goto quit;
  3281. }
  3282. }
  3283. else
  3284. {
  3285. // BUGBUG: handle the error case more gracefully!
  3286. goto quit;
  3287. }
  3288. // Go back to ssl handshake state
  3289. pClientConn->ConnectionState = UcConnectStatePerformingSslHandshake;
  3290. pClientConn->SslState = UcSslStateServerCertReceived;
  3291. //
  3292. // Before overwriting ServerCertInfo, make sure it does not
  3293. // contain any serialized blobs or Issuer List
  3294. //
  3295. ASSERT(pClientConn->ServerCertInfo.Cert.pSerializedCert == NULL);
  3296. ASSERT(pClientConn->ServerCertInfo.Cert.pSerializedCertStore == NULL);
  3297. ASSERT(pClientConn->ServerCertInfo.IssuerInfo.pIssuerList == NULL);
  3298. RtlCopyMemory(&pClientConn->ServerCertInfo,
  3299. pServerCertInfo,
  3300. sizeof(pClientConn->ServerCertInfo));
  3301. Status = STATUS_SUCCESS;
  3302. quit:
  3303. UlReleaseSpinLockFromDpcLevel(&pClientConn->SpinLock);
  3304. if (!NT_SUCCESS(Status))
  3305. {
  3306. // An error occured.
  3307. // Free serialized server certificate if any
  3308. UC_FREE_SERIALIZED_CERT(pServerCertInfo,
  3309. pClientConn->pServerInfo->pProcess);
  3310. // Free issuer list if any
  3311. UC_FREE_CERT_ISSUER_LIST(pServerCertInfo,
  3312. pClientConn->pServerInfo->pProcess);
  3313. }
  3314. return Status;
  3315. }
  3316. /**************************************************************************++
  3317. Routine Description:
  3318. This routine fails
  3319. Arguments:
  3320. pConnection - Pointer to client connection.
  3321. Return Value:
  3322. TRUE - A request was failed.
  3323. FALSE - Could not fail arequest.
  3324. --**************************************************************************/
  3325. PUC_HTTP_REQUEST
  3326. UcpFindRequestToFail(
  3327. PUC_CLIENT_CONNECTION pConnection
  3328. )
  3329. {
  3330. PUC_HTTP_REQUEST pRequest = NULL;
  3331. PLIST_ENTRY pListEntry;
  3332. PIRP pIrp;
  3333. //
  3334. // Sanity checks.
  3335. //
  3336. ASSERT(UC_IS_VALID_CLIENT_CONNECTION(pConnection));
  3337. ASSERT(UlDbgSpinLockOwned(&pConnection->SpinLock));
  3338. //
  3339. // Try to fail a pending request. Start searching from the head of the
  3340. // pending request list.
  3341. //
  3342. for (pListEntry = pConnection->PendingRequestList.Flink;
  3343. pListEntry != &pConnection->PendingRequestList;
  3344. pListEntry = pListEntry->Flink)
  3345. {
  3346. pRequest = CONTAINING_RECORD(pConnection->PendingRequestList.Flink,
  3347. UC_HTTP_REQUEST,
  3348. Linkage);
  3349. ASSERT(UC_IS_VALID_HTTP_REQUEST(pRequest));
  3350. ASSERT(pRequest->RequestState == UcRequestStateCaptured);
  3351. pIrp = UcPrepareRequestIrp(pRequest, STATUS_RETRY);
  3352. if (pIrp)
  3353. {
  3354. // Prepared the request IRP for completion. Now complete it.
  3355. UlCompleteRequest(pIrp, 0);
  3356. break;
  3357. }
  3358. // Set to NULL so that it not returned.
  3359. pRequest = NULL;
  3360. }
  3361. return pRequest;
  3362. }
  3363. /***************************************************************************++
  3364. Routine Description:
  3365. Compares a server certificate present on a connection to a server
  3366. certificate on a server context.
  3367. Arguments:
  3368. pConnection - Client connection
  3369. Return Value:
  3370. TRUE - Continue sending requests.
  3371. FALSE - Do not send requests.
  3372. --***************************************************************************/
  3373. BOOLEAN
  3374. UcpCompareServerCert(
  3375. IN PUC_CLIENT_CONNECTION pConnection
  3376. )
  3377. {
  3378. BOOLEAN action = FALSE;
  3379. KIRQL OldIrql;
  3380. PUC_PROCESS_SERVER_INFORMATION pServInfo;
  3381. PUC_HTTP_REQUEST pRequest = NULL;
  3382. // Sanity check.
  3383. ASSERT(UC_IS_VALID_CLIENT_CONNECTION(pConnection));
  3384. //
  3385. // Retrieve server information from the connection.
  3386. //
  3387. pServInfo = pConnection->pServerInfo;
  3388. ASSERT(IS_VALID_SERVER_INFORMATION(pServInfo));
  3389. //
  3390. // Acquire the server information push lock followed by the
  3391. // connection spinlock.
  3392. //
  3393. UlAcquirePushLockExclusive(&pServInfo->PushLock);
  3394. UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
  3395. //
  3396. // Make sure the connection is still in ssl handshake state
  3397. // (This check is needed since we released the connection spinlock
  3398. // before calling this function.)
  3399. //
  3400. if (pConnection->ConnectionState != UcConnectStatePerformingSslHandshake
  3401. || pConnection->SslState != UcSslStateServerCertReceived)
  3402. {
  3403. action = FALSE;
  3404. goto Release;
  3405. }
  3406. //
  3407. // Cert::Flags is used to optimize certain cases.
  3408. // If HTTP_SSL_SERIALIZED_CERT_PRESENT is not set,
  3409. // the server certificate was accepted and was not
  3410. // stored in the connection.
  3411. //
  3412. if (!(pConnection->ServerCertInfo.Cert.Flags &
  3413. HTTP_SSL_SERIALIZED_CERT_PRESENT))
  3414. {
  3415. // Okay to send request on this connection.
  3416. action = TRUE;
  3417. goto Release;
  3418. }
  3419. //
  3420. // Unoptimized cases.
  3421. //
  3422. //
  3423. // For Ignore and Automatic modes, no validation is needed.
  3424. // If there is not server cert info on pServInfo, copy now.
  3425. //
  3426. if (pServInfo->ServerCertValidation ==HttpSslServerCertValidationIgnore ||
  3427. pServInfo->ServerCertValidation ==HttpSslServerCertValidationAutomatic)
  3428. {
  3429. if (pServInfo->ServerCertInfoState ==
  3430. HttpSslServerCertInfoStateNotPresent)
  3431. {
  3432. // Update the state of server cert info on servinfo.
  3433. pServInfo->ServerCertInfoState =
  3434. HttpSslServerCertInfoStateNotValidated;
  3435. // Move Cert Issuer List from connection to server info.
  3436. UC_MOVE_CERT_ISSUER_LIST(pServInfo, pConnection);
  3437. // Move certificate from connection to servinfo.
  3438. UC_MOVE_SERIALIZED_CERT(pServInfo, pConnection);
  3439. }
  3440. // Okay to send requests on this connection.
  3441. action = TRUE;
  3442. goto Release;
  3443. }
  3444. //
  3445. // Take action based on the server cert info state in pServInfo.
  3446. //
  3447. switch (pServInfo->ServerCertInfoState)
  3448. {
  3449. case HttpSslServerCertInfoStateNotPresent:
  3450. NotPresent:
  3451. ASSERT(pServInfo->ServerCertValidation ==
  3452. HttpSslServerCertValidationManual ||
  3453. pServInfo->ServerCertValidation ==
  3454. HttpSslServerCertValidationManualOnce);
  3455. //
  3456. // Find a pending request to fail.
  3457. //
  3458. pRequest = UcpFindRequestToFail(pConnection);
  3459. if (pRequest == NULL)
  3460. {
  3461. //
  3462. // We could not find a request to fail.
  3463. // Hence, we can't send requests on this pConnection.
  3464. //
  3465. action = FALSE;
  3466. }
  3467. else
  3468. {
  3469. // Update the state of server cert info on servinfo.
  3470. pServInfo->ServerCertInfoState =
  3471. HttpSslServerCertInfoStateNotValidated;
  3472. // Move Cert Issuer List from connection to server info.
  3473. UC_MOVE_CERT_ISSUER_LIST(pServInfo, pConnection);
  3474. // Move certificate from connection to servinfo.
  3475. UC_MOVE_SERIALIZED_CERT(pServInfo, pConnection);
  3476. // Update the ssl state on the connection.
  3477. pConnection->SslState = UcSslStateValidatingServerCert;
  3478. //
  3479. // Reference the request so that it doesn't go away
  3480. // before we fail it below.
  3481. //
  3482. UC_REFERENCE_REQUEST(pRequest);
  3483. //
  3484. // Can't send request on pConnection as we are waiting for
  3485. // server certificate validation.
  3486. //
  3487. action = FALSE;
  3488. }
  3489. break;
  3490. case HttpSslServerCertInfoStateNotValidated:
  3491. //
  3492. // Server Certificate is already present on servinfo but has not
  3493. // been validated.
  3494. //
  3495. ASSERT(pServInfo->ServerCertValidation ==
  3496. HttpSslServerCertValidationManual ||
  3497. pServInfo->ServerCertValidation ==
  3498. HttpSslServerCertValidationManualOnce);
  3499. // Can't send any requests on pConnection right now.
  3500. action = FALSE;
  3501. break;
  3502. case HttpSslServerCertInfoStateValidated:
  3503. ASSERT(pServInfo->ServerCertValidation ==
  3504. HttpSslServerCertValidationManual ||
  3505. pServInfo->ServerCertValidation ==
  3506. HttpSslServerCertValidationManualOnce);
  3507. if (pServInfo->ServerCertValidation ==
  3508. HttpSslServerCertValidationManualOnce)
  3509. {
  3510. // Is the new certificate same as old one?
  3511. if (UC_COMPARE_CERT_HASH(&pServInfo->ServerCertInfo,
  3512. &pConnection->ServerCertInfo))
  3513. {
  3514. // New certificate is same as the old one.
  3515. // Just move Cert Issuer List from connection to server info.
  3516. UC_MOVE_CERT_ISSUER_LIST(pServInfo, pConnection);
  3517. // Okay to send requests on this connection.
  3518. action = TRUE;
  3519. }
  3520. else
  3521. {
  3522. goto NotPresent;
  3523. }
  3524. }
  3525. else // HttpSslServerCertValidationManual
  3526. {
  3527. // Treat this case as if the certificate was not present.
  3528. goto NotPresent;
  3529. }
  3530. break;
  3531. default:
  3532. ASSERT(FALSE);
  3533. break;
  3534. }
  3535. Release:
  3536. if (action)
  3537. {
  3538. //
  3539. // Handshake is complete. Requests can be sent out on this connection.
  3540. //
  3541. pConnection->SslState = UcSslStateHandshakeComplete;
  3542. pConnection->ConnectionState = UcConnectStateConnectReady;
  3543. //
  3544. // Free any certificate and issuers list on this connection.
  3545. //
  3546. UC_FREE_SERIALIZED_CERT(&pConnection->ServerCertInfo,
  3547. pConnection->pServerInfo->pProcess);
  3548. UC_FREE_CERT_ISSUER_LIST(&pConnection->ServerCertInfo,
  3549. pConnection->pServerInfo->pProcess);
  3550. }
  3551. //
  3552. // Release the connection spinlock and server information pushlock.
  3553. //
  3554. UlReleaseSpinLock(&pConnection->SpinLock, OldIrql);
  3555. UlReleasePushLock(&pServInfo->PushLock);
  3556. if (pRequest)
  3557. {
  3558. UlAcquireSpinLock(&pConnection->SpinLock, &OldIrql);
  3559. UcFailRequest(pRequest, STATUS_RETRY, OldIrql);
  3560. UC_DEREFERENCE_REQUEST(pRequest);
  3561. }
  3562. return action;
  3563. }