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.

1415 lines
36 KiB

  1. /*++
  2. Copyright (c) 1994 Microsoft Corporation
  3. Module Name:
  4. socket.cxx
  5. Abstract:
  6. This file contains general socket utilities.
  7. Contents:
  8. HTTP_REQUEST_HANDLE_OBJECT::OpenConnection
  9. CFsm_OpenConnection::RunSM
  10. HTTP_REQUEST_HANDLE_OBJECT::OpenConnection_Fsm
  11. HTTP_REQUEST_HANDLE_OBJECT::CloseConnection
  12. HTTP_REQUEST_HANDLE_OBJECT::ReleaseConnection
  13. HTTP_REQUEST_HANDLE_OBJECT::AbortConnection
  14. HTTP_REQUEST_HANDLE_OBJECT::OpenProxyTunnel
  15. CFsm_OpenProxyTunnel::RunSM
  16. HTTP_REQUEST_HANDLE_OBJECT::OpenProxyTunnel_Fsm
  17. HTTP_REQUEST_HANDLE_OBJECT::CloneResponseBuffer
  18. Author:
  19. Keith Moore (keithmo) 16-Nov-1994
  20. Revision History:
  21. 18-Dec-1995 rfirth
  22. Reworked for C++
  23. 27-Mar-1996 arthurbi
  24. Added OpenProxyTunnel Method
  25. --*/
  26. #include <wininetp.h>
  27. #include <perfdiag.hxx>
  28. #include "httpp.h"
  29. //
  30. // functions
  31. //
  32. DWORD
  33. HTTP_REQUEST_HANDLE_OBJECT::OpenConnection(
  34. IN BOOL bNewConnection,
  35. IN BOOL fNoCreate /* = FALSE */
  36. )
  37. /*++
  38. Routine Description:
  39. Get a connection to the web server. Either use a pre-existing keep-alive
  40. connection from the global pool or create a new connection
  41. Arguments:
  42. bNewConnection - TRUE if we are NOT to get a connection from the keep-alive
  43. pool
  44. fNoCreate - TRUE if we should NOT create a new socket if a k-a isn't found.
  45. This is currently for the SSL tunneling case where we want to break
  46. and send a CONNECT if a k-a doesn't match our criteria.
  47. Return Value:
  48. DWORD
  49. Success - ERROR_SUCCESS
  50. Opened connection
  51. ERROR_IO_PENDING
  52. Operation will complete asynchronously
  53. Failure -
  54. --*/
  55. {
  56. DEBUG_ENTER((DBG_HTTP,
  57. Dword,
  58. "HTTP_REQUEST_HANDLE_OBJECT::OpenConnection",
  59. "%B",
  60. bNewConnection
  61. ));
  62. DWORD error = DoFsm(New CFsm_OpenConnection(bNewConnection, this, fNoCreate));
  63. DEBUG_LEAVE(error);
  64. return error;
  65. }
  66. DWORD
  67. CFsm_OpenConnection::RunSM(
  68. IN CFsm * Fsm
  69. )
  70. /*++
  71. Routine Description:
  72. Runs next OpenConnection state
  73. Arguments:
  74. Fsm - containing open connection state info
  75. Return Value:
  76. DWORD
  77. Success - ERROR_SUCCESS
  78. Failure -
  79. --*/
  80. {
  81. DEBUG_ENTER((DBG_HTTP,
  82. Dword,
  83. "CFsm_OpenConnection::RunSM",
  84. "%#x",
  85. Fsm
  86. ));
  87. DWORD error;
  88. HTTP_REQUEST_HANDLE_OBJECT * pRequest;
  89. CFsm_OpenConnection * stateMachine = (CFsm_OpenConnection *)Fsm;
  90. START_SENDREQ_PERF();
  91. pRequest = (HTTP_REQUEST_HANDLE_OBJECT *)Fsm->GetContext();
  92. switch (Fsm->GetState()) {
  93. case FSM_STATE_INIT:
  94. case FSM_STATE_CONTINUE:
  95. error = pRequest->OpenConnection_Fsm(stateMachine);
  96. break;
  97. default:
  98. error = ERROR_WINHTTP_INTERNAL_ERROR;
  99. Fsm->SetDone(ERROR_WINHTTP_INTERNAL_ERROR);
  100. INET_ASSERT(FALSE);
  101. break;
  102. }
  103. STOP_SENDREQ_PERF();
  104. DEBUG_LEAVE(error);
  105. return error;
  106. }
  107. DWORD
  108. HTTP_REQUEST_HANDLE_OBJECT::OpenConnection_Fsm(
  109. IN CFsm_OpenConnection * Fsm
  110. )
  111. /*++
  112. Routine Description:
  113. Open connection FSM
  114. Arguments:
  115. Fsm - containing state info
  116. Return Value:
  117. DWORD
  118. Success - ERROR_SUCCESS
  119. Failure -
  120. --*/
  121. {
  122. DEBUG_ENTER((DBG_HTTP,
  123. Dword,
  124. "HTTP_REQUEST_HANDLE_OBJECT::OpenConnection_Fsm",
  125. "%#x",
  126. Fsm
  127. ));
  128. CFsm_OpenConnection & fsm = *Fsm;
  129. DWORD error = fsm.GetError();
  130. CServerInfo * pServerInfo = GetServerInfo();
  131. if (error != ERROR_SUCCESS) {
  132. goto quit;
  133. }
  134. //
  135. // BUGBUG - redundancy. Either put these in the FSM or figure out why we need
  136. // to do proxy name processing here
  137. //
  138. //
  139. // if this object was created from an InternetOpen() handle which specified
  140. // INTERNET_OPEN_TYPE_PROXY then we connect to the proxy, otherwise we
  141. // connect to the server specified in InternetConnect()
  142. //
  143. LPSTR hostName;
  144. LPSTR hostNameServer;
  145. DWORD hostLength;
  146. INTERNET_PORT hostPort;
  147. hostName = hostNameServer = GetHostName(&hostLength);
  148. hostPort = GetHostPort();
  149. LPSTR proxyHostName;
  150. DWORD proxyHostNameLength;
  151. INTERNET_PORT proxyHostPort;
  152. GetProxyName(&proxyHostName,
  153. &proxyHostNameLength,
  154. &proxyHostPort
  155. );
  156. if ((proxyHostName != NULL) && (proxyHostNameLength > 0)) {
  157. SetViaProxy(TRUE);
  158. hostName = proxyHostName;
  159. hostLength = proxyHostNameLength;
  160. hostPort = proxyHostPort;
  161. }
  162. INET_ASSERT(hostName != NULL);
  163. INET_ASSERT(hostPort != INTERNET_INVALID_PORT_NUMBER);
  164. if (fsm.GetState() != FSM_STATE_INIT) {
  165. switch (fsm.GetFunctionState()) {
  166. case FSM_STATE_1:
  167. goto get_continue;
  168. case FSM_STATE_2:
  169. goto connect_continue;
  170. default:
  171. INET_ASSERT(FALSE);
  172. error = ERROR_WINHTTP_INTERNAL_ERROR;
  173. goto quit;
  174. }
  175. }
  176. //
  177. // we may already have a keep-alive connection - don't ask for a new one.
  178. // This happens in the challenge phase of a multi-part (e.g. NTLM) auth
  179. // negotiation over keep-alive
  180. //
  181. if (IsWantKeepAlive() && !fsm.m_bNewConnection && (_Socket != NULL)
  182. && _Socket->IsOpen()) {
  183. //INET_ASSERT(_bKeepAliveConnection);
  184. //if ( IsTunnel() )
  185. //{
  186. // dprintf("Tunnel for nested req=%#x, ALREADY open on Socket=%#x\n", this, _Socket );
  187. //}
  188. error = ERROR_SUCCESS;
  189. goto quit;
  190. }
  191. INET_ASSERT(pServerInfo != NULL);
  192. if (pServerInfo == NULL) {
  193. error = ERROR_WINHTTP_INTERNAL_ERROR;
  194. goto quit;
  195. }
  196. //
  197. // if this request wants a keep-alive connection AND we are allowed to use
  198. // one (i.e. not forced to generate a new connection) AND we can find one
  199. // then we're done, otherwise we have to generate a new connection
  200. //
  201. DWORD dwSocketFlags;
  202. dwSocketFlags = IsAsyncHandle() ? SF_NON_BLOCKING : 0;
  203. if ((IsWantKeepAlive() || (GetOpenFlags() & INTERNET_FLAG_KEEP_CONNECTION))
  204. && !fsm.m_bNewConnection) {
  205. dwSocketFlags |= SF_KEEP_ALIVE;
  206. }
  207. if (GetOpenFlags() & WINHTTP_FLAG_SECURE) {
  208. dwSocketFlags |= SF_SECURE;
  209. }
  210. if ( IsTunnel() )
  211. {
  212. dwSocketFlags |= SF_TUNNEL;
  213. // dprintf("Opening Tunnel for nested req=%#x, Socket Flags=%#x, K-A=%B, Secure=%B, N-B=%B\n",
  214. // this, dwSocketFlags, (dwSocketFlags & SF_KEEP_ALIVE), (dwSocketFlags & SF_SECURE),
  215. // (dwSocketFlags & SF_NON_BLOCKING));
  216. }
  217. INET_ASSERT(_Socket == NULL);
  218. _Socket = NULL;
  219. fsm.SetFunctionState(FSM_STATE_1);
  220. // If m_fNoCreate flag is set, then we're attempting to find
  221. // a matching SSL tunnel that's already been established.
  222. error = DoFsm(new CFsm_GetConnection(
  223. dwSocketFlags,
  224. fsm.m_fNoCreate ? GetHostPort() : hostPort,
  225. GetTimeoutValue(WINHTTP_OPTION_CONNECT_TIMEOUT),
  226. 10000, // dwLimitTimeout
  227. &_Socket,
  228. pServerInfo,
  229. fsm.m_fNoCreate ? hostNameServer : NULL
  230. ));
  231. get_continue:
  232. if (error != ERROR_SUCCESS) {
  233. goto quit;
  234. }
  235. if (_Socket != NULL) {
  236. //
  237. // _bKeepAliveConnection now means "this is a pre-existing k-a socket".
  238. // Only meaningful when re-establishing connect when dropped by server
  239. //
  240. //if ( IsTunnel() )
  241. //{
  242. // dprintf("Tunnel for nested req=%#x\n opened on K-A Socket=%#x\n", this, _Socket );
  243. //}
  244. //dprintf("%s existing K-A connection %#x\n", GetURL(), _Socket->GetSocket());
  245. _bKeepAliveConnection = TRUE;
  246. //
  247. // Get any security Info
  248. //
  249. if (_Socket->IsSecure()) {
  250. if (m_pSecurityInfo != NULL) {
  251. /* SCLE ref */
  252. m_pSecurityInfo->Release();
  253. }
  254. /* SCLE ref */
  255. m_pSecurityInfo = ((ICSecureSocket *)_Socket)->GetSecurityEntry();
  256. ((ICSecureSocket*)_Socket)->SetSecureFlags(SECURITY_FLAG_SECURE);
  257. }
  258. //
  259. // successfully got keep-alive connection from the pool
  260. //
  261. DEBUG_PRINT(HTTP,
  262. INFO,
  263. ("%skeep-alive connection: socket %#x, port %d\n",
  264. _Socket->IsSecure() ? "SSL " : "",
  265. _Socket->GetSocket(),
  266. _Socket->GetSourcePort()
  267. ));
  268. goto quit;
  269. }
  270. else if (fsm.m_fNoCreate)
  271. {
  272. goto quit;
  273. }
  274. //
  275. // the socket didn't come from the pool
  276. //
  277. _bKeepAliveConnection = FALSE;
  278. //
  279. // we may already have a socket if we're reusing the object
  280. //
  281. if (GetOpenFlags() & WINHTTP_FLAG_SECURE)
  282. {
  283. _Socket = New ICSecureSocket();
  284. // Search on the appropriate cache, session vs. global
  285. INTERNET_HANDLE_OBJECT * pInternet;
  286. pInternet = GetRootHandle (this);
  287. if (m_pSecurityInfo == NULL)
  288. {
  289. /* SCLE ref */
  290. m_pSecurityInfo = (pInternet->GetSslSessionCache())->Find(GetHostName());
  291. if (NULL == m_pSecurityInfo)
  292. {
  293. /* SCLE ref */
  294. m_pSecurityInfo = New SECURITY_CACHE_LIST_ENTRY(GetHostName());
  295. }
  296. }
  297. if (_Socket != NULL)
  298. {
  299. _Socket->SetEncryption();
  300. /* SCLE ref */
  301. ((ICSecureSocket *)_Socket)->SetSecurityEntry(m_pSecurityInfo);
  302. /* SCLE ref */
  303. ((ICSecureSocket *)_Socket)->SetHostName(GetHostName(), pInternet->GetSslSessionCache());
  304. ((ICSecureSocket *)_Socket)->SetSecureFlags(GetOpenFlags() & SECURITY_INTERNET_MASK);
  305. if (GetEnableFlags() & WINHTTP_ENABLE_SSL_REVOCATION)
  306. ((ICSecureSocket *)_Socket)->SetSecureFlags(SECURITY_FLAG_CHECK_REVOCATION);
  307. }
  308. }
  309. else
  310. {
  311. if (dwSocketFlags & SF_TUNNEL)
  312. {
  313. _Socket = New ICSecureSocket;
  314. ((ICSecureSocket *)_Socket)->ResetFlags(FALSE);
  315. }
  316. else
  317. {
  318. _Socket = New ICSocket;
  319. }
  320. }
  321. if (_Socket != NULL)
  322. {
  323. fsm.m_bCreatedSocket = TRUE;
  324. }
  325. else
  326. {
  327. //
  328. // balance number of available connections
  329. //
  330. ReleaseConnection(FALSE, FALSE, FALSE);
  331. error = ERROR_NOT_ENOUGH_MEMORY;
  332. goto quit;
  333. }
  334. //
  335. // Turn on Socks, if needed.
  336. //
  337. GetSocksProxyName(&proxyHostName,
  338. &proxyHostNameLength,
  339. &proxyHostPort
  340. );
  341. if ((proxyHostName != NULL) && (proxyHostNameLength > 0)) {
  342. _Socket->EnableSocks(proxyHostName, proxyHostPort);
  343. }
  344. //
  345. // NOTE: if secure connection is required, TargetServer must
  346. // be a fully qualified domain name.
  347. // The hostname is used in comparison with CN found in
  348. // the certificate. The hostname MUST NOT BE the
  349. // result of a DNS lookup. DNS lookups are open to
  350. // spoofing, and that may prevent a security from
  351. // being detected.
  352. //
  353. //
  354. // If we're Posting or sending data, make sure
  355. // the SSL connection code knows about it. Therefore we set
  356. // the flag "SF_SENDING_DATA" for the purposes of
  357. // generating errors if found while making the connection.
  358. //
  359. _Socket->SetPort(hostPort);
  360. fsm.SetFunctionState(FSM_STATE_2);
  361. error = _Socket->Connect(GetTimeoutValue(WINHTTP_OPTION_CONNECT_TIMEOUT),
  362. GetTimeoutValue(WINHTTP_OPTION_CONNECT_RETRIES),
  363. SF_INDICATE
  364. | (IsAsyncHandle() ? SF_NON_BLOCKING : 0)
  365. | (((GetMethodType() == HTTP_METHOD_TYPE_POST)
  366. || (GetMethodType() == HTTP_METHOD_TYPE_PUT))
  367. ? SF_SENDING_DATA
  368. : 0)
  369. );
  370. connect_continue:
  371. if (error == ERROR_SUCCESS) {
  372. //dprintf("%s NEW connection %#x\n", GetURL(), _Socket->GetSocket());
  373. DEBUG_PRINT(HTTP,
  374. INFO,
  375. ("new connection: socket %#x\n",
  376. _Socket->GetSocket()
  377. ));
  378. //if ( IsTunnel() )
  379. //{
  380. // dprintf("Tunnel for nested req=%#x opened for Socket=%#x\n", this, _Socket );
  381. //}
  382. /*
  383. 24918: Invalid ASSERT because the m_Socket member in _Socket can be invalidated
  384. due to closing handle : code downstream of this assert takes that into consideration.
  385. for eg. SetLinger puts the call in a try..except.
  386. */
  387. //INET_ASSERT(_Socket->IsOpen());
  388. //pServerInfo->AddActiveConnection();
  389. // enable send and receive timeout - ignore any errors
  390. _Socket->SetTimeout(SEND_TIMEOUT,
  391. GetTimeoutValue(WINHTTP_OPTION_SEND_TIMEOUT)
  392. );
  393. _Socket->SetTimeout(RECEIVE_TIMEOUT,
  394. GetTimeoutValue(WINHTTP_OPTION_RECEIVE_TIMEOUT)
  395. );
  396. //
  397. // set zero linger: force connection closed at transport level when
  398. // we close the socket. Ignore the error
  399. //
  400. _Socket->SetLinger(TRUE, 0);
  401. }
  402. quit:
  403. if (error != ERROR_IO_PENDING) {
  404. //
  405. // if we created the socket but failed to connect then delete the socket
  406. // object
  407. //
  408. if ((error != ERROR_SUCCESS) && fsm.m_bCreatedSocket) {
  409. //
  410. // we created a socket so we must increase the available connection
  411. // count on failure
  412. //
  413. INET_ASSERT(_Socket != NULL);
  414. ReleaseConnection(TRUE, // close socket (if open)
  415. FALSE, // don't indicate
  416. TRUE // dispose of socket object
  417. );
  418. }
  419. //dprintf("%s get/connect pending socket %#x\n", GetURL(), _Socket ? _Socket->GetSocket() : 0);
  420. fsm.SetDone();
  421. }
  422. DEBUG_LEAVE(error);
  423. return error;
  424. }
  425. DWORD
  426. HTTP_REQUEST_HANDLE_OBJECT::CloseConnection(
  427. IN BOOL bForceClosed
  428. )
  429. /*++
  430. Routine Description:
  431. Performs the opposite of OpenConnection(), i.e. closes the socket or marks
  432. it not in use if keep-alive
  433. Arguments:
  434. bForceClosed - TRUE if we are to forcibly release a keep-alive connection
  435. (i.e. the server timed out before we did)
  436. Return Value:
  437. DWORD
  438. Success - ERROR_SUCCESS
  439. Failure - WSA error
  440. --*/
  441. {
  442. DEBUG_ENTER((DBG_HTTP,
  443. Dword,
  444. "HTTP_REQUEST_HANDLE_OBJECT::CloseConnection",
  445. "%B",
  446. bForceClosed
  447. ));
  448. //dprintf("*** closing %s%s socket %#x\n",
  449. // (_bKeepAliveConnection || IsKeepAlive()) ? "K-A " : "",
  450. // GetURL(),
  451. // _Socket ? _Socket->GetSocket() : 0
  452. // );
  453. DWORD error = ERROR_SUCCESS;
  454. BOOL bClose = TRUE;
  455. BOOL bDelete = TRUE;
  456. if (_Socket == NULL) {
  457. DEBUG_PRINT(HTTP,
  458. WARNING,
  459. ("socket already deleted\n"
  460. ));
  461. goto quit;
  462. }
  463. if (_bKeepAliveConnection || IsKeepAlive()) {
  464. //
  465. // keep-alive connection: just return the connection to the pool
  466. //
  467. if ((IsContentLength() && (GetBytesInSocket() != 0))
  468. || (IsChunkEncoding() && !IsDecodingFinished())
  469. || IsNoLongerKeepAlive() || _Socket->IsClosed()
  470. || ((_State & 0x0F) < (HttpRequestStateObjectData & 0x0F))) {
  471. DEBUG_PRINT(HTTP,
  472. INFO,
  473. ("forcing %#x [%#x] closed: bytes left = %d/%d; no longer k-a = %B; closed = %B\n",
  474. _Socket,
  475. _Socket->GetSocket(),
  476. GetBytesInSocket(),
  477. GetContentLength(),
  478. IsNoLongerKeepAlive(),
  479. _Socket->IsClosed()
  480. ));
  481. //dprintf("forcing k-a %#x closed - bytes=%d/%d, no longer=%B, chunked=%B, chunk-finished=%B\n",
  482. // _Socket->GetSocket(),
  483. // GetBytesInSocket(),
  484. // GetContentLength(),
  485. // IsNoLongerKeepAlive(),
  486. // IsChunkEncoding(),
  487. // IsDecodingFinished()
  488. // );
  489. bForceClosed = TRUE;
  490. }
  491. if (!bForceClosed) {
  492. bClose = FALSE;
  493. bDelete = FALSE;
  494. } else {
  495. //dprintf("%#x forced close\n", _Socket->GetSocket());
  496. }
  497. }
  498. ReleaseConnection(bClose, TRUE, bDelete);
  499. _Socket = NULL;
  500. _bKeepAliveConnection = FALSE;
  501. _bNoLongerKeepAlive = FALSE;
  502. quit:
  503. DEBUG_LEAVE(error);
  504. return error;
  505. }
  506. VOID
  507. HTTP_REQUEST_HANDLE_OBJECT::ReleaseConnection(
  508. IN BOOL bClose,
  509. IN BOOL bIndicate,
  510. IN BOOL bDispose
  511. )
  512. /*++
  513. Routine Description:
  514. Releases the connection back to the server limited pool and optionally
  515. closes the socket handle and destroys the socket object
  516. Arguments:
  517. bClose - if TRUE, increments the available connection count in the
  518. server info object and closes the handle, else we are
  519. returning a keep-alive connection; after this call we no
  520. longer have a socket object owned by this request handle
  521. object
  522. bIndicate - TRUE if we indicate to the user when we close the socket
  523. handle
  524. bDispose - TRUE if we are disposing of the socket object (mutually
  525. exclusive with !bClose), in which case we will no longer have
  526. a socket object after this call returns
  527. Return Value:
  528. None.
  529. --*/
  530. {
  531. DEBUG_ENTER((DBG_HTTP,
  532. None,
  533. "HTTP_REQUEST_HANDLE_OBJECT::ReleaseConnection",
  534. "%B, %B, %B",
  535. bClose,
  536. bIndicate,
  537. bDispose
  538. ));
  539. INET_ASSERT(_Socket != NULL);
  540. //INET_ASSERT(_Socket->IsOpen());
  541. CServerInfo * pServerInfo = GetServerInfo();
  542. // Always disconnect sockets which have been marked as authenticated.
  543. // This is to avoid posting data to IIS4 while preauthenticating
  544. // and inducing the server to close the connection.
  545. if (_Socket)
  546. bClose = (bClose || _Socket->IsAuthenticated());
  547. ICSocket * pSocket = bClose ? NULL : _Socket;
  548. INET_ASSERT(pServerInfo != NULL);
  549. if (pServerInfo != NULL) {
  550. if (bClose && (_Socket != NULL)) {
  551. //
  552. // BUGBUG - this should be set based on bGraceful parameter
  553. //
  554. _Socket->SetLinger(FALSE, 0);
  555. //INET_ASSERT(!_bKeepAliveConnection || _bNoLongerKeepAlive);
  556. _Socket->Shutdown(SD_BOTH);
  557. _Socket->Disconnect(bIndicate ? SF_INDICATE : 0);
  558. if (bDispose) {
  559. _Socket->Dereference();
  560. _Socket = NULL;
  561. }
  562. } else {
  563. _Socket = NULL;
  564. }
  565. //if (IsResponseHttp1_1() && IsKeepAlive()) {
  566. // pServerInfo->ReleasePipelinedConnection(pSocket);
  567. //} else {
  568. pServerInfo->ReleaseConnection(pSocket);
  569. //}
  570. }
  571. DEBUG_LEAVE(0);
  572. }
  573. DWORD
  574. HTTP_REQUEST_HANDLE_OBJECT::AbortConnection(
  575. IN BOOL bForce
  576. )
  577. /*++
  578. Routine Description:
  579. Aborts the current connection. Closes the socket and frees up all receive
  580. buffers. If the connection is keep-alive, we have the option to forcefully
  581. terminate the connection, or just return the socket to the keep-alive pool
  582. Arguments:
  583. bForce - if TRUE and keep-alive, forcefully close the keep-alive socket
  584. Return Value:
  585. DWORD
  586. Success - ERROR_SUCCESS
  587. Failure - WSA error
  588. --*/
  589. {
  590. DEBUG_ENTER((DBG_HTTP,
  591. Dword,
  592. "HTTP_REQUEST_HANDLE_OBJECT::AbortConnection",
  593. "%B",
  594. bForce
  595. ));
  596. DWORD error;
  597. error = CloseConnection(bForce);
  598. if (error == ERROR_SUCCESS) {
  599. //
  600. // destroy all response variables. This is similar to ReuseObject()
  601. // except we don't change the object state, or reset the end-of-file
  602. // state
  603. //
  604. _ResponseHeaders.FreeHeaders();
  605. FreeResponseBuffer();
  606. ResetResponseVariables();
  607. }
  608. DEBUG_LEAVE(error);
  609. return error;
  610. }
  611. DWORD
  612. HTTP_REQUEST_HANDLE_OBJECT::OpenProxyTunnel(
  613. VOID
  614. )
  615. /*++
  616. Routine Description:
  617. Creates a connection with the requested server via a Proxy
  618. tunnelling method.
  619. Works by creating a nested child HTTP and Connect request object.
  620. These objects send a "CONNECT" verb to the proxy server asking for
  621. a connection to made with the destination server. Upon completion the
  622. child objects are discarded. If a class 200 response is not received from
  623. proxy server, the proxy response is copied into this object
  624. and returned to the user.
  625. Arguments:
  626. none.
  627. Return Value:
  628. DWORD
  629. Success - ERROR_SUCCESS
  630. Failure -
  631. ERROR_NOT_ENOUGH_MEMORY
  632. Ran out of resources
  633. --*/
  634. {
  635. DEBUG_ENTER((DBG_HTTP,
  636. Dword,
  637. "HTTP_REQUEST_HANDLE_OBJECT::OpenProxyTunnel",
  638. NULL
  639. ));
  640. DWORD error = DoFsm(New CFsm_OpenProxyTunnel(this));
  641. DEBUG_LEAVE(error);
  642. return error;
  643. }
  644. DWORD
  645. CFsm_OpenProxyTunnel::RunSM(
  646. IN CFsm * Fsm
  647. )
  648. /*++
  649. Routine Description:
  650. Runs next OpenProxyTunnel state
  651. Arguments:
  652. Fsm - contains state info
  653. Return Value:
  654. DWORD
  655. Success - ERROR_SUCCESS
  656. Failure -
  657. --*/
  658. {
  659. DEBUG_ENTER((DBG_HTTP,
  660. Dword,
  661. "CFsm_OpenProxyTunnel::RunSM",
  662. "%#x",
  663. Fsm
  664. ));
  665. DWORD error;
  666. HTTP_REQUEST_HANDLE_OBJECT * pRequest;
  667. CFsm_OpenProxyTunnel * stateMachine = (CFsm_OpenProxyTunnel *)Fsm;
  668. START_SENDREQ_PERF();
  669. pRequest = (HTTP_REQUEST_HANDLE_OBJECT *)Fsm->GetContext();
  670. switch (Fsm->GetState()) {
  671. case FSM_STATE_INIT:
  672. case FSM_STATE_CONTINUE:
  673. error = pRequest->OpenProxyTunnel_Fsm(stateMachine);
  674. break;
  675. default:
  676. error = ERROR_WINHTTP_INTERNAL_ERROR;
  677. Fsm->SetDone(ERROR_WINHTTP_INTERNAL_ERROR);
  678. INET_ASSERT(FALSE);
  679. break;
  680. }
  681. STOP_SENDREQ_PERF();
  682. DEBUG_LEAVE(error);
  683. return error;
  684. }
  685. DWORD
  686. HTTP_REQUEST_HANDLE_OBJECT::OpenProxyTunnel_Fsm(
  687. IN CFsm_OpenProxyTunnel * Fsm
  688. )
  689. /*++
  690. Routine Description:
  691. State machine for OpenProxyTunnel
  692. Arguments:
  693. Fsm - contains state info
  694. Return Value:
  695. DWORD
  696. Success - ERROR_SUCCESS
  697. Failure -
  698. --*/
  699. {
  700. DEBUG_ENTER((DBG_HTTP,
  701. Dword,
  702. "HTTP_REQUEST_HANDLE_OBJECT::OpenProxyTunnel_Fsm",
  703. "%#x",
  704. Fsm
  705. ));
  706. CFsm_OpenProxyTunnel & fsm = *Fsm;
  707. DWORD error = fsm.GetError();
  708. LPINTERNET_THREAD_INFO lpThreadInfo = fsm.GetThreadInfo();
  709. // Need to bury error on blocked async item that failed to find
  710. // established SSL tunnel
  711. if (error != ERROR_SUCCESS && fsm.GetFunctionState() != FSM_STATE_2) {
  712. goto quit;
  713. }
  714. if (lpThreadInfo == NULL) {
  715. INET_ASSERT(FALSE);
  716. error = ERROR_WINHTTP_INTERNAL_ERROR;
  717. goto quit;
  718. }
  719. //
  720. // Need to use handle magic for async requests
  721. // so we'll have the reference before jumping.
  722. //
  723. INTERNET_HANDLE_OBJECT * pInternet;
  724. pInternet = GetRootHandle (this);
  725. if (fsm.GetState() != FSM_STATE_INIT) {
  726. switch (fsm.GetFunctionState()) {
  727. case FSM_STATE_1:
  728. goto send_continue;
  729. case FSM_STATE_2:
  730. goto keep_alive_tunnel;
  731. default:
  732. error = ERROR_WINHTTP_INTERNAL_ERROR;
  733. INET_ASSERT(FALSE);
  734. goto quit;
  735. }
  736. }
  737. // Do not continue if handle is in NTLM challenge state - we
  738. // already have a valid socket set up for tunnelling.
  739. if ((_Socket != NULL) && (GetAuthState() == AUTHSTATE_CHALLENGE))
  740. {
  741. error = ERROR_SUCCESS;
  742. goto quit;
  743. }
  744. // First, try and fetch an already established tunnel
  745. // from the keep-alive pool. If so, we can avoid the nested
  746. // CONNECT request.
  747. //
  748. if (_Socket == NULL)
  749. {
  750. error = OpenConnection(FALSE, TRUE);
  751. if (error == ERROR_IO_PENDING)
  752. {
  753. fsm.SetFunctionState(FSM_STATE_2);
  754. goto quit;
  755. }
  756. keep_alive_tunnel:
  757. if (error == ERROR_SUCCESS && _Socket != NULL)
  758. {
  759. // No need to create nested request. We found an active SSL tunnel
  760. // for this server in the keep-alive pool.
  761. goto quit;
  762. }
  763. else
  764. {
  765. // Start over as normal tunnel since bypass to find
  766. // keep-alive failed.
  767. error = ERROR_SUCCESS;
  768. }
  769. }
  770. //
  771. // With the Internet Handle Object,
  772. // construct a new Connect Object, and new HttpRequest Object.
  773. //
  774. //
  775. // increment the nested request level around InternetConnect(). This is
  776. // required to stop InternetConnect() believing this is the async part of
  777. // a two-part (FTP) request (original async hackery)
  778. //
  779. _InternetIncNestingCount();
  780. fsm.m_hConnect = InternetConnect(pInternet->GetPseudoHandle(),
  781. GetHostName(),
  782. GetHostPort(),
  783. 0, // no flags
  784. NULL
  785. );
  786. _InternetDecNestingCount(1);
  787. if (!fsm.m_hConnect) {
  788. error = GetLastError();
  789. INET_ASSERT(error != ERROR_IO_PENDING);
  790. goto quit;
  791. }
  792. //
  793. // Now do an Open Request. This will pick up the secure proxy flag.
  794. //
  795. fsm.m_hRequest = HttpOpenRequest(fsm.m_hConnect,
  796. "CONNECT",
  797. "/", // we don't need this for a CONNECT
  798. NULL,
  799. NULL,
  800. NULL,
  801. 0,
  802. NULL
  803. );
  804. if (!fsm.m_hRequest) {
  805. error = GetLastError();
  806. goto quit;
  807. }
  808. //
  809. // map the handle
  810. //
  811. error = MapHandleToAddress(fsm.m_hRequest,
  812. (LPVOID *)&fsm.m_hRequestMapped,
  813. FALSE);
  814. if ((error != ERROR_SUCCESS) || (fsm.m_hRequestMapped == NULL)) {
  815. goto quit;
  816. }
  817. fsm.m_pRequest = (HTTP_REQUEST_HANDLE_OBJECT *)fsm.m_hRequestMapped;
  818. //
  819. // we need to set the special secure proxy flag in the request object
  820. //
  821. fsm.m_pRequest->SetTunnel();
  822. LPSTR proxyHostName;
  823. DWORD proxyHostNameLength;
  824. INTERNET_PORT proxyHostPort;
  825. GetProxyName(&proxyHostName,
  826. &proxyHostNameLength,
  827. &proxyHostPort
  828. );
  829. fsm.m_pRequest->SetProxyName(proxyHostName,
  830. proxyHostNameLength,
  831. proxyHostPort
  832. );
  833. //
  834. // Transfer any proxy user/pass from the handle.
  835. //
  836. LPSTR lpszUser, lpszPass;
  837. // Get username + password off of outer handle.
  838. if (GetUserAndPass(IS_PROXY, &lpszUser, &lpszPass))
  839. {
  840. // This will automatically re-validate the username/password
  841. // on the tunneling handle.
  842. fsm.m_pRequest->SetProp (WINHTTP_OPTION_PROXY_USERNAME
  843. & WINHTTP_OPTION_MASK, lpszUser);
  844. fsm.m_pRequest->SetProp (WINHTTP_OPTION_PROXY_PASSWORD
  845. & WINHTTP_OPTION_MASK, lpszPass);
  846. }
  847. if (_pProxyCreds)
  848. {
  849. fsm.m_pRequest->_pProxyCreds = new WINHTTP_REQUEST_CREDENTIALS(_pProxyCreds->_AuthScheme,
  850. _pProxyCreds->_pszRealm,
  851. _pProxyCreds->_pszUserName,
  852. _pProxyCreds->_pszPassword);
  853. if (fsm.m_pRequest->_pProxyCreds == NULL)
  854. {
  855. // If we couldn't clone it, transfer it. This is not efficient, but it will work.
  856. fsm.m_pRequest->_pProxyCreds = _pProxyCreds;
  857. _pProxyCreds = NULL; // ownership transferred
  858. }
  859. }
  860. //
  861. // Transfer any authentication context to the tunnelling handle.
  862. //
  863. //fsm.m_pRequest->SetAuthCtx (_pTunnelAuthCtx);
  864. //dprintf("New tunnel request %#x making nested request= %#x\n", this, fsm.m_pRequest);
  865. //
  866. // Do the Nested SendRequest to the Proxy Server.
  867. // ie send the CONNECT method.
  868. //
  869. fsm.SetFunctionState(FSM_STATE_1);
  870. if (!HttpSendRequest(fsm.m_hRequest, NULL, 0, NULL, 0)) {
  871. error = GetLastError();
  872. if (error == ERROR_IO_PENDING) {
  873. goto done;
  874. }
  875. goto quit;
  876. }
  877. send_continue:
  878. //
  879. // Check Status Code Returned from proxy Server Here.
  880. // If its not 200 we let the user view it as a Proxy Error
  881. // and DON'T continue our connection to the SSL/PCT Server.
  882. //
  883. //dprintf("Received Nested Response, Socket=%#x, org request=%#x, nested request=%#x\n", fsm.m_pRequest->_Socket, this, fsm.m_pRequest);
  884. _StatusCode = fsm.m_pRequest->GetStatusCode();
  885. switch (_StatusCode) {
  886. case HTTP_STATUS_OK:
  887. break;
  888. case HTTP_STATUS_PROXY_AUTH_REQ:
  889. if ((error = CloneResponseBuffer(fsm.m_pRequest)) != ERROR_SUCCESS)
  890. goto quit;
  891. break;
  892. default:
  893. if ((error = CloneResponseBuffer(fsm.m_pRequest)) != ERROR_SUCCESS)
  894. goto quit;
  895. goto quit;
  896. }
  897. //
  898. // Transfer any authentication context back to the outer handle.
  899. //
  900. if ( _pTunnelAuthCtx ) {
  901. delete _pTunnelAuthCtx;
  902. }
  903. _pTunnelAuthCtx = fsm.m_pRequest->GetAuthCtx();
  904. // Don't leave the potential for a deleted request reference
  905. // to be left in the trasferred context.
  906. if (_pTunnelAuthCtx)
  907. {
  908. _pTunnelAuthCtx->_pRequest = NULL;
  909. }
  910. fsm.m_pRequest->SetAuthCtx (NULL);
  911. _PreferredScheme = fsm.m_pRequest->_PreferredScheme;
  912. _SupportedSchemes = fsm.m_pRequest->_SupportedSchemes;
  913. _AuthTarget = fsm.m_pRequest->_AuthTarget;
  914. //
  915. // pull the socket handle from the socket object used to communicate with
  916. // the proxy
  917. //
  918. INET_ASSERT(fsm.m_pRequest->_Socket != NULL);
  919. /*
  920. if server returned anything other than 200 then we failed; revert to non-
  921. secure socket
  922. */
  923. if (_Socket == NULL)
  924. {
  925. // transfer socket reference from nested request
  926. _Socket = fsm.m_pRequest->_Socket;
  927. fsm.m_pRequest->_Socket = NULL;
  928. // Socket should now be marked as secure
  929. ((ICSecureSocket *)_Socket)->ResetFlags(TRUE);
  930. }
  931. if(m_pSecurityInfo == NULL)
  932. {
  933. /* SCLE ref */
  934. m_pSecurityInfo = pInternet->GetSslSessionCache()->Find(GetHostName());
  935. if (NULL == m_pSecurityInfo)
  936. {
  937. /* SCLE ref */
  938. m_pSecurityInfo = New SECURITY_CACHE_LIST_ENTRY(GetHostName());
  939. }
  940. }
  941. if (_Socket != NULL)
  942. {
  943. INET_ASSERT(_Socket->IsSecure());
  944. /* SCLE ref */
  945. ((ICSecureSocket *)_Socket)->SetSecurityEntry(m_pSecurityInfo);
  946. /* SCLE ref */
  947. ((ICSecureSocket *)_Socket)->SetHostName(GetHostName(), pInternet->GetSslSessionCache());
  948. ((ICSecureSocket *)_Socket)->SetSecureFlags(GetOpenFlags() & SECURITY_INTERNET_MASK);
  949. if (GetEnableFlags() & WINHTTP_ENABLE_SSL_REVOCATION)
  950. ((ICSecureSocket *)_Socket)->SetSecureFlags(SECURITY_FLAG_CHECK_REVOCATION);
  951. // Update values for the established tunnel
  952. _Socket->SetPort(fsm.m_pRequest->GetHostPort());
  953. }
  954. else
  955. {
  956. error = ERROR_NOT_ENOUGH_MEMORY;
  957. goto quit;
  958. }
  959. quit:
  960. if (fsm.m_hRequestMapped != NULL) {
  961. DereferenceObject((LPVOID)fsm.m_hRequestMapped);
  962. }
  963. if (fsm.m_hRequest != NULL) {
  964. BOOL bOk;
  965. bOk = WinHttpCloseHandle(fsm.m_hRequest);
  966. INET_ASSERT(bOk);
  967. }
  968. if (fsm.m_hConnect != NULL) {
  969. BOOL bOk;
  970. bOk = WinHttpCloseHandle(fsm.m_hConnect);
  971. INET_ASSERT(bOk);
  972. }
  973. //
  974. // We Reset the ThreadInfo back to the the previous
  975. // object handle, and context values.
  976. //
  977. if (lpThreadInfo != NULL) {
  978. _InternetSetObjectHandle(lpThreadInfo, GetPseudoHandle(), (HINTERNET)this);
  979. _InternetClearLastError(lpThreadInfo);
  980. }
  981. done:
  982. if (error != ERROR_IO_PENDING) {
  983. fsm.SetDone();
  984. }
  985. DEBUG_LEAVE(error);
  986. return error;
  987. }
  988. //
  989. // private methods
  990. //
  991. PRIVATE
  992. DWORD
  993. HTTP_REQUEST_HANDLE_OBJECT::CloneResponseBuffer(
  994. IN HTTP_REQUEST_HANDLE_OBJECT *pChildRequestObj
  995. )
  996. /*++
  997. Routine Description:
  998. HTTP_REQUEST_HANDLE_OBJECT CloneResponseBuffer method.
  999. Copies a Child Request Object's Response Buffer into "this"
  1000. request object. Also forces header parsing to be rerun on
  1001. the header.
  1002. Arguments:
  1003. none.
  1004. Return Value:
  1005. DWORD
  1006. Success - ERROR_SUCCESS
  1007. Failure - ERROR_NOT_ENOUGH_MEMORY
  1008. Ran out of resources
  1009. --*/
  1010. {
  1011. DEBUG_ENTER((DBG_HTTP,
  1012. Dword,
  1013. "HTTP_REQUEST_HANDLE_OBJECT::CloneResponseBuffer",
  1014. "%#x",
  1015. pChildRequestObj
  1016. ));
  1017. DWORD error;
  1018. LPBYTE lpBuffer;
  1019. error = ERROR_SUCCESS;
  1020. lpBuffer = (LPBYTE)ALLOCATE_FIXED_MEMORY(pChildRequestObj->_BytesReceived);
  1021. if ( lpBuffer == NULL )
  1022. {
  1023. error = ERROR_NOT_ENOUGH_MEMORY;
  1024. goto quit;
  1025. }
  1026. //
  1027. // pull out headers, and data from Child Request into our request.
  1028. //
  1029. CopyMemory(
  1030. lpBuffer,
  1031. pChildRequestObj->_ResponseBuffer,
  1032. pChildRequestObj->_BytesReceived
  1033. );
  1034. //
  1035. // Recreate and reparse our header structure into our Object,
  1036. // this is kindof inefficent, but it only happens on errors
  1037. //
  1038. error = CreateResponseHeaders(
  1039. (LPSTR*) &lpBuffer,
  1040. pChildRequestObj->_BytesReceived
  1041. );
  1042. if (error != ERROR_SUCCESS) {
  1043. goto quit;
  1044. }
  1045. SetState(HttpRequestStateObjectData);
  1046. //
  1047. // record the amount of data immediately available to the app
  1048. //
  1049. SetAvailableDataLength(BufferedDataLength());
  1050. quit:
  1051. if (lpBuffer) {
  1052. FREE_MEMORY (lpBuffer);
  1053. }
  1054. DEBUG_LEAVE(error);
  1055. return error;
  1056. }