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.

2265 lines
63 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name :
  4. cgi_handler.cxx
  5. Abstract:
  6. Handle CGI requests
  7. Author:
  8. Taylor Weiss (TaylorW) 27-Jan-2000
  9. Environment:
  10. Win32 - User Mode
  11. Project:
  12. ULW3.DLL
  13. --*/
  14. #include "precomp.hxx"
  15. #include "cgi_handler.h"
  16. //
  17. // W3_CGI_HANDLER statics
  18. //
  19. BOOL W3_CGI_HANDLER::sm_fForwardServerEnvironmentBlock = FALSE;
  20. WCHAR * W3_CGI_HANDLER::sm_pEnvString = NULL;
  21. DWORD W3_CGI_HANDLER::sm_cchEnvLength = 0;
  22. LIST_ENTRY W3_CGI_HANDLER::sm_CgiListHead;
  23. CRITICAL_SECTION W3_CGI_HANDLER::sm_CgiListLock;
  24. //
  25. // Environment variable block used for CGI
  26. //
  27. LPSTR g_CGIServerVars[] =
  28. {
  29. {"ALL_HTTP"}, // Means insert all HTTP_ headers here
  30. {"APP_POOL_ID"},
  31. {"AUTH_TYPE"},
  32. {"AUTH_PASSWORD"},
  33. {"AUTH_USER"},
  34. {"CERT_COOKIE"},
  35. {"CERT_FLAGS"},
  36. {"CERT_ISSUER"},
  37. {"CERT_SERIALNUMBER"},
  38. {"CERT_SUBJECT"},
  39. {"CONTENT_LENGTH"},
  40. {"CONTENT_TYPE"},
  41. {"GATEWAY_INTERFACE"},
  42. {"HTTPS"},
  43. {"HTTPS_KEYSIZE"},
  44. {"HTTPS_SECRETKEYSIZE"},
  45. {"HTTPS_SERVER_ISSUER"},
  46. {"HTTPS_SERVER_SUBJECT"},
  47. {"INSTANCE_ID"},
  48. {"LOCAL_ADDR"},
  49. {"LOGON_USER"},
  50. {"PATH_INFO"},
  51. {"PATH_TRANSLATED"},
  52. {"QUERY_STRING"},
  53. {"REMOTE_ADDR"},
  54. {"REMOTE_HOST"},
  55. {"REMOTE_USER"},
  56. {"REQUEST_METHOD"},
  57. {"SCRIPT_NAME"},
  58. {"SERVER_NAME"},
  59. {"SERVER_PORT"},
  60. {"SERVER_PORT_SECURE"},
  61. {"SERVER_PROTOCOL"},
  62. {"SERVER_SOFTWARE"},
  63. {"UNMAPPED_REMOTE_USER"},
  64. {NULL}
  65. };
  66. // static
  67. HRESULT W3_CGI_HANDLER::Initialize()
  68. {
  69. DBGPRINTF((DBG_CONTEXT, "W3_CGI_HANDLER::Initialize() called\n"));
  70. //
  71. // Read some CGI configuration from the registry
  72. //
  73. HRESULT hr;
  74. if (!InitializeCriticalSectionAndSpinCount(&sm_CgiListLock, 10))
  75. {
  76. return HRESULT_FROM_WIN32(GetLastError());
  77. }
  78. InitializeListHead(&sm_CgiListHead);
  79. HKEY w3Params;
  80. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  81. W3_PARAMETERS_KEY,
  82. 0,
  83. KEY_READ,
  84. &w3Params) == NO_ERROR)
  85. {
  86. DWORD dwType;
  87. DWORD cbData = sizeof BOOL;
  88. if ((RegQueryValueEx(w3Params,
  89. L"ForwardServerEnvironmentBlock",
  90. NULL,
  91. &dwType,
  92. (LPBYTE)&sm_fForwardServerEnvironmentBlock,
  93. &cbData) != NO_ERROR) ||
  94. (dwType != REG_DWORD))
  95. {
  96. sm_fForwardServerEnvironmentBlock = TRUE;
  97. }
  98. RegCloseKey(w3Params);
  99. }
  100. //
  101. // Read the environment
  102. //
  103. if (sm_fForwardServerEnvironmentBlock)
  104. {
  105. WCHAR *EnvString = GetEnvironmentStrings();
  106. if (EnvString == NULL)
  107. {
  108. hr = HRESULT_FROM_WIN32(GetLastError());
  109. DBGPRINTF((DBG_CONTEXT, "GetEnvironmentStrings failed\n"));
  110. goto Exit;
  111. }
  112. //
  113. // Compute length of environment block (excluding block delimiter)
  114. //
  115. DWORD length;
  116. sm_cchEnvLength = 0;
  117. while ((length = (DWORD)wcslen(EnvString + sm_cchEnvLength)) != 0)
  118. {
  119. sm_cchEnvLength += length + 1;
  120. }
  121. //
  122. // store it
  123. //
  124. if ((sm_pEnvString = new WCHAR[sm_cchEnvLength]) == NULL)
  125. {
  126. hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  127. FreeEnvironmentStrings(EnvString);
  128. goto Exit;
  129. }
  130. memcpy(sm_pEnvString,
  131. EnvString,
  132. sm_cchEnvLength * sizeof(WCHAR));
  133. FreeEnvironmentStrings(EnvString);
  134. }
  135. return S_OK;
  136. Exit:
  137. DBG_ASSERT(FAILED(hr));
  138. //
  139. // Cleanup partially created stuff
  140. //
  141. Terminate();
  142. return hr;
  143. }
  144. W3_CGI_HANDLER::~W3_CGI_HANDLER()
  145. {
  146. //
  147. // Close all open handles related to this request
  148. //
  149. EnterCriticalSection(&sm_CgiListLock);
  150. RemoveEntryList(&m_CgiListEntry);
  151. LeaveCriticalSection(&sm_CgiListLock);
  152. if (m_hTimer)
  153. {
  154. if (!DeleteTimerQueueTimer(NULL,
  155. m_hTimer,
  156. INVALID_HANDLE_VALUE))
  157. {
  158. DBGPRINTF((DBG_CONTEXT,
  159. "DeleteTimerQueueTimer failed, %d\n",
  160. GetLastError()));
  161. }
  162. m_hTimer = NULL;
  163. }
  164. if (m_hStdOut != INVALID_HANDLE_VALUE)
  165. {
  166. if (!CloseHandle(m_hStdOut))
  167. {
  168. DBGPRINTF((DBG_CONTEXT,
  169. "CloseHandle failed on StdOut, %d\n",
  170. GetLastError()));
  171. }
  172. m_hStdOut = INVALID_HANDLE_VALUE;
  173. }
  174. if (m_hStdIn != INVALID_HANDLE_VALUE)
  175. {
  176. if (!CloseHandle(m_hStdIn))
  177. {
  178. DBGPRINTF((DBG_CONTEXT,
  179. "CloseHandle failed on StdIn, %d\n",
  180. GetLastError()));
  181. }
  182. m_hStdIn = INVALID_HANDLE_VALUE;
  183. }
  184. if (m_hProcess)
  185. {
  186. //
  187. // Just in case it is still running
  188. //
  189. TerminateProcess(m_hProcess, 0);
  190. DBG_REQUIRE(CloseHandle(m_hProcess));
  191. m_hProcess = NULL;
  192. }
  193. // perf ctr
  194. QueryW3Context()->QuerySite()->DecCgiReqs();
  195. if ( ETW_IS_TRACE_ON(ETW_LEVEL_CP) )
  196. {
  197. HTTP_REQUEST_ID RequestId = QueryW3Context()->QueryRequest()->QueryRequestId();
  198. g_pEtwTracer->EtwTraceEvent( &CgiEventGuid,
  199. ETW_TYPE_END,
  200. &RequestId,
  201. sizeof(HTTP_REQUEST_ID),
  202. NULL,
  203. 0 );
  204. }
  205. }
  206. VOID CALLBACK W3_CGI_HANDLER::OnPipeIoCompletion(
  207. DWORD dwErrorCode,
  208. DWORD dwNumberOfBytesTransfered,
  209. LPOVERLAPPED lpOverlapped)
  210. {
  211. if (dwErrorCode && dwErrorCode != ERROR_BROKEN_PIPE)
  212. {
  213. DBGPRINTF((DBG_CONTEXT, "Error %d on CGI_HANDLER::OnPipeIoCompletion\n", dwErrorCode));
  214. }
  215. HRESULT hr = S_OK;
  216. BOOL fIsCgiError = TRUE;
  217. W3_CGI_HANDLER *pHandler = CONTAINING_RECORD(lpOverlapped,
  218. W3_CGI_HANDLER,
  219. m_Overlapped);
  220. if (dwErrorCode ||
  221. (dwNumberOfBytesTransfered == 0))
  222. {
  223. if (pHandler->m_dwRequestState == CgiStateProcessingRequestEntity)
  224. {
  225. //
  226. // If we could not write the request entity, for example because
  227. // the CGI did not wait to read the entity, ignore the error and
  228. // continue on to reading the output
  229. //
  230. //
  231. // If this is an nph cgi, we do not parse header
  232. //
  233. if (pHandler->QueryIsNphCgi())
  234. {
  235. pHandler->m_dwRequestState = CgiStateProcessingResponseEntity;
  236. }
  237. else
  238. {
  239. pHandler->m_dwRequestState = CgiStateProcessingResponseHeaders;
  240. }
  241. pHandler->m_cbData = 0;
  242. if (SUCCEEDED(hr = pHandler->CGIReadCGIOutput()))
  243. {
  244. return;
  245. }
  246. }
  247. else
  248. {
  249. hr = HRESULT_FROM_WIN32(dwErrorCode);
  250. }
  251. fIsCgiError = TRUE;
  252. goto ErrorExit;
  253. }
  254. if (pHandler->m_dwRequestState == CgiStateProcessingResponseHeaders)
  255. {
  256. //
  257. // Copy the headers to the header buffer to be parsed when we have
  258. // all the headers
  259. //
  260. if (!pHandler->m_bufResponseHeaders.Resize(
  261. pHandler->m_cbData + dwNumberOfBytesTransfered + 1))
  262. {
  263. hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  264. goto ErrorExit;
  265. }
  266. memcpy((LPSTR)pHandler->m_bufResponseHeaders.QueryPtr() +
  267. pHandler->m_cbData,
  268. pHandler->m_DataBuffer,
  269. dwNumberOfBytesTransfered);
  270. pHandler->m_cbData += dwNumberOfBytesTransfered;
  271. ((LPSTR)pHandler->m_bufResponseHeaders.QueryPtr())[pHandler->m_cbData] = '\0';
  272. }
  273. else if (pHandler->m_dwRequestState == CgiStateProcessingResponseEntity)
  274. {
  275. pHandler->m_cbData = dwNumberOfBytesTransfered;
  276. }
  277. if (FAILED(hr = pHandler->CGIContinueOnPipeCompletion(&fIsCgiError)))
  278. {
  279. goto ErrorExit;
  280. }
  281. return;
  282. ErrorExit:
  283. if (hr != HRESULT_FROM_WIN32(ERROR_BROKEN_PIPE))
  284. {
  285. pHandler->QueryW3Context()->SetErrorStatus(hr);
  286. }
  287. //
  288. // If the error happened due to an CGI problem, mark it as 502
  289. // appropriately
  290. //
  291. if (fIsCgiError)
  292. {
  293. if (pHandler->m_dwRequestState != CgiStateProcessingResponseEntity &&
  294. pHandler->m_dwRequestState != CgiStateDoneWithRequest)
  295. {
  296. pHandler->QueryW3Context()->QueryResponse()->Clear();
  297. DWORD dwExitCode;
  298. if (GetExitCodeProcess(pHandler->m_hProcess,
  299. &dwExitCode) &&
  300. dwExitCode == CGI_PREMATURE_DEATH_CODE)
  301. {
  302. STACK_STRU( strUrl, 128);
  303. STACK_STRU( strQuery, 128);
  304. if (SUCCEEDED(pHandler->QueryW3Context()->QueryRequest()->GetUrl(&strUrl)) &&
  305. SUCCEEDED(pHandler->QueryW3Context()->QueryRequest()->GetQueryString(&strQuery)))
  306. {
  307. LPCWSTR apsz[2];
  308. apsz[0] = strUrl.QueryStr();
  309. apsz[1] = strQuery.QueryStr();
  310. g_pW3Server->LogEvent(W3_EVENT_KILLING_SCRIPT,
  311. 2,
  312. apsz);
  313. }
  314. pHandler->QueryW3Context()->QueryResponse()->SetStatus(
  315. HttpStatusBadGateway,
  316. Http502Timeout);
  317. }
  318. else
  319. {
  320. pHandler->QueryW3Context()->QueryResponse()->SetStatus(
  321. HttpStatusBadGateway,
  322. Http502PrematureExit);
  323. }
  324. }
  325. }
  326. else
  327. {
  328. pHandler->QueryW3Context()->QueryResponse()->SetStatus(
  329. HttpStatusBadRequest);
  330. }
  331. pHandler->m_dwRequestState = CgiStateDoneWithRequest;
  332. POST_MAIN_COMPLETION( pHandler->QueryW3Context()->QueryMainContext() );
  333. }
  334. HRESULT
  335. GeneratePipeNames(
  336. STRU *pstrStdoutName,
  337. STRU *pstrStdinName)
  338. {
  339. RPC_STATUS rpcStatus;
  340. UUID pipeUuid;
  341. LPWSTR pszPipeUuid = NULL;
  342. HRESULT hr;
  343. rpcStatus = UuidCreate(&pipeUuid);
  344. if (rpcStatus != RPC_S_OK)
  345. {
  346. return E_FAIL;
  347. }
  348. rpcStatus = UuidToString(&pipeUuid, &pszPipeUuid);
  349. if (rpcStatus != RPC_S_OK)
  350. {
  351. return E_FAIL;
  352. }
  353. if (FAILED(hr = pstrStdoutName->Copy(L"\\\\.\\pipe\\IISCgiStdout")) ||
  354. FAILED(hr = pstrStdoutName->Append(pszPipeUuid)) ||
  355. FAILED(hr = pstrStdinName->Copy(L"\\\\.\\pipe\\IISCgiStdin")) ||
  356. FAILED(hr = pstrStdinName->Append(pszPipeUuid)))
  357. {
  358. RpcStringFree(&pszPipeUuid);
  359. return hr;
  360. }
  361. RpcStringFree(&pszPipeUuid);
  362. return hr;
  363. }
  364. // static
  365. HRESULT W3_CGI_HANDLER::SetupChildPipes(
  366. HANDLE *phStdOut,
  367. HANDLE *phStdIn,
  368. STARTUPINFO *pstartupinfo)
  369. /*++
  370. Synopsis
  371. Setup the pipes to use for communicating with the child process
  372. Arguments
  373. phStdOut: this will be populated with the server side of CGIs stdout
  374. phStdIn: this will be populated with the server side of CGIs stdin
  375. pstartupinfo: this will be populated with the startinfo that can be
  376. passed to a CreateProcess call
  377. Return Value
  378. HRESULT
  379. --*/
  380. {
  381. STACK_STRU( strStdoutName, 128);
  382. STACK_STRU( strStdinName, 128);
  383. HRESULT hr = S_OK;
  384. DBG_ASSERT(phStdOut != NULL);
  385. DBG_ASSERT(phStdIn != NULL);
  386. DBG_ASSERT(pstartupinfo != NULL);
  387. SECURITY_ATTRIBUTES sa;
  388. sa.nLength = sizeof(sa);
  389. sa.lpSecurityDescriptor = NULL;
  390. sa.bInheritHandle = TRUE;
  391. pstartupinfo->hStdOutput = INVALID_HANDLE_VALUE;
  392. pstartupinfo->hStdInput = INVALID_HANDLE_VALUE;
  393. pstartupinfo->dwFlags = STARTF_USESTDHANDLES;
  394. hr = GeneratePipeNames( &strStdoutName,
  395. &strStdinName );
  396. if (FAILED(hr))
  397. {
  398. goto ErrorExit;
  399. }
  400. *phStdOut = CreateNamedPipe(strStdoutName.QueryStr(),
  401. PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE,
  402. 0,
  403. 1,
  404. MAX_CGI_BUFFERING,
  405. MAX_CGI_BUFFERING,
  406. INFINITE,
  407. NULL);
  408. if (*phStdOut == INVALID_HANDLE_VALUE)
  409. {
  410. hr = HRESULT_FROM_WIN32(GetLastError());
  411. goto ErrorExit;
  412. }
  413. if (!ThreadPoolBindIoCompletionCallback(*phStdOut,
  414. OnPipeIoCompletion,
  415. 0))
  416. {
  417. DBGPRINTF((DBG_CONTEXT, "ThreadPoolBindIo failed\n"));
  418. hr = E_FAIL;
  419. goto ErrorExit;
  420. }
  421. pstartupinfo->hStdOutput = CreateFile(strStdoutName.QueryStr(),
  422. GENERIC_WRITE,
  423. 0,
  424. &sa,
  425. OPEN_EXISTING,
  426. FILE_FLAG_OVERLAPPED,
  427. NULL);
  428. if (pstartupinfo->hStdOutput == INVALID_HANDLE_VALUE)
  429. {
  430. hr = HRESULT_FROM_WIN32(GetLastError());
  431. goto ErrorExit;
  432. }
  433. *phStdIn = CreateNamedPipe(strStdinName.QueryStr(),
  434. PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE,
  435. 0,
  436. 1,
  437. MAX_CGI_BUFFERING,
  438. MAX_CGI_BUFFERING,
  439. INFINITE,
  440. NULL);
  441. if (*phStdIn == INVALID_HANDLE_VALUE)
  442. {
  443. hr = HRESULT_FROM_WIN32(GetLastError());
  444. goto ErrorExit;
  445. }
  446. if (!ThreadPoolBindIoCompletionCallback(*phStdIn,
  447. OnPipeIoCompletion,
  448. 0))
  449. {
  450. DBGPRINTF((DBG_CONTEXT, "ThreadPoolBindIo failed\n"));
  451. hr = E_FAIL;
  452. goto ErrorExit;
  453. }
  454. pstartupinfo->hStdInput = CreateFile(strStdinName.QueryStr(),
  455. GENERIC_READ,
  456. 0,
  457. &sa,
  458. OPEN_EXISTING,
  459. FILE_FLAG_OVERLAPPED,
  460. NULL);
  461. if (pstartupinfo->hStdInput == INVALID_HANDLE_VALUE)
  462. {
  463. hr = HRESULT_FROM_WIN32(GetLastError());
  464. goto ErrorExit;
  465. }
  466. //
  467. // Stdout and Stderror will use the same pipe.
  468. //
  469. pstartupinfo->hStdError = pstartupinfo->hStdOutput;
  470. return S_OK;
  471. ErrorExit:
  472. DBGPRINTF((DBG_CONTEXT, "SetupChildPipes Failed, hr %x\n", hr));
  473. //
  474. // Need to close these now so that other instances do not connect to it
  475. //
  476. if (pstartupinfo->hStdOutput != INVALID_HANDLE_VALUE)
  477. {
  478. DBG_REQUIRE(CloseHandle(pstartupinfo->hStdOutput));
  479. pstartupinfo->hStdOutput = INVALID_HANDLE_VALUE;
  480. }
  481. if (pstartupinfo->hStdInput != INVALID_HANDLE_VALUE)
  482. {
  483. DBG_REQUIRE(CloseHandle(pstartupinfo->hStdInput));
  484. pstartupinfo->hStdInput = INVALID_HANDLE_VALUE;
  485. }
  486. if (*phStdOut != INVALID_HANDLE_VALUE)
  487. {
  488. DBG_REQUIRE(CloseHandle(*phStdOut));
  489. *phStdOut = INVALID_HANDLE_VALUE;
  490. }
  491. if (*phStdIn != INVALID_HANDLE_VALUE)
  492. {
  493. DBG_REQUIRE(CloseHandle(*phStdIn));
  494. *phStdIn = INVALID_HANDLE_VALUE;
  495. }
  496. return hr;
  497. }
  498. BOOL IsCmdExe(const WCHAR *pchPath)
  499. /*++
  500. Tells whether the CGI call is for a cmd shell
  501. --*/
  502. {
  503. //
  504. // do case-insensitive search for cmd.exe (there is no function in
  505. // msvcrt for this)
  506. //
  507. for (; *pchPath != L'\0'; pchPath++)
  508. {
  509. if ((pchPath[0] == L'c' || pchPath[0] == 'C') &&
  510. (pchPath[1] == L'm' || pchPath[1] == 'M') &&
  511. (pchPath[2] == L'd' || pchPath[2] == 'D') &&
  512. pchPath[3] == L'.' &&
  513. (pchPath[4] == L'e' || pchPath[4] == 'E') &&
  514. (pchPath[5] == L'x' || pchPath[5] == 'X') &&
  515. (pchPath[6] == L'e' || pchPath[6] == 'E'))
  516. {
  517. return TRUE;
  518. }
  519. }
  520. return FALSE;
  521. }
  522. BOOL IsNphCgi(const WCHAR *pszUrl)
  523. /*++
  524. Tells whether the URL is for an nph cgi script
  525. --*/
  526. {
  527. LPWSTR pszLastSlash = wcsrchr(pszUrl, L'/');
  528. if (pszLastSlash != NULL)
  529. {
  530. if (_wcsnicmp(pszLastSlash + 1, L"nph-", 4) == 0)
  531. {
  532. return TRUE;
  533. }
  534. }
  535. return FALSE;
  536. }
  537. HRESULT SetupCmdLine(W3_REQUEST *pRequest,
  538. STRU *pstrCmdLine)
  539. {
  540. STACK_STRU (queryString, MAX_PATH);
  541. HRESULT hr = S_OK;
  542. if (FAILED(hr = pRequest->GetQueryString(&queryString)))
  543. {
  544. return hr;
  545. }
  546. //
  547. // If there is no QueryString OR if an unencoded "=" is found, don't
  548. // append QueryString to the command line according to spec
  549. //
  550. if (queryString.QueryCCH() == 0 ||
  551. wcschr(queryString.QueryStr(), L'='))
  552. {
  553. return S_OK;
  554. }
  555. queryString.Unescape();
  556. return pstrCmdLine->Append(queryString);
  557. } // SetupCmdLine
  558. HRESULT
  559. W3_CGI_HANDLER::SetupChildEnv(OUT BUFFER *pBuff)
  560. {
  561. //
  562. // Build the environment block for CGI
  563. //
  564. DWORD cchCurrentPos = 0;
  565. //
  566. // Copy the environment variables
  567. //
  568. if (sm_fForwardServerEnvironmentBlock)
  569. {
  570. if (!pBuff->Resize(sm_cchEnvLength * sizeof(WCHAR), 512))
  571. {
  572. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  573. }
  574. memcpy(pBuff->QueryPtr(),
  575. sm_pEnvString,
  576. sm_cchEnvLength * sizeof(WCHAR));
  577. cchCurrentPos = sm_cchEnvLength;
  578. }
  579. CHAR * pszName;
  580. STACK_STRU (strVal, 512);
  581. STACK_STRU (struName, 32);
  582. for (DWORD i = 0; (pszName = g_CGIServerVars[i]) != NULL; i++)
  583. {
  584. HRESULT hr;
  585. if (FAILED(hr = SERVER_VARIABLE_HASH::GetServerVariableW(
  586. QueryW3Context(),
  587. pszName,
  588. &strVal)))
  589. {
  590. return hr;
  591. }
  592. DWORD cchName = (DWORD)strlen(pszName);
  593. DWORD cchValue = strVal.QueryCCH();
  594. //
  595. // We need space for the terminating '\0' and the '='
  596. //
  597. DWORD cbNeeded = (cchName + cchValue + 1 + 1) * sizeof(WCHAR);
  598. if (!pBuff->Resize(cchCurrentPos * sizeof(WCHAR) + cbNeeded,
  599. 512))
  600. {
  601. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  602. }
  603. WCHAR * pchtmp = (WCHAR *)pBuff->QueryPtr();
  604. if (strcmp(pszName, "ALL_HTTP") == 0)
  605. {
  606. //
  607. // ALL_HTTP means we're adding all of the HTTP_ header
  608. // fields which requires a little bit of special processing
  609. //
  610. memcpy(pchtmp + cchCurrentPos,
  611. strVal.QueryStr(),
  612. (cchValue + 1) * sizeof(WCHAR));
  613. WCHAR * pszColonPosition = wcschr(pchtmp + cchCurrentPos, L':');
  614. WCHAR * pszNewLinePosition;
  615. //
  616. // Convert the Name:Value\n entries to Name=Value\0 entries
  617. // for the environment table
  618. //
  619. while (pszColonPosition != NULL)
  620. {
  621. *pszColonPosition = L'=';
  622. pszNewLinePosition = wcschr(pszColonPosition + 1, L'\n');
  623. DBG_ASSERT(pszNewLinePosition != NULL);
  624. *pszNewLinePosition = L'\0';
  625. pszColonPosition = wcschr(pszNewLinePosition + 1, L':');
  626. }
  627. cchCurrentPos += cchValue;
  628. }
  629. else
  630. {
  631. //
  632. // Normal ServerVariable, add it
  633. //
  634. if (FAILED(hr = struName.CopyA(pszName,
  635. cchName)))
  636. {
  637. return hr;
  638. }
  639. memcpy(pchtmp + cchCurrentPos,
  640. struName.QueryStr(),
  641. cchName * sizeof(WCHAR));
  642. *(pchtmp + cchCurrentPos + cchName) = L'=';
  643. memcpy(pchtmp + cchCurrentPos + cchName + 1,
  644. strVal.QueryStr(),
  645. (cchValue + 1) * sizeof(WCHAR));
  646. cchCurrentPos += cchName + cchValue + 1 + 1;
  647. }
  648. }
  649. //
  650. // Add an extra null terminator to the environment list
  651. //
  652. if (!pBuff->Resize((cchCurrentPos + 1) * sizeof(WCHAR)))
  653. {
  654. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  655. }
  656. *((WCHAR *)pBuff->QueryPtr() + cchCurrentPos) = L'\0';
  657. return S_OK;
  658. }
  659. VOID CALLBACK W3_CGI_HANDLER::CGITerminateProcess(PVOID pContext,
  660. BOOLEAN)
  661. /*++
  662. Routine Description:
  663. This function is the callback called by the scheduler thread after the
  664. specified timeout period has elapsed.
  665. Arguments:
  666. pContext - Handle of process to kill
  667. --*/
  668. {
  669. W3_CGI_HANDLER *pHandler = reinterpret_cast<W3_CGI_HANDLER *>(pContext);
  670. if (pHandler->m_hProcess &&
  671. !TerminateProcess(pHandler->m_hProcess, CGI_PREMATURE_DEATH_CODE))
  672. {
  673. DBGPRINTF((DBG_CONTEXT,
  674. "CGITerminateProcess - TerminateProcess failed, error %d\n",
  675. GetLastError()));
  676. }
  677. if (pHandler->m_hStdIn != INVALID_HANDLE_VALUE &&
  678. !DisconnectNamedPipe(pHandler->m_hStdIn))
  679. {
  680. DBGPRINTF((DBG_CONTEXT,
  681. "CGITerminateProcess - DisconnectNamedPipe failed, error %d\n",
  682. GetLastError()));
  683. }
  684. if (pHandler->m_hStdOut != INVALID_HANDLE_VALUE &&
  685. !DisconnectNamedPipe(pHandler->m_hStdOut))
  686. {
  687. DBGPRINTF((DBG_CONTEXT,
  688. "CGITerminateProcess - DisconnectNamedPipe failed, error %d\n",
  689. GetLastError()));
  690. }
  691. } // CGITerminateProcess
  692. BOOL CheckForEndofHeaders(IN LPSTR pbuff,
  693. IN int cbData,
  694. OUT DWORD *pcbIndexStartOfData)
  695. {
  696. //
  697. // If the response starts with a newline (\n, \r\n or \r\r\n), then,
  698. // no headers present, it is all data
  699. //
  700. if (pbuff[0] == '\n')
  701. {
  702. *pcbIndexStartOfData = 1;
  703. return TRUE;
  704. }
  705. if ((pbuff[0] == '\r') && (pbuff[1] == '\n'))
  706. {
  707. *pcbIndexStartOfData = 2;
  708. return TRUE;
  709. }
  710. if ((pbuff[0] == '\r') && (pbuff[1] == '\r') && (pbuff[2] == '\n'))
  711. {
  712. *pcbIndexStartOfData = 3;
  713. return TRUE;
  714. }
  715. //
  716. // Look for two consecutive newline, \n\r\r\n, \n\r\n or \n\n
  717. // No problem with running beyond the end of buffer as the buffer is
  718. // null terminated
  719. //
  720. int index;
  721. for (index = 0; index < cbData - 1; index++)
  722. {
  723. if (pbuff[index] == '\n')
  724. {
  725. if (pbuff[index + 1] == '\n')
  726. {
  727. *pcbIndexStartOfData = index + 2;
  728. return TRUE;
  729. }
  730. else if (pbuff[index + 1] == '\r')
  731. {
  732. if (pbuff[index + 2] == '\n')
  733. {
  734. *pcbIndexStartOfData = index + 3;
  735. return TRUE;
  736. }
  737. else if ((pbuff[index + 2] == '\r') &&
  738. (pbuff[index + 3] == '\n'))
  739. {
  740. *pcbIndexStartOfData = index + 4;
  741. return TRUE;
  742. }
  743. }
  744. }
  745. }
  746. return FALSE;
  747. }
  748. HRESULT
  749. W3_CGI_HANDLER::ProcessCGIOutput()
  750. /*++
  751. Synopsis
  752. This function parses the CGI output and seperates out the headers and
  753. interprets them as appropriate
  754. Return Value
  755. HRESULT
  756. --*/
  757. {
  758. W3_REQUEST *pRequest = QueryW3Context()->QueryRequest();
  759. W3_RESPONSE *pResponse = QueryW3Context()->QueryResponse();
  760. HRESULT hr = S_OK;
  761. STACK_STRA (strReason, 32);
  762. DWORD index = 0;
  763. BOOL fCanKeepConn = FALSE;
  764. //
  765. // The end of CGI headers are marked by a blank line, check to see if
  766. // we've hit that line.
  767. //
  768. LPSTR Headers = (LPSTR)m_bufResponseHeaders.QueryPtr();
  769. DWORD cbIndexStartOfData;
  770. if (!CheckForEndofHeaders(Headers,
  771. m_cbData,
  772. &cbIndexStartOfData))
  773. {
  774. return CGIReadCGIOutput();
  775. }
  776. m_dwRequestState = CgiStateProcessingResponseEntity;
  777. //
  778. // We've found the end of the headers, process them
  779. //
  780. // if request header contains:
  781. //
  782. // Location: xxxx - if starts with /, send doc, otherwise send
  783. // redirect message
  784. // URI: preferred synonym to Location:
  785. // Status: nnn xxxx - Send as status code (HTTP/1.1 nnn xxxx)
  786. //
  787. //
  788. // The first line in the response could (optionally) look like
  789. // HTTP/n.n nnn xxxx\r\n
  790. //
  791. if (!strncmp(Headers, "HTTP/", 5))
  792. {
  793. USHORT statusCode;
  794. LPSTR pszSpace;
  795. LPSTR pszNewline;
  796. if ((pszNewline = strchr(Headers, '\n')) == NULL)
  797. {
  798. goto ErrorExit;
  799. }
  800. index = (DWORD)DIFF(pszNewline - Headers) + 1;
  801. if (pszNewline[-1] == '\r')
  802. pszNewline--;
  803. if (((pszSpace = strchr(Headers, ' ')) == NULL) ||
  804. (pszSpace >= pszNewline))
  805. {
  806. goto ErrorExit;
  807. }
  808. while (pszSpace[0] == ' ')
  809. pszSpace++;
  810. statusCode = (USHORT) atoi(pszSpace);
  811. //
  812. // UL only allows status codes upto 999, so reject others
  813. //
  814. if (statusCode > 999)
  815. {
  816. goto ErrorExit;
  817. }
  818. if (((pszSpace = strchr(pszSpace, ' ')) == NULL) ||
  819. (pszSpace >= pszNewline))
  820. {
  821. goto ErrorExit;
  822. }
  823. while (pszSpace[0] == ' ')
  824. pszSpace++;
  825. if (FAILED(hr = strReason.Copy(pszSpace,
  826. (DWORD)DIFF(pszNewline - pszSpace))) ||
  827. FAILED(hr = pResponse->SetStatus(statusCode, strReason)))
  828. {
  829. goto ErrorExit;
  830. }
  831. if (pResponse->QueryStatusCode() == HttpStatusUnauthorized.statusCode)
  832. {
  833. pResponse->SetStatus(HttpStatusUnauthorized,
  834. Http401Application);
  835. }
  836. }
  837. while ((index + 3) < cbIndexStartOfData)
  838. {
  839. //
  840. // Find the ':' in Header : Value\r\n
  841. //
  842. LPSTR pchColon = strchr(Headers + index, ':');
  843. //
  844. // Find the '\n' in Header : Value\r\n
  845. //
  846. LPSTR pchNewline = strchr(Headers + index, '\n');
  847. //
  848. // Take care of header continuation
  849. //
  850. while (pchNewline[1] == ' ' ||
  851. pchNewline[1] == '\t')
  852. {
  853. pchNewline = strchr(pchNewline + 1, '\n');
  854. }
  855. if ((pchColon == NULL) ||
  856. (pchColon >= pchNewline))
  857. {
  858. goto ErrorExit;
  859. }
  860. //
  861. // Skip over any spaces before the ':'
  862. //
  863. LPSTR pchEndofHeaderName;
  864. for (pchEndofHeaderName = pchColon - 1;
  865. (pchEndofHeaderName >= Headers + index) &&
  866. (*pchEndofHeaderName == ' ');
  867. pchEndofHeaderName--)
  868. {}
  869. //
  870. // Copy the header name
  871. //
  872. STACK_STRA (strHeaderName, 32);
  873. if (FAILED(hr = strHeaderName.Copy(Headers + index,
  874. (DWORD)DIFF(pchEndofHeaderName - Headers) - index + 1)))
  875. {
  876. goto ErrorExit;
  877. }
  878. //
  879. // Skip over the ':' and any trailing spaces
  880. //
  881. for (index = (DWORD)DIFF(pchColon - Headers) + 1;
  882. Headers[index] == ' ';
  883. index++)
  884. {}
  885. //
  886. // Skip over any spaces before the '\n'
  887. //
  888. LPSTR pchEndofHeaderValue;
  889. for (pchEndofHeaderValue = pchNewline - 1;
  890. (pchEndofHeaderValue >= Headers + index) &&
  891. ((*pchEndofHeaderValue == ' ') ||
  892. (*pchEndofHeaderValue == '\r'));
  893. pchEndofHeaderValue--)
  894. {}
  895. //
  896. // Copy the header value
  897. //
  898. STACK_STRA (strHeaderValue, 32);
  899. if (FAILED(hr = strHeaderValue.Copy(Headers + index,
  900. (DWORD)DIFF(pchEndofHeaderValue - Headers) - index + 1)))
  901. {
  902. goto ErrorExit;
  903. }
  904. if (!_stricmp("Status", strHeaderName.QueryStr()))
  905. {
  906. USHORT statusCode = (USHORT) atoi(strHeaderValue.QueryStr());
  907. //
  908. // UL only allows status codes upto 999, so reject others
  909. //
  910. if (statusCode > 999)
  911. {
  912. goto ErrorExit;
  913. }
  914. CHAR * pchReason = strchr(strHeaderValue.QueryStr(), ' ');
  915. if (pchReason != NULL)
  916. {
  917. pchReason++;
  918. if (FAILED(hr = strReason.Copy(pchReason)) ||
  919. FAILED(hr = pResponse->SetStatus(statusCode, strReason)))
  920. {
  921. goto ErrorExit;
  922. }
  923. if (pResponse->QueryStatusCode() == HttpStatusUnauthorized.statusCode)
  924. {
  925. pResponse->SetStatus(HttpStatusUnauthorized,
  926. Http401Application);
  927. }
  928. }
  929. }
  930. else if (!_stricmp("Location", strHeaderName.QueryStr()) ||
  931. !_stricmp("URI", strHeaderName.QueryStr()))
  932. {
  933. //
  934. // The CGI script is redirecting us to another URL. If it
  935. // begins with a '/', then get it, otherwise send a redirect
  936. // message
  937. //
  938. m_dwRequestState = CgiStateDoneWithRequest;
  939. m_fResponseRedirected = TRUE;
  940. if (strHeaderValue.QueryStr()[0] == '/')
  941. {
  942. //
  943. // Execute a child request
  944. //
  945. pResponse->Clear();
  946. pResponse->SetStatus(HttpStatusOk);
  947. if (FAILED(hr = pRequest->SetUrlA(strHeaderValue)) ||
  948. FAILED(hr = QueryW3Context()->ExecuteChildRequest(
  949. pRequest,
  950. FALSE,
  951. W3_FLAG_ASYNC )))
  952. {
  953. goto ErrorExit;
  954. }
  955. return S_OK;
  956. }
  957. else
  958. {
  959. HTTP_STATUS httpStatus;
  960. pResponse->GetStatus(&httpStatus);
  961. //
  962. // Plain old redirect since this was not a "local" URL
  963. // If the CGI had already set the status to some 3xx value,
  964. // honor it
  965. //
  966. if (FAILED(hr = QueryW3Context()->SetupHttpRedirect(
  967. strHeaderValue,
  968. FALSE,
  969. ((httpStatus.statusCode / 100) == 3) ? httpStatus : HttpStatusRedirect)))
  970. {
  971. goto ErrorExit;
  972. }
  973. }
  974. }
  975. else
  976. {
  977. //
  978. // Remember the Content-Length the cgi specified
  979. //
  980. if (!_stricmp("Content-Length", strHeaderName.QueryStr()))
  981. {
  982. fCanKeepConn = TRUE;
  983. m_bytesToSend = atoi(strHeaderValue.QueryStr());
  984. }
  985. else if (!_stricmp("Transfer-Encoding", strHeaderName.QueryStr()) &&
  986. !_stricmp("chunked", strHeaderValue.QueryStr()))
  987. {
  988. fCanKeepConn = TRUE;
  989. }
  990. else if (!_stricmp("Connection", strHeaderName.QueryStr()) &&
  991. !_stricmp("close", strHeaderValue.QueryStr()))
  992. {
  993. QueryW3Context()->SetDisconnect(TRUE);
  994. }
  995. if (strHeaderName.QueryCCH() > MAXUSHORT ||
  996. strHeaderValue.QueryCCH() > MAXUSHORT)
  997. {
  998. hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
  999. goto ErrorExit;
  1000. }
  1001. //
  1002. // Copy any other fields the script specified
  1003. //
  1004. hr = pResponse->SetHeader(strHeaderName.QueryStr(),
  1005. (USHORT)strHeaderName.QueryCCH(),
  1006. strHeaderValue.QueryStr(),
  1007. (USHORT)strHeaderValue.QueryCCH());
  1008. if (FAILED(hr))
  1009. {
  1010. goto ErrorExit;
  1011. }
  1012. }
  1013. index = (DWORD)DIFF(pchNewline - Headers) + 1;
  1014. } // while
  1015. if (m_fResponseRedirected)
  1016. {
  1017. return QueryW3Context()->SendResponse(W3_FLAG_ASYNC);
  1018. }
  1019. //
  1020. // We allow CGI to send its own entity if no custom-error is defined
  1021. //
  1022. if (pResponse->QueryStatusCode() >= 400)
  1023. {
  1024. BOOL fIsFileError;
  1025. STACK_STRU ( strError, 64 );
  1026. HTTP_SUB_ERROR subError;
  1027. pResponse->QuerySubError( &subError );
  1028. W3_METADATA *pMetaData = QueryW3Context()->QueryUrlContext()->QueryMetaData();
  1029. hr = pMetaData->FindCustomError( pResponse->QueryStatusCode(),
  1030. subError.mdSubError,
  1031. &fIsFileError,
  1032. &strError );
  1033. if (SUCCEEDED(hr))
  1034. {
  1035. //
  1036. // Found a custom error, send it
  1037. //
  1038. m_dwRequestState = CgiStateDoneWithRequest;
  1039. return QueryW3Context()->SendResponse(W3_FLAG_ASYNC);
  1040. }
  1041. }
  1042. //
  1043. // If the CGI did not say how much data was present, mark the
  1044. // connection for closing
  1045. //
  1046. if (!fCanKeepConn)
  1047. {
  1048. QueryW3Context()->SetDisconnect(TRUE);
  1049. }
  1050. //
  1051. // Now send any data trailing the headers
  1052. //
  1053. if (cbIndexStartOfData < m_cbData)
  1054. {
  1055. if (m_bytesToSend < m_cbData - cbIndexStartOfData)
  1056. {
  1057. m_cbData = m_bytesToSend + cbIndexStartOfData;
  1058. }
  1059. if (FAILED(hr = pResponse->AddMemoryChunkByReference(
  1060. Headers + cbIndexStartOfData,
  1061. m_cbData - cbIndexStartOfData)))
  1062. {
  1063. return hr;
  1064. }
  1065. m_bytesToSend -= m_cbData - cbIndexStartOfData;
  1066. if (m_bytesToSend == 0)
  1067. {
  1068. m_dwRequestState = CgiStateDoneWithRequest;
  1069. }
  1070. }
  1071. return QueryW3Context()->SendResponse(W3_FLAG_ASYNC
  1072. | W3_FLAG_MORE_DATA
  1073. | W3_FLAG_NO_CONTENT_LENGTH
  1074. | W3_FLAG_NO_ERROR_BODY);
  1075. ErrorExit:
  1076. m_dwRequestState = CgiStateProcessingResponseHeaders;
  1077. if (FAILED(hr))
  1078. {
  1079. return hr;
  1080. }
  1081. return E_FAIL;
  1082. }
  1083. HRESULT W3_CGI_HANDLER::CGIReadRequestEntity(BOOL *pfIoPending)
  1084. /*++
  1085. Synopsis
  1086. This function reads the next chunk of request entity
  1087. Arguments
  1088. pfIoPending: On return indicates if there is an I/O pending
  1089. Return Value
  1090. HRESULT
  1091. --*/
  1092. {
  1093. if (m_bytesToReceive == 0)
  1094. {
  1095. *pfIoPending = FALSE;
  1096. return S_OK;
  1097. }
  1098. HRESULT hr;
  1099. DWORD cbRead;
  1100. if (FAILED(hr = QueryW3Context()->ReceiveEntity(
  1101. W3_FLAG_ASYNC,
  1102. m_DataBuffer,
  1103. (DWORD)min(m_bytesToReceive, sizeof(m_DataBuffer)),
  1104. &cbRead)))
  1105. {
  1106. if (hr == HRESULT_FROM_WIN32(ERROR_HANDLE_EOF))
  1107. {
  1108. *pfIoPending = FALSE;
  1109. return S_OK;
  1110. }
  1111. DBGPRINTF((DBG_CONTEXT,
  1112. "CGIReadEntity Error reading gateway data, hr %x\n",
  1113. hr));
  1114. return hr;
  1115. }
  1116. *pfIoPending = TRUE;
  1117. return S_OK;
  1118. }
  1119. HRESULT W3_CGI_HANDLER::CGIWriteResponseEntity()
  1120. /*++
  1121. Synopsis
  1122. This function writes the next chunk of response entity
  1123. Arguments
  1124. None
  1125. Return Value
  1126. HRESULT
  1127. --*/
  1128. {
  1129. W3_RESPONSE *pResponse = QueryW3Context()->QueryResponse();
  1130. pResponse->Clear( TRUE );
  1131. //
  1132. // At this point, we got to be reading the entity
  1133. //
  1134. DBG_ASSERT(m_dwRequestState == CgiStateProcessingResponseEntity);
  1135. if (m_bytesToSend <= m_cbData)
  1136. {
  1137. m_cbData = m_bytesToSend;
  1138. m_bytesToSend = 0;
  1139. m_dwRequestState = CgiStateDoneWithRequest;
  1140. }
  1141. else
  1142. {
  1143. m_bytesToSend -= m_cbData;
  1144. }
  1145. HRESULT hr;
  1146. if (FAILED(hr = pResponse->AddMemoryChunkByReference(m_DataBuffer,
  1147. m_cbData)))
  1148. {
  1149. return hr;
  1150. }
  1151. return QueryW3Context()->SendEntity(W3_FLAG_ASYNC | W3_FLAG_MORE_DATA);
  1152. }
  1153. HRESULT W3_CGI_HANDLER::CGIReadCGIOutput()
  1154. /*++
  1155. Synopsis
  1156. This function Reads the next chunk of data from the CGI
  1157. Arguments
  1158. None
  1159. Return Value
  1160. S_OK if async I/O posted
  1161. Failure otherwise
  1162. --*/
  1163. {
  1164. if (!ReadFile(m_hStdOut,
  1165. m_DataBuffer,
  1166. MAX_CGI_BUFFERING,
  1167. NULL,
  1168. &m_Overlapped))
  1169. {
  1170. DWORD dwErr = GetLastError();
  1171. if (dwErr == ERROR_IO_PENDING)
  1172. return S_OK;
  1173. if (dwErr != ERROR_BROKEN_PIPE)
  1174. {
  1175. DBGPRINTF((DBG_CONTEXT,
  1176. "ReadFile from child stdout failed, error %d\n",
  1177. GetLastError()));
  1178. }
  1179. return HRESULT_FROM_WIN32(dwErr);
  1180. }
  1181. return S_OK;
  1182. }
  1183. HRESULT W3_CGI_HANDLER::CGIWriteCGIInput()
  1184. /*++
  1185. Synopsis
  1186. This function
  1187. Arguments
  1188. None
  1189. Return Value
  1190. HRESULT
  1191. --*/
  1192. {
  1193. if (!WriteFile(m_hStdIn,
  1194. m_DataBuffer,
  1195. m_cbData,
  1196. NULL,
  1197. &m_Overlapped))
  1198. {
  1199. if (GetLastError() == ERROR_IO_PENDING)
  1200. return S_OK;
  1201. DBGPRINTF((DBG_CONTEXT, "WriteFile failed, error %d\n",
  1202. GetLastError()));
  1203. return HRESULT_FROM_WIN32(GetLastError());
  1204. }
  1205. return S_OK;
  1206. }
  1207. HRESULT W3_CGI_HANDLER::CGIContinueOnPipeCompletion(
  1208. BOOL *pfIsCgiError)
  1209. /*++
  1210. Synopsis
  1211. This function continues on I/O completion to a pipe.
  1212. If the I/O completion was because of a write on the pipe, it reads
  1213. the next chunk of request entity, or if there is no more request entity,
  1214. reads the first chunk of CGI output.
  1215. If the I/O completion was because of a read on the pipe, it processes
  1216. that data by either adding it to the header buffer or sending it to the
  1217. client
  1218. Arguments
  1219. pfIsCgiError - Tells whether any error occurring inside this function
  1220. was the fault of the CGI or the client
  1221. Return Value
  1222. HRESULT
  1223. --*/
  1224. {
  1225. HRESULT hr;
  1226. if (m_dwRequestState == CgiStateProcessingRequestEntity)
  1227. {
  1228. BOOL fIoPending = FALSE;
  1229. if (FAILED(hr = CGIReadRequestEntity(&fIoPending)) ||
  1230. (fIoPending == TRUE))
  1231. {
  1232. *pfIsCgiError = FALSE;
  1233. return hr;
  1234. }
  1235. //
  1236. // There was no more request entity to read
  1237. //
  1238. DBG_ASSERT(fIoPending == FALSE);
  1239. //
  1240. // If this is an nph cgi, we do not parse header
  1241. //
  1242. if (QueryIsNphCgi())
  1243. {
  1244. m_dwRequestState = CgiStateProcessingResponseEntity;
  1245. }
  1246. else
  1247. {
  1248. m_dwRequestState = CgiStateProcessingResponseHeaders;
  1249. }
  1250. m_cbData = 0;
  1251. if (FAILED(hr = CGIReadCGIOutput()))
  1252. {
  1253. *pfIsCgiError = TRUE;
  1254. }
  1255. return hr;
  1256. }
  1257. else if (m_dwRequestState == CgiStateProcessingResponseHeaders)
  1258. {
  1259. if (FAILED(hr = ProcessCGIOutput()))
  1260. {
  1261. *pfIsCgiError = TRUE;
  1262. }
  1263. return hr;
  1264. }
  1265. else
  1266. {
  1267. DBG_ASSERT(m_dwRequestState == CgiStateProcessingResponseEntity);
  1268. if (FAILED(hr = CGIWriteResponseEntity()))
  1269. {
  1270. *pfIsCgiError = FALSE;
  1271. }
  1272. return hr;
  1273. }
  1274. }
  1275. HRESULT W3_CGI_HANDLER::CGIStartProcessing()
  1276. /*++
  1277. Synopsis
  1278. This function kicks off the CGI processing by reading request entity
  1279. if any or reading the first chunk of CGI output
  1280. Arguments
  1281. None
  1282. Return Value
  1283. HRESULT
  1284. --*/
  1285. {
  1286. DWORD cbAvailableAlready;
  1287. PVOID pbAvailableAlready;
  1288. m_dwRequestState = CgiStateProcessingRequestEntity;
  1289. //
  1290. // First we have to write any entity body to the program's stdin
  1291. //
  1292. // Start with the Entity Body already read
  1293. //
  1294. QueryW3Context()->QueryAlreadyAvailableEntity( &pbAvailableAlready,
  1295. &cbAvailableAlready );
  1296. m_bytesToReceive = QueryW3Context()->QueryRemainingEntityFromUl();
  1297. if ( cbAvailableAlready != 0 )
  1298. {
  1299. if (WriteFile(m_hStdIn,
  1300. pbAvailableAlready,
  1301. cbAvailableAlready,
  1302. NULL,
  1303. &m_Overlapped))
  1304. {
  1305. return S_OK;
  1306. }
  1307. if (GetLastError() == ERROR_IO_PENDING)
  1308. {
  1309. return S_OK;
  1310. }
  1311. DBGPRINTF((DBG_CONTEXT, "WriteFile failed, error %d\n",
  1312. GetLastError()));
  1313. //
  1314. // If we could not write the request entity, for example because
  1315. // the CGI did not wait to read the entity, ignore the error and
  1316. // continue on to reading the output
  1317. //
  1318. }
  1319. //
  1320. // Now continue with either reading the rest of the request entity
  1321. // or the CGI response
  1322. //
  1323. BOOL fIsCgiError;
  1324. return CGIContinueOnPipeCompletion(&fIsCgiError);
  1325. }
  1326. HRESULT W3_CGI_HANDLER::CGIContinueOnClientCompletion()
  1327. {
  1328. if (m_dwRequestState == CgiStateProcessingRequestEntity)
  1329. {
  1330. if (SUCCEEDED(CGIWriteCGIInput()))
  1331. {
  1332. return S_OK;
  1333. }
  1334. //
  1335. // If we could not write the request entity, for example because
  1336. // the CGI did not wait to read the entity, ignore the error and
  1337. // continue on to reading the output
  1338. //
  1339. //
  1340. // If this is an nph cgi, we do not parse header
  1341. //
  1342. if (QueryIsNphCgi())
  1343. {
  1344. m_dwRequestState = CgiStateProcessingResponseEntity;
  1345. }
  1346. else
  1347. {
  1348. m_dwRequestState = CgiStateProcessingResponseHeaders;
  1349. }
  1350. m_cbData = 0;
  1351. }
  1352. return CGIReadCGIOutput();
  1353. }
  1354. CONTEXT_STATUS W3_CGI_HANDLER::OnCompletion(DWORD cbCompletion,
  1355. DWORD dwCompletionStatus)
  1356. {
  1357. DBG_ASSERT(m_dwRequestState != CgiStateProcessingResponseHeaders);
  1358. HRESULT hr = S_OK;
  1359. if (dwCompletionStatus)
  1360. {
  1361. hr = HRESULT_FROM_WIN32(dwCompletionStatus);
  1362. DBGPRINTF((DBG_CONTEXT, "Error %d on CGI_HANDLER::OnCompletion\n", dwCompletionStatus));
  1363. }
  1364. //
  1365. // Is this completion for the entity body preload? If so note the
  1366. // number of bytes and start handling the CGI request
  1367. //
  1368. if (!m_fEntityBodyPreloadComplete)
  1369. {
  1370. BOOL fComplete = FALSE;
  1371. //
  1372. // This completion is for entity body preload
  1373. //
  1374. W3_REQUEST *pRequest = QueryW3Context()->QueryRequest();
  1375. hr = pRequest->PreloadCompletion(QueryW3Context(),
  1376. cbCompletion,
  1377. dwCompletionStatus,
  1378. &fComplete);
  1379. if (SUCCEEDED(hr))
  1380. {
  1381. if (!fComplete)
  1382. {
  1383. return CONTEXT_STATUS_PENDING;
  1384. }
  1385. m_fEntityBodyPreloadComplete = TRUE;
  1386. //
  1387. // Finally we can call the CGI
  1388. //
  1389. return DoWork();
  1390. }
  1391. }
  1392. //
  1393. // UL can return EOF on the async completion, rather than on the
  1394. // original call (especially on chunked requests), treat them as no error
  1395. //
  1396. if (hr == HRESULT_FROM_WIN32(ERROR_HANDLE_EOF))
  1397. {
  1398. hr = S_OK;
  1399. }
  1400. W3_RESPONSE *pResponse = QueryW3Context()->QueryResponse();
  1401. DBG_ASSERT(pResponse != NULL);
  1402. if (SUCCEEDED(hr) &&
  1403. m_dwRequestState != CgiStateDoneWithRequest)
  1404. {
  1405. if (m_dwRequestState == CgiStateProcessingRequestEntity)
  1406. {
  1407. m_bytesToReceive -= cbCompletion;
  1408. m_cbData = cbCompletion;
  1409. }
  1410. if (SUCCEEDED(hr = CGIContinueOnClientCompletion()))
  1411. {
  1412. return CONTEXT_STATUS_PENDING;
  1413. }
  1414. if (m_dwRequestState != CgiStateProcessingResponseEntity &&
  1415. m_dwRequestState != CgiStateDoneWithRequest)
  1416. {
  1417. pResponse->SetStatus(HttpStatusBadGateway,
  1418. Http502PrematureExit);
  1419. }
  1420. }
  1421. else
  1422. {
  1423. if (hr == HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY))
  1424. {
  1425. pResponse->SetStatus(HttpStatusServerError);
  1426. }
  1427. else if ((m_dwRequestState == CgiStateProcessingRequestEntity) ||
  1428. !m_fEntityBodyPreloadComplete)
  1429. {
  1430. pResponse->SetStatus(HttpStatusBadRequest);
  1431. }
  1432. }
  1433. if (FAILED(hr))
  1434. {
  1435. QueryW3Context()->SetErrorStatus(hr);
  1436. }
  1437. if (!m_fResponseRedirected &&
  1438. m_dwRequestState != CgiStateProcessingResponseEntity &&
  1439. m_dwRequestState != CgiStateDoneWithRequest)
  1440. {
  1441. //
  1442. // If we reached here, i.e. no response was sent, status should be
  1443. // 502 or 400 or 500
  1444. //
  1445. DBG_ASSERT(pResponse->QueryStatusCode() == HttpStatusBadGateway.statusCode ||
  1446. pResponse->QueryStatusCode() == HttpStatusBadRequest.statusCode ||
  1447. pResponse->QueryStatusCode() == HttpStatusServerError.statusCode );
  1448. QueryW3Context()->SendResponse(W3_FLAG_SYNC);
  1449. }
  1450. return CONTEXT_STATUS_CONTINUE;
  1451. }
  1452. CONTEXT_STATUS
  1453. W3_CGI_HANDLER::DoWork()
  1454. {
  1455. W3_CONTEXT *pW3Context = QueryW3Context();
  1456. DBG_ASSERT( pW3Context != NULL );
  1457. W3_RESPONSE *pResponse;
  1458. W3_REQUEST *pRequest;
  1459. URL_CONTEXT *pUrlContext;
  1460. W3_METADATA *pMetaData;
  1461. HRESULT hr = S_OK;
  1462. STACK_STRU( strSSICommandLine, 256 );
  1463. STRU *pstrPhysical;
  1464. HANDLE hImpersonationToken;
  1465. HANDLE hPrimaryToken;
  1466. DWORD dwFlags = DETACHED_PROCESS;
  1467. STACK_STRU( strCmdLine, 256);
  1468. BOOL fIsCmdExe;
  1469. WCHAR * pszWorkingDir;
  1470. STACK_BUFFER( buffEnv, MAX_CGI_BUFFERING);
  1471. STACK_STRU ( strApplicationName, 256);
  1472. WCHAR * pszCommandLine = NULL;
  1473. DWORD dwFileAttributes = 0;
  1474. BOOL fImageDisabled = FALSE;
  1475. BOOL fIsVrToken;
  1476. STARTUPINFO startupinfo;
  1477. pRequest = pW3Context->QueryRequest();
  1478. DBG_ASSERT( pRequest != NULL );
  1479. pResponse = pW3Context->QueryResponse();
  1480. DBG_ASSERT( pResponse != NULL );
  1481. ZeroMemory(&startupinfo, sizeof(startupinfo));
  1482. startupinfo.cb = sizeof(startupinfo);
  1483. startupinfo.hStdOutput = INVALID_HANDLE_VALUE;
  1484. startupinfo.hStdInput = INVALID_HANDLE_VALUE;
  1485. pResponse->SetStatus( HttpStatusOk );
  1486. if (!m_fEntityBodyPreloadComplete)
  1487. {
  1488. BOOL fComplete = FALSE;
  1489. hr = pRequest->PreloadEntityBody( pW3Context,
  1490. &fComplete );
  1491. if (FAILED(hr))
  1492. {
  1493. goto Exit;
  1494. }
  1495. if (!fComplete)
  1496. {
  1497. return CONTEXT_STATUS_PENDING;
  1498. }
  1499. m_fEntityBodyPreloadComplete = TRUE;
  1500. }
  1501. DBG_ASSERT( m_fEntityBodyPreloadComplete );
  1502. pUrlContext = pW3Context->QueryUrlContext();
  1503. DBG_ASSERT( pUrlContext != NULL );
  1504. pMetaData = pUrlContext->QueryMetaData();
  1505. DBG_ASSERT( pMetaData != NULL );
  1506. if (m_pszSSICommandLine == NULL)
  1507. {
  1508. pstrPhysical = pUrlContext->QueryPhysicalPath();
  1509. DBG_ASSERT(pstrPhysical != NULL);
  1510. }
  1511. else
  1512. {
  1513. hr = strSSICommandLine.CopyA(m_pszSSICommandLine);
  1514. if (FAILED(hr))
  1515. {
  1516. goto Exit;
  1517. }
  1518. pstrPhysical = &strSSICommandLine;
  1519. }
  1520. hImpersonationToken = pW3Context->QueryImpersonationToken( &fIsVrToken );
  1521. hPrimaryToken = pW3Context->QueryPrimaryToken();
  1522. if (QueryScriptMapEntry() != NULL &&
  1523. !QueryScriptMapEntry()->QueryIsStarScriptMap())
  1524. {
  1525. STRU *pstrExe = QueryScriptMapEntry()->QueryExecutable();
  1526. //
  1527. // Check to see if script mapped CGI is enabled.
  1528. //
  1529. if ( g_pW3Server->QueryIsCgiImageEnabled( pstrExe->QueryStr() ) == FALSE )
  1530. {
  1531. DBGPRINTF(( DBG_CONTEXT,
  1532. "CGI image disabled: %S.\r\n",
  1533. pstrExe->QueryStr() ));
  1534. hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
  1535. fImageDisabled = TRUE;
  1536. goto Exit;
  1537. }
  1538. if (wcschr(pstrPhysical->QueryStr(), '\"') != NULL)
  1539. {
  1540. DBGPRINTF((DBG_CONTEXT,
  1541. "Refusing request for CGI due to \" in path\n"));
  1542. hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
  1543. goto Exit;
  1544. }
  1545. fIsCmdExe = IsCmdExe(pstrExe->QueryStr());
  1546. STACK_STRU (strDecodedQueryString, MAX_PATH);
  1547. if (FAILED(hr = SetupCmdLine(pRequest, &strDecodedQueryString)))
  1548. {
  1549. goto Exit;
  1550. }
  1551. STACK_BUFFER (bufCmdLine, MAX_PATH);
  1552. DWORD cchLen = pstrExe->QueryCCH() +
  1553. pstrPhysical->QueryCCH() +
  1554. strDecodedQueryString.QueryCCH();
  1555. if (!bufCmdLine.Resize(cchLen*sizeof(WCHAR) + sizeof(WCHAR)))
  1556. {
  1557. hr = HRESULT_FROM_WIN32(GetLastError());
  1558. goto Exit;
  1559. }
  1560. cchLen = _snwprintf((LPWSTR)bufCmdLine.QueryPtr(),
  1561. cchLen,
  1562. pstrExe->QueryStr(),
  1563. pstrPhysical->QueryStr(),
  1564. strDecodedQueryString.QueryStr());
  1565. if (FAILED(hr = strCmdLine.Copy((LPWSTR)bufCmdLine.QueryPtr(),
  1566. cchLen)))
  1567. {
  1568. goto Exit;
  1569. }
  1570. }
  1571. else
  1572. {
  1573. //
  1574. // Check to see if non-script-mapped CGI is enabled
  1575. //
  1576. if ( g_pW3Server->QueryIsCgiImageEnabled( pstrPhysical->QueryStr() ) == FALSE )
  1577. {
  1578. DBGPRINTF(( DBG_CONTEXT,
  1579. "CGI image disabled: %S.\r\n",
  1580. pstrPhysical->QueryStr() ));
  1581. hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
  1582. fImageDisabled = TRUE;
  1583. goto Exit;
  1584. }
  1585. fIsCmdExe = IsCmdExe(pstrPhysical->QueryStr());
  1586. if (FAILED(hr = strCmdLine.Copy(L"\"", 1)) ||
  1587. FAILED(hr = strCmdLine.Append(*pstrPhysical)) ||
  1588. FAILED(hr = strCmdLine.Append(L"\" ", 2)) ||
  1589. FAILED(hr = SetupCmdLine(pRequest, &strCmdLine)))
  1590. {
  1591. goto Exit;
  1592. }
  1593. }
  1594. if (FAILED(hr = SetupChildEnv(&buffEnv)))
  1595. {
  1596. goto Exit;
  1597. }
  1598. pszWorkingDir = pMetaData->QueryVrPath()->QueryStr();
  1599. //
  1600. // Check to see if we're spawning cmd.exe, if so, refuse the request if
  1601. // there are any special shell characters. Note we do the check here
  1602. // so that the command line has been fully unescaped
  1603. //
  1604. // Also, if invoking cmd.exe for a UNC script then don't set the
  1605. // working directory. Otherwise cmd.exe will complain about
  1606. // working dir on UNC being not supported, which will destroy the
  1607. // HTTP headers.
  1608. //
  1609. if (fIsCmdExe)
  1610. {
  1611. if (ISUNC(pstrPhysical->QueryStr()))
  1612. {
  1613. pszWorkingDir = NULL;
  1614. }
  1615. DWORD i;
  1616. //
  1617. // We'll either match one of the characters or the '\0'
  1618. //
  1619. i = (DWORD)wcscspn(strCmdLine.QueryStr(), L"&|(),;%<>");
  1620. if (strCmdLine.QueryStr()[i])
  1621. {
  1622. DBGPRINTF((DBG_CONTEXT,
  1623. "Refusing request for command shell due to special characters\n"));
  1624. hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
  1625. goto Exit;
  1626. }
  1627. //
  1628. // If this is a cmd.exe invocation, then ensure that the script
  1629. // does exist
  1630. //
  1631. hr = pW3Context->CheckPathInfoExists(NULL);
  1632. if (FAILED(hr))
  1633. {
  1634. goto Exit;
  1635. }
  1636. }
  1637. //
  1638. // Now check if it is an nph cgi (if not already)
  1639. //
  1640. if (!m_fIsNphCgi)
  1641. {
  1642. m_fIsNphCgi = IsNphCgi( pUrlContext->QueryUrlInfo()->QueryProcessedUrl()->QueryStr() );
  1643. }
  1644. if (m_fIsNphCgi)
  1645. {
  1646. pW3Context->SetDisconnect(TRUE);
  1647. }
  1648. //
  1649. // We specify an unnamed desktop so a new windowstation will be
  1650. // created in the context of the calling user
  1651. //
  1652. startupinfo.lpDesktop = L"";
  1653. //
  1654. // Setup the pipes information
  1655. //
  1656. if (FAILED(hr = SetupChildPipes(&m_hStdOut,
  1657. &m_hStdIn,
  1658. &startupinfo)))
  1659. {
  1660. goto Exit;
  1661. }
  1662. if (pMetaData->QueryCreateProcessNewConsole())
  1663. {
  1664. dwFlags = CREATE_NEW_CONSOLE;
  1665. }
  1666. //
  1667. // Depending what type of CGI this is (SSI command exec, Scriptmap,
  1668. // Explicit), the command line and application path are different
  1669. //
  1670. if (m_pszSSICommandLine != NULL )
  1671. {
  1672. pszCommandLine = strSSICommandLine.QueryStr();
  1673. }
  1674. else
  1675. {
  1676. if (QueryScriptMapEntry() == NULL )
  1677. {
  1678. if (FAILED(hr = MakePathCanonicalizationProof(pstrPhysical->QueryStr(),
  1679. &strApplicationName)))
  1680. {
  1681. goto Exit;
  1682. }
  1683. }
  1684. pszCommandLine = strCmdLine.QueryStr();
  1685. }
  1686. if (!pMetaData->QueryCreateProcessAsUser())
  1687. {
  1688. //
  1689. // If we are not creating the process as user, make sure to fix the
  1690. // default ACL on the token
  1691. //
  1692. if ((fIsVrToken && !pW3Context->QueryVrToken()->QueryOOPToken()) ||
  1693. (!fIsVrToken && !pW3Context->QueryUserContext()->QueryIsCachedToken()) ||
  1694. (!fIsVrToken && !pW3Context->QueryUserContext()->QueryCachedToken()->QueryOOPToken()))
  1695. {
  1696. if (FAILED(hr = AddWpgToTokenDefaultDacl(hImpersonationToken)))
  1697. {
  1698. goto Exit;
  1699. }
  1700. }
  1701. }
  1702. //
  1703. // Spawn the process and close the handles since we don't need them
  1704. //
  1705. if (!SetThreadToken(NULL, hImpersonationToken))
  1706. {
  1707. DBGPRINTF((DBG_CONTEXT,
  1708. "SetThreadToken failed, error %d\n",
  1709. GetLastError()));
  1710. hr = HRESULT_FROM_WIN32(GetLastError());
  1711. goto Exit;
  1712. }
  1713. PROCESS_INFORMATION processinfo;
  1714. BOOL fThreadsIncremented = FALSE;
  1715. if (QueryScriptMapEntry() == NULL)
  1716. {
  1717. if (ISUNC(pstrPhysical->QueryStr()))
  1718. {
  1719. ThreadPoolSetInfo( ThreadPoolIncMaxPoolThreads, 0 );
  1720. fThreadsIncremented = TRUE;
  1721. }
  1722. }
  1723. else
  1724. {
  1725. if (ISUNC(pszCommandLine))
  1726. {
  1727. ThreadPoolSetInfo( ThreadPoolIncMaxPoolThreads, 0 );
  1728. fThreadsIncremented = TRUE;
  1729. }
  1730. }
  1731. if (!pMetaData->QueryCreateProcessAsUser())
  1732. {
  1733. if (!CreateProcess(strApplicationName.QueryCCH() ? strApplicationName.QueryStr() : NULL,
  1734. pszCommandLine,
  1735. NULL, // Process security
  1736. NULL, // Thread security
  1737. TRUE, // Inherit handles
  1738. dwFlags | CREATE_UNICODE_ENVIRONMENT,
  1739. buffEnv.QueryPtr(),
  1740. pszWorkingDir,
  1741. &startupinfo,
  1742. &processinfo))
  1743. {
  1744. DBGPRINTF((DBG_CONTEXT,
  1745. "CreateProcess failed, error %d\n",
  1746. GetLastError()));
  1747. hr = HRESULT_FROM_WIN32(GetLastError());
  1748. }
  1749. }
  1750. else
  1751. {
  1752. if (!CreateProcessAsUser(hPrimaryToken,
  1753. strApplicationName.QueryCCH() ? strApplicationName.QueryStr() : NULL,
  1754. pszCommandLine,
  1755. NULL, // Process security
  1756. NULL, // Thread security
  1757. TRUE, // Inherit handles
  1758. dwFlags | CREATE_UNICODE_ENVIRONMENT,
  1759. buffEnv.QueryPtr(),
  1760. pszWorkingDir,
  1761. &startupinfo,
  1762. &processinfo))
  1763. {
  1764. DBGPRINTF((DBG_CONTEXT,
  1765. "CreateProcessAsUser failed, error %d\n",
  1766. GetLastError()));
  1767. hr = HRESULT_FROM_WIN32(GetLastError());
  1768. }
  1769. }
  1770. if (fThreadsIncremented)
  1771. {
  1772. ThreadPoolSetInfo( ThreadPoolDecMaxPoolThreads, 0 );
  1773. }
  1774. DBG_REQUIRE(RevertToSelf());
  1775. if (FAILED(hr))
  1776. {
  1777. goto Exit;
  1778. }
  1779. DBG_REQUIRE(CloseHandle(startupinfo.hStdInput));
  1780. startupinfo.hStdInput = INVALID_HANDLE_VALUE;
  1781. DBG_REQUIRE(CloseHandle(startupinfo.hStdOutput));
  1782. startupinfo.hStdOutput = INVALID_HANDLE_VALUE;
  1783. DBG_REQUIRE(CloseHandle(processinfo.hThread));
  1784. //
  1785. // Save the process handle in case we need to terminate it later on
  1786. //
  1787. m_hProcess = processinfo.hProcess;
  1788. //
  1789. // Schedule a callback to kill the process if it doesn't die
  1790. // in a timely manner
  1791. //
  1792. if (!CreateTimerQueueTimer(&m_hTimer,
  1793. NULL,
  1794. CGITerminateProcess,
  1795. this,
  1796. pMetaData->QueryScriptTimeout() * 1000,
  1797. 0,
  1798. WT_EXECUTEONLYONCE))
  1799. {
  1800. DBGPRINTF((DBG_CONTEXT,
  1801. "CreateTimerQueueTimer failed, error %d\n",
  1802. GetLastError()));
  1803. }
  1804. if (SUCCEEDED(hr = CGIStartProcessing()))
  1805. {
  1806. return CONTEXT_STATUS_PENDING;
  1807. }
  1808. Exit:
  1809. if (startupinfo.hStdInput != INVALID_HANDLE_VALUE)
  1810. {
  1811. DBG_REQUIRE(CloseHandle(startupinfo.hStdInput));
  1812. startupinfo.hStdInput = INVALID_HANDLE_VALUE;
  1813. }
  1814. if (startupinfo.hStdOutput != INVALID_HANDLE_VALUE)
  1815. {
  1816. DBG_REQUIRE(CloseHandle(startupinfo.hStdOutput));
  1817. startupinfo.hStdOutput = INVALID_HANDLE_VALUE;
  1818. }
  1819. if (FAILED(hr))
  1820. {
  1821. switch (WIN32_FROM_HRESULT(hr))
  1822. {
  1823. case ERROR_FILE_NOT_FOUND:
  1824. case ERROR_PATH_NOT_FOUND:
  1825. case ERROR_INVALID_NAME:
  1826. if (fImageDisabled)
  1827. {
  1828. pResponse->SetStatus(HttpStatusNotFound,
  1829. Http404DeniedByPolicy);
  1830. }
  1831. else
  1832. {
  1833. pResponse->SetStatus(HttpStatusNotFound);
  1834. }
  1835. break;
  1836. case ERROR_ACCESS_DENIED:
  1837. case ERROR_ACCOUNT_DISABLED:
  1838. case ERROR_LOGON_FAILURE:
  1839. pResponse->SetStatus(HttpStatusUnauthorized,
  1840. Http401Resource);
  1841. break;
  1842. case ERROR_PRIVILEGE_NOT_HELD:
  1843. pResponse->SetStatus(HttpStatusForbidden,
  1844. Http403InsufficientPrivilegeForCgi);
  1845. break;
  1846. case ERROR_NOT_ENOUGH_MEMORY:
  1847. pResponse->SetStatus(HttpStatusServerError);
  1848. break;
  1849. default:
  1850. //
  1851. // If we were not able to preload the request entity, we will
  1852. // blame the client for it
  1853. //
  1854. if (!m_fEntityBodyPreloadComplete)
  1855. {
  1856. pResponse->SetStatus(HttpStatusBadRequest);
  1857. }
  1858. else
  1859. {
  1860. pResponse->SetStatus(HttpStatusServerError);
  1861. }
  1862. }
  1863. if ( fImageDisabled )
  1864. {
  1865. pW3Context->SetErrorStatus( ERROR_ACCESS_DISABLED_BY_POLICY );
  1866. }
  1867. else
  1868. {
  1869. pW3Context->SetErrorStatus(hr);
  1870. }
  1871. }
  1872. //
  1873. // If we reached here, there was some error, response should not be 200
  1874. //
  1875. DBG_ASSERT(pResponse->QueryStatusCode() != HttpStatusOk.statusCode);
  1876. m_dwRequestState = CgiStateDoneWithRequest;
  1877. if (FAILED(hr = pW3Context->SendResponse(W3_FLAG_ASYNC)))
  1878. {
  1879. return CONTEXT_STATUS_CONTINUE;
  1880. }
  1881. return CONTEXT_STATUS_PENDING;
  1882. }
  1883. // static
  1884. VOID W3_CGI_HANDLER::KillAllCgis()
  1885. {
  1886. DBGPRINTF((DBG_CONTEXT, "W3_CGI_HANDLER::KillAllCgis() called\n"));
  1887. //
  1888. // Kill all outstanding processes
  1889. //
  1890. EnterCriticalSection(&sm_CgiListLock);
  1891. for (LIST_ENTRY *pEntry = sm_CgiListHead.Flink;
  1892. pEntry != &sm_CgiListHead;
  1893. pEntry = pEntry->Flink)
  1894. {
  1895. W3_CGI_HANDLER *pCgi = CONTAINING_RECORD(pEntry,
  1896. W3_CGI_HANDLER,
  1897. m_CgiListEntry);
  1898. CGITerminateProcess(pCgi, 0);
  1899. }
  1900. LeaveCriticalSection(&sm_CgiListLock);
  1901. }
  1902. // static
  1903. VOID W3_CGI_HANDLER::Terminate()
  1904. {
  1905. DBGPRINTF((DBG_CONTEXT, "W3_CGI_HANDLER::Terminate() called\n"));
  1906. if (sm_pEnvString != NULL)
  1907. {
  1908. delete sm_pEnvString;
  1909. sm_pEnvString = NULL;
  1910. }
  1911. DeleteCriticalSection(&sm_CgiListLock);
  1912. }