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.

5374 lines
142 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name :
  4. w3context.cxx
  5. Abstract:
  6. Drive the state machine
  7. Author:
  8. Bilal Alam (balam) 10-Jan-2000
  9. Environment:
  10. Win32 - User Mode
  11. Project:
  12. ULW3.DLL
  13. --*/
  14. #include "precomp.hxx"
  15. #include "anonymousprovider.hxx"
  16. #include "customprovider.hxx"
  17. #include "staticfile.hxx"
  18. #include "isapi_handler.h"
  19. #include "cgi_handler.h"
  20. #include "trace_handler.h"
  21. #include "dav_handler.h"
  22. #include "options_handler.hxx"
  23. #include "generalhandler.hxx"
  24. #include "redirectionhandler.hxx"
  25. //
  26. // Global context list
  27. //
  28. CHAR W3_CONTEXT::sm_achRedirectMessage[ 512 ];
  29. DWORD W3_CONTEXT::sm_cbRedirectMessage;
  30. CHAR * W3_CONTEXT::sm_pszAccessDeniedMessage;
  31. CHAR g_szErrorMessagePreHtml[] =
  32. "<html><head><title>Error</title></head><body>";
  33. CHAR g_szErrorMessagePostHtml[] =
  34. "</body></html>";
  35. ALLOC_CACHE_HANDLER * EXECUTE_CONTEXT::sm_pachExecuteContexts;
  36. HRESULT
  37. EXECUTE_CONTEXT::InitializeFromExecUrlInfo(
  38. EXEC_URL_INFO * pExecUrlInfo
  39. )
  40. /*++
  41. Routine Description:
  42. Initialize an execution context from an HSE_EXEC_URL_INFO structure.
  43. This function does the necessary copying
  44. Arguments:
  45. pExecUrlInfo - Describes the child request to execute
  46. Return Value:
  47. HRESULT
  48. --*/
  49. {
  50. HRESULT hr;
  51. if ( pExecUrlInfo == NULL )
  52. {
  53. DBG_ASSERT( FALSE );
  54. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  55. }
  56. //
  57. // Copy entity if (any)
  58. //
  59. if ( pExecUrlInfo->pEntity != NULL )
  60. {
  61. _EntityInfo.cbAvailable = pExecUrlInfo->pEntity->cbAvailable;
  62. _EntityInfo.lpbData = pExecUrlInfo->pEntity->lpbData;
  63. _ExecUrlInfo.pEntity = &_EntityInfo;
  64. }
  65. //
  66. // Copy user (if any)
  67. //
  68. if ( pExecUrlInfo->pUserInfo != NULL )
  69. {
  70. _UserInfo.hImpersonationToken = pExecUrlInfo->pUserInfo->hImpersonationToken;
  71. hr = _HeaderBuffer.AllocateSpace(
  72. pExecUrlInfo->pUserInfo->cbUserName,
  73. (LPWSTR *)&_UserInfo.pszUserName );
  74. if ( FAILED( hr ) )
  75. {
  76. return hr;
  77. }
  78. memcpy(_UserInfo.pszUserName,
  79. pExecUrlInfo->pUserInfo->pszUserName,
  80. pExecUrlInfo->pUserInfo->cbUserName);
  81. _UserInfo.cbUserName = pExecUrlInfo->pUserInfo->cbUserName;
  82. _UserInfo.fIsUserNameUnicode = pExecUrlInfo->pUserInfo->fIsUserNameUnicode;
  83. hr = _HeaderBuffer.AllocateSpace(
  84. pExecUrlInfo->pUserInfo->pszAuthType,
  85. strlen( pExecUrlInfo->pUserInfo->pszAuthType ),
  86. &( _UserInfo.pszAuthType ) );
  87. if ( FAILED( hr ) )
  88. {
  89. return hr;
  90. }
  91. _ExecUrlInfo.pUserInfo = &_UserInfo;
  92. }
  93. //
  94. // Copy URL (if any)
  95. //
  96. if ( pExecUrlInfo->pszUrl != NULL )
  97. {
  98. hr = _HeaderBuffer.AllocateSpace(
  99. pExecUrlInfo->cbUrl,
  100. (LPWSTR *)&_ExecUrlInfo.pszUrl );
  101. if ( FAILED( hr ) )
  102. {
  103. return hr;
  104. }
  105. memcpy(_ExecUrlInfo.pszUrl,
  106. pExecUrlInfo->pszUrl,
  107. pExecUrlInfo->cbUrl);
  108. _ExecUrlInfo.cbUrl = pExecUrlInfo->cbUrl;
  109. _ExecUrlInfo.fIsUrlUnicode = pExecUrlInfo->fIsUrlUnicode;
  110. }
  111. //
  112. // Copy method (if any)
  113. //
  114. if ( pExecUrlInfo->pszMethod != NULL )
  115. {
  116. hr = _HeaderBuffer.AllocateSpace(
  117. pExecUrlInfo->pszMethod,
  118. strlen( pExecUrlInfo->pszMethod ),
  119. &( _ExecUrlInfo.pszMethod ) );
  120. if ( FAILED( hr ) )
  121. {
  122. return hr;
  123. }
  124. }
  125. //
  126. // Copy child headers
  127. //
  128. if ( pExecUrlInfo->pszChildHeaders != NULL )
  129. {
  130. hr = _HeaderBuffer.AllocateSpace(
  131. pExecUrlInfo->pszChildHeaders,
  132. strlen( pExecUrlInfo->pszChildHeaders ),
  133. &( _ExecUrlInfo.pszChildHeaders ) );
  134. if ( FAILED( hr ) )
  135. {
  136. return hr;
  137. }
  138. }
  139. _ExecUrlInfo.dwExecUrlFlags = pExecUrlInfo->dwExecUrlFlags;
  140. return NO_ERROR;
  141. }
  142. //static
  143. HRESULT
  144. EXECUTE_CONTEXT::Initialize(
  145. VOID
  146. )
  147. /*++
  148. Routine Description:
  149. Initialize execute_context lookaside
  150. Arguments:
  151. None
  152. Return Value:
  153. HRESULT
  154. --*/
  155. {
  156. ALLOC_CACHE_CONFIGURATION acConfig;
  157. //
  158. // Setup allocation lookaside
  159. //
  160. acConfig.nConcurrency = 1;
  161. acConfig.nThreshold = 100;
  162. acConfig.cbSize = sizeof( EXECUTE_CONTEXT );
  163. DBG_ASSERT( sm_pachExecuteContexts == NULL );
  164. sm_pachExecuteContexts = new ALLOC_CACHE_HANDLER( "EXECUTE_CONTEXT",
  165. &acConfig );
  166. if ( sm_pachExecuteContexts == NULL )
  167. {
  168. return HRESULT_FROM_WIN32( GetLastError() );
  169. }
  170. return NO_ERROR;
  171. }
  172. //static
  173. VOID
  174. EXECUTE_CONTEXT::Terminate(
  175. VOID
  176. )
  177. /*++
  178. Routine Description:
  179. Cleanup execute_context lookaside
  180. Arguments:
  181. None
  182. Return Value:
  183. None
  184. --*/
  185. {
  186. if ( sm_pachExecuteContexts != NULL )
  187. {
  188. delete sm_pachExecuteContexts;
  189. sm_pachExecuteContexts = NULL;
  190. }
  191. }
  192. HRESULT
  193. W3_CONTEXT::IsapiExecuteUrl(
  194. EXEC_URL_INFO *pExecUrlInfo
  195. )
  196. /*++
  197. Routine Description:
  198. Do an ISAPI execute URL request.
  199. SO WHY IS THIS CODE NOT IN THE ISAPI_CORE?
  200. Because I don't want to preclude the same child execute API exposed to
  201. ISAPI filters (though of course just the synchronous version)
  202. Arguments:
  203. pExecUrlInfo - Describes the child request to execute
  204. Return Value:
  205. HRESULT
  206. --*/
  207. {
  208. DWORD dwExecFlags = 0;
  209. WCHAR * pszRequiredAppPool = NULL;
  210. BOOL fIgnoreValidation = FALSE;
  211. W3_REQUEST * pChildRequest = NULL;
  212. W3_CHILD_CONTEXT * pChildContext = NULL;
  213. HRESULT hr = NO_ERROR;
  214. DWORD dwCloneRequestFlags = 0;
  215. BOOL fFinished = FALSE;
  216. STACK_STRA( strNewVerb, 10 );
  217. BOOL fIsSSICommandExecution = FALSE;
  218. W3_HANDLER * pSSICommandHandler = NULL;
  219. HANDLE hImpersonationToken = NULL;
  220. W3_USER_CONTEXT * pCustomUser = NULL;
  221. STACK_STRA( strAuthorization, 24 );
  222. STACK_STRA( strCustomAuthType, 32 );
  223. STACK_STRA( strCustomUserName, 64 );
  224. BOOL fSameUrl = FALSE;
  225. DWORD dwRemainingData;
  226. if ( pExecUrlInfo == NULL )
  227. {
  228. DBG_ASSERT( FALSE );
  229. hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  230. goto Finished;
  231. }
  232. if ( _dwRecursionLevel >= W3_CONTEXT_MAX_RECURSION_LEVEL )
  233. {
  234. hr = HRESULT_FROM_WIN32( ERROR_STACK_OVERFLOW );
  235. goto Finished;
  236. }
  237. //
  238. // Is this a SSI hack
  239. //
  240. if ( pExecUrlInfo->dwExecUrlFlags & HSE_EXEC_URL_SSI_CMD )
  241. {
  242. //
  243. // Issue-10-11-2000-BAlam
  244. //
  245. // This is a really dangerous code path (executing a raw command)
  246. // so verify that SSI is calling us
  247. //
  248. fIsSSICommandExecution = TRUE;
  249. }
  250. //
  251. // We always execute the request asynchronously
  252. //
  253. dwExecFlags |= W3_FLAG_ASYNC;
  254. //
  255. // Should we suppress headers in the response (useful for SSI)
  256. //
  257. if ( pExecUrlInfo->dwExecUrlFlags & HSE_EXEC_URL_NO_HEADERS )
  258. {
  259. dwExecFlags |= W3_FLAG_NO_HEADERS;
  260. }
  261. //
  262. // Should we disable custom errors
  263. //
  264. if ( pExecUrlInfo->dwExecUrlFlags & HSE_EXEC_URL_DISABLE_CUSTOM_ERROR )
  265. {
  266. dwExecFlags |= W3_FLAG_NO_ERROR_BODY;
  267. }
  268. //
  269. // In every case we will clone the basics
  270. //
  271. dwCloneRequestFlags = W3_REQUEST_CLONE_BASICS;
  272. //
  273. // If the client specified headers, then we don't want to clone any
  274. // headers
  275. //
  276. if ( pExecUrlInfo->pszChildHeaders == NULL )
  277. {
  278. dwCloneRequestFlags |= W3_REQUEST_CLONE_HEADERS;
  279. }
  280. //
  281. // Now, should we also clone the precondition headers?
  282. //
  283. if ( pExecUrlInfo->dwExecUrlFlags & HSE_EXEC_URL_IGNORE_VALIDATION_AND_RANGE )
  284. {
  285. dwCloneRequestFlags |= W3_REQUEST_CLONE_NO_PRECONDITION;
  286. }
  287. //
  288. // Now, should we also clone the preloaded entity?
  289. //
  290. if ( pExecUrlInfo->pEntity == NULL )
  291. {
  292. dwCloneRequestFlags |= W3_REQUEST_CLONE_ENTITY;
  293. }
  294. //
  295. // OK. Start the fun by cloning the current request (as much as needed)
  296. //
  297. DBG_ASSERT( QueryRequest() != NULL );
  298. hr = QueryRequest()->CloneRequest( dwCloneRequestFlags,
  299. &pChildRequest );
  300. if ( FAILED( hr ) )
  301. {
  302. goto Finished;
  303. }
  304. DBG_ASSERT( pChildRequest != NULL );
  305. //
  306. // If the parent specified headers, add them now
  307. //
  308. if ( pExecUrlInfo->pszChildHeaders != NULL )
  309. {
  310. hr = pChildRequest->SetHeadersByStream( pExecUrlInfo->pszChildHeaders );
  311. if ( FAILED( hr ) )
  312. {
  313. goto Finished;
  314. }
  315. }
  316. //
  317. // If we have entity to stuff in, do so now
  318. //
  319. if ( pExecUrlInfo->pEntity != NULL )
  320. {
  321. hr = pChildRequest->AppendEntityBody( pExecUrlInfo->pEntity->lpbData,
  322. pExecUrlInfo->pEntity->cbAvailable );
  323. if ( FAILED( hr ) )
  324. {
  325. goto Finished;
  326. }
  327. //
  328. // Fix up the content-length header for the child.
  329. //
  330. // Note that if the request is chunk transfer
  331. // encoded then the size of remaining data will
  332. // be 0xffffffff, and we should not mess with it.
  333. //
  334. dwRemainingData = QueryRemainingEntityFromUl();
  335. if ( dwRemainingData < 0xffffffff )
  336. {
  337. STACK_STRA( strName, 32 );
  338. STACK_STRA( strValue, 16 );
  339. DWORD dwNewContentLength;
  340. dwNewContentLength = dwRemainingData + pExecUrlInfo->pEntity->cbAvailable;
  341. //
  342. // It would be bad to wrap around the length
  343. //
  344. if ( dwNewContentLength < dwRemainingData &&
  345. dwNewContentLength < pExecUrlInfo->pEntity->cbAvailable )
  346. {
  347. dwNewContentLength = 0xfffffffe;
  348. }
  349. hr = strName.Copy( "Content-Length" );
  350. if ( FAILED( hr ) )
  351. {
  352. goto Finished;
  353. }
  354. //
  355. // Only safe because 0xfffffffe is 10 digits
  356. // as a string!
  357. //
  358. _ultoa( dwNewContentLength, strValue.QueryStr(), 10 );
  359. strValue.SetLen( strlen( strValue.QueryStr() ) );
  360. hr = pChildRequest->SetHeader( strName, strValue );
  361. if ( FAILED( hr ) )
  362. {
  363. DBG_ASSERT( FALSE );
  364. goto Finished;
  365. }
  366. }
  367. }
  368. //
  369. // Setup the URL for the request, if specified (it can contain a
  370. // query string)
  371. //
  372. // If this is a command execution, then ignore the URL since it really
  373. // isn't a URL in this case (it is a command to execute)
  374. //
  375. // Also figure out if the new URL is the same as the old one (except maybe
  376. // the querystring) because that affects how *-ScriptMaps get executed.
  377. //
  378. if ( pExecUrlInfo->pszUrl == NULL )
  379. {
  380. fSameUrl = TRUE;
  381. }
  382. if ( pExecUrlInfo->pszUrl != NULL &&
  383. !fIsSSICommandExecution )
  384. {
  385. if (pExecUrlInfo->fIsUrlUnicode)
  386. {
  387. STACK_STRU( strNewUrl, 256 );
  388. hr = strNewUrl.Copy( (WCHAR *)pExecUrlInfo->pszUrl );
  389. if ( FAILED( hr ) )
  390. {
  391. goto Finished;
  392. }
  393. //
  394. // Now set the new URL/Querystring
  395. //
  396. hr = pChildRequest->SetUrl( strNewUrl );
  397. if ( FAILED( hr ) )
  398. {
  399. goto Finished;
  400. }
  401. }
  402. else
  403. {
  404. STACK_STRA( strNewUrl, 256 );
  405. hr = strNewUrl.Copy( (CHAR *)pExecUrlInfo->pszUrl );
  406. if ( FAILED( hr ) )
  407. {
  408. goto Finished;
  409. }
  410. //
  411. // Now set the new URL/Querystring
  412. //
  413. hr = pChildRequest->SetUrlA( strNewUrl );
  414. if ( FAILED( hr ) )
  415. {
  416. goto Finished;
  417. }
  418. }
  419. STACK_STRU( strParentUrl, 256);
  420. STACK_STRU( strChildUrl, 256);
  421. if (FAILED( hr = QueryRequest()->GetUrl( &strParentUrl ) ) ||
  422. FAILED( hr = pChildRequest->GetUrl( &strChildUrl ) ) )
  423. {
  424. goto Finished;
  425. }
  426. if ( strChildUrl.QueryCCH() == strParentUrl.QueryCCH() &&
  427. _wcsicmp(strChildUrl.QueryStr(), strParentUrl.QueryStr()) == 0 )
  428. {
  429. fSameUrl = TRUE;
  430. }
  431. }
  432. //
  433. // Set the verb for this request if specified
  434. //
  435. if ( pExecUrlInfo->pszMethod != NULL )
  436. {
  437. hr = strNewVerb.Copy( pExecUrlInfo->pszMethod );
  438. if ( FAILED( hr ) )
  439. {
  440. goto Finished;
  441. }
  442. hr = pChildRequest->SetVerb( strNewVerb );
  443. if ( FAILED( hr ) )
  444. {
  445. goto Finished;
  446. }
  447. }
  448. //
  449. // If a user context was set for this child request, then create a
  450. // custom user context and do the necessary hookup
  451. //
  452. if ( pExecUrlInfo->pUserInfo != NULL )
  453. {
  454. if ( pExecUrlInfo->pUserInfo->hImpersonationToken != NULL )
  455. {
  456. hImpersonationToken = (HANDLE)pExecUrlInfo->pUserInfo->hImpersonationToken;
  457. }
  458. else
  459. {
  460. hImpersonationToken = QueryImpersonationToken();
  461. }
  462. DBG_ASSERT( hImpersonationToken != NULL );
  463. //
  464. // Create the user context
  465. //
  466. pCustomUser = new CUSTOM_USER_CONTEXT(
  467. W3_STATE_AUTHENTICATION::QueryCustomProvider() );
  468. if ( pCustomUser == NULL )
  469. {
  470. goto Finished;
  471. }
  472. hr = ((CUSTOM_USER_CONTEXT*)pCustomUser)->Create(
  473. hImpersonationToken,
  474. pExecUrlInfo->pUserInfo->pszUserName,
  475. pExecUrlInfo->pUserInfo->fIsUserNameUnicode,
  476. QueryUserContext()->QueryAuthType() );
  477. if ( FAILED( hr ) )
  478. {
  479. goto Finished;
  480. }
  481. //
  482. // Make sure REMOTE_USER is calculated properly
  483. //
  484. if (pExecUrlInfo->pUserInfo->fIsUserNameUnicode)
  485. {
  486. hr = strCustomUserName.CopyW( (WCHAR *)pExecUrlInfo->pUserInfo->pszUserName );
  487. }
  488. else
  489. {
  490. hr = strCustomUserName.Copy( (CHAR *)pExecUrlInfo->pUserInfo->pszUserName );
  491. }
  492. if ( FAILED( hr ) )
  493. {
  494. goto Finished;
  495. }
  496. hr = pChildRequest->SetRequestUserName( strCustomUserName );
  497. if ( FAILED( hr ) )
  498. {
  499. goto Finished;
  500. }
  501. //
  502. // Note that AUTH_TYPE server variable is a request'ism. So stuff it
  503. // into the request now indirectly by setting authorization header
  504. //
  505. hr = strAuthorization.Copy( "Authorization" );
  506. if ( FAILED( hr ) )
  507. {
  508. goto Finished;
  509. }
  510. hr = strCustomAuthType.Copy( pExecUrlInfo->pUserInfo->pszAuthType );
  511. if ( FAILED( hr ) )
  512. {
  513. goto Finished;
  514. }
  515. hr = pChildRequest->SetHeader( strAuthorization,
  516. strCustomAuthType,
  517. FALSE );
  518. if ( FAILED( hr ) )
  519. {
  520. goto Finished;
  521. }
  522. }
  523. else
  524. {
  525. //
  526. // No custom user. Use this current request's user context
  527. //
  528. pCustomUser = QueryUserContext();
  529. if ( pCustomUser != NULL )
  530. {
  531. pCustomUser->ReferenceUserContext();
  532. }
  533. }
  534. //
  535. // If a *-ScriptMap wants to be ignored for all child-requests from here
  536. // on out, oblige it
  537. //
  538. if ( pExecUrlInfo->dwExecUrlFlags & HSE_EXEC_URL_IGNORE_CURRENT_INTERCEPTOR )
  539. {
  540. META_SCRIPT_MAP_ENTRY *pScriptMapEntry = QueryHandler()->QueryScriptMapEntry();
  541. if (pScriptMapEntry != NULL &&
  542. pScriptMapEntry->QueryIsStarScriptMap())
  543. {
  544. hr = QueryMainContext()->AppendIgnoreInterceptor(*pScriptMapEntry->QueryExecutable());
  545. if ( FAILED( hr ) )
  546. {
  547. goto Finished;
  548. }
  549. }
  550. }
  551. //
  552. // If we are re-executing the same URL and there is no current *-ScriptMap
  553. // it must be DAV, so remove the DAV headers
  554. //
  555. if (fSameUrl &&
  556. QueryUrlContext()->QueryMetaData()->QueryScriptMap()->QueryStarScriptMap( QueryCurrentStarScriptMapIndex() ) == NULL)
  557. {
  558. pChildRequest->RemoveDav();
  559. }
  560. //
  561. // Now we can create a child context
  562. //
  563. pChildContext = new W3_CHILD_CONTEXT( QueryMainContext(),
  564. this, // parent
  565. pChildRequest,
  566. TRUE, // child owns
  567. pCustomUser,
  568. dwExecFlags,
  569. fSameUrl,
  570. _dwRecursionLevel + 1 );
  571. if ( pChildContext == NULL )
  572. {
  573. hr = HRESULT_FROM_WIN32( GetLastError() );
  574. goto Finished;
  575. }
  576. //
  577. // Get the URL context for this new context
  578. //
  579. hr = pChildContext->RetrieveUrlContext( &fFinished );
  580. if ( FAILED( hr ) )
  581. {
  582. goto Finished;
  583. }
  584. if ( fFinished )
  585. {
  586. //
  587. // Filter wants to bail.
  588. //
  589. hr = HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
  590. goto Finished;
  591. }
  592. //
  593. // ULTRA-GROSS. If this is an explicit command execution from SSI, then
  594. // we explicitly setup the CGI handler. Otherwise, we determine the
  595. // handler using sane rules
  596. //
  597. if ( fIsSSICommandExecution )
  598. {
  599. DBG_ASSERT( !pExecUrlInfo->fIsUrlUnicode );
  600. pSSICommandHandler = new W3_CGI_HANDLER( pChildContext,
  601. NULL,
  602. (CHAR *)pExecUrlInfo->pszUrl );
  603. if ( pSSICommandHandler == NULL )
  604. {
  605. hr = HRESULT_FROM_WIN32( GetLastError() );
  606. goto Finished;
  607. }
  608. pChildContext->SetSSICommandHandler( pSSICommandHandler );
  609. }
  610. else
  611. {
  612. //
  613. // Properly find a handler for this request
  614. //
  615. hr = pChildContext->DetermineHandler();
  616. if ( FAILED( hr ) )
  617. {
  618. goto Finished;
  619. }
  620. }
  621. DBG_ASSERT( pChildContext->QueryHandler() != NULL );
  622. hr = pChildContext->ExecuteHandler( dwExecFlags );
  623. //
  624. // If we failed, then we should just cleanup and report the error.
  625. //
  626. // NOTE: Failure means failure to spawn the child request. Not that
  627. // the child request encountered a failure.
  628. //
  629. Finished:
  630. //
  631. // If we spawned this async, and there was no failure, then return out
  632. //
  633. if ( SUCCEEDED( hr ) && ( dwExecFlags & W3_FLAG_ASYNC ) )
  634. {
  635. return NO_ERROR;
  636. }
  637. //
  638. // If we're here, we either failed, or succeeded synchronously
  639. //
  640. //
  641. // If the urlcontext/child-request was attached to the child context,
  642. // the child context will clean it up
  643. //
  644. if ( pChildContext != NULL )
  645. {
  646. delete pChildContext;
  647. }
  648. else
  649. {
  650. if ( pChildRequest != NULL )
  651. {
  652. delete pChildRequest;
  653. }
  654. if ( pCustomUser != NULL )
  655. {
  656. pCustomUser->DereferenceUserContext();
  657. pCustomUser = NULL;
  658. }
  659. }
  660. return hr;
  661. }
  662. HRESULT
  663. W3_CONTEXT::ExecuteChildRequest(
  664. W3_REQUEST * pNewRequest,
  665. BOOL fOwnRequest,
  666. DWORD dwFlags
  667. )
  668. /*++
  669. Routine Description:
  670. Execute a child request (for internal W3CORE use only)
  671. Arguments:
  672. pNewRequest - W3_REQUEST * representing the new request
  673. fOwnRequest - Should the child context be responsible for cleaning up
  674. request?
  675. dwFlags - W3_FLAG_ASYNC async
  676. W3_FLAG_SYNC sync
  677. W3_FLAG_MORE_DATA caller needs to send data too,
  678. W3_FLAG_NO_CUSTOM_ERROR do not execute custom errors
  679. Return Value:
  680. HRESULT
  681. --*/
  682. {
  683. HRESULT hr;
  684. W3_CHILD_CONTEXT * pChildContext = NULL;
  685. BOOL fFinished = FALSE;
  686. W3_USER_CONTEXT * pUserContext = NULL;
  687. if ( !VALID_W3_FLAGS( dwFlags ) )
  688. {
  689. DBG_ASSERT( FALSE );
  690. hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  691. goto Finished;
  692. }
  693. if ( _dwRecursionLevel >= W3_CONTEXT_MAX_RECURSION_LEVEL )
  694. {
  695. hr = HRESULT_FROM_WIN32( ERROR_STACK_OVERFLOW );
  696. goto Finished;
  697. }
  698. //
  699. // Use the current user
  700. //
  701. pUserContext = QueryUserContext();
  702. if ( pUserContext != NULL )
  703. {
  704. pUserContext->ReferenceUserContext();
  705. }
  706. //
  707. // Ignore the fFinished value on a child
  708. //
  709. pChildContext = new W3_CHILD_CONTEXT( QueryMainContext(),
  710. this,
  711. pNewRequest,
  712. fOwnRequest,
  713. pUserContext,
  714. dwFlags,
  715. FALSE,
  716. _dwRecursionLevel + 1 );
  717. if ( pChildContext == NULL )
  718. {
  719. hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  720. goto Finished;
  721. }
  722. //
  723. // First read the metadata for this new request
  724. //
  725. hr = pChildContext->RetrieveUrlContext( &fFinished );
  726. if ( FAILED( hr ) )
  727. {
  728. goto Finished;
  729. }
  730. if ( fFinished )
  731. {
  732. //
  733. // Filter wants to bail.
  734. //
  735. hr = HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
  736. goto Finished;
  737. }
  738. //
  739. // Find handler
  740. //
  741. hr = pChildContext->DetermineHandler();
  742. if ( FAILED( hr ) )
  743. {
  744. goto Finished;
  745. }
  746. DBG_ASSERT( pChildContext->QueryHandler() );
  747. //
  748. // Execute the handler and take the error in any non-pending case
  749. // (we can't get the status on pending since the value we would be
  750. // getting is instable)
  751. //
  752. hr = pChildContext->ExecuteHandler( dwFlags );
  753. Finished:
  754. //
  755. // If we spawned this async, and there was no failure, then return out
  756. //
  757. if ( !FAILED( hr ) && ( dwFlags & W3_FLAG_ASYNC ) )
  758. {
  759. return NO_ERROR;
  760. }
  761. //
  762. // If we got here, then either something bad happened OR a synchronous
  763. // child execute is complete. Either way, we can delete the context
  764. // here
  765. //
  766. if ( pChildContext != NULL )
  767. {
  768. delete pChildContext;
  769. }
  770. else
  771. {
  772. if ( pNewRequest && fOwnRequest )
  773. {
  774. delete pNewRequest;
  775. }
  776. if ( pUserContext != NULL )
  777. {
  778. pUserContext->DereferenceUserContext();
  779. pUserContext = NULL;
  780. }
  781. }
  782. return hr;
  783. }
  784. HRESULT
  785. W3_CONTEXT::SetupAllowHeader(
  786. VOID
  787. )
  788. /*++
  789. Routine Description:
  790. Setup a 405 response. This means setting the response status as well
  791. as setting up an appropriate Allow: header
  792. Arguments:
  793. None
  794. Return Value:
  795. HRESULT
  796. --*/
  797. {
  798. STACK_STRA( strAllowHeader, 256 );
  799. URL_CONTEXT * pUrlContext;
  800. HRESULT hr;
  801. W3_URL_INFO * pUrlInfo;
  802. W3_METADATA * pMetaData;
  803. MULTISZA * pAllowedVerbs;
  804. const CHAR * pszAllowedVerb;
  805. pUrlContext = QueryUrlContext();
  806. if ( pUrlContext == NULL )
  807. {
  808. //
  809. // If we have no metadata, then don't send an Allow: header. The
  810. // only case this would happen is if a filter decided to send the
  811. // response and in that case it is up to it to send an appropriate
  812. // Allow: header
  813. //
  814. return NO_ERROR;
  815. }
  816. pUrlInfo = pUrlContext->QueryUrlInfo();
  817. if ( pUrlInfo == NULL )
  818. {
  819. DBG_ASSERT( FALSE );
  820. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  821. }
  822. pMetaData = pUrlContext->QueryMetaData();
  823. if ( pMetaData == NULL )
  824. {
  825. DBG_ASSERT( FALSE );
  826. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  827. }
  828. //
  829. // We always support OPTIONS and TRACE
  830. //
  831. hr = strAllowHeader.Append( "OPTIONS, TRACE" );
  832. if ( FAILED( hr ) )
  833. {
  834. return hr;
  835. }
  836. //
  837. // Is this URL script mapped? If so, then the verbs allowed is equal to
  838. // to the verbs specified in the script map, provided that we have
  839. // script execute permissions
  840. //
  841. if ( pUrlInfo->QueryScriptMapEntry() != NULL )
  842. {
  843. if ( IS_ACCESS_ALLOWED( QueryRequest(),
  844. pMetaData->QueryAccessPerms(),
  845. SCRIPT ) )
  846. {
  847. pAllowedVerbs = pUrlInfo->QueryScriptMapEntry()->QueryAllowedVerbs();
  848. DBG_ASSERT( pAllowedVerbs != NULL );
  849. pszAllowedVerb = pAllowedVerbs->First();
  850. while ( pszAllowedVerb != NULL )
  851. {
  852. hr = strAllowHeader.Append( ", " );
  853. if ( FAILED( hr ) )
  854. {
  855. return hr;
  856. }
  857. hr = strAllowHeader.Append( pszAllowedVerb );
  858. if (FAILED(hr))
  859. {
  860. return hr;
  861. }
  862. pszAllowedVerb = pAllowedVerbs->Next( pszAllowedVerb );
  863. }
  864. }
  865. }
  866. else
  867. {
  868. //
  869. // Must be a static file or a explicit gateway
  870. //
  871. switch( pUrlInfo->QueryGateway() )
  872. {
  873. case GATEWAY_UNKNOWN:
  874. case GATEWAY_MAP:
  875. hr = strAllowHeader.Append( ", GET, HEAD" );
  876. break;
  877. case GATEWAY_CGI:
  878. case GATEWAY_ISAPI:
  879. hr = strAllowHeader.Append( ", GET, HEAD, POST" );
  880. break;
  881. }
  882. if ( FAILED( hr ) )
  883. {
  884. return hr;
  885. }
  886. }
  887. //
  888. // Now add some DAV verbs (man, this is broken)
  889. //
  890. if ( IS_ACCESS_ALLOWED( QueryRequest(),
  891. pMetaData->QueryAccessPerms(),
  892. WRITE ) &&
  893. g_pW3Server->QueryIsDavEnabled() )
  894. {
  895. hr = strAllowHeader.Append( ", DELETE, PUT" );
  896. if ( FAILED( hr ) )
  897. {
  898. return hr;
  899. }
  900. }
  901. //
  902. // Finally, set the header
  903. //
  904. hr = QueryResponse()->SetHeader( HttpHeaderAllow,
  905. strAllowHeader.QueryStr(),
  906. (USHORT)strAllowHeader.QueryCCH() );
  907. return hr;
  908. }
  909. HRESULT
  910. W3_CONTEXT::SetupCustomErrorFileResponse(
  911. STRU & strErrorFile
  912. )
  913. /*++
  914. Routine Description:
  915. Open the custom error file (nor URL) from cache if possible and setup
  916. the response (which means, fill in entity body with file)
  917. Arguments:
  918. strErrorFile - Custom Error File
  919. Return Value:
  920. HRESULT (HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND) to indicate to caller
  921. that it should just generate hard coded custom error.
  922. --*/
  923. {
  924. W3_FILE_INFO * pOpenFile = NULL;
  925. HRESULT hr = NO_ERROR;
  926. DWORD dwFlags;
  927. W3_RESPONSE * pResponse = QueryResponse();
  928. CACHE_USER fileUser;
  929. ULARGE_INTEGER liFileSize;
  930. W3_METADATA * pMetaData;
  931. URL_CONTEXT * pUrlContext;
  932. STACK_STRA( strContentType, 32 );
  933. DBG_ASSERT( pResponse != NULL );
  934. DBG_ASSERT( !pResponse->QueryEntityExists() );
  935. DBG_ASSERT( pResponse->QueryStatusCode() >= 400 );
  936. //
  937. // Get the file from the cache. We will enable deferred directory
  938. // monitor changes. This means that we will register for the appropriate
  939. // parent directory changes as needed and remove as the cached file
  940. // object goes away.
  941. //
  942. DBG_ASSERT( g_pW3Server->QueryFileCache() != NULL );
  943. hr = g_pW3Server->QueryFileCache()->GetFileInfo( strErrorFile,
  944. NULL,
  945. &fileUser,
  946. TRUE,
  947. &pOpenFile );
  948. if ( FAILED( hr ) )
  949. {
  950. //
  951. // If we cannot open the file for whatever reason, return
  952. // as if the file didn't exist, so that caller can proceed by sending
  953. // no custom error
  954. return HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
  955. }
  956. DBG_ASSERT( pOpenFile != NULL );
  957. //
  958. // We might already have a custom error file IF we encountered an error
  959. // setting up the last custom error response
  960. //
  961. if ( _pCustomErrorFile != NULL )
  962. {
  963. _pCustomErrorFile->DereferenceCacheEntry();
  964. _pCustomErrorFile = NULL;
  965. }
  966. DBG_ASSERT( pOpenFile != NULL );
  967. _pCustomErrorFile = pOpenFile;
  968. //
  969. // Determine the content-type and set the header
  970. //
  971. pUrlContext = QueryUrlContext();
  972. DBG_ASSERT( pUrlContext != NULL );
  973. pMetaData = pUrlContext->QueryMetaData();
  974. DBG_ASSERT( pMetaData != NULL );
  975. hr = SelectMimeMappingForFileExt( strErrorFile.QueryStr(),
  976. pMetaData->QueryMimeMap(),
  977. &strContentType );
  978. if ( SUCCEEDED( hr ) )
  979. {
  980. hr = pResponse->SetHeader( HttpHeaderContentType,
  981. strContentType.QueryStr(),
  982. (USHORT)strContentType.QueryCCH() );
  983. if ( FAILED( hr ) )
  984. {
  985. return hr;
  986. }
  987. }
  988. //
  989. // Now setup the response chunk
  990. //
  991. pOpenFile->QuerySize( &liFileSize );
  992. if ( pOpenFile->QueryFileBuffer() != NULL )
  993. {
  994. hr = pResponse->AddMemoryChunkByReference(
  995. pOpenFile->QueryFileBuffer(),
  996. liFileSize.LowPart );
  997. }
  998. else
  999. {
  1000. hr = pResponse->AddFileHandleChunk(pOpenFile->QueryFileHandle(),
  1001. 0,
  1002. liFileSize.LowPart );
  1003. }
  1004. return hr;
  1005. }
  1006. HANDLE
  1007. W3_CONTEXT::QueryImpersonationToken(
  1008. BOOL * pfIsVrToken
  1009. )
  1010. /*++
  1011. Routine Description:
  1012. Get the impersonation token for this request. This routine will
  1013. choose correctly based on whether there is a VRoot token
  1014. Arguments:
  1015. pfIsVrToken - Set to TRUE if this is the metabase user (instead of
  1016. authenticated user)
  1017. Return Value:
  1018. HANDLE
  1019. --*/
  1020. {
  1021. W3_METADATA * pMetaData;
  1022. W3_USER_CONTEXT * pUserContext;
  1023. HANDLE hToken;
  1024. if ( QueryUrlContext() == NULL )
  1025. {
  1026. return NULL;
  1027. }
  1028. pMetaData = QueryUrlContext()->QueryMetaData();
  1029. DBG_ASSERT( pMetaData != NULL );
  1030. if( _pctVrToken == NULL )
  1031. {
  1032. pMetaData->GetAndRefVrAccessToken( &_pctVrToken );
  1033. }
  1034. if ( _pctVrToken == NULL )
  1035. {
  1036. if ( pfIsVrToken )
  1037. {
  1038. *pfIsVrToken = FALSE;
  1039. }
  1040. pUserContext = QueryUserContext();
  1041. hToken = pUserContext != NULL ? pUserContext->QueryImpersonationToken() : NULL;
  1042. }
  1043. else
  1044. {
  1045. DBG_ASSERT( _pctVrToken != NULL );
  1046. if ( pfIsVrToken )
  1047. {
  1048. *pfIsVrToken = TRUE;
  1049. }
  1050. hToken = _pctVrToken->QueryImpersonationToken();
  1051. }
  1052. return hToken;
  1053. }
  1054. VOID
  1055. W3_CONTEXT::QueryFileCacheUser(
  1056. CACHE_USER * pFileCacheUser
  1057. )
  1058. /*++
  1059. Routine Description:
  1060. Get a user descriptor suitable for use with file cache
  1061. Arguments:
  1062. pFileCacheUser - Filled with file cache user information
  1063. Return Value:
  1064. None
  1065. --*/
  1066. {
  1067. W3_METADATA * pMetaData;
  1068. W3_USER_CONTEXT * pUserContext;
  1069. HANDLE hReturnToken = NULL;
  1070. PSID pReturnSid = NULL;
  1071. DBG_ASSERT( pFileCacheUser != NULL );
  1072. DBG_ASSERT( QueryUrlContext() != NULL );
  1073. pMetaData = QueryUrlContext()->QueryMetaData();
  1074. DBG_ASSERT( pMetaData != NULL );
  1075. pUserContext = QueryUserContext();
  1076. DBG_ASSERT( pUserContext != NULL );
  1077. if( _pctVrToken == NULL )
  1078. {
  1079. pMetaData->GetAndRefVrAccessToken( &_pctVrToken );
  1080. }
  1081. if ( _pctVrToken == NULL )
  1082. {
  1083. hReturnToken = pUserContext->QueryImpersonationToken();
  1084. pReturnSid = pUserContext->QuerySid();
  1085. }
  1086. else
  1087. {
  1088. DBG_ASSERT( _pctVrToken != NULL );
  1089. pReturnSid = _pctVrToken->QuerySid();
  1090. hReturnToken = _pctVrToken->QueryImpersonationToken();
  1091. }
  1092. //
  1093. // Setup the file cache user
  1094. //
  1095. pFileCacheUser->_hToken = hReturnToken;
  1096. pFileCacheUser->_pSid = pReturnSid;
  1097. }
  1098. HANDLE
  1099. W3_CONTEXT::QueryPrimaryToken(
  1100. VOID
  1101. )
  1102. /*++
  1103. Routine Description:
  1104. Get the primary token for this request. This routine will
  1105. choose correctly based on whether there is a VRoot token
  1106. Arguments:
  1107. None
  1108. Return Value:
  1109. HANDLE
  1110. --*/
  1111. {
  1112. W3_METADATA * pMetaData;
  1113. W3_USER_CONTEXT * pUserContext;
  1114. HANDLE hToken;
  1115. DBG_ASSERT( QueryUrlContext() != NULL );
  1116. pMetaData = QueryUrlContext()->QueryMetaData();
  1117. DBG_ASSERT( pMetaData != NULL );
  1118. pUserContext = QueryUserContext();
  1119. DBG_ASSERT( pUserContext != NULL );
  1120. if( _pctVrToken == NULL )
  1121. {
  1122. pMetaData->GetAndRefVrAccessToken( &_pctVrToken );
  1123. }
  1124. if ( _pctVrToken == NULL )
  1125. {
  1126. hToken = pUserContext->QueryPrimaryToken();
  1127. }
  1128. else
  1129. {
  1130. DBG_ASSERT( _pctVrToken != NULL );
  1131. hToken = _pctVrToken->QueryPrimaryToken();
  1132. }
  1133. return hToken;
  1134. }
  1135. HRESULT
  1136. W3_CONTEXT::GetCertificateInfoEx(
  1137. IN DWORD cbAllocated,
  1138. OUT DWORD * pdwCertEncodingType,
  1139. OUT unsigned char * pbCertEncoded,
  1140. OUT DWORD * pcbCertEncoded,
  1141. OUT DWORD * pdwCertificateFlags
  1142. )
  1143. /*++
  1144. Routine Description:
  1145. Retrieve certificate info if available
  1146. Arguments:
  1147. Return Value:
  1148. HRESULT
  1149. --*/
  1150. {
  1151. CERTIFICATE_CONTEXT * pCertContext = NULL;
  1152. DWORD cbCert = 0;
  1153. PVOID pvCert = NULL;
  1154. if ( pdwCertEncodingType == NULL ||
  1155. pbCertEncoded == NULL ||
  1156. pcbCertEncoded == NULL ||
  1157. pdwCertificateFlags == NULL )
  1158. {
  1159. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  1160. }
  1161. pCertContext = QueryCertificateContext();
  1162. if ( pCertContext == NULL )
  1163. {
  1164. //
  1165. // If we don't have a certificate, then just succeed with nothing
  1166. // to keep in line with IIS 5 behaviour
  1167. //
  1168. *pbCertEncoded = NULL;
  1169. *pcbCertEncoded = 0;
  1170. return NO_ERROR;
  1171. }
  1172. pCertContext->QueryEncodedCertificate( &pvCert, &cbCert );
  1173. DBG_ASSERT( pvCert != NULL );
  1174. DBG_ASSERT( cbCert != 0 );
  1175. //
  1176. // Fill in the parameters
  1177. //
  1178. *pcbCertEncoded = cbCert;
  1179. if ( cbAllocated < *pcbCertEncoded )
  1180. {
  1181. //
  1182. // Not enough space
  1183. //
  1184. return HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER );
  1185. }
  1186. else
  1187. {
  1188. memcpy( pbCertEncoded,
  1189. pvCert,
  1190. *pcbCertEncoded );
  1191. //
  1192. // pdwCertificateFlags - legacy value
  1193. // In IIS3 days client certificates were not verified by IIS
  1194. // so applications needed certificate flags to make
  1195. // their own decisions regarding trust
  1196. // Now only valid certificate allow access so value of 1 is
  1197. // the only one that is valid for post IIS3 versions of IIS
  1198. //
  1199. *pdwCertificateFlags = 1;
  1200. *pdwCertEncodingType = X509_ASN_ENCODING;
  1201. return NO_ERROR;
  1202. }
  1203. }
  1204. HRESULT
  1205. W3_CONTEXT::SendResponse(
  1206. DWORD dwFlags
  1207. )
  1208. /*++
  1209. Routine Description:
  1210. Sends a response to the client thru ULATQ.
  1211. Arguments:
  1212. dwFlags - Flags
  1213. W3_FLAG_ASYNC - Send response asynchronously
  1214. W3_FLAG_SYNC - Send response synchronously
  1215. W3_FLAG_MORE_DATA - Send more data
  1216. W3_FLAG_NO_ERROR_BODY - Don't generate error body
  1217. W3_FLAG_NO_CONTENT_LENGTH - Don't generate content-length
  1218. Return Value:
  1219. HRESULT
  1220. --*/
  1221. {
  1222. HRESULT hr = NO_ERROR;
  1223. BOOL fIsFileError = FALSE;
  1224. BOOL fFinished = FALSE;
  1225. DWORD cbSent = 0;
  1226. BOOL fAddContentLength = TRUE;
  1227. BOOL fSendHeaders = FALSE;
  1228. BOOL fHandlerManagesHead = FALSE;
  1229. DWORD dwResponseFlags = 0;
  1230. AUTH_PROVIDER * pAnonymousProvider = NULL;
  1231. W3_RESPONSE * pResponse;
  1232. W3_FILTER_CONTEXT * pFilterContext;
  1233. W3_USER_CONTEXT * pUserContext = NULL;
  1234. HTTP_CACHE_POLICY cachePolicy;
  1235. if ( !VALID_W3_FLAGS( dwFlags ) )
  1236. {
  1237. DBG_ASSERT( FALSE );
  1238. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  1239. }
  1240. pResponse = QueryResponse();
  1241. DBG_ASSERT( pResponse != NULL );
  1242. //
  1243. // Should we be sending headers at all?
  1244. //
  1245. fSendHeaders = QuerySendHeaders();
  1246. if ( !fSendHeaders ||
  1247. dwFlags & W3_FLAG_NO_CONTENT_LENGTH ||
  1248. pResponse->QueryStatusCode() == HttpStatusNotModified.statusCode )
  1249. {
  1250. fAddContentLength = FALSE;
  1251. }
  1252. //
  1253. // Do a quick precheck to see whether we require an END_OF_REQUEST
  1254. // notification. If so, then this won't be our last send
  1255. //
  1256. //
  1257. // Even if the caller didn't want to send any more data, if we are
  1258. // expecting a post-response notification (END_OF_REQUEST/LOG), then
  1259. // we need to tell UL to expect a final send
  1260. //
  1261. if ( IsNotificationNeeded( SF_NOTIFY_END_OF_REQUEST ) ||
  1262. IsNotificationNeeded( SF_NOTIFY_LOG ) )
  1263. {
  1264. dwFlags |= W3_FLAG_MORE_DATA;
  1265. }
  1266. //
  1267. // Remember whether we need to send an empty done on our own at the end
  1268. //
  1269. if ( dwFlags & W3_FLAG_MORE_DATA )
  1270. {
  1271. SetNeedFinalDone();
  1272. }
  1273. //
  1274. // Was this an access denial not handled by an authentication provider?
  1275. //
  1276. if ( pResponse->QueryStatusCode() == HttpStatusUnauthorized.statusCode &&
  1277. !QueryProviderHandled() &&
  1278. QueryUrlContext() != NULL )
  1279. {
  1280. //
  1281. // First call access denied filters
  1282. //
  1283. if ( IsNotificationNeeded( SF_NOTIFY_ACCESS_DENIED ) )
  1284. {
  1285. STACK_STRA( straUrl, 256 );
  1286. STACK_STRA( straPhys, 256 );
  1287. STACK_STRU( strUrl, 256 );
  1288. STRU * pstrPhysicalPath;
  1289. hr = QueryRequest()->GetUrl( &strUrl );
  1290. if ( FAILED( hr ) )
  1291. {
  1292. return hr;
  1293. }
  1294. pstrPhysicalPath = QueryUrlContext()->QueryPhysicalPath();
  1295. DBG_ASSERT( pstrPhysicalPath != NULL );
  1296. if (FAILED(hr = straUrl.CopyW(strUrl.QueryStr())) ||
  1297. FAILED(hr = straPhys.CopyW(pstrPhysicalPath->QueryStr())))
  1298. {
  1299. return hr;
  1300. }
  1301. fFinished = FALSE;
  1302. if ( !QueryFilterContext()->NotifyAccessDenied(
  1303. straUrl.QueryStr(),
  1304. straPhys.QueryStr(),
  1305. &fFinished ) )
  1306. {
  1307. return HRESULT_FROM_WIN32( GetLastError() );
  1308. }
  1309. if ( fFinished )
  1310. {
  1311. if ( dwFlags & W3_FLAG_ASYNC )
  1312. {
  1313. POST_MAIN_COMPLETION( QueryMainContext() );
  1314. }
  1315. return NO_ERROR;
  1316. }
  1317. }
  1318. //
  1319. // Now, notify all authentication providers so they can add headers
  1320. // if necessary. We ignore the return value here since we want to be
  1321. // able to send possible authentication headers back along with the
  1322. // response
  1323. //
  1324. W3_STATE_AUTHENTICATION::CallAllAccessDenied( QueryMainContext() );
  1325. //
  1326. // If a response has been sent, then we can finish
  1327. //
  1328. if ( pResponse->QueryResponseSent() )
  1329. {
  1330. if ( dwFlags & W3_FLAG_ASYNC )
  1331. {
  1332. POST_MAIN_COMPLETION( QueryMainContext() );
  1333. }
  1334. return NO_ERROR;
  1335. }
  1336. }
  1337. //
  1338. // Send a custom error for this response if all of following are true:
  1339. //
  1340. // a) The caller wants an error body
  1341. // b) This request (and all children) actually allow an error body
  1342. // c) The response code is greater than 400 (error)
  1343. //
  1344. if ( !(dwFlags & W3_FLAG_NO_ERROR_BODY ) &&
  1345. QuerySendErrorBody() &&
  1346. pResponse->QueryStatusCode() >= 400 )
  1347. {
  1348. STACK_STRU( strError, 64 );
  1349. STACK_STRU( strFullUrl, 64 );
  1350. WCHAR achNum[ 32 ];
  1351. HTTP_SUB_ERROR subError;
  1352. W3_REQUEST * pChildRequest = NULL;
  1353. DBG_ASSERT( pResponse->QueryEntityExists() == FALSE );
  1354. DisableUlCache();
  1355. //
  1356. // Get the sub error for this response (if any)
  1357. //
  1358. hr = pResponse->QuerySubError( &subError );
  1359. if ( FAILED( hr ) )
  1360. {
  1361. return hr;
  1362. }
  1363. //
  1364. // Check for a configured custom error. This check is only possible
  1365. // if we have read metadata. We may not have read metadata if
  1366. // some fatal error occurred early in the pipeline (for example,
  1367. // an out-of-mem error in the URLINFO state)
  1368. //
  1369. if ( QueryUrlContext() != NULL &&
  1370. QuerySendCustomError() )
  1371. {
  1372. W3_METADATA * pMetaData = NULL;
  1373. pMetaData = QueryUrlContext()->QueryMetaData();
  1374. DBG_ASSERT( pMetaData != NULL );
  1375. hr = pMetaData->FindCustomError( pResponse->QueryStatusCode(),
  1376. subError.mdSubError,
  1377. &fIsFileError,
  1378. &strError );
  1379. //
  1380. // If this is a file error, check for file existence now. If it
  1381. // does not exist, then act as if there is no custom error set
  1382. //
  1383. if ( SUCCEEDED( hr ) )
  1384. {
  1385. if ( fIsFileError )
  1386. {
  1387. hr = SetupCustomErrorFileResponse( strError );
  1388. }
  1389. else
  1390. {
  1391. //
  1392. // This is a custom error URL
  1393. //
  1394. //
  1395. // If there is no user context, then executing a child
  1396. // request would be tough.
  1397. //
  1398. // But we can still try to get the anonymous token, if
  1399. // it exists and use it
  1400. //
  1401. if ( QueryUserContext() == NULL )
  1402. {
  1403. pAnonymousProvider = W3_STATE_AUTHENTICATION::QueryAnonymousProvider();
  1404. DBG_ASSERT( pAnonymousProvider != NULL );
  1405. hr = pAnonymousProvider->DoAuthenticate( QueryMainContext(),
  1406. &fFinished );
  1407. if ( FAILED( hr ) ||
  1408. QueryMainContext()->QueryUserContext() == NULL )
  1409. {
  1410. //
  1411. // Ok. We really can't get anonymous user.
  1412. // No custom error URL
  1413. //
  1414. hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
  1415. }
  1416. }
  1417. }
  1418. }
  1419. }
  1420. else
  1421. {
  1422. hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
  1423. }
  1424. if ( SUCCEEDED( hr ) )
  1425. {
  1426. //
  1427. // Great. Send the configured custom error
  1428. //
  1429. if ( !fIsFileError )
  1430. {
  1431. //
  1432. // This is a custom error URL. Reset and launch custom URL
  1433. //
  1434. // The URL is of the form:
  1435. //
  1436. // /customerrorurl?<status>;http://<server>/<original url>
  1437. //
  1438. hr = strError.Append( L"?" );
  1439. if ( FAILED( hr ) )
  1440. {
  1441. return hr;
  1442. }
  1443. _itow( pResponse->QueryStatusCode(), achNum, 10 );
  1444. hr = strError.Append( achNum );
  1445. if ( FAILED( hr ) )
  1446. {
  1447. return hr;
  1448. }
  1449. hr = strError.Append( L";" );
  1450. if ( FAILED( hr ) )
  1451. {
  1452. return hr;
  1453. }
  1454. hr = QueryRequest()->GetFullUrl( &strFullUrl );
  1455. if ( FAILED( hr ) )
  1456. {
  1457. return hr;
  1458. }
  1459. hr = strError.Append( strFullUrl );
  1460. if ( FAILED( hr ) )
  1461. {
  1462. return hr;
  1463. }
  1464. //
  1465. // Now build up a new child request by first cloning the
  1466. // existing request
  1467. //
  1468. hr = QueryRequest()->CloneRequest(
  1469. W3_REQUEST_CLONE_BASICS |
  1470. W3_REQUEST_CLONE_HEADERS |
  1471. W3_REQUEST_CLONE_NO_PRECONDITION |
  1472. W3_REQUEST_CLONE_NO_DAV,
  1473. &pChildRequest );
  1474. if ( FAILED( hr ) )
  1475. {
  1476. return hr;
  1477. }
  1478. DBG_ASSERT( pChildRequest != NULL );
  1479. //
  1480. // Change the URL and verb of the request
  1481. //
  1482. hr = pChildRequest->SetUrl( strError );
  1483. if ( FAILED( hr ) )
  1484. {
  1485. delete pChildRequest;
  1486. return hr;
  1487. }
  1488. //
  1489. // Remove DAVFS'ness
  1490. //
  1491. pChildRequest->RemoveDav();
  1492. //
  1493. // All custom error URLs are GET/HEADs
  1494. //
  1495. if ( pChildRequest->QueryVerbType() == HttpVerbHEAD )
  1496. {
  1497. pChildRequest->SetVerbType( HttpVerbHEAD );
  1498. }
  1499. else
  1500. {
  1501. pChildRequest->SetVerbType( HttpVerbGET );
  1502. }
  1503. pResponse->SetStatus( HttpStatusOk );
  1504. //
  1505. // From now on, ExecuteChildRequest owns the request
  1506. //
  1507. hr = ExecuteChildRequest( pChildRequest,
  1508. TRUE, // child context cleans up
  1509. dwFlags | W3_FLAG_NO_CUSTOM_ERROR );
  1510. if ( FAILED( hr ) )
  1511. {
  1512. return hr;
  1513. }
  1514. return NO_ERROR;
  1515. }
  1516. else
  1517. {
  1518. // Increment the perf counter
  1519. QuerySite()->IncFilesSent();
  1520. }
  1521. }
  1522. else
  1523. {
  1524. //
  1525. // Not finding a custom error is OK, but any other error is
  1526. // fatal
  1527. //
  1528. DBG_ASSERT( FAILED( hr ) );
  1529. if ( hr != HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ) )
  1530. {
  1531. return hr;
  1532. }
  1533. CHAR *achImageError;
  1534. DWORD cbImageError = 512;
  1535. if (FAILED(hr = QueryHeaderBuffer()->AllocateSpace(
  1536. cbImageError,
  1537. &achImageError)))
  1538. {
  1539. return hr;
  1540. }
  1541. //
  1542. // Check the image for a resource string which may apply.
  1543. //
  1544. if ( subError.dwStringId != 0 )
  1545. {
  1546. hr = g_pW3Server->LoadString( subError.dwStringId,
  1547. achImageError,
  1548. &cbImageError );
  1549. }
  1550. else if ( pResponse->QueryStatusCode() ==
  1551. HttpStatusUnauthorized.statusCode &&
  1552. sm_pszAccessDeniedMessage != NULL )
  1553. {
  1554. //
  1555. // Note: 401 message is actually configured in the registry
  1556. //
  1557. strncpy( achImageError,
  1558. sm_pszAccessDeniedMessage,
  1559. cbImageError - 1 );
  1560. achImageError[ cbImageError - 1 ] = '\0';
  1561. cbImageError = strlen( achImageError );
  1562. hr = NO_ERROR;
  1563. }
  1564. else
  1565. {
  1566. hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  1567. }
  1568. if ( FAILED( hr ) )
  1569. {
  1570. if ( FAILED( QueryErrorStatus() ) )
  1571. {
  1572. cbImageError = 512;
  1573. //
  1574. // If there is a particular error status set, find the
  1575. // message for that error and send it. This is that last
  1576. // thing we can do to attempt to send a useful message to
  1577. // client
  1578. //
  1579. cbImageError = FormatMessageA(
  1580. FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
  1581. NULL,
  1582. QueryErrorStatus(),
  1583. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  1584. achImageError,
  1585. cbImageError,
  1586. NULL);
  1587. if (cbImageError == 0)
  1588. {
  1589. cbImageError = sprintf(
  1590. achImageError,
  1591. "%d (0x%08x)",
  1592. WIN32_FROM_HRESULT( QueryErrorStatus() ),
  1593. WIN32_FROM_HRESULT( QueryErrorStatus() ) );
  1594. }
  1595. }
  1596. else
  1597. {
  1598. //
  1599. // No error message to give
  1600. //
  1601. cbImageError = 0;
  1602. }
  1603. }
  1604. //
  1605. // Only add an error message if we created one
  1606. //
  1607. if ( cbImageError != 0 )
  1608. {
  1609. //
  1610. // Add content type
  1611. //
  1612. hr = pResponse->SetHeader( HttpHeaderContentType,
  1613. "text/html",
  1614. 9 );
  1615. if ( FAILED( hr ) )
  1616. {
  1617. return hr;
  1618. }
  1619. hr = pResponse->AddMemoryChunkByReference(
  1620. g_szErrorMessagePreHtml,
  1621. sizeof( g_szErrorMessagePreHtml ) - 1 );
  1622. if ( FAILED( hr ) )
  1623. {
  1624. return hr;
  1625. }
  1626. hr = pResponse->AddMemoryChunkByReference(
  1627. achImageError,
  1628. cbImageError );
  1629. if ( FAILED( hr ) )
  1630. {
  1631. return hr;
  1632. }
  1633. hr = pResponse->AddMemoryChunkByReference(
  1634. g_szErrorMessagePostHtml,
  1635. sizeof( g_szErrorMessagePostHtml ) - 1 );
  1636. if ( FAILED( hr ) )
  1637. {
  1638. return hr;
  1639. }
  1640. }
  1641. }
  1642. }
  1643. //
  1644. // Send custom headers if any
  1645. //
  1646. if ( fSendHeaders &&
  1647. QueryUrlContext() != NULL )
  1648. {
  1649. W3_METADATA *pMetaData = QueryUrlContext()->QueryMetaData();
  1650. DBG_ASSERT( pMetaData != NULL );
  1651. //
  1652. // Add Custom HTTP Headers if defined in the metabase.
  1653. //
  1654. if ( !pMetaData->QueryCustomHeaders()->IsEmpty() )
  1655. {
  1656. hr = pResponse->AppendResponseHeaders( *(pMetaData->QueryCustomHeaders()) );
  1657. if ( FAILED( hr ) )
  1658. {
  1659. pResponse->Clear();
  1660. return hr;
  1661. }
  1662. }
  1663. }
  1664. //
  1665. // Does the current handler manage content length on its own?
  1666. // We'll assume (i.e. the choice which means we do nothing to supply
  1667. // a content-length header or suppress HEADs)
  1668. //
  1669. if ( QueryHandler() != NULL )
  1670. {
  1671. fHandlerManagesHead = QueryHandler()->QueryManagesOwnHead();
  1672. }
  1673. //
  1674. // Should we be adding a content-length header? If so, do it now
  1675. //
  1676. if ( fAddContentLength )
  1677. {
  1678. CHAR achNum[ 32 ];
  1679. //
  1680. // If there is already a Content-Length header, then no need to
  1681. // make one (this must mean the handler handled HEAD themselves)
  1682. //
  1683. if ( pResponse->GetHeader( HttpHeaderContentLength ) == NULL )
  1684. {
  1685. _ui64toa( pResponse->QueryContentLength(),
  1686. achNum,
  1687. 10 );
  1688. hr = pResponse->SetHeader( HttpHeaderContentLength,
  1689. achNum,
  1690. strlen( achNum ) );
  1691. if ( FAILED( hr ) )
  1692. {
  1693. SetErrorStatus( hr );
  1694. pResponse->SetStatus( HttpStatusServerError );
  1695. return hr;
  1696. }
  1697. }
  1698. }
  1699. //
  1700. // Should we be suppressing entity. This should be done if this is a
  1701. // HEAD request and the current handler doesn't take responsibility
  1702. // to handle HEADs
  1703. //
  1704. if ( QueryRequest()->QueryVerbType() == HttpVerbHEAD &&
  1705. !fHandlerManagesHead )
  1706. {
  1707. DisableUlCache();
  1708. _fSuppressEntity = TRUE;
  1709. }
  1710. //
  1711. // Remember what type of operation this is, so that the completion
  1712. // can do the proper book keeping
  1713. //
  1714. if ( dwFlags & W3_FLAG_ASYNC )
  1715. {
  1716. SetLastIOPending( LOG_WRITE_IO );
  1717. }
  1718. //
  1719. // If a filter added response headers (either for regular responses or
  1720. // for denial responses), then add them now
  1721. //
  1722. pFilterContext = QueryFilterContext( FALSE );
  1723. if ( pFilterContext != NULL )
  1724. {
  1725. //
  1726. // Add denial headers if this is a denial
  1727. //
  1728. if ( pResponse->QueryStatusCode() == HttpStatusUnauthorized.statusCode )
  1729. {
  1730. //
  1731. // Add filter denied headers
  1732. //
  1733. hr = pResponse->AppendResponseHeaders(
  1734. *pFilterContext->QueryAddDenialHeaders() );
  1735. if ( FAILED( hr ) )
  1736. {
  1737. return hr;
  1738. }
  1739. pFilterContext->QueryAddDenialHeaders()->Reset();
  1740. }
  1741. //
  1742. // Add regular headers
  1743. //
  1744. hr = pResponse->AppendResponseHeaders(
  1745. *pFilterContext->QueryAddResponseHeaders() );
  1746. if ( FAILED( hr ) )
  1747. {
  1748. return hr;
  1749. }
  1750. pFilterContext->QueryAddResponseHeaders()->Reset();
  1751. }
  1752. //
  1753. // If there is any auth token magic to send back, do so now
  1754. //
  1755. pUserContext = QueryMainContext()->QueryUserContext();
  1756. if ( pUserContext != NULL )
  1757. {
  1758. if ( pUserContext->QueryAuthToken() != NULL &&
  1759. !pUserContext->QueryAuthToken()->IsEmpty() )
  1760. {
  1761. hr = pResponse->SetHeader( "WWW-Authenticate",
  1762. 16,
  1763. pUserContext->QueryAuthToken()->QueryStr(),
  1764. (USHORT)pUserContext->QueryAuthToken()->QueryCCH() );
  1765. if ( FAILED( hr ) )
  1766. {
  1767. return hr;
  1768. }
  1769. }
  1770. }
  1771. //
  1772. // Notify Response header filters
  1773. //
  1774. if ( fSendHeaders &&
  1775. IsNotificationNeeded( SF_NOTIFY_SEND_RESPONSE ) )
  1776. {
  1777. HTTP_FILTER_SEND_RESPONSE sendResponse;
  1778. BOOL fRet;
  1779. sendResponse.GetHeader = GetSendHeader;
  1780. sendResponse.SetHeader = SetSendHeader;
  1781. sendResponse.AddHeader = AddSendHeader;
  1782. sendResponse.HttpStatus = pResponse->QueryStatusCode();
  1783. fRet = NotifyFilters( SF_NOTIFY_SEND_RESPONSE,
  1784. &sendResponse,
  1785. &fFinished );
  1786. if ( !fRet || fFinished )
  1787. {
  1788. //
  1789. // Just cause a disconnect
  1790. //
  1791. SetDisconnect( TRUE );
  1792. SetNeedFinalDone();
  1793. hr = HRESULT_FROM_WIN32( GetLastError() );
  1794. if ( dwFlags & W3_FLAG_ASYNC )
  1795. {
  1796. POST_MAIN_COMPLETION( QueryMainContext() );
  1797. hr = NO_ERROR;
  1798. }
  1799. }
  1800. if ( !fRet )
  1801. {
  1802. return hr;
  1803. }
  1804. if ( fFinished )
  1805. {
  1806. return NO_ERROR;
  1807. }
  1808. }
  1809. // perf ctr
  1810. if (QuerySite() != NULL)
  1811. {
  1812. if ( pResponse->QueryStatusCode() == HttpStatusNotFound.statusCode )
  1813. {
  1814. QuerySite()->IncNotFound();
  1815. }
  1816. else if ( pResponse->QueryStatusCode() == HttpStatusLockedError.statusCode )
  1817. {
  1818. QuerySite()->IncLockedError();
  1819. }
  1820. }
  1821. //
  1822. // If any one sees a reason why this response and this kernel mode cache
  1823. // should not be together, speak now or forever hold your peace (or
  1824. // at least until the UL cache flushes the response)
  1825. //
  1826. DBG_ASSERT( g_pW3Server->QueryUlCache() != NULL );
  1827. cachePolicy.Policy = HttpCachePolicyNocache;
  1828. if ( g_pW3Server->QueryUlCache()->CheckUlCacheability( this ) )
  1829. {
  1830. //
  1831. // The response is UL cacheable, so setup a ul cache entry token
  1832. //
  1833. if ( _pHandler != NULL )
  1834. {
  1835. DBG_ASSERT( _pHandler->QueryIsUlCacheable() );
  1836. _pHandler->SetupUlCachedResponse( this, &cachePolicy );
  1837. }
  1838. }
  1839. //
  1840. // Generate response flags now
  1841. //
  1842. if ( QueryNeedFinalDone() )
  1843. {
  1844. dwResponseFlags |= W3_RESPONSE_MORE_DATA;
  1845. }
  1846. if ( QueryDisconnect() )
  1847. {
  1848. dwResponseFlags |= W3_RESPONSE_DISCONNECT;
  1849. }
  1850. if ( !fSendHeaders )
  1851. {
  1852. dwResponseFlags |= W3_RESPONSE_SUPPRESS_HEADERS;
  1853. }
  1854. if ( _fSuppressEntity )
  1855. {
  1856. dwResponseFlags |= W3_RESPONSE_SUPPRESS_ENTITY;
  1857. }
  1858. if ( dwFlags & W3_FLAG_ASYNC )
  1859. {
  1860. dwResponseFlags |= W3_RESPONSE_ASYNC;
  1861. }
  1862. //
  1863. // Send out the full response now
  1864. //
  1865. hr = pResponse->SendResponse( this,
  1866. dwResponseFlags,
  1867. &cachePolicy,
  1868. &cbSent );
  1869. if (!(dwFlags & W3_FLAG_ASYNC))
  1870. {
  1871. IncrementBytesSent(cbSent);
  1872. }
  1873. if (FAILED(hr))
  1874. {
  1875. if (_pHandler == NULL)
  1876. {
  1877. DBGPRINTF((DBG_CONTEXT,
  1878. "SendResponse failed: no handler, hr %x\n",
  1879. hr));
  1880. }
  1881. else
  1882. {
  1883. DBGPRINTF((DBG_CONTEXT,
  1884. "SendResponse failed: handler %S, hr %x\n",
  1885. _pHandler->QueryName(),
  1886. hr));
  1887. }
  1888. }
  1889. return hr;
  1890. }
  1891. HRESULT
  1892. W3_CONTEXT::SendEntity(
  1893. DWORD dwFlags
  1894. )
  1895. /*++
  1896. Routine Description:
  1897. Sends a response entity to the client thru ULATQ.
  1898. Arguments:
  1899. dwFlags - Flags
  1900. W3_FLAG_ASYNC - Send response asynchronously
  1901. W3_FLAG_SYNC - Send response synchronously
  1902. W3_FLAG_PAST_END_OF_REQ - Send after END_OF_REQUEST notification
  1903. to let UL know we are done
  1904. W3_FLAG_MORE_DATA - More data to be sent
  1905. Return Value:
  1906. HRESULT
  1907. --*/
  1908. {
  1909. DWORD dwResponseFlags = 0;
  1910. HRESULT hr;
  1911. if ( !VALID_W3_FLAGS( dwFlags ) ||
  1912. ( dwFlags & W3_FLAG_NO_CUSTOM_ERROR ) )
  1913. {
  1914. DBG_ASSERT( FALSE );
  1915. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  1916. }
  1917. //
  1918. // Set the final send just in case we never did a SendResponse (which
  1919. // would normally have set this).
  1920. //
  1921. if (dwFlags & W3_FLAG_ASYNC)
  1922. {
  1923. SetLastIOPending(LOG_WRITE_IO);
  1924. }
  1925. //
  1926. // Setup response flags
  1927. //
  1928. if ( ( dwFlags & W3_FLAG_MORE_DATA ) ||
  1929. ( QueryNeedFinalDone() &&
  1930. !( dwFlags & W3_FLAG_PAST_END_OF_REQ ) ) )
  1931. {
  1932. SetNeedFinalDone();
  1933. dwResponseFlags |= W3_RESPONSE_MORE_DATA;
  1934. }
  1935. if ( dwFlags & W3_FLAG_ASYNC )
  1936. {
  1937. dwResponseFlags |= W3_RESPONSE_ASYNC;
  1938. }
  1939. if ( QueryDisconnect() )
  1940. {
  1941. dwResponseFlags |= W3_RESPONSE_DISCONNECT;
  1942. }
  1943. if ( _fSuppressEntity )
  1944. {
  1945. dwResponseFlags |= W3_RESPONSE_SUPPRESS_ENTITY;
  1946. }
  1947. //
  1948. // Do the send now
  1949. //
  1950. DWORD cbSent = 0;
  1951. hr = QueryResponse()->SendEntity(
  1952. this,
  1953. dwResponseFlags,
  1954. &cbSent );
  1955. if (!(dwFlags & W3_FLAG_ASYNC))
  1956. {
  1957. IncrementBytesSent(cbSent);
  1958. }
  1959. if (FAILED(hr))
  1960. {
  1961. if (_pHandler == NULL)
  1962. {
  1963. DBGPRINTF((DBG_CONTEXT,
  1964. "SendEntity failed: no handler, hr %x\n",
  1965. hr));
  1966. }
  1967. else
  1968. {
  1969. DBGPRINTF((DBG_CONTEXT,
  1970. "SendEntity failed: handler %S, hr %x\n",
  1971. _pHandler->QueryName(),
  1972. hr));
  1973. }
  1974. }
  1975. return hr;
  1976. }
  1977. DWORD
  1978. W3_CONTEXT::QueryRemainingEntityFromUl(
  1979. VOID
  1980. )
  1981. /*++
  1982. Routine Description:
  1983. Returns how much entity can be read from UL
  1984. Arguments:
  1985. None
  1986. Return Value:
  1987. Number of bytes available (INFINITE if chunked)
  1988. --*/
  1989. {
  1990. return QueryMainContext()->QueryRemainingEntityFromUl();
  1991. }
  1992. VOID
  1993. W3_CONTEXT::SetRemainingEntityFromUl(
  1994. DWORD cbRemaining
  1995. )
  1996. /*++
  1997. Routine Description:
  1998. Sets how much entity can be read from UL
  1999. Arguments:
  2000. DWORD
  2001. Return Value:
  2002. None
  2003. --*/
  2004. {
  2005. QueryMainContext()->SetRemainingEntityFromUl(cbRemaining);
  2006. }
  2007. VOID
  2008. W3_CONTEXT::QueryAlreadyAvailableEntity(
  2009. VOID ** ppvBuffer,
  2010. DWORD * pcbBuffer
  2011. )
  2012. /*++
  2013. Routine Description:
  2014. Returns the already preloaded entity found in the current request
  2015. Arguments:
  2016. ppvBuffer - filled with pointer to available entity
  2017. pcbBuffer - filled with size of buffer pointed to by *ppvBuffer
  2018. Return Value:
  2019. None
  2020. --*/
  2021. {
  2022. W3_REQUEST * pRequest;
  2023. DBG_ASSERT( ppvBuffer != NULL );
  2024. DBG_ASSERT( pcbBuffer != NULL );
  2025. pRequest = QueryRequest();
  2026. DBG_ASSERT( pRequest != NULL );
  2027. *ppvBuffer = pRequest->QueryEntityBody();
  2028. *pcbBuffer = pRequest->QueryAvailableBytes();
  2029. }
  2030. HRESULT
  2031. W3_CONTEXT::ReceiveEntity(
  2032. DWORD dwFlags,
  2033. VOID * pBuffer,
  2034. DWORD cbBuffer,
  2035. DWORD * pBytesReceived
  2036. )
  2037. /*++
  2038. Routine Description:
  2039. Receives request entity from the client thru ULATQ.
  2040. Arguments:
  2041. dwFlags - W3_FLAG_ASYNC or W3_FLAG_SYNC
  2042. pBuffer - Buffer to contain the data
  2043. cbBuffer - The size of the buffer
  2044. pBytesReceived - Upon return, the number of bytes
  2045. copied to the buffer
  2046. Return Value:
  2047. HRESULT
  2048. --*/
  2049. {
  2050. W3_MAIN_CONTEXT * pMainContext;
  2051. W3_METADATA * pMetaData;
  2052. DWORD dwMaxEntityAllowed;
  2053. DWORD dwEntityReadSoFar;
  2054. HRESULT hr;
  2055. if (dwFlags & W3_FLAG_ASYNC)
  2056. {
  2057. SetLastIOPending(LOG_READ_IO);
  2058. }
  2059. pMainContext = QueryMainContext();
  2060. DBG_ASSERT( pMainContext != NULL );
  2061. pMetaData = QueryUrlContext()->QueryMetaData();
  2062. DBG_ASSERT( pMetaData );
  2063. dwMaxEntityAllowed = pMetaData->QueryMaxRequestEntityAllowed();
  2064. dwEntityReadSoFar = pMainContext->QueryEntityReadSoFar();
  2065. if ( dwEntityReadSoFar >= dwMaxEntityAllowed )
  2066. {
  2067. //
  2068. // We've already read all the data we're allowed
  2069. // to read. Fail the call
  2070. //
  2071. return HRESULT_FROM_WIN32( ERROR_CONNECTION_INVALID );
  2072. }
  2073. //
  2074. // If necessary, set cbBuffer so that we cannot overrun
  2075. // the max entity allowed.
  2076. //
  2077. if ( cbBuffer + dwEntityReadSoFar > dwMaxEntityAllowed )
  2078. {
  2079. cbBuffer = dwMaxEntityAllowed - dwEntityReadSoFar;
  2080. }
  2081. hr = pMainContext->ReceiveEntityBody( !!(dwFlags & W3_FLAG_ASYNC),
  2082. pBuffer,
  2083. cbBuffer,
  2084. pBytesReceived );
  2085. if (!(dwFlags & W3_FLAG_ASYNC))
  2086. {
  2087. IncrementBytesRecvd(*pBytesReceived);
  2088. }
  2089. if (FAILED(hr))
  2090. {
  2091. if (_pHandler == NULL)
  2092. {
  2093. DBGPRINTF((DBG_CONTEXT,
  2094. "ReceiveEntity failed: no handler, hr %x\n",
  2095. hr));
  2096. }
  2097. else if (hr != HRESULT_FROM_WIN32(ERROR_HANDLE_EOF))
  2098. {
  2099. DBGPRINTF((DBG_CONTEXT,
  2100. "ReceiveEntity failed: handler %S, hr %x\n",
  2101. _pHandler->QueryName(),
  2102. hr));
  2103. }
  2104. }
  2105. return hr;
  2106. }
  2107. HRESULT
  2108. W3_CONTEXT::CleanIsapiExecuteUrl(
  2109. EXEC_URL_INFO * pExecUrlInfo
  2110. )
  2111. /*++
  2112. Routine Description:
  2113. Wrapper for IsapiExecuteUrl() which insured it is called on a clean
  2114. thread (non-coinited). COM bites
  2115. Arguments:
  2116. pExecUrlInfo - EXEC_URL_INFO
  2117. Return Value:
  2118. HRESULT
  2119. --*/
  2120. {
  2121. EXECUTE_CONTEXT * pExecuteContext;
  2122. HRESULT hr;
  2123. BOOL fRet;
  2124. if ( pExecUrlInfo == NULL )
  2125. {
  2126. DBG_ASSERT( FALSE );
  2127. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  2128. }
  2129. pExecuteContext = new EXECUTE_CONTEXT( this );
  2130. if ( pExecuteContext == NULL )
  2131. {
  2132. return HRESULT_FROM_WIN32( GetLastError() );
  2133. }
  2134. hr = pExecuteContext->InitializeFromExecUrlInfo( pExecUrlInfo );
  2135. if ( FAILED( hr ) )
  2136. {
  2137. delete pExecuteContext;
  2138. return hr;
  2139. }
  2140. fRet = ThreadPoolPostCompletion( 0,
  2141. W3_CONTEXT::OnCleanIsapiExecuteUrl,
  2142. (LPOVERLAPPED) pExecuteContext );
  2143. if ( !fRet )
  2144. {
  2145. hr = HRESULT_FROM_WIN32( GetLastError() );
  2146. delete pExecuteContext;
  2147. return hr;
  2148. }
  2149. return NO_ERROR;
  2150. }
  2151. HRESULT
  2152. W3_CONTEXT::CleanIsapiSendCustomError(
  2153. HSE_CUSTOM_ERROR_INFO * pCustomErrorInfo
  2154. )
  2155. /*++
  2156. Routine Description:
  2157. Wrapper for IsapiSendCustomError() which insured it is called on a clean
  2158. thread (non-coinited). COM bites
  2159. Arguments:
  2160. pCustomErrorInfo - Custom error info
  2161. Return Value:
  2162. HRESULT
  2163. --*/
  2164. {
  2165. EXECUTE_CONTEXT * pExecuteContext;
  2166. HRESULT hr;
  2167. HANDLE hEvent = NULL;
  2168. BOOL fRet;
  2169. W3_RESPONSE * pResponse = QueryResponse();
  2170. W3_METADATA * pMetaData = NULL;
  2171. STACK_STRU( strError, 256 );
  2172. BOOL fIsFileError;
  2173. HTTP_SUB_ERROR subError;
  2174. if ( pCustomErrorInfo == NULL )
  2175. {
  2176. DBG_ASSERT( FALSE );
  2177. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  2178. }
  2179. DBG_ASSERT( pResponse != NULL );
  2180. //
  2181. // If we're already sending a custom error, then stop the recursion
  2182. //
  2183. if ( !QuerySendCustomError() )
  2184. {
  2185. return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
  2186. }
  2187. pResponse->Clear();
  2188. //
  2189. // We don't expect an empty status line here (we expect an error)
  2190. //
  2191. if ( pCustomErrorInfo->pszStatus == NULL ||
  2192. pCustomErrorInfo->pszStatus[ 0 ] == '\0' )
  2193. {
  2194. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  2195. }
  2196. //
  2197. // Setup the response status/substatus
  2198. //
  2199. hr = pResponse->BuildStatusFromIsapi( pCustomErrorInfo->pszStatus );
  2200. if ( FAILED( hr ) )
  2201. {
  2202. return hr;
  2203. }
  2204. subError.mdSubError = pCustomErrorInfo->uHttpSubError;
  2205. subError.dwStringId = 0;
  2206. pResponse->SetSubError( &subError );
  2207. //
  2208. // Attempt to match a string ID. If not, thats ok.
  2209. //
  2210. pResponse->FindStringId();
  2211. //
  2212. // An error better have been sent
  2213. //
  2214. if ( pResponse->QueryStatusCode() < 400 )
  2215. {
  2216. return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
  2217. }
  2218. //
  2219. // Now try to find a custom error for the given response. If it doesn't
  2220. // exist then we error our
  2221. //
  2222. DBG_ASSERT( QueryUrlContext() != NULL );
  2223. pMetaData = QueryUrlContext()->QueryMetaData();
  2224. DBG_ASSERT( pMetaData != NULL );
  2225. hr = pMetaData->FindCustomError( QueryResponse()->QueryStatusCode(),
  2226. subError.mdSubError,
  2227. &fIsFileError,
  2228. &strError );
  2229. if ( FAILED( hr ) )
  2230. {
  2231. return HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
  2232. }
  2233. //
  2234. // Now start executing the custom error
  2235. //
  2236. if ( pCustomErrorInfo->fAsync == FALSE )
  2237. {
  2238. hEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  2239. if ( hEvent == NULL )
  2240. {
  2241. return HRESULT_FROM_WIN32( GetLastError() );
  2242. }
  2243. }
  2244. pExecuteContext = new EXECUTE_CONTEXT( this );
  2245. if ( pExecuteContext == NULL )
  2246. {
  2247. return HRESULT_FROM_WIN32( GetLastError() );
  2248. }
  2249. pExecuteContext->SetCompleteEvent( hEvent );
  2250. //
  2251. // Post the call
  2252. //
  2253. fRet = ThreadPoolPostCompletion( 0,
  2254. W3_CONTEXT::OnCleanIsapiSendCustomError,
  2255. (LPOVERLAPPED) pExecuteContext );
  2256. if ( !fRet )
  2257. {
  2258. hr = HRESULT_FROM_WIN32( GetLastError() );
  2259. CloseHandle( hEvent );
  2260. pExecuteContext->SetCompleteEvent( NULL );
  2261. delete pExecuteContext;
  2262. return hr;
  2263. }
  2264. if ( pCustomErrorInfo->fAsync == FALSE )
  2265. {
  2266. WaitForSingleObject( hEvent, INFINITE );
  2267. CloseHandle( hEvent );
  2268. }
  2269. return NO_ERROR;
  2270. }
  2271. HRESULT
  2272. W3_CONTEXT::ExecuteHandler(
  2273. DWORD dwFlags,
  2274. BOOL * pfDidImmediateFinish
  2275. )
  2276. /*++
  2277. Routine Description:
  2278. Run the context's handler
  2279. Arguments:
  2280. dwFlags - W3_FLAG_ASYNC async
  2281. W3_FLAG_SYNC sync
  2282. W3_FLAG_MORE_DATA caller needs to send data too
  2283. pfDidImmediateFinish - If the caller sets this value (i.e. non NULL) AND
  2284. the callers asks for async execution, then in the
  2285. case where the handler executes synchronously, we
  2286. will set *pfDidImmediateFinish to TRUE and not
  2287. bother with faking a completion. This is an
  2288. optimization to handle the synchronous ISAPI
  2289. execution (non-child) case.
  2290. Return Value:
  2291. HRESULT
  2292. --*/
  2293. {
  2294. CONTEXT_STATUS status;
  2295. W3_HANDLER * pHandler;
  2296. HTTP_SUB_ERROR subError;
  2297. HRESULT hr;
  2298. if ( pfDidImmediateFinish != NULL )
  2299. {
  2300. *pfDidImmediateFinish = FALSE;
  2301. }
  2302. //
  2303. // If this is synchronous execution, then setup an event to signal
  2304. // upon the handler completion
  2305. //
  2306. if ( dwFlags & W3_FLAG_SYNC )
  2307. {
  2308. SetupCompletionEvent();
  2309. }
  2310. //
  2311. // Clear any response lingering
  2312. //
  2313. hr = QueryResponse()->Clear();
  2314. if ( FAILED( hr ) )
  2315. {
  2316. return hr;
  2317. }
  2318. //
  2319. // Redirect all completions to the current context (this)
  2320. //
  2321. QueryMainContext()->PushCurrentContext( this );
  2322. //
  2323. // Execute the handler
  2324. //
  2325. status = ExecuteCurrentHandler();
  2326. if ( status == CONTEXT_STATUS_CONTINUE )
  2327. {
  2328. //
  2329. // Remember not to execute completion routine for handler
  2330. //
  2331. DBG_ASSERT( _pHandler != NULL );
  2332. //
  2333. // We don't need the handler any more
  2334. //
  2335. delete _pHandler;
  2336. _pHandler = NULL;
  2337. //
  2338. // The handler completed synchronously. If async call was required
  2339. // then post a fake completion, unless the caller set
  2340. // pfDidImmediateFinish in which case we'll finish now and set the
  2341. // flag to TRUE
  2342. //
  2343. if ( dwFlags & W3_FLAG_ASYNC &&
  2344. pfDidImmediateFinish == NULL )
  2345. {
  2346. POST_MAIN_COMPLETION( QueryMainContext() );
  2347. }
  2348. else
  2349. {
  2350. //
  2351. // We indicate the "success/failure" of this child context
  2352. // to the parent context, so that the parent can send this
  2353. // info to ISAPIs if needed
  2354. //
  2355. if ( QueryParentContext() != NULL )
  2356. {
  2357. QueryResponse()->QuerySubError( &subError );
  2358. QueryParentContext()->SetChildStatusAndError(
  2359. QueryResponse()->QueryStatusCode(),
  2360. subError.mdSubError,
  2361. QueryErrorStatus() );
  2362. }
  2363. //
  2364. // Don't both signalling completion event since there is nothing
  2365. // to wait for. (Might revisit this decision later)
  2366. //
  2367. //
  2368. // Current context is no longer needed in completion stack
  2369. //
  2370. QueryMainContext()->PopCurrentContext();
  2371. //
  2372. // If caller wanted status, tell them now
  2373. //
  2374. if ( pfDidImmediateFinish != NULL )
  2375. {
  2376. *pfDidImmediateFinish = TRUE;
  2377. }
  2378. }
  2379. }
  2380. else
  2381. {
  2382. DBG_ASSERT( status == CONTEXT_STATUS_PENDING );
  2383. //
  2384. // The handler will complete asynchronsouly. But we are asked for
  2385. // synchronous execution. So wait here until complete
  2386. //
  2387. if ( dwFlags & W3_FLAG_SYNC )
  2388. {
  2389. WaitForCompletion();
  2390. }
  2391. }
  2392. return NO_ERROR;
  2393. }
  2394. CONTEXT_STATUS
  2395. W3_CONTEXT::ExecuteCurrentHandler(
  2396. VOID
  2397. )
  2398. /*++
  2399. Routine Description:
  2400. Execute the handler for this context
  2401. Arguments:
  2402. None
  2403. Return Value:
  2404. CONTEXT_STATUS_PENDING if async pending, else CONTEXT_STATUS_CONTINUE
  2405. --*/
  2406. {
  2407. HRESULT hr;
  2408. W3_TRACE_LOG * pTraceLog;
  2409. DBG_ASSERT( _pHandler != NULL );
  2410. pTraceLog = QueryMainContext()->QueryTraceLog();
  2411. if ( pTraceLog != NULL )
  2412. {
  2413. pTraceLog->Trace( L"%I64x: Starting execution of handler '%s'\n",
  2414. QueryRequest()->QueryRequestId(),
  2415. _pHandler->QueryName() );
  2416. }
  2417. //
  2418. // Access is allowed and no URL redirection. Let'r rip!
  2419. //
  2420. return _pHandler->MainDoWork();
  2421. }
  2422. CONTEXT_STATUS
  2423. W3_CONTEXT::ExecuteHandlerCompletion(
  2424. DWORD cbCompletion,
  2425. DWORD dwCompletionStatus
  2426. )
  2427. /*++
  2428. Routine Description:
  2429. Execute the current handler's completion.
  2430. Arguments:
  2431. cbCompletion - Completion bytes
  2432. dwCompletionStatus - Completion error
  2433. Return Value:
  2434. CONTEXT_STATUS_PENDING if async pending, else CONTEXT_STATUS_CONTINUE
  2435. --*/
  2436. {
  2437. CONTEXT_STATUS status;
  2438. W3_HANDLER * pHandler;
  2439. W3_CONTEXT * pParentContext;
  2440. DWORD dwChildStatus;
  2441. BOOL fAccessAllowed = FALSE;
  2442. HRESULT hr;
  2443. HTTP_SUB_ERROR subError;
  2444. W3_TRACE_LOG * pTraceLog;
  2445. //
  2446. // This is a completion for the handler.
  2447. //
  2448. // If this is a faked completion, the handler is already cleaned up
  2449. //
  2450. if ( _pHandler != NULL )
  2451. {
  2452. status = _pHandler->MainOnCompletion( cbCompletion,
  2453. dwCompletionStatus );
  2454. }
  2455. else
  2456. {
  2457. status = CONTEXT_STATUS_CONTINUE;
  2458. }
  2459. if ( status == CONTEXT_STATUS_PENDING )
  2460. {
  2461. return status;
  2462. }
  2463. else
  2464. {
  2465. //
  2466. // Cleanup the current context if necessary
  2467. //
  2468. DBG_ASSERT( status == CONTEXT_STATUS_CONTINUE );
  2469. //
  2470. // Current handler execute is complete. Delete it
  2471. //
  2472. if ( _pHandler != NULL )
  2473. {
  2474. delete _pHandler;
  2475. _pHandler = NULL;
  2476. }
  2477. //
  2478. // The current handler is complete. But if this is a child
  2479. // request we must first indicate handler completion to the caller
  2480. //
  2481. pParentContext = QueryParentContext();
  2482. if ( pParentContext != NULL )
  2483. {
  2484. //
  2485. // We indicate the "success/failure" of this child context
  2486. // to the parent context, so that the parent can send this
  2487. // info to ISAPIs if needed
  2488. //
  2489. QueryResponse()->QuerySubError( &subError );
  2490. pParentContext->SetChildStatusAndError(
  2491. QueryResponse()->QueryStatusCode(),
  2492. subError.mdSubError,
  2493. QueryErrorStatus() );
  2494. //
  2495. // Setup all future completions to indicate to parent
  2496. //
  2497. QueryMainContext()->PopCurrentContext();
  2498. //
  2499. // We are a child execute
  2500. //
  2501. if ( QueryIsSynchronous() )
  2502. {
  2503. IndicateCompletion();
  2504. //
  2505. // We assume the thread waiting on completion will be
  2506. // responsible for advancing the state machine
  2507. //
  2508. //
  2509. // The waiting thread will also cleanup the child context
  2510. //
  2511. return CONTEXT_STATUS_PENDING;
  2512. }
  2513. else
  2514. {
  2515. dwChildStatus = WIN32_FROM_HRESULT( QueryErrorStatus() );
  2516. //
  2517. // We can destroy the current context now. In the case of
  2518. // synchronous execution, it is the caller that cleans up
  2519. // the new context
  2520. //
  2521. delete this;
  2522. return pParentContext->ExecuteHandlerCompletion(
  2523. 0,
  2524. dwChildStatus );
  2525. }
  2526. }
  2527. pTraceLog = QueryMainContext()->QueryTraceLog();
  2528. if ( pTraceLog != NULL )
  2529. {
  2530. pTraceLog->Trace( L"%I64x: Handler completed\n",
  2531. QueryRequest()->QueryRequestId() );
  2532. }
  2533. return CONTEXT_STATUS_CONTINUE;
  2534. }
  2535. }
  2536. W3_CONTEXT::W3_CONTEXT(
  2537. DWORD dwExecFlags,
  2538. DWORD dwRecursionLevel
  2539. )
  2540. : _pHandler ( NULL ),
  2541. _hCompletion ( NULL ),
  2542. _errorStatus ( S_OK ),
  2543. _pCustomErrorFile ( NULL ),
  2544. _dwExecFlags ( dwExecFlags ),
  2545. _accessState ( ACCESS_STATE_START ),
  2546. _fDNSRequiredForAccess ( FALSE ),
  2547. _fAuthAccessCheckRequired ( TRUE ),
  2548. _childStatusCode ( 200 ),
  2549. _childSubErrorCode ( 0 ),
  2550. _childError ( S_OK ),
  2551. _fSuppressEntity ( FALSE ),
  2552. _pctVrToken ( NULL ),
  2553. _dwRecursionLevel ( dwRecursionLevel )
  2554. {
  2555. _dwSignature = W3_CONTEXT_SIGNATURE;
  2556. }
  2557. W3_CONTEXT::~W3_CONTEXT()
  2558. {
  2559. _dwSignature = W3_CONTEXT_SIGNATURE_FREE;
  2560. if ( _pHandler != NULL )
  2561. {
  2562. delete _pHandler;
  2563. _pHandler = NULL;
  2564. }
  2565. if ( _hCompletion != NULL )
  2566. {
  2567. CloseHandle( _hCompletion );
  2568. _hCompletion = NULL;
  2569. }
  2570. if ( _pCustomErrorFile != NULL )
  2571. {
  2572. _pCustomErrorFile->DereferenceCacheEntry();
  2573. _pCustomErrorFile = NULL;
  2574. }
  2575. if( _pctVrToken != NULL )
  2576. {
  2577. _pctVrToken->DereferenceCacheEntry();
  2578. _pctVrToken = NULL;
  2579. }
  2580. }
  2581. // static
  2582. HRESULT
  2583. W3_CONTEXT::Initialize(
  2584. VOID
  2585. )
  2586. /*++
  2587. Routine Description:
  2588. Global initialization routine for W3_CONTEXTs
  2589. Arguments:
  2590. None
  2591. Return Value:
  2592. HRESULT
  2593. --*/
  2594. {
  2595. HRESULT hr = NO_ERROR;
  2596. HKEY hKey = NULL;
  2597. DWORD dwError;
  2598. DWORD dwType;
  2599. DWORD cbBuffer;
  2600. BYTE bUnused;
  2601. //
  2602. // Read in the 302 message once
  2603. //
  2604. DBG_ASSERT( g_pW3Server != NULL );
  2605. sm_cbRedirectMessage = sizeof( sm_achRedirectMessage );
  2606. hr = g_pW3Server->LoadString( IDS_URL_MOVED,
  2607. sm_achRedirectMessage,
  2608. &sm_cbRedirectMessage );
  2609. if ( FAILED( hr ) )
  2610. {
  2611. return hr;
  2612. }
  2613. DBG_ASSERT( sm_cbRedirectMessage > 0 );
  2614. //
  2615. // Read the Access-Denied message from registry
  2616. //
  2617. dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  2618. W3_PARAMETERS_KEY,
  2619. 0,
  2620. KEY_READ,
  2621. &hKey );
  2622. if ( dwError == ERROR_SUCCESS )
  2623. {
  2624. DBG_ASSERT( hKey != NULL );
  2625. cbBuffer = 0;
  2626. dwType = 0;
  2627. dwError = RegQueryValueExA( hKey,
  2628. "AccessDeniedMessage",
  2629. NULL,
  2630. &dwType,
  2631. &bUnused,
  2632. &cbBuffer );
  2633. if ( dwError == ERROR_MORE_DATA && dwType == REG_SZ )
  2634. {
  2635. DBG_ASSERT( cbBuffer > 0 );
  2636. sm_pszAccessDeniedMessage = (CHAR*) LocalAlloc( LPTR, cbBuffer );
  2637. if ( sm_pszAccessDeniedMessage == NULL )
  2638. {
  2639. return HRESULT_FROM_WIN32( GetLastError() );
  2640. }
  2641. dwError = RegQueryValueExA( hKey,
  2642. "AccessDeniedMessage",
  2643. NULL,
  2644. NULL,
  2645. (LPBYTE) sm_pszAccessDeniedMessage,
  2646. &cbBuffer );
  2647. if ( dwError != ERROR_SUCCESS )
  2648. {
  2649. DBG_ASSERT( FALSE );
  2650. LocalFree( sm_pszAccessDeniedMessage );
  2651. sm_pszAccessDeniedMessage = NULL;
  2652. }
  2653. else
  2654. {
  2655. //
  2656. // Zero terminate the string just in case the string we got
  2657. // wasn't (tres lame)
  2658. //
  2659. DBG_ASSERT( sm_pszAccessDeniedMessage != NULL );
  2660. sm_pszAccessDeniedMessage[ cbBuffer - 1 ] = '\0';
  2661. }
  2662. }
  2663. }
  2664. //
  2665. // Setup child contexts
  2666. //
  2667. hr = W3_CHILD_CONTEXT::Initialize();
  2668. if ( FAILED( hr ) )
  2669. {
  2670. return hr;
  2671. }
  2672. //
  2673. // Setup main contexts
  2674. //
  2675. hr = W3_MAIN_CONTEXT::Initialize();
  2676. if ( FAILED( hr ) )
  2677. {
  2678. W3_CHILD_CONTEXT::Terminate();
  2679. return hr;
  2680. }
  2681. //
  2682. // Setup execute contexts
  2683. //
  2684. hr = EXECUTE_CONTEXT::Initialize();
  2685. if ( FAILED( hr ) )
  2686. {
  2687. W3_MAIN_CONTEXT::Terminate();
  2688. W3_CHILD_CONTEXT::Terminate();
  2689. return hr;
  2690. }
  2691. return NO_ERROR;
  2692. }
  2693. // static
  2694. VOID
  2695. W3_CONTEXT::Terminate(
  2696. VOID
  2697. )
  2698. /*++
  2699. Routine Description:
  2700. Terminate W3_CONTEXT globals
  2701. Arguments:
  2702. None
  2703. Return Value:
  2704. None
  2705. --*/
  2706. {
  2707. EXECUTE_CONTEXT::Terminate();
  2708. W3_CHILD_CONTEXT::Terminate();
  2709. W3_MAIN_CONTEXT::Terminate();
  2710. if ( sm_pszAccessDeniedMessage != NULL )
  2711. {
  2712. LocalFree( sm_pszAccessDeniedMessage );
  2713. sm_pszAccessDeniedMessage = NULL;
  2714. }
  2715. }
  2716. //static
  2717. VOID
  2718. W3_CONTEXT::OnCleanIsapiExecuteUrl(
  2719. DWORD dwCompletionStatus,
  2720. DWORD cbWritten,
  2721. LPOVERLAPPED lpo
  2722. )
  2723. /*++
  2724. Routine Description:
  2725. Actually calls ExecuteHandler() on behalf of a thread which may have
  2726. already been CoInited(). The thread this this guy runs on should not
  2727. have been (COM sucks)
  2728. Arguments:
  2729. dwCompletionStatus - Completion status (ignored)
  2730. cbWritten - Bytes written (ignored)
  2731. lpo - Pointer to EXECUTE_CONTEXT which contains info needed for
  2732. ExecuteHandler() call
  2733. Return Value:
  2734. None
  2735. --*/
  2736. {
  2737. EXECUTE_CONTEXT * pExecuteContext;
  2738. W3_CONTEXT * pW3Context;
  2739. HRESULT hr;
  2740. DBG_ASSERT( lpo != NULL );
  2741. pExecuteContext = (EXECUTE_CONTEXT*) lpo;
  2742. DBG_ASSERT( pExecuteContext->CheckSignature() );
  2743. pW3Context = pExecuteContext->QueryW3Context();
  2744. DBG_ASSERT( pW3Context != NULL );
  2745. DBG_ASSERT( pW3Context->CheckSignature() );
  2746. //
  2747. // Make the call (this is an async call)
  2748. //
  2749. hr = pW3Context->IsapiExecuteUrl( pExecuteContext->QueryExecUrlInfo() );
  2750. delete pExecuteContext;
  2751. if ( FAILED( hr ) )
  2752. {
  2753. //
  2754. // We failed before posting async operation. It is up to us to
  2755. // fake a completion
  2756. //
  2757. DBG_ASSERT( pW3Context->QueryHandler() != NULL );
  2758. pW3Context->SetChildStatusAndError( HttpStatusServerError.statusCode,
  2759. 0,
  2760. hr );
  2761. pW3Context->QueryHandler()->MainOnCompletion( 0,
  2762. ERROR_SUCCESS );
  2763. }
  2764. }
  2765. //static
  2766. VOID
  2767. W3_CONTEXT::OnCleanIsapiSendCustomError(
  2768. DWORD dwCompletionStatus,
  2769. DWORD cbWritten,
  2770. LPOVERLAPPED lpo
  2771. )
  2772. /*++
  2773. Routine Description:
  2774. Actually calls ExecuteHandler() on behalf of a thread which may have
  2775. already been CoInited(). The thread this this guy runs on should not
  2776. have been (COM sucks)
  2777. Arguments:
  2778. dwCompletionStatus - Completion status (ignored)
  2779. cbWritten - Bytes written (ignored)
  2780. lpo - Pointer to SEND_CUSTOM_ERROR_CONTEXT which contains info needed for
  2781. ExecuteHandler() call
  2782. Return Value:
  2783. None
  2784. --*/
  2785. {
  2786. EXECUTE_CONTEXT * pExecuteContext;
  2787. W3_CONTEXT * pW3Context;
  2788. HRESULT hr;
  2789. BOOL fAsync;
  2790. DBG_ASSERT( lpo != NULL );
  2791. pExecuteContext = (EXECUTE_CONTEXT*) lpo;
  2792. DBG_ASSERT( pExecuteContext->CheckSignature() );
  2793. pW3Context = pExecuteContext->QueryW3Context();
  2794. DBG_ASSERT( pW3Context != NULL );
  2795. DBG_ASSERT( pW3Context->CheckSignature() );
  2796. fAsync = pExecuteContext->QueryCompleteEvent() == NULL;
  2797. hr = pW3Context->SendResponse( fAsync ? W3_FLAG_ASYNC : W3_FLAG_SYNC );
  2798. delete pExecuteContext;
  2799. if ( FAILED( hr ) )
  2800. {
  2801. if ( fAsync )
  2802. {
  2803. //
  2804. // We failed before posting async operation. It is up to us to
  2805. // fake a completion
  2806. //
  2807. DBG_ASSERT( pW3Context->QueryHandler() != NULL );
  2808. pW3Context->SetErrorStatus( hr );
  2809. pW3Context->QueryHandler()->MainOnCompletion( 0,
  2810. WIN32_FROM_HRESULT( hr ) );
  2811. }
  2812. }
  2813. }
  2814. CONTEXT_STATUS
  2815. W3_CONTEXT::CheckAccess(
  2816. BOOL fCompletion,
  2817. DWORD cbCompletion,
  2818. DWORD dwCompletionStatus,
  2819. BOOL * pfAccessAllowed
  2820. )
  2821. /*++
  2822. Routine Description:
  2823. Check whether the given request is allowed for the current metadata
  2824. settings. In particular check:
  2825. 1) Is SSL required?
  2826. 2) Are IP address restrictions in force?
  2827. 3) Is a client certificate required?
  2828. 4) Is the authentication mechanism allowed? Optionally
  2829. If access is not allowed, then this routine will send the appropriate
  2830. response asychronously
  2831. Arguments:
  2832. fCompletion - Are we being called on a completion (i.e.
  2833. is this a subsequent call to CheckAccess())
  2834. cbCompletion - number of bytes completed
  2835. dwCompletionStatus - Completion status (if fCompletion is TRUE)
  2836. pfAccessAllowed - Set to TRUE if access is allowed, else FALSE
  2837. Return Value:
  2838. CONTEXT_STATUS_PENDING if we're not finished the check yet
  2839. CONTEXT_STATUS_CONTINUE if we are finished.
  2840. --*/
  2841. {
  2842. W3_REQUEST * pRequest;
  2843. W3_METADATA * pMetaData;
  2844. URL_CONTEXT * pUrlContext;
  2845. HTTP_SSL_INFO * pSslInfo;
  2846. HTTP_SSL_CLIENT_CERT_INFO * pClientCertInfo = NULL;
  2847. DWORD dwAccessPerms;
  2848. HRESULT hr = NO_ERROR;
  2849. BOOL fAccessAllowed = TRUE;
  2850. ADDRESS_CHECK * pAddressCheck = NULL;
  2851. AC_RESULT acResult;
  2852. BOOL fSyncDNS = FALSE;
  2853. LPSTR pszTemp;
  2854. BOOL fDoCertMap;
  2855. BOOL fFilterFinished = FALSE;
  2856. if ( pfAccessAllowed == NULL )
  2857. {
  2858. DBG_ASSERT( FALSE );
  2859. SetErrorStatus( HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ) );
  2860. return CONTEXT_STATUS_CONTINUE;
  2861. }
  2862. *pfAccessAllowed = FALSE;
  2863. pRequest = QueryRequest();
  2864. DBG_ASSERT( pRequest != NULL );
  2865. pUrlContext = QueryUrlContext();
  2866. DBG_ASSERT( pUrlContext != NULL );
  2867. pMetaData = pUrlContext->QueryMetaData();
  2868. DBG_ASSERT( pMetaData != NULL );
  2869. dwAccessPerms = pMetaData->QueryAccessPerms();
  2870. switch ( _accessState )
  2871. {
  2872. case ACCESS_STATE_SENDING_ERROR:
  2873. //
  2874. // We have sent an error. Just chill
  2875. //
  2876. fAccessAllowed = FALSE;
  2877. break;
  2878. case ACCESS_STATE_START:
  2879. //
  2880. // Is SSL required?
  2881. //
  2882. if ( dwAccessPerms & VROOT_MASK_SSL &&
  2883. !QueryRequest()->IsSecureRequest() )
  2884. {
  2885. QueryResponse()->SetStatus( HttpStatusForbidden,
  2886. Http403SSLRequired );
  2887. SetErrorStatus( HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) );
  2888. goto AccessDenied;
  2889. }
  2890. //
  2891. // Is 128bit required?
  2892. //
  2893. if ( dwAccessPerms & VROOT_MASK_SSL128 )
  2894. {
  2895. pSslInfo = QueryRequest()->QuerySslInfo();
  2896. if ( pSslInfo == NULL ||
  2897. pSslInfo->ConnectionKeySize < 128 )
  2898. {
  2899. QueryResponse()->SetStatus( HttpStatusForbidden,
  2900. Http403SSL128Required );
  2901. SetErrorStatus( HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) );
  2902. goto AccessDenied;
  2903. }
  2904. }
  2905. //
  2906. // Fall through to next phase in access check
  2907. //
  2908. case ACCESS_STATE_CLIENT_CERT_PRELOAD_ENTITY:
  2909. _accessState = ACCESS_STATE_CLIENT_CERT_PRELOAD_ENTITY;
  2910. //
  2911. // For the client certificate renegotiation to succeed
  2912. // it is necessary to assure that client is not blocked
  2913. // on sending entity body. That's why if client certificate
  2914. // is not present and client renegotiation is requested
  2915. // it is necessary to preload request body
  2916. // SSL preload will use EntityReadAhead() value used for ISAPI
  2917. // The only difference is that if ReadAhead is >= Content length then
  2918. // http error 413 is returned and connection is closed to prevent
  2919. // deadlock (client waiting to complete request entity, while
  2920. // server is waiting for renegotiation to complete, but renegotiation
  2921. // requires client to be able to send data)
  2922. if ( dwAccessPerms & VROOT_MASK_NEGO_CERT &&
  2923. QueryRequest()->IsSecureRequest() )
  2924. {
  2925. //
  2926. // If we don't already have client cert negotiated
  2927. // then entity body preload is necessary
  2928. //
  2929. pSslInfo = QueryRequest()->QuerySslInfo();
  2930. if ( pSslInfo == NULL ||
  2931. !pSslInfo->SslClientCertNegotiated )
  2932. {
  2933. if ( fCompletion )
  2934. {
  2935. fCompletion = FALSE;
  2936. //
  2937. // If we got an error, that's OK. We will fall thru
  2938. // and check whether we actually need a client cert
  2939. //
  2940. if ( dwCompletionStatus == NO_ERROR )
  2941. {
  2942. BOOL fComplete = FALSE;
  2943. //
  2944. // This completion is for entity body preload
  2945. //
  2946. hr = pRequest->PreloadCompletion(this,
  2947. cbCompletion,
  2948. dwCompletionStatus,
  2949. &fComplete);
  2950. //
  2951. // If we cannot read the request entity, we will assume it is the
  2952. // client's fault
  2953. //
  2954. if ( FAILED( hr ) )
  2955. {
  2956. SetErrorStatus( hr );
  2957. QueryResponse()->SetStatus( HttpStatusServerError );
  2958. goto AccessDenied;
  2959. }
  2960. if (!fComplete)
  2961. {
  2962. return CONTEXT_STATUS_PENDING;
  2963. }
  2964. W3_METADATA *pMetadata = QueryUrlContext()->QueryMetaData();
  2965. DWORD cbConfiguredReadAhead = pMetadata->QueryEntityReadAhead();
  2966. //
  2967. // If Content-Length of the request exceeds
  2968. // configured ReadAhead size
  2969. // and client certificate is not yet present
  2970. // then we have to return error and close connection
  2971. // to prevent client cert renegotiation deadlock
  2972. //
  2973. if ( pRequest->QueryAvailableBytes() >= cbConfiguredReadAhead )
  2974. {
  2975. //
  2976. // This code path should be hit only with chunk encoded request
  2977. // entity body.
  2978. // Make QueryRemainingEntityFromUl call to figure out if
  2979. // for we have read all the request data
  2980. // for the chunk encoded request
  2981. //
  2982. if ( QueryRemainingEntityFromUl() != 0 )
  2983. {
  2984. //
  2985. // There are still some outstanding chunks.
  2986. // This means error
  2987. //
  2988. QueryResponse()->SetStatus( HttpStatusEntityTooLarge );
  2989. SetDisconnect( TRUE );
  2990. goto AccessDenied;
  2991. }
  2992. }
  2993. //
  2994. // all the data has been preloaded. We can fall through to next state
  2995. //
  2996. }
  2997. }
  2998. else
  2999. {
  3000. BOOL fComplete = FALSE;
  3001. W3_REQUEST *pRequest = QueryRequest();
  3002. DBG_ASSERT( pRequest != NULL );
  3003. //
  3004. // Preload entity to eliminate blocking on
  3005. // client cert renegotiation when long entity body
  3006. // is sent from client
  3007. //
  3008. LPCSTR pszContentLength =
  3009. pRequest->GetHeader( HttpHeaderContentLength );
  3010. W3_METADATA *pMetadata = QueryUrlContext()->QueryMetaData();
  3011. DWORD cbConfiguredReadAhead = pMetadata->QueryEntityReadAhead();
  3012. //
  3013. // If Content-Length of the request exceeds
  3014. // configured ReadAhead size
  3015. // and client certificate is not yet present
  3016. // then we have to return error and close connection
  3017. // to prevent client cert renegotiation deadlock
  3018. //
  3019. //
  3020. // if pszContentLength is NULL it means that
  3021. // request entity body is chunked encoded
  3022. // in that case we have to preload entity body
  3023. // to find out it's length.
  3024. if ( pszContentLength != NULL &&
  3025. _strtoi64( pszContentLength, NULL, 10 ) > cbConfiguredReadAhead )
  3026. {
  3027. QueryResponse()->SetStatus( HttpStatusEntityTooLarge );
  3028. SetDisconnect( TRUE );
  3029. goto AccessDenied;
  3030. }
  3031. hr = pRequest->PreloadEntityBody( this,
  3032. &fComplete );
  3033. //
  3034. // If we cannot read the request entity, we will assume it is the
  3035. // client's fault
  3036. //
  3037. if ( FAILED( hr ) )
  3038. {
  3039. SetErrorStatus( hr );
  3040. QueryResponse()->SetStatus( HttpStatusServerError );
  3041. goto AccessDenied;
  3042. }
  3043. if ( !fComplete )
  3044. {
  3045. //
  3046. // Async read pending. Just bail
  3047. //
  3048. return CONTEXT_STATUS_PENDING;
  3049. }
  3050. if ( pRequest->QueryAvailableBytes() >= cbConfiguredReadAhead )
  3051. {
  3052. //
  3053. // This code path should be hit only with chunk encoded request
  3054. // entity body
  3055. //
  3056. QueryResponse()->SetStatus( HttpStatusEntityTooLarge );
  3057. SetDisconnect( TRUE );
  3058. goto AccessDenied;
  3059. }
  3060. //
  3061. // all the data has been preloaded. We can fall through to next state
  3062. //
  3063. }
  3064. }
  3065. }
  3066. //
  3067. // Fall through to next phase in access check
  3068. //
  3069. case ACCESS_STATE_CLIENT_CERT:
  3070. _accessState = ACCESS_STATE_CLIENT_CERT;
  3071. //
  3072. // Is a client certificate possible? First check is we even allow
  3073. // a client cert to be negotiated and that this is a secure request
  3074. //
  3075. if ( dwAccessPerms & VROOT_MASK_NEGO_CERT &&
  3076. QueryRequest()->IsSecureRequest() )
  3077. {
  3078. //
  3079. // Try for a client cert if we don't already have one associated
  3080. // with the request
  3081. //
  3082. if ( QueryCertificateContext() == NULL )
  3083. {
  3084. if ( fCompletion )
  3085. {
  3086. fCompletion = FALSE;
  3087. //
  3088. // If we got an error, that's OK. We will fall thru
  3089. // and check whether we actually need a client cert
  3090. //
  3091. if ( dwCompletionStatus == NO_ERROR )
  3092. {
  3093. //
  3094. // Are we cert mapping?
  3095. //
  3096. fDoCertMap = !!(dwAccessPerms & VROOT_MASK_MAP_CERT);
  3097. //
  3098. // All is well. Make a synchronous call to get
  3099. // the certificate
  3100. //
  3101. hr = UlAtqReceiveClientCertificate(
  3102. QueryUlatqContext(),
  3103. FALSE, // sync
  3104. fDoCertMap,
  3105. &pClientCertInfo );
  3106. if ( FAILED( hr ) )
  3107. {
  3108. QueryResponse()->SetStatus( HttpStatusServerError );
  3109. goto AccessDenied;
  3110. }
  3111. DBG_ASSERT( pClientCertInfo != NULL );
  3112. //
  3113. // Setup a client cert context for this request
  3114. //
  3115. hr = QueryMainContext()->SetupCertificateContext( pClientCertInfo );
  3116. if ( FAILED( hr ) )
  3117. {
  3118. QueryResponse()->SetStatus( HttpStatusServerError );
  3119. goto AccessDenied;
  3120. }
  3121. //
  3122. // Just because we got a client cert, doesn't mean
  3123. // it is acceptable. It might be revoked, time
  3124. // expired, etc. The policy of what to do when
  3125. // certs are non-totally-valid is metabase driven.
  3126. // (in case you are wondering why the stream filter
  3127. // doesn't do these checks and just fail the
  3128. // renegotiation)
  3129. //
  3130. if ( !CheckClientCertificateAccess() )
  3131. {
  3132. goto AccessDenied;
  3133. }
  3134. }
  3135. }
  3136. else
  3137. {
  3138. //
  3139. // Are we cert mapping?
  3140. //
  3141. fDoCertMap = !!(dwAccessPerms & VROOT_MASK_MAP_CERT);
  3142. //
  3143. // First time asking for a client cert. Do it
  3144. // async
  3145. //
  3146. fCompletion = FALSE;
  3147. hr = UlAtqReceiveClientCertificate(
  3148. QueryUlatqContext(),
  3149. TRUE, // async
  3150. fDoCertMap,
  3151. &pClientCertInfo );
  3152. if ( FAILED( hr ) )
  3153. {
  3154. QueryResponse()->SetStatus( HttpStatusServerError );
  3155. goto AccessDenied;
  3156. }
  3157. return CONTEXT_STATUS_PENDING;
  3158. }
  3159. }
  3160. }
  3161. //
  3162. // Ok. We're done the certificate crud. If we needed a client
  3163. // cert and didn't get it, then setup 403.7
  3164. //
  3165. if ( dwAccessPerms & VROOT_MASK_NEGO_MANDATORY &&
  3166. QueryCertificateContext() == NULL )
  3167. {
  3168. QueryResponse()->SetStatus( HttpStatusForbidden,
  3169. Http403CertRequired );
  3170. SetErrorStatus( HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) );
  3171. goto AccessDenied;
  3172. }
  3173. //
  3174. // Fall through to next phase in access check
  3175. //
  3176. case ACCESS_STATE_RDNS:
  3177. _accessState = ACCESS_STATE_RDNS;
  3178. if( pRequest->QueryRemoteAddressType() == AF_INET )
  3179. {
  3180. SOCKADDR_IN remoteAddress;
  3181. pAddressCheck = QueryMainContext()->QueryAddressCheck();
  3182. DBG_ASSERT( pAddressCheck != NULL );
  3183. if ( !fCompletion )
  3184. {
  3185. if ( pMetaData->QueryIpAccessCheckSize() != 0 ||
  3186. pMetaData->QueryDoReverseDNS() )
  3187. {
  3188. //
  3189. // Setup the RDNS crud (joy)
  3190. //
  3191. remoteAddress.sin_family = AF_INET;
  3192. remoteAddress.sin_port = QueryRequest()->QueryRemotePort();
  3193. remoteAddress.sin_addr.s_addr = QueryRequest()->QueryIPv4RemoteAddress();
  3194. ZeroMemory( remoteAddress.sin_zero, sizeof( remoteAddress.sin_zero ) );
  3195. pAddressCheck->BindAddr( (sockaddr*) &remoteAddress );
  3196. }
  3197. //
  3198. // Ok. If there is an access check set in metabase, then
  3199. // do the check.
  3200. //
  3201. if ( pMetaData->QueryIpAccessCheckSize() != 0 )
  3202. {
  3203. //
  3204. // Set the metadata IP blob
  3205. //
  3206. pAddressCheck->BindCheckList(
  3207. pMetaData->QueryIpAccessCheckBuffer(),
  3208. pMetaData->QueryIpAccessCheckSize() );
  3209. //
  3210. // Check access
  3211. //
  3212. acResult = pAddressCheck->CheckIpAccess( &_fDNSRequiredForAccess );
  3213. if ( !_fDNSRequiredForAccess )
  3214. {
  3215. pAddressCheck->UnbindCheckList();
  3216. }
  3217. }
  3218. else
  3219. {
  3220. //
  3221. // Fake a valid access check since there was no checks
  3222. // configured in metabase
  3223. //
  3224. acResult = AC_NO_LIST;
  3225. }
  3226. //
  3227. // Check if we now know our access status now
  3228. //
  3229. if ( acResult == AC_IN_DENY_LIST ||
  3230. ( acResult == AC_NOT_IN_GRANT_LIST &&
  3231. !_fDNSRequiredForAccess ) )
  3232. {
  3233. //
  3234. // We know we're rejected
  3235. //
  3236. QueryResponse()->SetStatus( HttpStatusForbidden,
  3237. Http403IPAddressReject );
  3238. goto AccessDenied;
  3239. }
  3240. //
  3241. // Do we need to do a DNS check to determine access?
  3242. // Do we need to do DNS check for logging/servervar purposes?
  3243. //
  3244. // In either case, we will do an async DNS check
  3245. //
  3246. if ( _fDNSRequiredForAccess ||
  3247. pMetaData->QueryDoReverseDNS() )
  3248. {
  3249. fSyncDNS = TRUE;
  3250. if ( !pAddressCheck->QueryDnsName( &fSyncDNS,
  3251. W3_MAIN_CONTEXT::AddressResolutionCallback,
  3252. QueryMainContext(),
  3253. &pszTemp ) )
  3254. {
  3255. //
  3256. // Only error if DNS was required for access check
  3257. // purposes
  3258. //
  3259. if ( _fDNSRequiredForAccess )
  3260. {
  3261. QueryResponse()->SetStatus( HttpStatusForbidden,
  3262. Http403IPAddressReject );
  3263. goto AccessDenied;
  3264. }
  3265. }
  3266. if ( fSyncDNS )
  3267. {
  3268. //
  3269. // Fake a completion if needed. This just prevents us from
  3270. // posting one more time to the thread pool
  3271. //
  3272. fCompletion = TRUE;
  3273. }
  3274. else
  3275. {
  3276. return CONTEXT_STATUS_PENDING;
  3277. }
  3278. }
  3279. }
  3280. if ( fCompletion )
  3281. {
  3282. fCompletion = FALSE;
  3283. //
  3284. // This is the completion for DNS check
  3285. //
  3286. if ( _fDNSRequiredForAccess )
  3287. {
  3288. _fDNSRequiredForAccess = FALSE;
  3289. acResult = pAddressCheck->CheckDnsAccess();
  3290. pAddressCheck->UnbindCheckList();
  3291. if ( acResult == AC_NOT_CHECKED ||
  3292. acResult == AC_IN_DENY_LIST ||
  3293. acResult == AC_NOT_IN_GRANT_LIST )
  3294. {
  3295. QueryResponse()->SetStatus( HttpStatusForbidden,
  3296. Http403IPAddressReject );
  3297. goto AccessDenied;
  3298. }
  3299. }
  3300. }
  3301. }
  3302. else
  3303. {
  3304. DBG_ASSERT( pRequest->QueryRemoteAddressType() == AF_INET6 );
  3305. }
  3306. //
  3307. // Fall through to next phase in access check
  3308. //
  3309. case ACCESS_STATE_AUTHENTICATION:
  3310. //
  3311. // Is the authentication method valid? We may not have a user
  3312. // context available at this point if this was an early
  3313. // custom error URL invocation
  3314. //
  3315. _accessState = ACCESS_STATE_AUTHENTICATION;
  3316. if ( QueryAuthAccessCheckRequired() &&
  3317. QueryUserContext() != NULL )
  3318. {
  3319. //
  3320. // Check if authentication scheme matches or cert mapping is enabled
  3321. //
  3322. if ( !( pMetaData->QueryAuthentication() & QueryUserContext()->QueryAuthType() ||
  3323. ( ( pMetaData->QuerySslAccessPerms() & MD_ACCESS_MAP_CERT ) &&
  3324. QueryUserContext()->QueryAuthType() == MD_ACCESS_MAP_CERT ) ) )
  3325. {
  3326. QueryResponse()->SetStatus( HttpStatusUnauthorized,
  3327. Http401Config );
  3328. SetErrorStatus( HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) );
  3329. goto AccessDenied;
  3330. }
  3331. }
  3332. }
  3333. //
  3334. // We know the access status (allowed or denied)
  3335. //
  3336. _accessState = ACCESS_STATE_DONE;
  3337. *pfAccessAllowed = fAccessAllowed;
  3338. return CONTEXT_STATUS_CONTINUE;
  3339. AccessDenied:
  3340. *pfAccessAllowed = FALSE;
  3341. _accessState = ACCESS_STATE_SENDING_ERROR;
  3342. //
  3343. // Send back the bad access response
  3344. //
  3345. hr = SendResponse( W3_FLAG_ASYNC );
  3346. if ( FAILED( hr ) )
  3347. {
  3348. _accessState = ACCESS_STATE_DONE;
  3349. return CONTEXT_STATUS_CONTINUE;
  3350. }
  3351. else
  3352. {
  3353. return CONTEXT_STATUS_PENDING;
  3354. }
  3355. }
  3356. CERTIFICATE_CONTEXT *
  3357. W3_CONTEXT::QueryCertificateContext(
  3358. VOID
  3359. )
  3360. /*++
  3361. Routine Description:
  3362. Get client cert info object. We get it from the main context
  3363. Arguments:
  3364. None
  3365. Return Value:
  3366. Pointer to client cert object
  3367. --*/
  3368. {
  3369. return QueryMainContext()->QueryCertificateContext();
  3370. }
  3371. BOOL
  3372. W3_CONTEXT::CheckClientCertificateAccess(
  3373. VOID
  3374. )
  3375. /*++
  3376. Routine Description:
  3377. Check the context's certificate for validity
  3378. Arguments:
  3379. None
  3380. Return Value:
  3381. TRUE if the cert is valid, else FALSE
  3382. --*/
  3383. {
  3384. W3_MAIN_CONTEXT * pMainContext;
  3385. CERTIFICATE_CONTEXT * pCertificateContext;
  3386. DWORD dwCertError;
  3387. pMainContext = QueryMainContext();
  3388. DBG_ASSERT( pMainContext != NULL );
  3389. pCertificateContext = pMainContext->QueryCertificateContext();
  3390. if ( pCertificateContext == NULL )
  3391. {
  3392. return FALSE;
  3393. }
  3394. dwCertError = pCertificateContext->QueryCertError();
  3395. if ( dwCertError == ERROR_SUCCESS )
  3396. {
  3397. return TRUE;
  3398. }
  3399. SetErrorStatus( (HRESULT) dwCertError );
  3400. if ( dwCertError == CERT_E_EXPIRED )
  3401. {
  3402. QueryResponse()->SetStatus( HttpStatusForbidden,
  3403. Http403CertTimeInvalid );
  3404. return FALSE;
  3405. }
  3406. else if ( dwCertError == CERT_E_REVOKED ||
  3407. dwCertError == CRYPT_E_REVOKED ||
  3408. dwCertError == CRYPT_E_REVOCATION_OFFLINE ||
  3409. dwCertError == CERT_E_REVOCATION_FAILURE )
  3410. {
  3411. QueryResponse()->SetStatus( HttpStatusForbidden,
  3412. Http403CertRevoked );
  3413. return FALSE;
  3414. }
  3415. else if ( dwCertError == CRYPT_E_NO_REVOCATION_CHECK )
  3416. {
  3417. // If client cert has no CDP we will not take it as error
  3418. // CRYPT_E_NO_REVOCATION_CHECK is returned in that case
  3419. return TRUE;
  3420. }
  3421. else
  3422. {
  3423. QueryResponse()->SetStatus( HttpStatusForbidden,
  3424. Http403CertInvalid );
  3425. return FALSE;
  3426. }
  3427. return TRUE;
  3428. }
  3429. HRESULT
  3430. W3_CONTEXT::CheckPathInfoExists(
  3431. W3_HANDLER ** ppHandler
  3432. )
  3433. /*++
  3434. Routine Description:
  3435. Utility to check whether path info exists. If it does exist, this
  3436. function succeeds without setting the handler.
  3437. If the file doesn't exist, this function succeeds but sets a general
  3438. handler to send the error
  3439. Arguments:
  3440. ppHandler - Set to handler
  3441. Return Value:
  3442. HRESULT
  3443. --*/
  3444. {
  3445. HRESULT hr = NO_ERROR;
  3446. W3_FILE_INFO * pOpenFile = NULL;
  3447. W3_HANDLER * pHandler = NULL;
  3448. URL_CONTEXT * pUrlContext;
  3449. CACHE_USER fileUser;
  3450. if ( ppHandler != NULL )
  3451. {
  3452. *ppHandler = NULL;
  3453. }
  3454. pUrlContext = QueryUrlContext();
  3455. DBG_ASSERT( pUrlContext != NULL );
  3456. QueryFileCacheUser( &fileUser );
  3457. //
  3458. // Access the file cache, but only for existence
  3459. //
  3460. hr = pUrlContext->OpenFile( &fileUser,
  3461. &pOpenFile,
  3462. NULL,
  3463. NULL,
  3464. FALSE,
  3465. TRUE );
  3466. if ( FAILED( hr ) )
  3467. {
  3468. DBG_ASSERT( pOpenFile == NULL );
  3469. if ( ppHandler == NULL )
  3470. {
  3471. goto Exit;
  3472. }
  3473. switch ( WIN32_FROM_HRESULT( hr ) )
  3474. {
  3475. case ERROR_FILE_NOT_FOUND:
  3476. case ERROR_PATH_NOT_FOUND:
  3477. case ERROR_INVALID_NAME:
  3478. pHandler = new W3_GENERAL_HANDLER( this,
  3479. HttpStatusNotFound );
  3480. break;
  3481. case ERROR_INVALID_PARAMETER:
  3482. pHandler = new W3_GENERAL_HANDLER( this,
  3483. HttpStatusUrlTooLong );
  3484. break;
  3485. case ERROR_ACCESS_DENIED:
  3486. case ERROR_ACCOUNT_DISABLED:
  3487. case ERROR_LOGON_FAILURE:
  3488. pHandler = new W3_GENERAL_HANDLER( this,
  3489. HttpStatusUnauthorized,
  3490. Http401Application );
  3491. break;
  3492. default:
  3493. pHandler = new W3_GENERAL_HANDLER( this,
  3494. HttpStatusServerError );
  3495. break;
  3496. }
  3497. if ( pHandler == NULL )
  3498. {
  3499. hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  3500. SetErrorStatus( hr );
  3501. }
  3502. else
  3503. {
  3504. SetErrorStatus( hr );
  3505. hr = NO_ERROR;
  3506. }
  3507. }
  3508. else
  3509. {
  3510. //
  3511. // We just wanted to check for existence, no need to keep the file
  3512. //
  3513. DBG_ASSERT( pOpenFile != NULL );
  3514. pOpenFile->DereferenceCacheEntry();
  3515. }
  3516. if ( ppHandler != NULL )
  3517. {
  3518. *ppHandler = pHandler;
  3519. }
  3520. Exit:
  3521. return hr;
  3522. }
  3523. HRESULT
  3524. W3_CONTEXT::ValidateAppPool(
  3525. BOOL * pfAppPoolValid
  3526. )
  3527. /*++
  3528. Routine Description:
  3529. Validate whether the apppool for the URL is allowed. Basically this
  3530. is code to ensure that filters/extensions/config don't attempt to
  3531. execute a URL outside this processes' AppPool space.
  3532. This problem is largely complicated by the fact WAS may route to an
  3533. apppool contrary to the URL config, if the AppPool in config is not
  3534. valid. Life is full of lame complications I guess.
  3535. Arguments:
  3536. pfAppPoolValid - Set to TRUE if AppPool is valid
  3537. Return Value:
  3538. HRESULT
  3539. --*/
  3540. {
  3541. W3_MAIN_CONTEXT * pMainContext;
  3542. W3_REQUEST * pRequest;
  3543. URL_CONTEXT * pUrlContext;
  3544. HRESULT hr;
  3545. W3_URL_INFO * pUrlInfo = NULL;
  3546. if ( pfAppPoolValid == NULL )
  3547. {
  3548. DBG_ASSERT( FALSE );
  3549. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  3550. }
  3551. *pfAppPoolValid = FALSE;
  3552. //
  3553. // If we're in backward compat mode, OR if we're being lanuched on the
  3554. // command line, then bail on the check
  3555. //
  3556. if ( g_pW3Server->QueryInBackwardCompatibilityMode() )
  3557. {
  3558. *pfAppPoolValid = TRUE;
  3559. return NO_ERROR;
  3560. }
  3561. if ( g_pW3Server->QueryIsCommandLineLaunch() )
  3562. {
  3563. *pfAppPoolValid = TRUE;
  3564. return NO_ERROR;
  3565. }
  3566. //
  3567. // Do the fast check. If the URL's AppPool matches the current AppPool
  3568. // then we know we're good!
  3569. //
  3570. pUrlContext = QueryUrlContext();
  3571. DBG_ASSERT( pUrlContext != NULL );
  3572. if ( pUrlContext->QueryMetaData()->QueryAppPoolMatches() )
  3573. {
  3574. *pfAppPoolValid = TRUE;
  3575. return NO_ERROR;
  3576. }
  3577. //
  3578. // If we're the original request (i.e. this is a main context), then
  3579. // life may be tricky.
  3580. //
  3581. // We know (we assume) that WAS will route correctly. Therefore if we
  3582. // see that the URL's AppPoolID property doesn't match the processes'
  3583. // AppPoolId, that is OK.
  3584. //
  3585. pMainContext = QueryMainContext();
  3586. if ( pMainContext == this )
  3587. {
  3588. pRequest = QueryRequest();
  3589. DBG_ASSERT( pRequest != NULL );
  3590. if ( !pRequest->QueryUrlChanged() )
  3591. {
  3592. //
  3593. // We URL hasn't changed. This means that the URL's apppool
  3594. // is invalid, and thus WAS routed somewhere else. At this point,
  3595. // we really can't check config AppPools since WAS has already
  3596. // changed the "rules". Therefore we store doing
  3597. // AppPool checks
  3598. //
  3599. pMainContext->SetIgnoreAppPoolCheck();
  3600. *pfAppPoolValid = TRUE;
  3601. }
  3602. else
  3603. {
  3604. STACK_STRU( strOriginalUrl, 256 );
  3605. //
  3606. // URL has changed. We must check the original URL
  3607. //
  3608. hr = pRequest->GetOriginalFullUrl( &strOriginalUrl );
  3609. if ( FAILED( hr ) )
  3610. {
  3611. return hr;
  3612. }
  3613. DBG_ASSERT( g_pW3Server->QueryUrlInfoCache() != NULL );
  3614. hr = g_pW3Server->QueryUrlInfoCache()->GetUrlInfo( this,
  3615. strOriginalUrl,
  3616. &pUrlInfo );
  3617. if ( FAILED( hr ) )
  3618. {
  3619. return hr;
  3620. }
  3621. DBG_ASSERT( pUrlInfo != NULL );
  3622. if ( !pUrlInfo->QueryMetaData()->QueryAppPoolMatches() )
  3623. {
  3624. //
  3625. // Even the original URL doesn't match. We have to
  3626. // allow this request to proceed.
  3627. //
  3628. pMainContext->SetIgnoreAppPoolCheck();
  3629. *pfAppPoolValid = TRUE;
  3630. }
  3631. else
  3632. {
  3633. *pfAppPoolValid = FALSE;
  3634. }
  3635. pUrlInfo->DereferenceCacheEntry();
  3636. }
  3637. }
  3638. else
  3639. {
  3640. //
  3641. // This is a child request. The only way we can let this request
  3642. // proceed is if the main request's apppool didn't match
  3643. //
  3644. *pfAppPoolValid = pMainContext->QueryIgnoreAppPoolCheck();
  3645. }
  3646. return NO_ERROR;
  3647. }
  3648. HRESULT
  3649. W3_CONTEXT::InternalDetermineHandler(
  3650. W3_HANDLER ** ppHandler,
  3651. BOOL fDoExistenceCheck
  3652. )
  3653. /*++
  3654. Routine Description:
  3655. Determine the handler for a given request, and return it to caller
  3656. How to determine handler is driven by the Magic Flow Chart (TM)
  3657. Arguments:
  3658. ppHandler - Set to point to handler on success
  3659. fDoExistenceCheck - Should we do the pathinfo existence check
  3660. Return Value:
  3661. HRESULT
  3662. --*/
  3663. {
  3664. W3_REQUEST * pRequest;
  3665. URL_CONTEXT * pUrlContext;
  3666. W3_METADATA * pMetaData;
  3667. HRESULT hr = NO_ERROR;
  3668. W3_HANDLER * pHandler = NULL;
  3669. META_SCRIPT_MAP_ENTRY * pScriptMapEntry = NULL;
  3670. STACK_STRA( strHeaderValue, 10 );
  3671. STACK_STRA( strVerb, 10 );
  3672. HTTP_VERB VerbType;
  3673. BOOL fKnownVerb = FALSE;
  3674. DWORD dwFilePerms;
  3675. GATEWAY_TYPE GatewayType;
  3676. BOOL fAccessAllowed = FALSE;
  3677. BOOL fSuspectUrl = FALSE;
  3678. LPCSTR szHeaderValue = NULL;
  3679. W3_TRACE_LOG * pTraceLog;
  3680. BOOL fAppPoolValid = FALSE;
  3681. LPCSTR szContentLength;
  3682. BOOL fRedirected = FALSE;
  3683. STACK_STRU ( strRedirection, 256);
  3684. HTTP_STATUS statusCode;
  3685. if ( ppHandler == NULL )
  3686. {
  3687. DBG_ASSERT( FALSE );
  3688. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  3689. }
  3690. *ppHandler = NULL;
  3691. pRequest = QueryRequest();
  3692. DBG_ASSERT( pRequest != NULL );
  3693. pUrlContext = QueryUrlContext();
  3694. DBG_ASSERT( pUrlContext != NULL );
  3695. pMetaData = pUrlContext->QueryMetaData();
  3696. DBG_ASSERT( pMetaData != NULL );
  3697. VerbType = pRequest->QueryVerbType();
  3698. //
  3699. // Here is as good a place as any to check apppool. Basically we
  3700. // need to ensure that the AppPool which applies to this request is the
  3701. // same as the current process's AppPool.
  3702. //
  3703. hr = ValidateAppPool( &fAppPoolValid );
  3704. if ( FAILED( hr ) )
  3705. {
  3706. goto Finished;
  3707. }
  3708. if ( !fAppPoolValid )
  3709. {
  3710. //
  3711. // No match. This request is forbidden.
  3712. //
  3713. pHandler = new W3_GENERAL_HANDLER( this,
  3714. HttpStatusForbidden,
  3715. Http403AppPoolDenied );
  3716. if ( pHandler == NULL )
  3717. {
  3718. hr = HRESULT_FROM_WIN32( GetLastError() );
  3719. }
  3720. goto Finished;
  3721. }
  3722. //
  3723. // Check to see if the content-length of this request
  3724. // exceeds the maximum allowed.
  3725. //
  3726. szContentLength = pRequest->GetHeader( HttpHeaderContentLength );
  3727. if ( szContentLength &&
  3728. ( strtoul( szContentLength, NULL, 10 ) >
  3729. pMetaData->QueryMaxRequestEntityAllowed() ) )
  3730. {
  3731. pHandler = new W3_GENERAL_HANDLER( this,
  3732. HttpStatusEntityTooLarge );
  3733. if ( pHandler == NULL )
  3734. {
  3735. hr = HRESULT_FROM_WIN32( GetLastError() );
  3736. }
  3737. goto Finished;
  3738. }
  3739. //
  3740. // Handle the incredibly important TRACE verb
  3741. //
  3742. if ( VerbType == HttpVerbTRACE )
  3743. {
  3744. if ( g_pW3Server->QueryIsTraceEnabled() )
  3745. {
  3746. pHandler = new W3_TRACE_HANDLER( this );
  3747. }
  3748. else
  3749. {
  3750. pHandler = new W3_GENERAL_HANDLER( this,
  3751. HttpStatusNotImplemented );
  3752. }
  3753. if ( pHandler == NULL )
  3754. {
  3755. hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  3756. }
  3757. goto Finished;
  3758. }
  3759. //
  3760. // Check if we should do client-side redirection for this request
  3761. //
  3762. hr = CheckUrlRedirection( &fRedirected,
  3763. &strRedirection,
  3764. &statusCode );
  3765. if (FAILED(hr))
  3766. {
  3767. goto Finished;
  3768. }
  3769. if (fRedirected)
  3770. {
  3771. pHandler = new W3_REDIRECTION_HANDLER( this );
  3772. if (pHandler == NULL)
  3773. {
  3774. hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  3775. }
  3776. else
  3777. {
  3778. hr = ((W3_REDIRECTION_HANDLER *)pHandler)->SetDestination(
  3779. strRedirection,
  3780. statusCode);
  3781. }
  3782. goto Finished;
  3783. }
  3784. //
  3785. // Check for a * script map. If one exists, use it if allowed
  3786. //
  3787. pScriptMapEntry = pMetaData->QueryScriptMap()->
  3788. QueryStarScriptMap( QueryCurrentStarScriptMapIndex() );
  3789. if ( pScriptMapEntry != NULL )
  3790. {
  3791. //
  3792. // If there is a ./ in the URL, then always check for existence. This
  3793. // prevents a trailing . metabase equivilency problem
  3794. //
  3795. fSuspectUrl = pRequest->IsSuspectUrl();
  3796. if ( fDoExistenceCheck &&
  3797. ( pScriptMapEntry->QueryCheckPathInfoExists() ||
  3798. fSuspectUrl ) )
  3799. {
  3800. hr = CheckPathInfoExists( &pHandler );
  3801. if ( FAILED( hr ) )
  3802. {
  3803. goto Finished;
  3804. }
  3805. if ( pHandler != NULL )
  3806. {
  3807. //
  3808. // We already have an error handler for this request.
  3809. // That means path info didn't really exist
  3810. //
  3811. goto Finished;
  3812. }
  3813. }
  3814. //
  3815. // Create the appropriate handler for the script mapping
  3816. //
  3817. if ( pScriptMapEntry->QueryGateway() == GATEWAY_CGI )
  3818. {
  3819. pHandler = new W3_CGI_HANDLER( this, pScriptMapEntry );
  3820. }
  3821. else
  3822. {
  3823. DBG_ASSERT( pScriptMapEntry->QueryGateway() == GATEWAY_ISAPI );
  3824. pHandler = new (this) W3_ISAPI_HANDLER( this, pScriptMapEntry );
  3825. }
  3826. if ( pHandler == NULL )
  3827. {
  3828. hr = HRESULT_FROM_WIN32( GetLastError() );
  3829. }
  3830. goto Finished;
  3831. }
  3832. //
  3833. // Check for Translate: f
  3834. //
  3835. // If there, it goes to DAVFS unless DAV is disabled
  3836. //
  3837. if ( g_pW3Server->QueryIsDavEnabled() )
  3838. {
  3839. szHeaderValue = pRequest->GetHeader( HttpHeaderTranslate );
  3840. if ( szHeaderValue &&
  3841. toupper( szHeaderValue[ 0 ] ) == 'F' &&
  3842. szHeaderValue[ 1 ] == '\0' )
  3843. {
  3844. //
  3845. // This is a DAV request
  3846. //
  3847. pHandler = new (this) W3_DAV_HANDLER( this );
  3848. if ( pHandler == NULL )
  3849. {
  3850. hr = HRESULT_FROM_WIN32( GetLastError() );
  3851. }
  3852. goto Finished;
  3853. }
  3854. }
  3855. //
  3856. // Does this request map to an extension
  3857. //
  3858. // Map can mean one of two things
  3859. // a) A script map (as in MD_SCRIPT_MAP)
  3860. // b) An explicit .DLL/.EXE/.COM/etc.
  3861. //
  3862. pScriptMapEntry = pUrlContext->QueryUrlInfo()->QueryScriptMapEntry();
  3863. if ( pScriptMapEntry != NULL ||
  3864. pUrlContext->QueryUrlInfo()->QueryGateway() == GATEWAY_ISAPI ||
  3865. pUrlContext->QueryUrlInfo()->QueryGateway() == GATEWAY_CGI )
  3866. {
  3867. dwFilePerms = pMetaData->QueryAccessPerms();
  3868. if ( pScriptMapEntry != NULL )
  3869. {
  3870. GatewayType = pScriptMapEntry->QueryGateway();
  3871. //
  3872. // We have a script map. Check access rights
  3873. //
  3874. if ( pScriptMapEntry->QueryAllowScriptAccess() &&
  3875. IS_ACCESS_ALLOWED( pRequest, dwFilePerms, SCRIPT ) )
  3876. {
  3877. fAccessAllowed = TRUE;
  3878. }
  3879. if ( !fAccessAllowed &&
  3880. IS_ACCESS_ALLOWED( pRequest, dwFilePerms, EXECUTE ) )
  3881. {
  3882. fAccessAllowed = TRUE;
  3883. }
  3884. if ( !fAccessAllowed )
  3885. {
  3886. pHandler = new W3_GENERAL_HANDLER( this,
  3887. HttpStatusForbidden,
  3888. Http403ExecAccessDenied );
  3889. if ( pHandler == NULL )
  3890. {
  3891. hr = HRESULT_FROM_WIN32( GetLastError() );
  3892. }
  3893. goto Finished;
  3894. }
  3895. //
  3896. // If there is a ./ in the URL, then always check for existence. This
  3897. // prevents a trailing . metabase equivilency problem
  3898. //
  3899. fSuspectUrl = pRequest->IsSuspectUrl();
  3900. //
  3901. // Should we verify that path info does exist?
  3902. //
  3903. if ( fDoExistenceCheck &&
  3904. ( pScriptMapEntry->QueryCheckPathInfoExists() ||
  3905. fSuspectUrl ) )
  3906. {
  3907. hr = CheckPathInfoExists( &pHandler );
  3908. if ( FAILED( hr ) )
  3909. {
  3910. goto Finished;
  3911. }
  3912. if ( pHandler != NULL )
  3913. {
  3914. //
  3915. // We already have an error handler for this request.
  3916. // That means path info didn't really exist
  3917. //
  3918. goto Finished;
  3919. }
  3920. }
  3921. //
  3922. // Does the script map support the verb?
  3923. //
  3924. hr = pRequest->GetVerbString( &strVerb );
  3925. if ( FAILED( hr ) )
  3926. {
  3927. goto Finished;
  3928. }
  3929. if ( !pScriptMapEntry->IsVerbAllowed( strVerb ) )
  3930. {
  3931. pHandler = new W3_GENERAL_HANDLER( this,
  3932. HttpStatusForbidden,
  3933. Http403ExecAccessDenied );
  3934. if ( pHandler == NULL )
  3935. {
  3936. hr = HRESULT_FROM_WIN32( GetLastError() );
  3937. }
  3938. goto Finished;
  3939. }
  3940. }
  3941. else
  3942. {
  3943. GatewayType = pUrlContext->QueryUrlInfo()->QueryGateway();
  3944. }
  3945. //
  3946. // OK. If we got to here, we can setup an executable handler
  3947. //
  3948. if ( GatewayType == GATEWAY_CGI )
  3949. {
  3950. pHandler = new W3_CGI_HANDLER( this, pScriptMapEntry );
  3951. }
  3952. else
  3953. {
  3954. DBG_ASSERT( GatewayType == GATEWAY_ISAPI );
  3955. pHandler = new (this) W3_ISAPI_HANDLER( this, pScriptMapEntry );
  3956. }
  3957. if ( pHandler == NULL )
  3958. {
  3959. hr = HRESULT_FROM_WIN32( GetLastError() );
  3960. }
  3961. goto Finished;
  3962. }
  3963. //
  3964. // Ok. No script map applied. Is this an unknown verb?
  3965. // (i.e. not GET, HEAD, POST, TRACE)
  3966. //
  3967. if ( VerbType == HttpVerbGET ||
  3968. VerbType == HttpVerbPOST ||
  3969. VerbType == HttpVerbHEAD )
  3970. {
  3971. fKnownVerb = TRUE;
  3972. }
  3973. else
  3974. {
  3975. fKnownVerb = FALSE;
  3976. }
  3977. //
  3978. // If this verb is unknown, then it goes to DAV
  3979. //
  3980. if ( fKnownVerb == FALSE )
  3981. {
  3982. if (g_pW3Server->QueryIsDavEnabled())
  3983. {
  3984. pHandler = new (this) W3_DAV_HANDLER( this );
  3985. }
  3986. else if ( VerbType == HttpVerbOPTIONS )
  3987. {
  3988. //
  3989. // We handle OPTIONS if DAV is disabled
  3990. //
  3991. pHandler = new W3_OPTIONS_HANDLER( this );
  3992. }
  3993. else
  3994. {
  3995. pHandler = new W3_GENERAL_HANDLER( this,
  3996. HttpStatusNotImplemented );
  3997. }
  3998. if ( pHandler == NULL )
  3999. {
  4000. hr = HRESULT_FROM_WIN32( GetLastError() );
  4001. }
  4002. goto Finished;
  4003. }
  4004. //
  4005. // If there are special DAV verbs like If: and Lock-Token:, then it also
  4006. // go to DAV unless it is disabled
  4007. //
  4008. if ( g_pW3Server->QueryIsDavEnabled() )
  4009. {
  4010. if ( SUCCEEDED( pRequest->GetHeader( "If",
  4011. 2,
  4012. &strHeaderValue,
  4013. TRUE ) ) ||
  4014. SUCCEEDED( pRequest->GetHeader( "Lock-Token",
  4015. 10,
  4016. &strHeaderValue,
  4017. TRUE ) ) )
  4018. {
  4019. pHandler = new (this) W3_DAV_HANDLER( this );
  4020. if ( pHandler == NULL )
  4021. {
  4022. hr = HRESULT_FROM_WIN32( GetLastError() );
  4023. }
  4024. goto Finished;
  4025. }
  4026. }
  4027. //
  4028. // OK. Exchange/DAV-providers have had their chance. Now its our turn!
  4029. //
  4030. //
  4031. // Call the static file handler
  4032. //
  4033. pHandler = new (this) W3_STATIC_FILE_HANDLER( this );
  4034. if ( pHandler == NULL )
  4035. {
  4036. hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  4037. goto Finished;
  4038. }
  4039. Finished:
  4040. pTraceLog = QueryMainContext()->QueryTraceLog();
  4041. if ( FAILED( hr ) )
  4042. {
  4043. if ( pTraceLog != NULL )
  4044. {
  4045. pTraceLog->Trace( L"%I64x: Failed to find handler for request. hr = %08X\n",
  4046. QueryRequest()->QueryRequestId(),
  4047. hr );
  4048. }
  4049. DBG_ASSERT( pHandler == NULL );
  4050. }
  4051. else
  4052. {
  4053. if ( pTraceLog != NULL )
  4054. {
  4055. pTraceLog->Trace( L"%I64x: Found handler '%ws' for request\n",
  4056. QueryRequest()->QueryRequestId(),
  4057. pHandler->QueryName() );
  4058. }
  4059. }
  4060. *ppHandler = pHandler;
  4061. return hr;
  4062. }
  4063. HRESULT
  4064. W3_CONTEXT::SetupHttpRedirect(
  4065. STRA & strPath,
  4066. BOOL fIncludeParameters,
  4067. HTTP_STATUS & httpStatus
  4068. )
  4069. /*++
  4070. Routine Description:
  4071. Do an HTTP redirect (301 or 302)
  4072. Arguments:
  4073. strPath - New path component of destination
  4074. fIncludeParameters - Include query string in Location: header
  4075. httpStatus - Status for redirect (i.e. HttpStatusRedirect)
  4076. Return Value:
  4077. HRESULT
  4078. --*/
  4079. {
  4080. HRESULT hr;
  4081. STACK_STRA( strRedirect, MAX_PATH);
  4082. W3_RESPONSE *pResponse = QueryResponse();
  4083. W3_REQUEST *pRequest = QueryRequest();
  4084. //
  4085. // If it an absolute path add the protocol, host name and QueryString
  4086. // if specified, otherwise just assume it is a fully qualified URL.
  4087. //
  4088. if (strPath.QueryStr()[0] == '/')
  4089. {
  4090. // build the redirect URL (with http://) into strRedirect
  4091. hr = pRequest->BuildFullUrl( strPath,
  4092. &strRedirect,
  4093. fIncludeParameters );
  4094. }
  4095. else
  4096. {
  4097. hr = strRedirect.Copy( strPath );
  4098. }
  4099. if ( FAILED( hr ) )
  4100. {
  4101. return hr;
  4102. }
  4103. if (strRedirect.QueryCCH() > MAXUSHORT)
  4104. {
  4105. return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
  4106. }
  4107. //
  4108. // Setup the response, starting with status and location:
  4109. //
  4110. pResponse->SetStatus( httpStatus );
  4111. //
  4112. // Be careful to reset the response on subsequent failures so we don't
  4113. // end up with an incomplete response!
  4114. //
  4115. hr = pResponse->SetHeader( HttpHeaderLocation,
  4116. strRedirect.QueryStr(),
  4117. (USHORT)strRedirect.QueryCCH() );
  4118. if ( FAILED( hr ) )
  4119. {
  4120. goto Failed;
  4121. }
  4122. pResponse->SetHeaderByReference( HttpHeaderContentType,
  4123. "text/html", 9 );
  4124. //
  4125. // Now add any metabase configured "redirect" headers (lame)
  4126. //
  4127. STRA *pstrRedirectHeaders = QueryUrlContext()->QueryMetaData()->QueryRedirectHeaders();
  4128. if ( pstrRedirectHeaders != NULL )
  4129. {
  4130. hr = pResponse->AppendResponseHeaders( *pstrRedirectHeaders );
  4131. if ( FAILED( hr ) )
  4132. {
  4133. goto Failed;
  4134. }
  4135. }
  4136. //
  4137. // Add the canned redirect body. This means taking the format string,
  4138. // and inserting the URL into it
  4139. //
  4140. //
  4141. // First HTMLEncode the target URL
  4142. //
  4143. hr = strRedirect.HTMLEncode();
  4144. if ( FAILED( hr ) )
  4145. {
  4146. goto Failed;
  4147. }
  4148. CHAR *pvRedirect;
  4149. DWORD cbRedirect = sm_cbRedirectMessage + strRedirect.QueryCCH();
  4150. //
  4151. // Keep a buffer around
  4152. //
  4153. hr = QueryHeaderBuffer()->AllocateSpace( cbRedirect + 1,
  4154. &pvRedirect );
  4155. if ( FAILED( hr ) )
  4156. {
  4157. goto Failed;
  4158. }
  4159. cbRedirect = _snprintf( pvRedirect,
  4160. cbRedirect,
  4161. sm_achRedirectMessage,
  4162. strRedirect.QueryStr() );
  4163. //
  4164. // Setup the response
  4165. //
  4166. hr = pResponse->AddMemoryChunkByReference( pvRedirect,
  4167. cbRedirect );
  4168. if ( FAILED( hr ) )
  4169. {
  4170. goto Failed;
  4171. }
  4172. return S_OK;
  4173. Failed:
  4174. pResponse->Clear();
  4175. pResponse->SetStatus( HttpStatusServerError );
  4176. return hr;
  4177. }
  4178. HRESULT
  4179. W3_CONTEXT::SetupHttpRedirect(
  4180. STRU & strPath,
  4181. BOOL fIncludeParameters,
  4182. HTTP_STATUS & httpStatus
  4183. )
  4184. /*++
  4185. Routine Description:
  4186. Do an HTTP redirect (301 or 302)
  4187. Arguments:
  4188. strPath - New path component of destination
  4189. fIncludeParameters - Include query string in Location: header
  4190. httpStatus - Status for redirect (i.e. HttpStatusRedirect)
  4191. Return Value:
  4192. HRESULT
  4193. --*/
  4194. {
  4195. //
  4196. // Convert the unicode path to ansi
  4197. //
  4198. HRESULT hr;
  4199. STACK_STRA (straPath, MAX_PATH);
  4200. if (FAILED(hr = straPath.CopyWToUTF8Unescaped(strPath)) ||
  4201. FAILED(hr = straPath.Escape(TRUE))) // Only escape high-bit set chars
  4202. {
  4203. return hr;
  4204. }
  4205. return SetupHttpRedirect(straPath,
  4206. fIncludeParameters,
  4207. httpStatus);
  4208. }
  4209. BOOL W3_CONTEXT::QueryDoUlLogging()
  4210. {
  4211. if (QuerySite() == NULL ||
  4212. !QuerySite()->QueryDoUlLogging())
  4213. {
  4214. return FALSE;
  4215. }
  4216. if (QueryUrlContext() == NULL ||
  4217. QueryUrlContext()->QueryMetaData() == NULL)
  4218. {
  4219. return TRUE;
  4220. }
  4221. return !QueryUrlContext()->QueryMetaData()->QueryDontLog();
  4222. }
  4223. BOOL W3_CONTEXT::QueryDoCustomLogging()
  4224. {
  4225. if (QuerySite() == NULL ||
  4226. !QuerySite()->QueryDoCustomLogging())
  4227. {
  4228. return FALSE;
  4229. }
  4230. if (QueryUrlContext() == NULL ||
  4231. QueryUrlContext()->QueryMetaData() == NULL)
  4232. {
  4233. return TRUE;
  4234. }
  4235. return !QueryUrlContext()->QueryMetaData()->QueryDontLog();
  4236. }
  4237. VOID
  4238. W3_CONTEXT::SetSSICommandHandler(
  4239. W3_HANDLER * pHandler
  4240. )
  4241. /*++
  4242. Routine Description:
  4243. Explicitly sets the CGI_HANDLER for this request. This is the handler
  4244. which executes explicit command lines on behalf of SSI
  4245. The function is named SetSSICommandHandler (instead of just SetHandler())
  4246. to discourage others from using this function.
  4247. Arguments:
  4248. pHandler - Handler to set. Must be the CGI handler
  4249. Return Value:
  4250. None
  4251. --*/
  4252. {
  4253. DBG_ASSERT( pHandler != NULL );
  4254. DBG_ASSERT( _pHandler == NULL );
  4255. DBG_ASSERT( wcscmp( pHandler->QueryName(), L"CGIHandler" ) == 0 );
  4256. _pHandler = pHandler;
  4257. }
  4258. VOID *
  4259. W3_CONTEXT::ContextAlloc(
  4260. DWORD cbSize
  4261. )
  4262. /*++
  4263. Routine Description:
  4264. Allocate some per-W3Context memory
  4265. Arguments:
  4266. cbSize - Size to allocate
  4267. Return Value:
  4268. Pointer to memory (or NULL on failure)
  4269. --*/
  4270. {
  4271. VOID * pvRet = NULL;
  4272. HRESULT hr;
  4273. pvRet = QueryMainContext()->AllocateFromInlineMemory( cbSize );
  4274. if ( pvRet == NULL )
  4275. {
  4276. hr = _ChunkBuffer.AllocateSpace( cbSize,
  4277. &pvRet );
  4278. if ( FAILED( hr ) )
  4279. {
  4280. SetLastError( WIN32_FROM_HRESULT( hr ) );
  4281. }
  4282. else
  4283. {
  4284. DBG_ASSERT( pvRet != NULL );
  4285. }
  4286. }
  4287. return pvRet;
  4288. }