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.

3603 lines
109 KiB

  1. /*++
  2. Copyright (c) 1998-2002 Microsoft Corporation
  3. Module Name:
  4. ucparse.c
  5. Abstract:
  6. Contains all of the kernel mode HTTP parsing code for the client
  7. Author:
  8. Rajesh Sundaram (rajeshsu) 10-Oct-2000 Implemented client parser
  9. Revision History:
  10. Rajesh Sundaram (rajeshsu) 15-Feb-2002 Moved from parse.c
  11. --*/
  12. #include "precomp.h"
  13. #include "ucparse.h"
  14. #include "ucrcv.h"
  15. #ifdef ALLOC_PRAGMA
  16. #pragma alloc_text( PAGEUC, UcComputeRequestHeaderSize)
  17. #pragma alloc_text( PAGEUC, UcGenerateRequestHeaders)
  18. #pragma alloc_text( PAGEUC, UcGenerateContentLength)
  19. #pragma alloc_text( PAGEUC, UcComputeConnectVerbHeaderSize)
  20. #pragma alloc_text( PAGEUC, UcGenerateConnectVerbHeader)
  21. #pragma alloc_text( PAGEUC, UcCheckDisconnectInfo)
  22. #pragma alloc_text( PAGEUC, UcCanonicalizeURI)
  23. #pragma alloc_text( PAGEUC, UcParseWWWAuthenticateHeader)
  24. #pragma alloc_text( PAGEUC, UcpFindAttribValuePair)
  25. #pragma alloc_text( PAGEUC, UcpParseAuthParams)
  26. #pragma alloc_text( PAGEUC, UcpParseAuthBlob)
  27. #pragma alloc_text( PAGEUC, UcFindHeaderNameEnd)
  28. #pragma alloc_text( PAGEUC, UcpLookupHeader)
  29. #pragma alloc_text( PAGEUC, UcParseHeader)
  30. #pragma alloc_text( PAGEUC, UcSingleHeaderHandler)
  31. #pragma alloc_text( PAGEUC, UcMultipleHeaderHandler)
  32. #pragma alloc_text( PAGEUC, UcAuthenticateHeaderHandler)
  33. #pragma alloc_text( PAGEUC, UcContentLengthHeaderHandler)
  34. #pragma alloc_text( PAGEUC, UcTransferEncodingHeaderHandler)
  35. #pragma alloc_text( PAGEUC, UcConnectionHeaderHandler)
  36. #pragma alloc_text( PAGEUC, UcContentTypeHeaderHandler)
  37. #endif
  38. //
  39. // The enum->verb translation table
  40. //
  41. LONG_VERB_ENTRY EnumVerbTable[HttpVerbMaximum] =
  42. {
  43. CREATE_LONG_VERB_ENTRY(GET), // Unparsed defaults to GET
  44. CREATE_LONG_VERB_ENTRY(GET), // Unknown defaults to GET
  45. CREATE_LONG_VERB_ENTRY(GET), // Invalid defaults to GET
  46. CREATE_LONG_VERB_ENTRY(OPTIONS),
  47. CREATE_LONG_VERB_ENTRY(GET),
  48. CREATE_LONG_VERB_ENTRY(HEAD),
  49. CREATE_LONG_VERB_ENTRY(POST),
  50. CREATE_LONG_VERB_ENTRY(PUT),
  51. CREATE_LONG_VERB_ENTRY(DELETE),
  52. CREATE_LONG_VERB_ENTRY(TRACE),
  53. CREATE_LONG_VERB_ENTRY(CONNECT),
  54. CREATE_LONG_VERB_ENTRY(TRACK),
  55. CREATE_LONG_VERB_ENTRY(MOVE),
  56. CREATE_LONG_VERB_ENTRY(COPY),
  57. CREATE_LONG_VERB_ENTRY(PROPFIND),
  58. CREATE_LONG_VERB_ENTRY(PROPPATCH),
  59. CREATE_LONG_VERB_ENTRY(MKCOL),
  60. CREATE_LONG_VERB_ENTRY(LOCK),
  61. CREATE_LONG_VERB_ENTRY(UNLOCK),
  62. CREATE_LONG_VERB_ENTRY(SEARCH)
  63. };
  64. //
  65. // A macro to process the header value.
  66. // It stips leading LWS and trailing LWS and CRLF from a header value.
  67. //
  68. #define UC_PROCESS_HEADER_VALUE(pHeaderValue, HeaderValueLength) \
  69. { \
  70. while((HeaderValueLength) > 0 && IS_HTTP_LWS(*(pHeaderValue))) \
  71. { \
  72. (pHeaderValue)++; \
  73. (HeaderValueLength)--; \
  74. } \
  75. while((HeaderValueLength) > 0 && \
  76. IS_HTTP_WS_TOKEN((pHeaderValue)[(HeaderValueLength)-1])) \
  77. { \
  78. (HeaderValueLength)--; \
  79. } \
  80. }
  81. //
  82. // Private macros.
  83. //
  84. //
  85. // COPY_DATA_TO_BUFFER
  86. // copies source buffer to destination buffer after making sure that
  87. // the destination buffer can hold the data.
  88. //
  89. #define COPY_DATA_TO_BUFFER(pDest, DestLen, pSrc, SrcLen) \
  90. do { \
  91. if ((SrcLen) > (DestLen)) \
  92. { \
  93. ASSERT(FALSE); \
  94. return STATUS_BUFFER_TOO_SMALL; \
  95. } \
  96. RtlCopyMemory((pDest), (pSrc), (SrcLen)); \
  97. (pDest) += (SrcLen); \
  98. (DestLen) -= (SrcLen); \
  99. } while (0)
  100. //
  101. // ADVANCE_POINTER
  102. // Advances a pointer to buffer after making sure that the new pointer
  103. // does not point beyond the buffer.
  104. //
  105. #define ADVANCE_POINTER(pDest, DestLen, SrcLen) \
  106. do { \
  107. if ((SrcLen) > (DestLen)) \
  108. { \
  109. ASSERT(FALSE); \
  110. return STATUS_BUFFER_TOO_SMALL; \
  111. } \
  112. (pDest) += (SrcLen); \
  113. (DestLen) -= (SrcLen); \
  114. } while (0)
  115. //
  116. // COPY_UCHAR_TO_BUFFER
  117. //
  118. #define COPY_UCHAR_TO_BUFFER(pDest, DestLen, UChar) \
  119. do { \
  120. if (sizeof(UCHAR) > (DestLen)) \
  121. { \
  122. ASSERT(FALSE); \
  123. return STATUS_BUFFER_TOO_SMALL; \
  124. } \
  125. *(pDest)++ = (UChar); \
  126. (DestLen) -= sizeof(UCHAR); \
  127. } while (0)
  128. //
  129. // COPY_SP_TO_BUFFER
  130. //
  131. #define COPY_SP_TO_BUFFER(pDest, DestLen) \
  132. COPY_UCHAR_TO_BUFFER(pDest, DestLen, SP)
  133. //
  134. // COPY_CRLF_TO_BUFFER
  135. //
  136. #define COPY_CRLF_TO_BUFFER(pDest, DestLen) \
  137. do { \
  138. if (CRLF_SIZE > (DestLen)) \
  139. { \
  140. ASSERT(FALSE); \
  141. return STATUS_BUFFER_TOO_SMALL; \
  142. } \
  143. *((UNALIGNED64 USHORT *)(pDest)) = CRLF; \
  144. (pDest) += CRLF_SIZE; \
  145. (DestLen) -= CRLF_SIZE; \
  146. } while (0)
  147. //
  148. // COPY_HEADER_NAME_SP_TO_BUFFER
  149. //
  150. #define COPY_HEADER_NAME_SP_TO_BUFFER(pBuffer, BufferLen, i) \
  151. do { \
  152. PHEADER_MAP_ENTRY _pEntry; \
  153. _pEntry = &(g_RequestHeaderMapTable[g_RequestHeaderMap[i]]); \
  154. \
  155. if (_pEntry->HeaderLength + sizeof(UCHAR) > (BufferLen)) \
  156. { \
  157. ASSERT(FALSE); \
  158. return STATUS_BUFFER_TOO_SMALL; \
  159. } \
  160. \
  161. (BufferLen) -= (_pEntry->HeaderLength + sizeof(UCHAR)); \
  162. \
  163. RtlCopyMemory((pBuffer), \
  164. _pEntry->MixedCaseHeader, \
  165. _pEntry->HeaderLength); \
  166. \
  167. (pBuffer) += _pEntry->HeaderLength; \
  168. *(pBuffer)++ = SP; \
  169. \
  170. } while (0)
  171. //
  172. // Request Generator functions.
  173. //
  174. /***************************************************************************++
  175. Routine Description:
  176. Figures out how big the fixed headers are. Fixed headers include the
  177. request line, and any headers that don't have to be generated for
  178. every request (such as Date and Connection).
  179. The final CRLF separating headers from body is considered part of
  180. the variable headers.
  181. Arguments:
  182. pServInfo - The server information.
  183. pHttpRequest - The request structure.
  184. bChunked - a boolean that tells if the encoding is chunked.
  185. bContentLengthHeader - a boolean that tells if the Content-Length header
  186. is to be generated.
  187. UriLength - An OUT parameter that will contain the URI length.
  188. Return Values:
  189. The number of bytes in the fixed headers.
  190. --***************************************************************************/
  191. ULONG
  192. UcComputeRequestHeaderSize(
  193. IN PUC_PROCESS_SERVER_INFORMATION pServInfo,
  194. IN PHTTP_REQUEST pHttpRequest,
  195. IN BOOLEAN bChunked,
  196. IN BOOLEAN bContentLengthHeader,
  197. IN PUC_HTTP_AUTH pAuth,
  198. IN PUC_HTTP_AUTH pProxyAuth,
  199. IN PBOOLEAN bPreAuth,
  200. IN PBOOLEAN bProxyPreAuth
  201. )
  202. {
  203. ULONG MethodLength, HeaderLength;
  204. ULONG i;
  205. PHTTP_KNOWN_HEADER pKnownHeaders;
  206. PHTTP_UNKNOWN_HEADER pUnknownHeaders;
  207. PHEADER_MAP_ENTRY pEntry;
  208. pKnownHeaders = pHttpRequest->Headers.KnownHeaders;
  209. pUnknownHeaders = pHttpRequest->Headers.pUnknownHeaders;
  210. ASSERT(*bPreAuth == FALSE);
  211. ASSERT(*bProxyPreAuth == FALSE);
  212. //
  213. // Sanity check.
  214. //
  215. PAGED_CODE();
  216. HeaderLength = 0;
  217. if(pHttpRequest->UnknownVerbLength)
  218. {
  219. //
  220. // The app has passed an unknown verb.
  221. //
  222. HeaderLength = pHttpRequest->UnknownVerbLength;
  223. }
  224. else
  225. {
  226. // Enums are signed, so we have to do the < 0 check!
  227. if(pHttpRequest->Verb < 0 ||
  228. pHttpRequest->Verb >= HttpVerbMaximum ||
  229. pHttpRequest->Verb == HttpVerbUnparsed ||
  230. pHttpRequest->Verb == HttpVerbUnknown ||
  231. pHttpRequest->Verb == HttpVerbInvalid
  232. )
  233. {
  234. return 0;
  235. }
  236. else
  237. {
  238. HeaderLength = EnumVerbTable[pHttpRequest->Verb].RawVerbLength;
  239. }
  240. }
  241. MethodLength = HeaderLength;
  242. // SP
  243. HeaderLength ++;
  244. //
  245. // If we are going through a proxy, we need to compute space for the
  246. // scheme & the server name.
  247. //
  248. if(pServInfo->bProxy)
  249. {
  250. HeaderLength += pServInfo->pServerInfo->AnsiServerNameLength;
  251. if(pServInfo->bSecure)
  252. {
  253. HeaderLength += HTTPS_PREFIX_ANSI_LENGTH;
  254. }
  255. else
  256. {
  257. HeaderLength += HTTP_PREFIX_ANSI_LENGTH;
  258. }
  259. }
  260. //
  261. // Formulate the version. We'll support only 1.0 or 1.1 requests,
  262. // so we allocate space only for HTTP/1.1
  263. //
  264. HeaderLength += VERSION_SIZE;
  265. HeaderLength += CRLF_SIZE; // CRLF
  266. //
  267. // Loop through the known headers.
  268. //
  269. for (i = 0; i < HttpHeaderRequestMaximum; ++i)
  270. {
  271. ULONG RawValueLength = pKnownHeaders[i].RawValueLength;
  272. //
  273. // skip some headers that we generate.
  274. //
  275. if (RawValueLength > 0 &&
  276. !g_RequestHeaderMapTable[g_RequestHeaderMap[i]].AutoGenerate)
  277. {
  278. HeaderLength += g_RequestHeaderMapTable[
  279. g_RequestHeaderMap[i]
  280. ].HeaderLength + // Header-Name
  281. 1 + // SP
  282. RawValueLength + // Header-Value
  283. CRLF_SIZE; // CRLF
  284. }
  285. }
  286. //
  287. // Include the default headers we may need to generate on behalf of the
  288. // application. We do this only if the application has not specified any
  289. // headers by itself.
  290. //
  291. pEntry = &(g_RequestHeaderMapTable[g_RequestHeaderMap[
  292. HttpHeaderContentLength]]);
  293. if(bContentLengthHeader)
  294. {
  295. HeaderLength += (pEntry->HeaderLength + 1 + CRLF_SIZE);
  296. HeaderLength += MAX_ULONGLONG_STR;
  297. }
  298. else
  299. {
  300. if(!bChunked)
  301. {
  302. //
  303. // We are going to compute the content length at some point
  304. // in the future. Might as well allocate a content length
  305. // for this right away. We do this to avoid allocating a
  306. // new buffer + MDL when we know the actual length.
  307. //
  308. HeaderLength += (pEntry->HeaderLength + 1 + CRLF_SIZE);
  309. HeaderLength += MAX_ULONGLONG_STR;
  310. }
  311. }
  312. //
  313. // If we are using chunked encoding, we need to update the
  314. // TransferEncoding field. If the app has already passed a
  315. // value, we need to append "chunked" to the end of the Transfer
  316. // encoding.
  317. //
  318. if(bChunked)
  319. {
  320. pEntry = &(g_RequestHeaderMapTable[g_RequestHeaderMap[
  321. HttpHeaderTransferEncoding]]);
  322. HeaderLength += (pEntry->HeaderLength + 1 + CRLF_SIZE);
  323. HeaderLength += CHUNKED_HDR_LENGTH;
  324. }
  325. //
  326. // Add a host header - We'll override the app's header, even if they
  327. // have passed one. This is done by the "AutoGenerate" flag
  328. //
  329. HeaderLength += g_RequestHeaderMapTable[
  330. g_RequestHeaderMap[HttpHeaderHost]
  331. ].HeaderLength + // Header-Name
  332. 1 + // SP
  333. pServInfo->pServerInfo->AnsiServerNameLength + // Value
  334. CRLF_SIZE; // CRLF
  335. //
  336. // And the unknown headers (this might throw an exception).
  337. //
  338. if (pUnknownHeaders != NULL)
  339. {
  340. for (i = 0 ; i < pHttpRequest->Headers.UnknownHeaderCount; ++i)
  341. {
  342. if (pUnknownHeaders[i].NameLength > 0)
  343. {
  344. HeaderLength +=
  345. pUnknownHeaders[i].NameLength + // Header-Name
  346. 1 + // ':'
  347. 1 + // SP
  348. pUnknownHeaders[i].RawValueLength + // Header-Value
  349. CRLF_SIZE; // CRLF
  350. }
  351. }
  352. }
  353. if(pHttpRequest->Headers.KnownHeaders
  354. [HttpHeaderAuthorization].RawValueLength == 0)
  355. {
  356. if(pAuth)
  357. {
  358. // User has passed auth credentials. We'll just use this.
  359. HeaderLength += pAuth->RequestAuthHeaderMaxLength;
  360. }
  361. else if((pServInfo->PreAuthEnable &&
  362. pServInfo->GreatestAuthHeaderMaxLength))
  363. {
  364. HeaderLength += pServInfo->GreatestAuthHeaderMaxLength;
  365. *bPreAuth = TRUE;
  366. }
  367. }
  368. else
  369. {
  370. //
  371. // User has passed their creds, let's just use that. The space
  372. // for this was accounted when we computed the known header size.
  373. //
  374. }
  375. //
  376. // Do the same thing for Proxy Auth.
  377. //
  378. if(pHttpRequest->Headers.KnownHeaders
  379. [HttpHeaderProxyAuthorization].RawValueLength == 0)
  380. {
  381. if(pProxyAuth)
  382. {
  383. HeaderLength += pProxyAuth->RequestAuthHeaderMaxLength;
  384. }
  385. else if(pServInfo->ProxyPreAuthEnable && pServInfo->pProxyAuthInfo)
  386. {
  387. HeaderLength +=
  388. pServInfo->pProxyAuthInfo->RequestAuthHeaderMaxLength;
  389. *bProxyPreAuth = TRUE;
  390. }
  391. }
  392. else
  393. {
  394. //
  395. // User has passed their creds, let's just use that. The space
  396. // for this was accounted when we computed the known header size.
  397. //
  398. }
  399. // Header terminator.
  400. HeaderLength += CRLF_SIZE; // CRLF
  401. return HeaderLength;
  402. } // UcComputeRequestHeaderSize
  403. /***************************************************************************++
  404. Routine Description:
  405. Generates the header for HTTP requests.
  406. Arguments:
  407. pRequest - The HTTP request structure passed by the app
  408. pKeRequest - Our internal representation of the structure.
  409. pAuth - The Auth credentials as passed by the app.
  410. pProxyAuth - The Proxy auth credentials as passed by the app.
  411. bChunked - To indicate if we are using chunked encoding.
  412. ContentLength - Content Length
  413. --***************************************************************************/
  414. NTSTATUS
  415. UcGenerateRequestHeaders(
  416. IN PHTTP_REQUEST pRequest,
  417. IN PUC_HTTP_REQUEST pKeRequest,
  418. IN BOOLEAN bChunked,
  419. IN ULONGLONG ContentLength
  420. )
  421. {
  422. PUCHAR pStartHeaders;
  423. ULONG BytesCopied;
  424. ULONG i;
  425. PHTTP_UNKNOWN_HEADER pUnknownHeaders;
  426. ULONG RemainingLen = pKeRequest->MaxHeaderLength;
  427. PUCHAR pBuffer = pKeRequest->pHeaders;
  428. PSTR pMethod;
  429. ULONG MethodLength;
  430. NTSTATUS Status = STATUS_SUCCESS;
  431. BOOLEAN bProxySslRequest = FALSE;
  432. //
  433. // Sanity check.
  434. //
  435. PAGED_CODE();
  436. ASSERT(pRequest != NULL);
  437. ASSERT(pBuffer != NULL && RemainingLen > 0);
  438. //
  439. // Remember the start of the headers buffer.
  440. //
  441. pStartHeaders = pBuffer;
  442. //
  443. // Generate the request line.
  444. // Request-Line = Method SP Request-URI SP HTTP-Version CRLF
  445. //
  446. pMethod = (PSTR) pBuffer;
  447. if(pRequest->UnknownVerbLength)
  448. {
  449. //
  450. // The app has passed an unknown verb.
  451. //
  452. COPY_DATA_TO_BUFFER(pBuffer,
  453. RemainingLen,
  454. pRequest->pUnknownVerb,
  455. pRequest->UnknownVerbLength);
  456. }
  457. else
  458. {
  459. ASSERT(0 <= pRequest->Verb && pRequest->Verb < HttpVerbMaximum);
  460. COPY_DATA_TO_BUFFER(pBuffer,
  461. RemainingLen,
  462. EnumVerbTable[pRequest->Verb].RawVerb,
  463. EnumVerbTable[pRequest->Verb].RawVerbLength);
  464. }
  465. MethodLength = (ULONG)((PUCHAR)pBuffer - (PUCHAR)pMethod);
  466. //
  467. // Add a SP.
  468. //
  469. COPY_SP_TO_BUFFER(pBuffer, RemainingLen);
  470. //
  471. // Copy the request URI.
  472. //
  473. if(pKeRequest->pServerInfo->bProxy)
  474. {
  475. //
  476. // Normally, when a proxy is present, an absoluteURI is generated
  477. // in the request. In case of SSL, the proxy mainly acts as a tunnel.
  478. // We, therefore, don't generate an absoluteURI but generate
  479. // abs_path instead.
  480. //
  481. if(pKeRequest->pServerInfo->bSecure)
  482. {
  483. bProxySslRequest = TRUE;
  484. }
  485. else
  486. {
  487. //
  488. // Add "http://" prefix.
  489. //
  490. COPY_DATA_TO_BUFFER(pBuffer,
  491. RemainingLen,
  492. HTTP_PREFIX_ANSI,
  493. HTTP_PREFIX_ANSI_LENGTH);
  494. //
  495. // Now, copy the server name.
  496. //
  497. COPY_DATA_TO_BUFFER(
  498. pBuffer,
  499. RemainingLen,
  500. pKeRequest->pServerInfo->pServerInfo->pAnsiServerName,
  501. pKeRequest->pServerInfo->pServerInfo->AnsiServerNameLength
  502. );
  503. }
  504. }
  505. //
  506. // Copy the Uri.
  507. //
  508. COPY_DATA_TO_BUFFER(pBuffer,
  509. RemainingLen,
  510. pKeRequest->pUri,
  511. pKeRequest->UriLength);
  512. //
  513. // Add a SP.
  514. //
  515. COPY_SP_TO_BUFFER(pBuffer, RemainingLen);
  516. //
  517. // Add the protocol.
  518. //
  519. if(pRequest->Version.MajorVersion == 1)
  520. {
  521. if(pRequest->Version.MinorVersion == 1)
  522. {
  523. //
  524. // Copy "HTTP/1.1" string.
  525. //
  526. COPY_DATA_TO_BUFFER(pBuffer,
  527. RemainingLen,
  528. HTTP_VERSION_11,
  529. VERSION_SIZE);
  530. }
  531. else if(pRequest->Version.MinorVersion == 0)
  532. {
  533. //
  534. // Copy "HTTP/1.0" string.
  535. //
  536. COPY_DATA_TO_BUFFER(pBuffer,
  537. RemainingLen,
  538. HTTP_VERSION_10,
  539. VERSION_SIZE);
  540. }
  541. else
  542. {
  543. //
  544. // We don't support minor versions > 1.
  545. //
  546. return STATUS_INVALID_PARAMETER;
  547. }
  548. }
  549. else
  550. {
  551. //
  552. // We don't support major version != 1.
  553. //
  554. return STATUS_INVALID_PARAMETER;
  555. }
  556. //
  557. // Terminate the request-line with a CRLF.
  558. //
  559. COPY_CRLF_TO_BUFFER(pBuffer, RemainingLen);
  560. //
  561. // Determine if we have to close the TCP connection after sending
  562. // this request.
  563. //
  564. pKeRequest->RequestConnectionClose =
  565. UcCheckDisconnectInfo(&pRequest->Version,
  566. pRequest->Headers.KnownHeaders);
  567. //
  568. // Loop through the known headers.
  569. //
  570. for (i = 0; i < HttpHeaderRequestMaximum; ++i)
  571. {
  572. //
  573. // skip some headers we'll generate
  574. //
  575. if (pRequest->Headers.KnownHeaders[i].RawValueLength > 0)
  576. {
  577. PHEADER_MAP_ENTRY pEntry;
  578. pEntry = &(g_RequestHeaderMapTable[g_RequestHeaderMap[i]]);
  579. if(pEntry->AutoGenerate == FALSE)
  580. {
  581. //
  582. // Copy known header name followed by a ':' and a SP.
  583. //
  584. COPY_HEADER_NAME_SP_TO_BUFFER(pBuffer, RemainingLen, i);
  585. //
  586. // Copy known header value.
  587. //
  588. COPY_DATA_TO_BUFFER(
  589. pBuffer,
  590. RemainingLen,
  591. pRequest->Headers.KnownHeaders[i].pRawValue,
  592. pRequest->Headers.KnownHeaders[i].RawValueLength
  593. );
  594. //
  595. // Terminate the header by CRLF.
  596. //
  597. COPY_CRLF_TO_BUFFER(pBuffer, RemainingLen);
  598. }
  599. }
  600. }
  601. //
  602. // Add a host header - We'll override the app's header, even if they
  603. // have passed one. This is done by the "AutoGenerate" flag.
  604. //
  605. // Copy "Host: " string.
  606. COPY_HEADER_NAME_SP_TO_BUFFER(pBuffer, RemainingLen, HttpHeaderHost);
  607. // Copy server name.
  608. COPY_DATA_TO_BUFFER(
  609. pBuffer,
  610. RemainingLen,
  611. pKeRequest->pServerInfo->pServerInfo->pAnsiServerName,
  612. pKeRequest->pServerInfo->pServerInfo->AnsiServerNameLength
  613. );
  614. // Terminate the host header with a CRLF.
  615. COPY_CRLF_TO_BUFFER(pBuffer, RemainingLen);
  616. //
  617. // Generate the content length header.
  618. //
  619. if(ContentLength)
  620. {
  621. Status = UcGenerateContentLength(ContentLength,
  622. pBuffer,
  623. RemainingLen,
  624. &BytesCopied);
  625. if (!NT_SUCCESS(Status))
  626. {
  627. return Status;
  628. }
  629. ASSERT(BytesCopied <= RemainingLen);
  630. ADVANCE_POINTER(pBuffer, RemainingLen, BytesCopied);
  631. }
  632. if(bChunked)
  633. {
  634. //
  635. // If we are using chunked encoding, we have to add the
  636. // "Transfer-Encoding: chunked" header.
  637. //
  638. // Copy header name - "Transfer-Encoding: ".
  639. COPY_HEADER_NAME_SP_TO_BUFFER(pBuffer,
  640. RemainingLen,
  641. HttpHeaderTransferEncoding);
  642. // Copy header value - "chunked".
  643. COPY_DATA_TO_BUFFER(pBuffer,
  644. RemainingLen,
  645. CHUNKED_HDR,
  646. CHUNKED_HDR_LENGTH);
  647. // Terminate header with CRLF.
  648. COPY_CRLF_TO_BUFFER(pBuffer, RemainingLen);
  649. }
  650. //
  651. // And now the unknown headers
  652. //
  653. pUnknownHeaders = pRequest->Headers.pUnknownHeaders;
  654. if (pUnknownHeaders != NULL)
  655. {
  656. for (i = 0 ; i < pRequest->Headers.UnknownHeaderCount; ++i)
  657. {
  658. if (pUnknownHeaders[i].NameLength > 0)
  659. {
  660. // First, copy the header name.
  661. COPY_DATA_TO_BUFFER(pBuffer,
  662. RemainingLen,
  663. pUnknownHeaders[i].pName,
  664. pUnknownHeaders[i].NameLength);
  665. // Copy ':' after header name.
  666. COPY_UCHAR_TO_BUFFER(pBuffer, RemainingLen, ':');
  667. // Add a space.
  668. COPY_SP_TO_BUFFER(pBuffer, RemainingLen);
  669. // Now, copy the header value.
  670. COPY_DATA_TO_BUFFER(pBuffer,
  671. RemainingLen,
  672. pUnknownHeaders[i].pRawValue,
  673. pUnknownHeaders[i].RawValueLength);
  674. // Terminate the header with a CRLF.
  675. COPY_CRLF_TO_BUFFER(pBuffer, RemainingLen);
  676. } // if (pUnknownHeaders[i].NameLength > 0)
  677. }
  678. } // if (pUnknownHeaders != NULL)
  679. //
  680. // Generate the Authorization headers. This should be done at the very last
  681. // since we might have to update the Authorization header & re-issue the
  682. // request (for NTLM/kerberos). The size of the new Authorization header
  683. // will not be the same as the old one.
  684. //
  685. // If the Authorization header is at the end, we can easily re-generate it.
  686. // and append it with the existing headers.
  687. //
  688. // The one exception to this rule is content-length - If the app is
  689. // indicating data in chunks and has not specified a content-length, it will
  690. // get generated at the very end. But this is no big deal. We can easily
  691. // re-generate the content-length hdr.
  692. //
  693. if(pRequest->Headers.KnownHeaders[HttpHeaderAuthorization].RawValueLength
  694. == 0)
  695. {
  696. if(pKeRequest->pAuthInfo)
  697. {
  698. //
  699. // User has supplied credentials, we have to use it.
  700. //
  701. Status =
  702. UcGenerateAuthHeaderFromCredentials(
  703. pKeRequest->pServerInfo,
  704. pKeRequest->pAuthInfo,
  705. HttpHeaderAuthorization,
  706. pMethod,
  707. MethodLength,
  708. pKeRequest->pUri,
  709. pKeRequest->UriLength,
  710. pBuffer,
  711. RemainingLen,
  712. &BytesCopied,
  713. &pKeRequest->DontFreeMdls
  714. );
  715. if(!NT_SUCCESS(Status))
  716. {
  717. return(Status);
  718. }
  719. ASSERT(BytesCopied <= RemainingLen);
  720. ADVANCE_POINTER(pBuffer, RemainingLen, BytesCopied);
  721. }
  722. else if (pKeRequest->RequestFlags.UsePreAuth)
  723. {
  724. //
  725. // See if PreAuth is enabled. We cannot check for the
  726. // pServerInfo->PreAuth flag here. We check for this
  727. // in the UcpComputeAuthHeaderSize function. If we check
  728. // for this here, we cannot be sure that this flag was
  729. // set when we called UcpComputeAuthHeaderSize
  730. //
  731. UcFindURIEntry(pKeRequest->pServerInfo,
  732. pKeRequest->pUri,
  733. pKeRequest,
  734. pMethod,
  735. MethodLength,
  736. pBuffer,
  737. RemainingLen,
  738. &BytesCopied);
  739. ASSERT(BytesCopied <= RemainingLen);
  740. ADVANCE_POINTER(pBuffer, RemainingLen, BytesCopied);
  741. }
  742. }
  743. if (pRequest->Headers.KnownHeaders[HttpHeaderProxyAuthorization].
  744. RawValueLength == 0)
  745. {
  746. if (pKeRequest->pProxyAuthInfo)
  747. {
  748. if(!bProxySslRequest)
  749. {
  750. Status =
  751. UcGenerateAuthHeaderFromCredentials(
  752. pKeRequest->pServerInfo,
  753. pKeRequest->pProxyAuthInfo,
  754. HttpHeaderProxyAuthorization,
  755. pMethod,
  756. MethodLength,
  757. pKeRequest->pUri,
  758. pKeRequest->UriLength,
  759. pBuffer,
  760. RemainingLen,
  761. &BytesCopied,
  762. &pKeRequest->DontFreeMdls
  763. );
  764. if(!NT_SUCCESS(Status))
  765. {
  766. return(Status);
  767. }
  768. ASSERT(BytesCopied <= RemainingLen);
  769. ADVANCE_POINTER(pBuffer, RemainingLen, BytesCopied);
  770. }
  771. }
  772. else if (pKeRequest->RequestFlags.UseProxyPreAuth && !bProxySslRequest)
  773. {
  774. Status = UcGenerateProxyAuthHeaderFromCache(pKeRequest,
  775. pMethod,
  776. MethodLength,
  777. pBuffer,
  778. RemainingLen,
  779. &BytesCopied);
  780. if (!NT_SUCCESS(Status))
  781. {
  782. return Status;
  783. }
  784. ASSERT(BytesCopied <= RemainingLen);
  785. ADVANCE_POINTER(pBuffer, RemainingLen, BytesCopied);
  786. }
  787. }
  788. pKeRequest->HeaderLength = DIFF(pBuffer - pStartHeaders);
  789. //
  790. // Ensure we didn't use too much.
  791. //
  792. ASSERT(pBuffer <= pStartHeaders + pKeRequest->MaxHeaderLength);
  793. return Status;
  794. } // UcGenerateRequestHeaders
  795. /***************************************************************************++
  796. Routine Description:
  797. Generates the content length header.
  798. Arguments:
  799. ContentLength - Supplies the content length.
  800. pBuffer - Supplies pointer to the output buffer.
  801. BufferLen - Supplies length of the output buffer.
  802. BytesWritten - Returns the number of bytes consumed from the buffer.
  803. Return Value:
  804. NTSTATUS.
  805. --***************************************************************************/
  806. NTSTATUS
  807. UcGenerateContentLength(
  808. IN ULONGLONG ContentLength,
  809. IN PUCHAR pBuffer,
  810. IN ULONG BufferLen,
  811. OUT PULONG pBytesWritten
  812. )
  813. {
  814. PUCHAR pBufferTemp;
  815. ULONG BufferLenTemp;
  816. // Initialize locals.
  817. pBufferTemp = pBuffer;
  818. BufferLenTemp = BufferLen;
  819. *pBytesWritten = 0;
  820. //
  821. // Copy "Content-Length:" header name and a space.
  822. //
  823. COPY_HEADER_NAME_SP_TO_BUFFER(pBuffer,
  824. BufferLen,
  825. HttpHeaderContentLength);
  826. //
  827. // Check if there is enough space to copy ULONGLONG content length
  828. // in string format and a CRLF.
  829. //
  830. if (MAX_ULONGLONG_STR + CRLF_SIZE > BufferLen)
  831. {
  832. return STATUS_BUFFER_TOO_SMALL;
  833. }
  834. pBuffer = (PUCHAR) UlStrPrintUlonglong((PCHAR) pBuffer,
  835. ContentLength,
  836. '\0');
  837. //
  838. //
  839. //
  840. BufferLen -= MAX_ULONGLONG_STR;
  841. COPY_CRLF_TO_BUFFER(pBuffer, BufferLen);
  842. *pBytesWritten = (ULONG)(pBuffer - pBufferTemp);
  843. ASSERT(*pBytesWritten <= BufferLenTemp);
  844. return STATUS_SUCCESS;
  845. }
  846. /***************************************************************************++
  847. Routine Description:
  848. Figures out the header size for the CONNECT verb.
  849. Arguments:
  850. pServInfo - The server information.
  851. pProxyAuthInfo - Proxy auth info.
  852. Return Values:
  853. The number of bytes in the fixed headers.
  854. --***************************************************************************/
  855. ULONG
  856. UcComputeConnectVerbHeaderSize(
  857. IN PUC_PROCESS_SERVER_INFORMATION pServInfo,
  858. IN PUC_HTTP_AUTH pProxyAuthInfo
  859. )
  860. {
  861. #define PROXY_CONNECTION_KEEPALIVE "Proxy-Connection: Keep-Alive"
  862. #define PROXY_CONNECTION_KEEPALIVE_SIZE (sizeof(PROXY_CONNECTION_KEEPALIVE)-1)
  863. ULONG HeaderLength;
  864. //
  865. // IE adds the following headers as well.
  866. // UserAgent:
  867. // Content-Length : 0
  868. // Pragma: no-cache
  869. //
  870. HeaderLength =
  871. EnumVerbTable[HttpVerbCONNECT].RawVerbLength + // Method
  872. 1 + // SP
  873. pServInfo->pServerInfo->AnsiServerNameLength + // URI
  874. 4 + // port
  875. 1 + // SP
  876. VERSION_SIZE + // Version
  877. CRLF_SIZE;
  878. //
  879. // Add a host header
  880. //
  881. HeaderLength +=
  882. g_RequestHeaderMapTable[
  883. g_RequestHeaderMap[HttpHeaderHost]
  884. ].HeaderLength +
  885. 1 + // SP
  886. pServInfo->pServerInfo->AnsiServerNameLength + // Value
  887. CRLF_SIZE; // CRLF
  888. //
  889. // Add a Proxy keepalive.
  890. //
  891. HeaderLength += PROXY_CONNECTION_KEEPALIVE_SIZE + CRLF_SIZE;
  892. //
  893. // If we are doing proxy auth, add a proxy auth header. Since we have
  894. // already generated this header when we built the request, we can
  895. // just clone it from there.
  896. //
  897. if (pProxyAuthInfo)
  898. {
  899. HeaderLength += pProxyAuthInfo->RequestAuthHeaderMaxLength;
  900. }
  901. else if(pServInfo->ProxyPreAuthEnable && pServInfo->pProxyAuthInfo)
  902. {
  903. HeaderLength += pServInfo->pProxyAuthInfo->RequestAuthHeaderMaxLength;
  904. }
  905. //
  906. // Terminate
  907. //
  908. HeaderLength += CRLF_SIZE;
  909. return HeaderLength;
  910. }
  911. /***************************************************************************++
  912. Routine Description:
  913. Generates the header size for the CONNECT verb.
  914. Arguments:
  915. pServInfo - The server information.
  916. pProxyAuthInfo - Proxy auth info.
  917. Return Values:
  918. Status.
  919. --***************************************************************************/
  920. NTSTATUS
  921. UcGenerateConnectVerbHeader(
  922. IN PUC_HTTP_REQUEST pRequest,
  923. IN PUC_HTTP_REQUEST pHeadRequest,
  924. IN PUC_HTTP_AUTH pProxyAuthInfo
  925. )
  926. {
  927. PUCHAR pBuffer, pStartHeaders;
  928. PUCHAR pUri;
  929. USHORT UriLength;
  930. ULONG BytesWritten;
  931. ULONG RemainingLength;
  932. NTSTATUS Status;
  933. //
  934. // Remember the start of header and max total header length.
  935. //
  936. pStartHeaders = pBuffer = pRequest->pHeaders;
  937. RemainingLength = pRequest->MaxHeaderLength;
  938. //
  939. // Copy "CONNECT" verb.
  940. //
  941. COPY_DATA_TO_BUFFER(pBuffer,
  942. RemainingLength,
  943. EnumVerbTable[HttpVerbCONNECT].RawVerb,
  944. EnumVerbTable[HttpVerbCONNECT].RawVerbLength);
  945. // Copy a SP.
  946. COPY_SP_TO_BUFFER(pBuffer, RemainingLength);
  947. //
  948. // Now, the URI. The URI here is the name of the origin server
  949. // followed by the port number.
  950. //
  951. pUri = pBuffer;
  952. COPY_DATA_TO_BUFFER(
  953. pBuffer,
  954. RemainingLength,
  955. pRequest->pServerInfo->pServerInfo->pAnsiServerName,
  956. pRequest->pServerInfo->pServerInfo->AnsiServerNameLength
  957. );
  958. //
  959. // If server name does not have a port number, include the default
  960. // port number. Note that, the only way the port number could be
  961. // different is when it is present in the server name.
  962. //
  963. if (!pRequest->pServerInfo->pServerInfo->bPortNumber)
  964. {
  965. //
  966. // Copy the default port number.
  967. //
  968. COPY_DATA_TO_BUFFER(pBuffer,
  969. RemainingLength,
  970. ":443",
  971. STRLEN_LIT(":443"));
  972. }
  973. UriLength = DIFF_USHORT(pBuffer - pUri);
  974. //
  975. // Remember URI and Uri Length in Request structure for future use.
  976. //
  977. pRequest->UriLength = UriLength;
  978. pRequest->pUri = (PSTR) pUri;
  979. // Add a space
  980. COPY_SP_TO_BUFFER(pBuffer, RemainingLength);
  981. //
  982. // Add the protocol.
  983. //
  984. COPY_DATA_TO_BUFFER(pBuffer,
  985. RemainingLength,
  986. HTTP_VERSION_11,
  987. VERSION_SIZE);
  988. //
  989. // Terminate the request-line with a CRLF.
  990. //
  991. COPY_CRLF_TO_BUFFER(pBuffer, RemainingLength);
  992. //
  993. // Generate the host header
  994. //
  995. COPY_HEADER_NAME_SP_TO_BUFFER(pBuffer, RemainingLength, HttpHeaderHost);
  996. COPY_DATA_TO_BUFFER(
  997. pBuffer,
  998. RemainingLength,
  999. pRequest->pServerInfo->pServerInfo->pAnsiServerName,
  1000. pRequest->pServerInfo->pServerInfo->AnsiServerNameLength
  1001. );
  1002. COPY_CRLF_TO_BUFFER(pBuffer, RemainingLength);
  1003. //
  1004. // Proxy Keepalive
  1005. //
  1006. COPY_DATA_TO_BUFFER(pBuffer,
  1007. RemainingLength,
  1008. PROXY_CONNECTION_KEEPALIVE,
  1009. PROXY_CONNECTION_KEEPALIVE_SIZE);
  1010. COPY_CRLF_TO_BUFFER(pBuffer, RemainingLength);
  1011. //
  1012. // Proxy Auth
  1013. //
  1014. if (pProxyAuthInfo)
  1015. {
  1016. Status = UcGenerateAuthHeaderFromCredentials(
  1017. pRequest->pServerInfo,
  1018. pProxyAuthInfo,
  1019. HttpHeaderProxyAuthorization,
  1020. (PSTR)EnumVerbTable[HttpVerbCONNECT].RawVerb,
  1021. EnumVerbTable[HttpVerbCONNECT].RawVerbLength,
  1022. (PSTR) pUri,
  1023. UriLength,
  1024. pBuffer,
  1025. RemainingLength,
  1026. &BytesWritten,
  1027. &pRequest->DontFreeMdls
  1028. );
  1029. //
  1030. // What do we do if this fails ? It's probably OK to just send the
  1031. // request which will result in another 401. There is no clean way
  1032. // of propogating this to the app.
  1033. //
  1034. if (NT_SUCCESS(Status))
  1035. {
  1036. ASSERT(BytesWritten <= RemainingLength);
  1037. ADVANCE_POINTER(pBuffer, RemainingLength, BytesWritten);
  1038. }
  1039. }
  1040. else if(pRequest->pServerInfo->ProxyPreAuthEnable &&
  1041. pRequest->pServerInfo->pProxyAuthInfo)
  1042. {
  1043. Status = UcGenerateProxyAuthHeaderFromCache(
  1044. pHeadRequest,
  1045. (PSTR) EnumVerbTable[HttpVerbCONNECT].RawVerb,
  1046. EnumVerbTable[HttpVerbCONNECT].RawVerbLength,
  1047. pBuffer,
  1048. RemainingLength,
  1049. &BytesWritten
  1050. );
  1051. if (NT_SUCCESS(Status))
  1052. {
  1053. ASSERT(BytesWritten < RemainingLength);
  1054. ADVANCE_POINTER(pBuffer, RemainingLength, BytesWritten);
  1055. }
  1056. }
  1057. //
  1058. // Header end.
  1059. //
  1060. COPY_CRLF_TO_BUFFER(pBuffer, RemainingLength);
  1061. pRequest->HeaderLength = DIFF(pBuffer - pStartHeaders);
  1062. ASSERT(pRequest->HeaderLength <= pRequest->MaxHeaderLength);
  1063. return STATUS_SUCCESS;
  1064. }
  1065. //
  1066. // Convert '%xy' to byte value (works with Unicode strings)
  1067. //
  1068. NTSTATUS
  1069. UnescapeW(
  1070. IN PCWSTR pWChar,
  1071. OUT PWCHAR pOutWChar
  1072. )
  1073. {
  1074. WCHAR Result, Digit;
  1075. //
  1076. // Sanity check.
  1077. //
  1078. if (pWChar[0] != '%' || pWChar[1] >= 0x80 || pWChar[2] >= 0x80 ||
  1079. !IS_HTTP_HEX(pWChar[1]) || !IS_HTTP_HEX(pWChar[2]))
  1080. {
  1081. UlTraceError(PARSER, (
  1082. "ul!Unescape( %C%C%C ) not HTTP_HEX format\n",
  1083. pWChar[0],
  1084. pWChar[1],
  1085. pWChar[2]
  1086. ));
  1087. return STATUS_OBJECT_PATH_SYNTAX_BAD;
  1088. }
  1089. //
  1090. // HexToChar() inlined. Note: '0' < 'A' < 'a'
  1091. //
  1092. // uppercase #1
  1093. //
  1094. if ('a' <= pWChar[1])
  1095. {
  1096. ASSERT('a' <= pWChar[1] && pWChar[1] <= 'f');
  1097. Digit = pWChar[1] - 'a' + 0xA;
  1098. }
  1099. else if ('A' <= pWChar[1])
  1100. {
  1101. ASSERT('A' <= pWChar[1] && pWChar[1] <= 'F');
  1102. Digit = pWChar[1] - 'A' + 0xA;
  1103. }
  1104. else
  1105. {
  1106. ASSERT('0' <= pWChar[1] && pWChar[1] <= '9');
  1107. Digit = pWChar[1] - '0';
  1108. }
  1109. ASSERT(Digit < 0x10);
  1110. Result = Digit << 4;
  1111. // uppercase #2
  1112. //
  1113. if ('a' <= pWChar[2])
  1114. {
  1115. ASSERT('a' <= pWChar[2] && pWChar[2] <= 'f');
  1116. Digit = pWChar[2] - 'a' + 0xA;
  1117. }
  1118. else if ('A' <= pWChar[2])
  1119. {
  1120. ASSERT('A' <= pWChar[2] && pWChar[2] <= 'F');
  1121. Digit = pWChar[2] - 'A' + 0xA;
  1122. }
  1123. else
  1124. {
  1125. ASSERT('0' <= pWChar[2] && pWChar[2] <= '9');
  1126. Digit = pWChar[2] - '0';
  1127. }
  1128. ASSERT(Digit < 0x10);
  1129. Result |= Digit;
  1130. *pOutWChar = Result;
  1131. return STATUS_SUCCESS;
  1132. } // UnescapeW
  1133. __inline
  1134. BOOLEAN
  1135. UcCheckDisconnectInfo(
  1136. IN PHTTP_VERSION pVersion,
  1137. IN PHTTP_KNOWN_HEADER pKnownHeaders
  1138. )
  1139. {
  1140. BOOLEAN Disconnect;
  1141. //
  1142. // Sanity check
  1143. //
  1144. if (
  1145. //
  1146. // or version 1.0 with no Connection: Keep-Alive
  1147. // CODEWORK: and no Keep-Alive header
  1148. //
  1149. (HTTP_EQUAL_VERSION(*pVersion, 1, 0) &&
  1150. (pKnownHeaders[HttpHeaderConnection].RawValueLength == 0 ||
  1151. !(pKnownHeaders[HttpHeaderConnection].RawValueLength == 10 &&
  1152. (_stricmp(
  1153. (const char*) pKnownHeaders[HttpHeaderConnection].pRawValue,
  1154. "keep-alive"
  1155. ) == 0)))) ||
  1156. //
  1157. // or version 1.1 with a Connection: close
  1158. // CODEWORK: move to parser or just make better in general..
  1159. //
  1160. (HTTP_EQUAL_VERSION(*pVersion, 1, 1) &&
  1161. pKnownHeaders[HttpHeaderConnection].RawValueLength == 5 &&
  1162. _stricmp((const char*)
  1163. pKnownHeaders[HttpHeaderConnection].pRawValue, "close") == 0)
  1164. )
  1165. {
  1166. Disconnect = TRUE;
  1167. }
  1168. else
  1169. {
  1170. Disconnect = FALSE;
  1171. }
  1172. return Disconnect;
  1173. }
  1174. /***************************************************************************++
  1175. Routine Description:
  1176. Canonicalizes abs path in a URI. The tasks performed are:
  1177. - Remove extra '/' e.g. /a///b => /a/b
  1178. - Process '.' and '..' e.g. /./a/../b => /b
  1179. - Copy Query string as it is
  1180. - Copy Fragment as it is
  1181. - Encode the output in UTF8
  1182. - Optionally hex encode bytes >= 0x80
  1183. Arguments:
  1184. IN pInUri Input URI in Unicode
  1185. IN InUriLen Length of input URI (in CHAR)
  1186. IN pOutUri Pointer to the output buffer
  1187. IN OUT pOutUriLen Length of the output buffer
  1188. the actual number of bytes written is returned back
  1189. IN bEncode TRUE if chars >= 0x80 should be escaped
  1190. Return Values:
  1191. Status.
  1192. --***************************************************************************/
  1193. UCHAR NextStateTable[TOTAL_STATES][CHAR_TOTAL_TYPES+2] = INIT_TRANSITION_TABLE;
  1194. UCHAR (*ActionTable)[TOTAL_STATES][CHAR_TOTAL_TYPES+2] = &NextStateTable;
  1195. #define NEXT_STATE(state, type) ((NextStateTable[state][type])&0xf)
  1196. #define ACTION(state, type) ((((*ActionTable)[state][type])>>4)&0xf)
  1197. //
  1198. // Macro to read next char from the input URI
  1199. // The macro handles correctly a '.' char even if it is escaped as %2E (or %2e)
  1200. // HttpChars is used to quickly lookup '/', '.', '#' or '?' chars
  1201. //
  1202. #define GET_NEXT_CHAR(pInUri, CharsLeft, CurrChar, CurrCharType) \
  1203. do \
  1204. { \
  1205. CurrCharType = CHAR_END_OF_STRING; \
  1206. if (CharsLeft == 0) \
  1207. break; \
  1208. \
  1209. CurrChar = *pInUri++; \
  1210. CharsLeft--; \
  1211. \
  1212. CurrCharType = CHAR_EXTENDED_CHAR; \
  1213. if (CurrChar >= 0x80) \
  1214. break; \
  1215. \
  1216. if (CurrChar == '%') \
  1217. { \
  1218. WCHAR UnescapedChar; \
  1219. \
  1220. if (CharsLeft < 2) \
  1221. goto error; \
  1222. \
  1223. pInUri--; \
  1224. CharsLeft++; \
  1225. \
  1226. if (!NT_SUCCESS(UnescapeW(pInUri, &UnescapedChar))) \
  1227. goto error; \
  1228. \
  1229. pInUri++; \
  1230. CharsLeft--; \
  1231. \
  1232. if (UnescapedChar == '.') \
  1233. { \
  1234. CurrChar = L'.'; \
  1235. pInUri += 2; \
  1236. CharsLeft -= 2; \
  1237. } \
  1238. } \
  1239. \
  1240. CurrCharType = (HttpChars[CurrChar]>>HTTP_CHAR_SHIFT); \
  1241. if (CurrCharType == 0) \
  1242. CurrCharType = (IS_URL_TOKEN(CurrChar))? \
  1243. CHAR_PATH_CHAR : CHAR_INVALID_CHAR; \
  1244. \
  1245. } while (0)
  1246. //
  1247. // Output a BYTE to the output buffer.
  1248. // PERF NOTE: Replace the comparision of OutBufLeft by an ASSERT.
  1249. //
  1250. #define EMIT_A_BYTE(b) \
  1251. do \
  1252. { \
  1253. ASSERT(b < 0x80); \
  1254. \
  1255. if (OutBufLeft == 0) \
  1256. goto overflow; \
  1257. \
  1258. *pOutput++ = (b); \
  1259. OutBufLeft--; \
  1260. } while (0)
  1261. //
  1262. // Output a CHAR to the output buffer. Encodes a UNICODE char in UTF8.
  1263. // The UTF8 char is escaped if bEncode is specified.
  1264. //
  1265. #define EMIT_A_CHAR(c) \
  1266. do { \
  1267. ULONG adj; \
  1268. \
  1269. if (OutBufLeft == 0) \
  1270. goto overflow; \
  1271. \
  1272. if ((c) < 0x80) \
  1273. { \
  1274. *pOutput++ = (UCHAR)(c); \
  1275. OutBufLeft--; \
  1276. break; \
  1277. } \
  1278. \
  1279. if (!NT_SUCCESS(HttpUnicodeToUTF8Encode(&c, 1, pOutput, OutBufLeft, &adj, bEncode))) \
  1280. goto overflow; \
  1281. \
  1282. pOutput += adj; \
  1283. OutBufLeft -= adj; \
  1284. \
  1285. } while (0)
  1286. //
  1287. // Main routine.
  1288. //
  1289. NTSTATUS
  1290. UcCanonicalizeURI(
  1291. IN LPCWSTR pInUri, // Input URI in Unicode
  1292. IN USHORT InUriLen, // Length of input URI (in wchar)
  1293. IN OUT PUCHAR pOutUri, // buffer where the output goes
  1294. IN OUT PUSHORT pOutUriLen, // length of the output buffer
  1295. IN BOOLEAN bEncode // TRUE if char >= 0x80 should be escaped
  1296. )
  1297. {
  1298. ULONG state, nstate, action;
  1299. WCHAR CurrChar = L'\0';
  1300. ULONG CurrCharType;
  1301. PUCHAR pOutput = pOutUri;
  1302. ULONG OutBufLeft = *pOutUriLen;
  1303. // Sanity check
  1304. ASSERT(pInUri && InUriLen != 0);
  1305. ASSERT(pOutUri && *pOutUriLen != 0);
  1306. nstate = state = 0;
  1307. do
  1308. {
  1309. GET_NEXT_CHAR(pInUri, InUriLen, CurrChar, CurrCharType);
  1310. nstate = NEXT_STATE(state, CurrCharType);
  1311. action = ACTION(state, CurrCharType);
  1312. UlTraceVerbose(PARSER, ("UcCanonicalizeURI: CurrChar = 0x%02x, "
  1313. "CurrCharType = %ld, state %ld, nstate = %ld\n",
  1314. (ULONG)CurrChar, CurrCharType, state, nstate));
  1315. switch (action)
  1316. {
  1317. case ACT_EMIT_DOT_DOT_CHAR:
  1318. EMIT_A_BYTE('.');
  1319. // fall through
  1320. case ACT_EMIT_DOT_CHAR:
  1321. EMIT_A_BYTE('.');
  1322. // fall through
  1323. case ACT_EMIT_CHAR:
  1324. EMIT_A_CHAR(CurrChar);
  1325. break;
  1326. case ACT_NONE:
  1327. break;
  1328. case ACT_BACKUP:
  1329. case ACT_BACKUP_EMIT_CHAR:
  1330. ASSERT(pOutput > pOutUri && pOutput[-1] == '/' && *pOutUri == '/');
  1331. //
  1332. // Can we backup? (e.g. if the URI is "/../", we can't)
  1333. //
  1334. if (pOutput > pOutUri + 1)
  1335. {
  1336. // Yes we can backup to a pervious '/'
  1337. pOutput -= 2;
  1338. while (*pOutput != '/')
  1339. pOutput--, OutBufLeft++;
  1340. pOutput++;
  1341. OutBufLeft += 1;
  1342. ASSERT(pOutput > pOutUri);
  1343. }
  1344. if (action == ACT_BACKUP_EMIT_CHAR)
  1345. EMIT_A_CHAR(CurrChar);
  1346. break;
  1347. case ACT_ERROR:
  1348. // URI is invalid
  1349. goto error;
  1350. break;
  1351. case ACT_PANIC:
  1352. // Internal error...we shouldn't be here!
  1353. default:
  1354. UlTraceError(PARSER, ("UcCanonicalizeURI: internal error\n"));
  1355. ASSERT(FALSE);
  1356. break;
  1357. }
  1358. state = nstate;
  1359. } while (state != 6);
  1360. ASSERT(pOutput >= pOutUri && pOutput <= pOutUri + *pOutUriLen);
  1361. UlTrace(PARSER, ("UcCanonicalizeURI: Return length = %d\n",
  1362. pOutput - pOutUri));
  1363. // return the actual number of bytes written to the output buffer
  1364. *pOutUriLen = (USHORT)(pOutput - pOutUri);
  1365. return STATUS_SUCCESS;
  1366. error:
  1367. // Invalid URI
  1368. return STATUS_INVALID_PARAMETER;
  1369. overflow:
  1370. // Output buffer is not big enough
  1371. return STATUS_INSUFFICIENT_RESOURCES;
  1372. }
  1373. //
  1374. // Response Parser functions.
  1375. //
  1376. /***************************************************************************++
  1377. Routine Description:
  1378. Find the header name terminator of a header:value pair.
  1379. Arguments:
  1380. pHttpRequest - Pointer to the current request.
  1381. HttpRequestLength - Bytes left in the request.
  1382. HeaderNameLength - Pointer to return header name.
  1383. Return Value:
  1384. STATUS_SUCCESS : Worked.
  1385. STATUS_INVALID_DEVICE_REQUEST : Invalid header.
  1386. STATUS_MORE_PROCESSING_REQUIRED : More data required.
  1387. --***************************************************************************/
  1388. NTSTATUS
  1389. UcFindHeaderNameEnd(
  1390. IN PUCHAR pHttpRequest,
  1391. IN ULONG HttpRequestLength,
  1392. OUT PULONG HeaderNameLength
  1393. )
  1394. {
  1395. ULONG CurrentOffset;
  1396. NTSTATUS Status;
  1397. UCHAR CurrentChar;
  1398. for (CurrentOffset = 0; CurrentOffset < HttpRequestLength; CurrentOffset++)
  1399. {
  1400. CurrentChar = *(pHttpRequest + CurrentOffset);
  1401. if (CurrentChar == ':')
  1402. {
  1403. // We've found the end of the header.
  1404. break;
  1405. }
  1406. else
  1407. {
  1408. if (!IS_HTTP_TOKEN(CurrentChar))
  1409. {
  1410. // Uh-oh, this isn't a valid header. What do we do now?
  1411. //
  1412. UlTraceError(PARSER,
  1413. ("[UcFindHeaderNameEnd]: Bogus header \n"));
  1414. return STATUS_INVALID_DEVICE_REQUEST;
  1415. }
  1416. }
  1417. }
  1418. // Find out why we got out. If the current offset is less than the
  1419. // header length, we got out because we found the :.
  1420. if (CurrentOffset < HttpRequestLength)
  1421. {
  1422. // Found the terminator. Point Beyond the terminator.
  1423. *HeaderNameLength = CurrentOffset + 1;
  1424. if(*HeaderNameLength > ANSI_STRING_MAX_CHAR_LEN)
  1425. {
  1426. UlTraceError(PARSER,
  1427. ("[UcFindHeaderNameEnd]: Very long header name \n"));
  1428. *HeaderNameLength = 0;
  1429. Status = STATUS_INVALID_NETWORK_RESPONSE;
  1430. }
  1431. else
  1432. {
  1433. Status = STATUS_SUCCESS;
  1434. }
  1435. }
  1436. else
  1437. {
  1438. // Didn't find the :, need more.
  1439. //
  1440. *HeaderNameLength = 0;
  1441. Status = STATUS_MORE_PROCESSING_REQUIRED;
  1442. }
  1443. return Status;
  1444. }
  1445. /***************************************************************************++
  1446. Routine Description:
  1447. Find the end of header value.
  1448. Arguments:
  1449. pHeaderValue - Pointer to the header value
  1450. RemainingBufferLength - Bytes remaining
  1451. ppFoldingHeader - Will be Non NULL if we allocate from pool to do
  1452. header folding. Caller has to free this buffer.
  1453. pBytesTaken - Bytes Consumed.
  1454. Return Value:
  1455. STATUS_SUCCESS : Worked.
  1456. STATUS_INVALID_DEVICE_REQUEST : Invalid header.
  1457. STATUS_MORE_PROCESSING_REQUIRED : More data required.
  1458. --***************************************************************************/
  1459. NTSTATUS
  1460. UcFindHeaderValueEnd(
  1461. IN PUCHAR pHeaderValue,
  1462. IN ULONG RemainingBufferLength,
  1463. IN PUCHAR *ppFoldingHeader,
  1464. IN PULONG pBytesTaken
  1465. )
  1466. {
  1467. ULONG BytesTaken = 0;
  1468. PUCHAR pFoldingBuffer;
  1469. NTSTATUS Status;
  1470. ASSERT(NULL == *ppFoldingHeader);
  1471. //
  1472. // Find the end of the header value
  1473. //
  1474. Status = FindHeaderEndReadOnly(
  1475. pHeaderValue,
  1476. RemainingBufferLength,
  1477. &BytesTaken
  1478. );
  1479. if(STATUS_MORE_PROCESSING_REQUIRED == Status)
  1480. {
  1481. //
  1482. // The headers need to be folded. Since we can't modify TCP's data
  1483. // we'll allocate a buffer for this. We don't really care to optimize
  1484. // for this case, because header folding is pretty rare, so we won't
  1485. // bother with lookaside lists, etc.
  1486. //
  1487. pFoldingBuffer = UL_ALLOCATE_POOL(
  1488. NonPagedPool,
  1489. RemainingBufferLength,
  1490. UC_HEADER_FOLDING_POOL_TAG
  1491. );
  1492. if(!pFoldingBuffer)
  1493. {
  1494. // Can't use STATUS_INSUFFICIENT_RESOURCES because it means
  1495. // something else.
  1496. return STATUS_NO_MEMORY;
  1497. }
  1498. RtlCopyMemory(pFoldingBuffer,
  1499. pHeaderValue,
  1500. RemainingBufferLength
  1501. );
  1502. Status = FindHeaderEnd(
  1503. pFoldingBuffer,
  1504. RemainingBufferLength,
  1505. &BytesTaken
  1506. );
  1507. if(!NT_SUCCESS(Status))
  1508. {
  1509. ASSERT(BytesTaken == 0);
  1510. UL_FREE_POOL(pFoldingBuffer, UC_HEADER_FOLDING_POOL_TAG);
  1511. return Status;
  1512. }
  1513. else
  1514. {
  1515. if(BytesTaken == 0)
  1516. {
  1517. UL_FREE_POOL(pFoldingBuffer, UC_HEADER_FOLDING_POOL_TAG);
  1518. return STATUS_MORE_PROCESSING_REQUIRED;
  1519. }
  1520. else if(BytesTaken > ANSI_STRING_MAX_CHAR_LEN)
  1521. {
  1522. UL_FREE_POOL(pFoldingBuffer, UC_HEADER_FOLDING_POOL_TAG);
  1523. UlTraceError(PARSER,
  1524. ("[UcFindHeaderValueEnd]: Very long header value \n"));
  1525. return STATUS_INVALID_NETWORK_RESPONSE;
  1526. }
  1527. }
  1528. *ppFoldingHeader = pFoldingBuffer;
  1529. }
  1530. else if(NT_SUCCESS(Status))
  1531. {
  1532. if(BytesTaken == 0)
  1533. {
  1534. return STATUS_MORE_PROCESSING_REQUIRED;
  1535. }
  1536. else if(BytesTaken > ANSI_STRING_MAX_CHAR_LEN)
  1537. {
  1538. UlTraceError(PARSER,
  1539. ("[UcFindHeaderValueEnd]: Very long header value \n"));
  1540. return STATUS_INVALID_NETWORK_RESPONSE;
  1541. }
  1542. }
  1543. *pBytesTaken = BytesTaken;
  1544. return Status;
  1545. }
  1546. /***************************************************************************++
  1547. Routine Description:
  1548. Look up a header that we don't have in our fast lookup table. This
  1549. could be because it's a header we don't understand, or because we
  1550. couldn't use the fast lookup table due to insufficient buffer length.
  1551. The latter reason is uncommon, but we'll check the input table anyway
  1552. if we're given one. If we find a header match in our mapping table,
  1553. we'll call the header handler. Otherwise we'll try to allocate an
  1554. unknown header element, fill it in and chain it on the http connection.
  1555. Arguments:
  1556. pHttpConn - Pointer to the current connection on which the
  1557. request arrived.
  1558. pHttpRequest - Pointer to the current request.
  1559. HttpRequestLength - Bytes left in the request.
  1560. pHeaderMap - Pointer to start of an array of header map entries
  1561. (may be NULL).
  1562. HeaderMapCount - Number of entries in array pointed to by pHeaderMap.
  1563. Return Value:
  1564. Number of bytes in the header (including CRLF), or 0 if we couldn't
  1565. parse the header.
  1566. --***************************************************************************/
  1567. NTSTATUS
  1568. UcpLookupHeader(
  1569. IN PUC_HTTP_REQUEST pRequest,
  1570. IN PUCHAR pHttpRequest,
  1571. IN ULONG HttpRequestLength,
  1572. IN PHEADER_MAP_ENTRY pHeaderMap,
  1573. IN ULONG HeaderMapCount,
  1574. OUT ULONG * pBytesTaken
  1575. )
  1576. {
  1577. NTSTATUS Status = STATUS_SUCCESS;
  1578. ULONG HeaderNameLength;
  1579. ULONG i;
  1580. ULONG BytesTaken;
  1581. ULONG HeaderValueLength, RemainingBufferLength;
  1582. PUCHAR pBufferHead;
  1583. PUCHAR pBufferTail;
  1584. PHTTP_UNKNOWN_HEADER pUnknownHeader;
  1585. PUCHAR pHeaderValue;
  1586. PUCHAR pFoldingBuffer = NULL;
  1587. ULONG AlignNameLength, AlignValueLength;
  1588. // First, let's find the terminating : of the header name, if there is one.
  1589. // This will also give us the length of the header, which we can then
  1590. // use to search the header map table if we have one.
  1591. //
  1592. Status = UcFindHeaderNameEnd(
  1593. pHttpRequest,
  1594. HttpRequestLength,
  1595. &HeaderNameLength
  1596. );
  1597. if(!NT_SUCCESS(Status))
  1598. {
  1599. return Status;
  1600. }
  1601. // See if we have a header map array we need to search.
  1602. //
  1603. if (pHeaderMap != NULL)
  1604. {
  1605. // We do have an array to search.
  1606. for (i = 0; i < HeaderMapCount; i++)
  1607. {
  1608. ASSERT(pHeaderMap->pClientHandler != NULL);
  1609. if (HeaderNameLength == pHeaderMap->HeaderLength &&
  1610. _strnicmp(
  1611. (const char *)(pHttpRequest),
  1612. (const char *)(pHeaderMap->Header.HeaderChar),
  1613. HeaderNameLength
  1614. ) == 0 &&
  1615. pHeaderMap->pClientHandler != NULL)
  1616. {
  1617. // First, find the header end.
  1618. pHeaderValue = pHttpRequest + HeaderNameLength;
  1619. RemainingBufferLength = (HttpRequestLength - HeaderNameLength);
  1620. Status = UcFindHeaderValueEnd(
  1621. pHeaderValue,
  1622. RemainingBufferLength,
  1623. &pFoldingBuffer,
  1624. &BytesTaken
  1625. );
  1626. if(!NT_SUCCESS(Status))
  1627. {
  1628. return Status;
  1629. }
  1630. ASSERT(BytesTaken >= CRLF_SIZE);
  1631. HeaderValueLength = BytesTaken - CRLF_SIZE;
  1632. if(pFoldingBuffer != NULL)
  1633. {
  1634. pHeaderValue = pFoldingBuffer;
  1635. }
  1636. UC_PROCESS_HEADER_VALUE(pHeaderValue, HeaderValueLength);
  1637. // This header matches. Call the handling function for it.
  1638. Status = (*(pHeaderMap->pClientHandler))(
  1639. pRequest,
  1640. pHeaderValue,
  1641. HeaderValueLength,
  1642. pHeaderMap->HeaderID
  1643. );
  1644. goto end;
  1645. }
  1646. pHeaderMap++;
  1647. }
  1648. }
  1649. // OK, at this point either we had no header map array or none of them
  1650. // matched. We have an unknown header. Just make sure this header is
  1651. // terminated and save a pointer to it.
  1652. //
  1653. // Find the end of the header value
  1654. //
  1655. pHeaderValue = pHttpRequest + HeaderNameLength;
  1656. HeaderValueLength = (HttpRequestLength - HeaderNameLength);
  1657. Status = UcFindHeaderValueEnd(
  1658. pHeaderValue,
  1659. HeaderValueLength,
  1660. &pFoldingBuffer,
  1661. &BytesTaken
  1662. );
  1663. if(!NT_SUCCESS(Status))
  1664. {
  1665. return Status;
  1666. }
  1667. ASSERT(BytesTaken >= CRLF_SIZE);
  1668. HeaderValueLength = BytesTaken - CRLF_SIZE;
  1669. if(pFoldingBuffer != NULL)
  1670. {
  1671. pHeaderValue = pFoldingBuffer;
  1672. }
  1673. UC_PROCESS_HEADER_VALUE(pHeaderValue, HeaderValueLength);
  1674. //
  1675. // We Have an unknown header. We don't have to search our list of
  1676. // unknown headers to see if this unknown header already exists.
  1677. // even if it does exist, we cannot concatenate these two header
  1678. // values - Since the header is unknown, the syntax for the header-value
  1679. // is also unknown and merging might mess around with the field terminator
  1680. //
  1681. //
  1682. // Carve out a UNKNOWN_HEADER structure using pBufferHead
  1683. //
  1684. pBufferHead = pRequest->CurrentBuffer.pOutBufferHead;
  1685. pBufferTail = pRequest->CurrentBuffer.pOutBufferTail;
  1686. AlignNameLength = ALIGN_UP(HeaderNameLength, PVOID);
  1687. AlignValueLength = ALIGN_UP(HeaderValueLength, PVOID);
  1688. if(pRequest->CurrentBuffer.BytesAvailable >=
  1689. sizeof(HTTP_UNKNOWN_HEADER) + AlignNameLength + AlignValueLength)
  1690. {
  1691. pUnknownHeader = (PHTTP_UNKNOWN_HEADER)pBufferHead;
  1692. //
  1693. // The Header name has a ':'.
  1694. //
  1695. pUnknownHeader->NameLength = (USHORT) (HeaderNameLength - 1);
  1696. pBufferTail -= AlignNameLength;
  1697. pUnknownHeader->pName = (PCSTR) pBufferTail;
  1698. RtlCopyMemory(
  1699. pBufferTail,
  1700. pHttpRequest,
  1701. (USHORT) (HeaderNameLength - 1)
  1702. );
  1703. //
  1704. // header value
  1705. //
  1706. pUnknownHeader->RawValueLength = (USHORT) HeaderValueLength;
  1707. pBufferTail -= AlignValueLength;
  1708. pUnknownHeader->pRawValue = (PCSTR) pBufferTail;
  1709. RtlCopyMemory(
  1710. pBufferTail,
  1711. pHeaderValue,
  1712. (USHORT) HeaderValueLength
  1713. );
  1714. pRequest->CurrentBuffer.pResponse->Headers.UnknownHeaderCount ++;
  1715. pRequest->CurrentBuffer.pOutBufferHead =
  1716. pBufferHead + sizeof(HTTP_UNKNOWN_HEADER);
  1717. pRequest->CurrentBuffer.pOutBufferTail = pBufferTail;
  1718. pRequest->CurrentBuffer.BytesAvailable -=
  1719. (sizeof(HTTP_UNKNOWN_HEADER) + AlignNameLength + AlignValueLength);
  1720. }
  1721. else
  1722. {
  1723. Status = STATUS_INSUFFICIENT_RESOURCES;
  1724. }
  1725. end:
  1726. if (NT_SUCCESS(Status))
  1727. {
  1728. *pBytesTaken = HeaderNameLength + BytesTaken;
  1729. }
  1730. if(pFoldingBuffer)
  1731. {
  1732. UL_FREE_POOL(pFoldingBuffer, UC_HEADER_FOLDING_POOL_TAG);
  1733. }
  1734. return Status;
  1735. } // UcpLookupHeader
  1736. /***************************************************************************++
  1737. Routine Description:
  1738. The routine to parse an individual header. We take in a pointer to the
  1739. header and the bytes remaining in the request, and try to find
  1740. the header in our lookup table. We try first the fast way, and then
  1741. try again the slow way in case there wasn't quite enough data the first
  1742. time.
  1743. On input, HttpRequestLength is at least CRLF_SIZE.
  1744. Arguments:
  1745. pRequest - Pointer to the current connection on which the
  1746. request arrived.
  1747. pHttpRequest - Pointer to the current request.
  1748. HttpRequestLength - Bytes left in the request.
  1749. Return Value:
  1750. Number of bytes in the header (including CRLF), or 0 if we couldn't
  1751. parse the header.
  1752. --***************************************************************************/
  1753. NTSTATUS
  1754. UcParseHeader(
  1755. IN PUC_HTTP_REQUEST pRequest,
  1756. IN PUCHAR pHttpRequest,
  1757. IN ULONG HttpRequestLength,
  1758. OUT ULONG * pBytesTaken
  1759. )
  1760. {
  1761. NTSTATUS Status = STATUS_SUCCESS;
  1762. ULONG i;
  1763. ULONG j;
  1764. ULONG BytesTaken;
  1765. ULONGLONG Temp;
  1766. UCHAR c;
  1767. PHEADER_MAP_ENTRY pCurrentHeaderMap;
  1768. ULONG HeaderMapCount;
  1769. BOOLEAN SmallHeader = FALSE;
  1770. PHTTP_RESPONSE_HEADERS pResponseHeaders;
  1771. PUCHAR *pOutBufferHead;
  1772. PUCHAR *pOutBufferTail;
  1773. PULONG BytesAvailable;
  1774. PUCHAR pHeaderValue;
  1775. ULONG HeaderValueLength;
  1776. ULONG RemainingBufferLength;
  1777. PUCHAR pFoldingBuffer = NULL;
  1778. //
  1779. // Sanity check.
  1780. //
  1781. ASSERT(HttpRequestLength >= CRLF_SIZE);
  1782. pResponseHeaders = &pRequest->CurrentBuffer.pResponse->Headers;
  1783. pOutBufferHead = &pRequest->CurrentBuffer.pOutBufferHead;
  1784. pOutBufferTail = &pRequest->CurrentBuffer.pOutBufferTail;
  1785. BytesAvailable = &pRequest->CurrentBuffer.BytesAvailable;
  1786. c = *pHttpRequest;
  1787. // message-headers start with field-name [= token]
  1788. //
  1789. if (IS_HTTP_TOKEN(c) == FALSE)
  1790. {
  1791. UlTraceError(PARSER, (
  1792. "ul!UcParseHeader c = 0x%x ERROR: invalid header char\n",
  1793. c
  1794. ));
  1795. return STATUS_INVALID_DEVICE_REQUEST;
  1796. }
  1797. // Does the header start with an alpha?
  1798. //
  1799. if (IS_HTTP_ALPHA(c))
  1800. {
  1801. // Uppercase the character, and find the appropriate set of header map
  1802. // entries.
  1803. //
  1804. c = UPCASE_CHAR(c);
  1805. c -= 'A';
  1806. pCurrentHeaderMap = g_ResponseHeaderIndexTable[c].pHeaderMap;
  1807. HeaderMapCount = g_ResponseHeaderIndexTable[c].Count;
  1808. // Loop through all the header map entries that might match
  1809. // this header, and check them. The count will be 0 if there
  1810. // are no entries that might match and we'll skip the loop.
  1811. for (i = 0; i < HeaderMapCount; i++)
  1812. {
  1813. ASSERT(pCurrentHeaderMap->pClientHandler != NULL);
  1814. // If we have enough bytes to do the fast check, do it.
  1815. // Otherwise skip this. We may skip a valid match, but if
  1816. // so we'll catch it later.
  1817. if (HttpRequestLength >= pCurrentHeaderMap->MinBytesNeeded)
  1818. {
  1819. ASSERT(HttpRequestLength >= ((pCurrentHeaderMap->ArrayCount-1) *
  1820. sizeof(ULONGLONG)));
  1821. for (j = 0; j < pCurrentHeaderMap->ArrayCount; j++)
  1822. {
  1823. Temp = *(UNALIGNED64 ULONGLONG *)(pHttpRequest +
  1824. (j * sizeof(ULONGLONG)));
  1825. if ((Temp & pCurrentHeaderMap->HeaderMask[j]) !=
  1826. pCurrentHeaderMap->Header.HeaderLong[j] )
  1827. {
  1828. break;
  1829. }
  1830. }
  1831. // See why we exited out.
  1832. if (j == pCurrentHeaderMap->ArrayCount &&
  1833. pCurrentHeaderMap->pClientHandler != NULL)
  1834. {
  1835. pHeaderValue = pHttpRequest +
  1836. pCurrentHeaderMap->HeaderLength;
  1837. RemainingBufferLength = (HttpRequestLength -
  1838. pCurrentHeaderMap->HeaderLength);
  1839. Status = UcFindHeaderValueEnd(
  1840. pHeaderValue,
  1841. RemainingBufferLength,
  1842. &pFoldingBuffer,
  1843. &BytesTaken
  1844. );
  1845. if(!NT_SUCCESS(Status))
  1846. {
  1847. return Status;
  1848. }
  1849. ASSERT(BytesTaken >= CRLF_SIZE);
  1850. HeaderValueLength = BytesTaken - CRLF_SIZE;
  1851. if(pFoldingBuffer != NULL)
  1852. {
  1853. pHeaderValue = pFoldingBuffer;
  1854. }
  1855. UC_PROCESS_HEADER_VALUE(pHeaderValue, HeaderValueLength);
  1856. // Exited because we found a match. Call the
  1857. // handler for this header to take cake of this.
  1858. Status = (*(pCurrentHeaderMap->pClientHandler))(
  1859. pRequest,
  1860. pHeaderValue,
  1861. HeaderValueLength,
  1862. pCurrentHeaderMap->HeaderID
  1863. );
  1864. if (NT_SUCCESS(Status) == FALSE)
  1865. goto end;
  1866. ASSERT(BytesTaken != 0);
  1867. *pBytesTaken = pCurrentHeaderMap->HeaderLength + BytesTaken;
  1868. goto end;
  1869. }
  1870. // If we get here, we exited out early because a match
  1871. // failed, so keep going.
  1872. }
  1873. else if (SmallHeader == FALSE)
  1874. {
  1875. //
  1876. // Remember that we didn't check a header map entry
  1877. // because the bytes in the buffer was not LONGLONG
  1878. // aligned
  1879. //
  1880. SmallHeader = TRUE;
  1881. }
  1882. // Either didn't match or didn't have enough bytes for the
  1883. // check. In either case, check the next header map entry.
  1884. pCurrentHeaderMap++;
  1885. }
  1886. // Got all the way through the appropriate header map entries
  1887. // without a match. This could be because we're dealing with a
  1888. // header we don't know about or because it's a header we
  1889. // care about that was too small to do the fast check. The
  1890. // latter case should be very rare, but we still need to
  1891. // handle it.
  1892. // Update the current header map pointer to point back to the
  1893. // first of the possibles. If there were no possibles,
  1894. // the pointer will be NULL and the HeaderMapCount 0, so it'll
  1895. // stay NULL. Otherwise the subtraction will back it up the
  1896. // appropriate amount.
  1897. if (SmallHeader)
  1898. {
  1899. pCurrentHeaderMap -= HeaderMapCount;
  1900. }
  1901. else
  1902. {
  1903. pCurrentHeaderMap = NULL;
  1904. HeaderMapCount = 0;
  1905. }
  1906. }
  1907. else
  1908. {
  1909. pCurrentHeaderMap = NULL;
  1910. HeaderMapCount = 0;
  1911. }
  1912. // At this point either the header starts with a non-alphabetic
  1913. // character or we don't have a set of header map entries for it.
  1914. Status = UcpLookupHeader(
  1915. pRequest,
  1916. pHttpRequest,
  1917. HttpRequestLength,
  1918. pCurrentHeaderMap,
  1919. HeaderMapCount,
  1920. &BytesTaken
  1921. );
  1922. if (NT_SUCCESS(Status) == FALSE)
  1923. goto end;
  1924. // Lookup header returns the total bytes taken, including the header name
  1925. //
  1926. *pBytesTaken = BytesTaken;
  1927. end:
  1928. if(pFoldingBuffer != NULL)
  1929. {
  1930. UL_FREE_POOL(pFoldingBuffer, UC_HEADER_FOLDING_POOL_TAG);
  1931. }
  1932. return Status;
  1933. } // UcParseHeader
  1934. /***************************************************************************++
  1935. Routine Description:
  1936. Parses WWW-Authenticate header value. The value can contain multiple
  1937. challenges. Each challenge can have zero or more comma-separated
  1938. auth-parameters.
  1939. The routine returns pointers (pointing into the original header value)
  1940. to each auth scheme found in the header.
  1941. Arguments:
  1942. IN pAuthHeader - WWW-Authenticate header value (value only)
  1943. IN AuthHeaderLength - Length of the header value
  1944. OUT AuthSchemes - Contains pointers to various auth schemes
  1945. present in the header value.
  1946. e.g. AuthSchemes[HttpAuthTypeBasic] will
  1947. be initialized to point to Basic scheme
  1948. portion of the AuthHeader.
  1949. Return Values:
  1950. Status.
  1951. --***************************************************************************/
  1952. NTSTATUS
  1953. UcParseWWWAuthenticateHeader(
  1954. IN PCSTR pAuthHeader,
  1955. IN ULONG AuthHeaderLength,
  1956. OUT PHTTP_AUTH_PARSED_PARAMS pAuthParsedParams
  1957. )
  1958. {
  1959. ULONG i;
  1960. NTSTATUS Status;
  1961. PCSTR ptr = pAuthHeader;
  1962. // Sanity check
  1963. ASSERT(pAuthHeader && AuthHeaderLength);
  1964. ASSERT(pAuthParsedParams);
  1965. do
  1966. {
  1967. // skip white space
  1968. while (AuthHeaderLength && IS_HTTP_LWS(*ptr))
  1969. AuthHeaderLength--, ptr++;
  1970. // See if any header left to parse
  1971. if (AuthHeaderLength == 0)
  1972. break;
  1973. // See if any scheme name matches
  1974. for (i = 1; i < HttpAuthTypesCount; i++)
  1975. {
  1976. // Quick test for lengths and delimiters
  1977. if ((AuthHeaderLength == HttpAuthScheme[i].NameLength) ||
  1978. ((AuthHeaderLength > HttpAuthScheme[i].NameLength) &&
  1979. (IS_HTTP_LWS(ptr[HttpAuthScheme[i].NameLength]) ||
  1980. ptr[HttpAuthScheme[i].NameLength] == ',')))
  1981. {
  1982. // See if the scheme name matches
  1983. if (_strnicmp(
  1984. ptr,
  1985. HttpAuthScheme[i].Name,
  1986. HttpAuthScheme[i].NameLength) == 0)
  1987. {
  1988. // An auth scheme should not appear more than once!
  1989. if (pAuthParsedParams[i].bPresent)
  1990. return STATUS_INVALID_PARAMETER;
  1991. // Parse its parameters, if any
  1992. Status = HttpAuthScheme[i].ParamParser(
  1993. &HttpAuthScheme[i],
  1994. &pAuthParsedParams[i],
  1995. &ptr,
  1996. &AuthHeaderLength
  1997. );
  1998. if (!NT_SUCCESS(Status))
  1999. return Status;
  2000. // No need to loop thro' other schemes
  2001. break;
  2002. }
  2003. }
  2004. }
  2005. // Error if we don't identify a scheme
  2006. if (i >= HttpAuthTypesCount)
  2007. return STATUS_INVALID_PARAMETER;
  2008. } while (1);
  2009. return STATUS_SUCCESS;
  2010. }
  2011. /***************************************************************************++
  2012. Routine Description:
  2013. Searches for an attribute=value pair.
  2014. The routine returns
  2015. -1 : if parse error
  2016. 0 : if no attribute value pair is found
  2017. 1 : if only "attribute" is found (not followed by an '=')
  2018. 3 : if a valid attribute=value pair is found
  2019. WWW-Authenticate header pointer and length are updated to next non-space
  2020. character. If the return value is 3, the pointer and length are updated
  2021. past the attribute-value pair.
  2022. Arguments:
  2023. IN OUT ppHeader - Pointer to WWW-Authenticate header value
  2024. IN OUT pHeaderLength - Pointer to length of WWW-Auth header value
  2025. OUT Attrib - Pointer to attribute
  2026. OUT AttribLen - Length of the attribute string
  2027. OUT Value - Pointer to value
  2028. OUT ValueLen - Length of the value string
  2029. Return Values:
  2030. Status.
  2031. --***************************************************************************/
  2032. LONG
  2033. UcpFindAttribValuePair(
  2034. PCSTR *ppHeader,
  2035. ULONG *pHeaderLength,
  2036. PCSTR *Attrib,
  2037. ULONG *AttribLen,
  2038. PCSTR *Value,
  2039. ULONG *ValueLen
  2040. )
  2041. {
  2042. LONG retval = 3;
  2043. PCSTR pHeader = *ppHeader;
  2044. ULONG HeaderLength = *pHeaderLength;
  2045. // Initialize return values
  2046. *Attrib = NULL;
  2047. *AttribLen = 0;
  2048. *Value = NULL;
  2049. *ValueLen = 0;
  2050. // Skip space
  2051. while (HeaderLength && IS_HTTP_LWS(*pHeader))
  2052. HeaderLength--, pHeader++;
  2053. // Update header pointer and length
  2054. *ppHeader = pHeader;
  2055. *pHeaderLength = HeaderLength;
  2056. // Remember the start of an attribute
  2057. *Attrib = pHeader;
  2058. // Skip the attribute name
  2059. while (HeaderLength && IS_HTTP_TOKEN(*pHeader))
  2060. HeaderLength--, pHeader++;
  2061. // Length of the attribute
  2062. *AttribLen = (ULONG)(pHeader - *Attrib);
  2063. // If we did not see any attribute name
  2064. if (pHeader == *Attrib)
  2065. {
  2066. // Nope.
  2067. retval = 0;
  2068. goto done;
  2069. }
  2070. // an attribute must be terminated by an '='
  2071. if (HeaderLength == 0 || *pHeader != '=')
  2072. {
  2073. // Saw only an attribute
  2074. retval = 1;
  2075. goto done;
  2076. }
  2077. // Skip '='
  2078. HeaderLength--, pHeader++;
  2079. // Quoted string
  2080. if (HeaderLength && *pHeader == '"')
  2081. {
  2082. // Skip '"'
  2083. HeaderLength--, pHeader++;
  2084. // Remember the start of value ('"' not included)
  2085. *Value = pHeader;
  2086. // Find the matching '"'
  2087. while (HeaderLength && *pHeader != '"')
  2088. {
  2089. if (*pHeader == '\\')
  2090. {
  2091. // Skip '\\' char
  2092. HeaderLength--, pHeader++;
  2093. if (HeaderLength == 0)
  2094. {
  2095. // Error! There must be at least one char following '\\'.
  2096. retval = -1;
  2097. goto done;
  2098. }
  2099. // Else skip the char that appeared after '\\'.
  2100. }
  2101. HeaderLength--, pHeader++;
  2102. }
  2103. // Calculate length of the value string
  2104. *ValueLen = (ULONG)(pHeader - *Value);
  2105. // Error if we did not find a matching '"'
  2106. if (HeaderLength == 0)
  2107. retval = -1;
  2108. else
  2109. // Skip '"'
  2110. HeaderLength--, pHeader++;
  2111. }
  2112. // Token
  2113. else
  2114. {
  2115. // Remember start of the value string
  2116. *Value = pHeader;
  2117. // Find the end of value string
  2118. while (HeaderLength && IS_HTTP_TOKEN(*pHeader))
  2119. HeaderLength--, pHeader++;
  2120. // Calculate the length of the value string
  2121. *ValueLen = (ULONG)(pHeader - *Value);
  2122. }
  2123. // Update header pointer and length
  2124. *pHeaderLength = HeaderLength;
  2125. *ppHeader = pHeader;
  2126. done:
  2127. return retval;
  2128. }
  2129. /***************************************************************************++
  2130. Routine Description:
  2131. Parses WWW-Authenticate header for an authentication scheme which
  2132. has parameters in the form of attribute value pairs. (e.g. Digest)
  2133. The routine returns pointer to the parameter values and their lengths.
  2134. WWW-Authenticate header pointer and length are updated.
  2135. Arguments:
  2136. IN pAuthScheme - Pointer to the auth scheme being parsed
  2137. OUT pAuthParamValues - Output parameter value pointers and lengths
  2138. IN OUT ppHeader - Pointer to WWW-Authenticate header value
  2139. IN OUT pHeaderLength - Pointer to length of WWW-Auth header value
  2140. Return Values:
  2141. Status.
  2142. --***************************************************************************/
  2143. NTSTATUS
  2144. UcpParseAuthParams(
  2145. PHTTP_AUTH_SCHEME pAuthScheme,
  2146. PHTTP_AUTH_PARSED_PARAMS pAuthParsedParams,
  2147. PCSTR *ppHeader,
  2148. ULONG *pHeaderLength
  2149. )
  2150. {
  2151. ULONG i;
  2152. LONG retval;
  2153. PCSTR attrib, value;
  2154. ULONG attribLen, valueLen;
  2155. ULONG ParamCount = 0;
  2156. PCSTR pHeader = *ppHeader;
  2157. ULONG HeaderLength = *pHeaderLength;
  2158. PHTTP_AUTH_PARAM_VALUE pAuthParamValues;
  2159. // Sanity check
  2160. ASSERT(pAuthParsedParams);
  2161. ASSERT(pHeader && HeaderLength);
  2162. ASSERT(pAuthScheme);
  2163. ASSERT(pAuthScheme->NumberParams);
  2164. ASSERT(pAuthScheme->NameLength <= HeaderLength);
  2165. ASSERT(_strnicmp(pAuthScheme->Name, pHeader, pAuthScheme->NameLength) == 0);
  2166. pAuthParsedParams->pScheme = pHeader;
  2167. pAuthParamValues = pAuthParsedParams->Params;
  2168. // Zero out return value
  2169. if (pAuthParamValues)
  2170. RtlZeroMemory(
  2171. pAuthParamValues,
  2172. sizeof(*pAuthParamValues) * pAuthScheme->NumberParams
  2173. );
  2174. // Skip the scheme name
  2175. pHeader += pAuthScheme->NameLength;
  2176. HeaderLength -= pAuthScheme->NameLength;
  2177. do {
  2178. // Find an attribute value pair
  2179. retval = UcpFindAttribValuePair(
  2180. &pHeader,
  2181. &HeaderLength,
  2182. &attrib,
  2183. &attribLen,
  2184. &value,
  2185. &valueLen
  2186. );
  2187. // Parse error!
  2188. if (retval < 0)
  2189. return STATUS_INVALID_PARAMETER;
  2190. switch (retval)
  2191. {
  2192. case 0: // No attribute value pair found
  2193. case 1: // Only attribute found (not followed by '=')
  2194. goto done;
  2195. case 3: // valid attribute value pair found
  2196. // A valid parameter was found.
  2197. ParamCount++;
  2198. // See if the caller is interested in parameter values
  2199. if (pAuthParamValues)
  2200. {
  2201. // See if the auth scheme supports the attribute
  2202. for (i = 0; i < pAuthScheme->NumberParams; i++)
  2203. {
  2204. if (attribLen == pAuthScheme->ParamAttribs[i].Length &&
  2205. _strnicmp(attrib, pAuthScheme->ParamAttribs[i].Name,
  2206. attribLen) == 0)
  2207. {
  2208. if (pAuthParamValues[i].Value ||
  2209. pAuthParamValues[i].Length)
  2210. return STATUS_INVALID_PARAMETER;
  2211. pAuthParsedParams->NumberKnownParams++;
  2212. // Return the parameter value
  2213. pAuthParamValues[i].Value = value;
  2214. pAuthParamValues[i].Length = valueLen;
  2215. break;
  2216. }
  2217. }
  2218. if (i >= pAuthScheme->NumberParams)
  2219. pAuthParsedParams->NumberUnknownParams++;
  2220. }
  2221. // Skip blank spaces
  2222. while (HeaderLength && IS_HTTP_LWS(*pHeader))
  2223. HeaderLength--, pHeader++;
  2224. // A ',' must be present.
  2225. if (HeaderLength)
  2226. {
  2227. if (*pHeader != ',')
  2228. return STATUS_INVALID_PARAMETER;
  2229. HeaderLength--, pHeader++;
  2230. }
  2231. break;
  2232. default:
  2233. // Should not be here!
  2234. ASSERT(FALSE);
  2235. break;
  2236. }
  2237. } while (HeaderLength);
  2238. done:
  2239. // We must have parsed at least one parameter.
  2240. if (ParamCount == 0)
  2241. {
  2242. return STATUS_INVALID_PARAMETER;
  2243. }
  2244. // Update WWW-Authenticate header pointer and length
  2245. pAuthParsedParams->bPresent = TRUE;
  2246. pAuthParsedParams->Length = (ULONG)(pHeader - pAuthParsedParams->pScheme);
  2247. *ppHeader = pHeader;
  2248. *pHeaderLength = HeaderLength;
  2249. return STATUS_SUCCESS;
  2250. }
  2251. /***************************************************************************++
  2252. Routine Description:
  2253. Parses WWW-Authenticate header for an authentication scheme which
  2254. has only one parameter NOT in the form of attribute value pair.
  2255. (e.g. auth scheme NTLM).
  2256. The routine returns pointer to the parameter value and its length.
  2257. WWW-Authenticate header pointer and length are updated.
  2258. Arguments:
  2259. IN pAuthScheme - Pointer to the auth scheme being parsed
  2260. OUT pAuthParams - Output parameter pointer and its length
  2261. IN OUT ppHeader - Pointer to WWW-Authenticate header value
  2262. IN OUT pHeaderLength - Pointer to the length of WWW-Auth header value
  2263. Return Values:
  2264. Status.
  2265. --***************************************************************************/
  2266. NTSTATUS
  2267. UcpParseAuthBlob(
  2268. PHTTP_AUTH_SCHEME pAuthScheme,
  2269. PHTTP_AUTH_PARSED_PARAMS pAuthParsedParams,
  2270. PCSTR *ppHeader,
  2271. ULONG *pHeaderLength
  2272. )
  2273. {
  2274. PCSTR pHeader = *ppHeader;
  2275. ULONG HeaderLength = *pHeaderLength;
  2276. PCSTR value; // Pointer to the parameter value
  2277. PHTTP_AUTH_PARAM_VALUE pAuthParams;
  2278. // Sanity check
  2279. ASSERT(pAuthParsedParams);
  2280. ASSERT(pHeader);
  2281. ASSERT(pAuthScheme);
  2282. ASSERT(pAuthScheme->NumberParams == 0);
  2283. ASSERT(pAuthScheme->NameLength <= HeaderLength);
  2284. ASSERT(_strnicmp(pAuthScheme->Name, pHeader, pAuthScheme->NameLength) == 0);
  2285. pAuthParsedParams->pScheme = pHeader;
  2286. pAuthParams = pAuthParsedParams->Params;
  2287. // Zero out the return values
  2288. if (pAuthParams)
  2289. RtlZeroMemory(pAuthParams, sizeof(*pAuthParams));
  2290. // Skip the scheme name
  2291. pHeader += pAuthScheme->NameLength;
  2292. HeaderLength -= pAuthScheme->NameLength;
  2293. // Skip white spaces
  2294. while (HeaderLength && IS_HTTP_LWS(*pHeader))
  2295. HeaderLength--, pHeader++;
  2296. // Begining of paramter value
  2297. value = pHeader;
  2298. // Search for the end
  2299. while (HeaderLength && *pHeader != ',')
  2300. HeaderLength--, pHeader++;
  2301. // Return the parameter value, if any, if asked
  2302. if (pHeader != value && pAuthParams)
  2303. {
  2304. pAuthParsedParams->NumberUnknownParams++;
  2305. pAuthParams->Value = value;
  2306. pAuthParams->Length = (ULONG)(pHeader - value);
  2307. }
  2308. // Skip the trailing ','
  2309. if (HeaderLength > 0 && *pHeader == ',')
  2310. HeaderLength--, pHeader++;
  2311. pAuthParsedParams->bPresent = TRUE;
  2312. // Update header pointer and length
  2313. pAuthParsedParams->Length = (ULONG)(pHeader - pAuthParsedParams->pScheme);
  2314. *ppHeader = pHeader;
  2315. *pHeaderLength = HeaderLength;
  2316. return STATUS_SUCCESS;
  2317. }
  2318. /****************************************************************************++
  2319. Routine Description:
  2320. The default routine for handling single headers.
  2321. Arguments:
  2322. pRequest - pointer to internal request.
  2323. pHeader - Pointer to the header value.
  2324. HeaderLength - Length of data pointed to by pHeader.
  2325. HeaderID - ID of the header.
  2326. pBytesTaken - BytesTaken
  2327. Return Value:
  2328. STATUS_SUCCESS if success, else failure.
  2329. --****************************************************************************/
  2330. NTSTATUS
  2331. UcSingleHeaderHandler(
  2332. IN PUC_HTTP_REQUEST pRequest,
  2333. IN PUCHAR pHeader,
  2334. IN ULONG HeaderValueLength,
  2335. IN HTTP_HEADER_ID HeaderID
  2336. )
  2337. {
  2338. NTSTATUS Status = STATUS_SUCCESS;
  2339. ULONG AlignLength;
  2340. PUCHAR pBufferHead;
  2341. PUCHAR pBufferTail;
  2342. PHTTP_KNOWN_HEADER pKnownHeaders;
  2343. pKnownHeaders = pRequest->CurrentBuffer.pResponse->Headers.KnownHeaders;
  2344. pBufferHead = pRequest->CurrentBuffer.pOutBufferHead;
  2345. pBufferTail = pRequest->CurrentBuffer.pOutBufferTail;
  2346. // do we have an existing header?
  2347. //
  2348. if (pKnownHeaders[HeaderID].RawValueLength == 0)
  2349. {
  2350. // No existing header, just save this pointer for now.
  2351. //
  2352. AlignLength = ALIGN_UP(HeaderValueLength, PVOID);
  2353. if(pRequest->CurrentBuffer.BytesAvailable >= AlignLength)
  2354. {
  2355. PUCHAR pBuffer = pBufferTail-AlignLength;
  2356. ASSERT(pBuffer >= pBufferHead);
  2357. //
  2358. // copy and NULL terminate.
  2359. //
  2360. RtlCopyMemory(pBuffer, pHeader, HeaderValueLength);
  2361. pKnownHeaders[HeaderID].RawValueLength =
  2362. (USHORT)HeaderValueLength;
  2363. pKnownHeaders[HeaderID].pRawValue = (PCSTR) pBuffer;
  2364. pRequest->CurrentBuffer.pOutBufferTail = pBuffer;
  2365. pRequest->CurrentBuffer.BytesAvailable -= AlignLength;
  2366. }
  2367. else
  2368. {
  2369. Status = STATUS_INSUFFICIENT_RESOURCES;
  2370. goto end;
  2371. }
  2372. }
  2373. else
  2374. {
  2375. //
  2376. // uh oh. Have an existing header, fail the request.
  2377. //
  2378. UlTraceError(PARSER, (
  2379. "[UcSingleHeaderHandler]: (pHeader = %p)\n"
  2380. " ERROR: multiple headers not allowed.\n",
  2381. pHeader
  2382. ));
  2383. Status = STATUS_INVALID_NETWORK_RESPONSE;
  2384. goto end;
  2385. }
  2386. end:
  2387. return Status;
  2388. } // UcSingleHeaderHandler
  2389. /****************************************************************************++
  2390. Routine Description:
  2391. The default routine for handling multiple headers. This function handles
  2392. multiple headers with the same name, and appends the values together
  2393. separated by commas.
  2394. Arguments:
  2395. pRequest - pointer to internal request.
  2396. pHeader - Pointer to the header value.
  2397. HeaderLength - Length of data pointed to by pHeader.
  2398. HeaderID - ID of the header.
  2399. pBytesTaken - BytesTaken
  2400. Return Value:
  2401. STATUS_SUCCESS if success, else failure.
  2402. --****************************************************************************/
  2403. NTSTATUS
  2404. UcMultipleHeaderHandler(
  2405. IN PUC_HTTP_REQUEST pRequest,
  2406. IN PUCHAR pHeader,
  2407. IN ULONG HeaderValueLength,
  2408. IN HTTP_HEADER_ID HeaderID
  2409. )
  2410. {
  2411. NTSTATUS Status = STATUS_SUCCESS;
  2412. ULONG AlignLength;
  2413. PUCHAR pBufferHead;
  2414. PUCHAR pBufferTail;
  2415. PHTTP_KNOWN_HEADER pKnownHeaders;
  2416. pKnownHeaders = pRequest->CurrentBuffer.pResponse->Headers.KnownHeaders;
  2417. pBufferHead = pRequest->CurrentBuffer.pOutBufferHead;
  2418. pBufferTail = pRequest->CurrentBuffer.pOutBufferTail;
  2419. // do we have an existing header?
  2420. //
  2421. if (pKnownHeaders[HeaderID].RawValueLength == 0)
  2422. {
  2423. AlignLength = ALIGN_UP(HeaderValueLength, PVOID);
  2424. if(pRequest->CurrentBuffer.BytesAvailable >= AlignLength)
  2425. {
  2426. PUCHAR pBuffer = pBufferTail-AlignLength;
  2427. ASSERT(pBuffer >= pBufferHead);
  2428. //
  2429. // copy & null terminate it.
  2430. //
  2431. RtlCopyMemory(pBuffer, pHeader, HeaderValueLength);
  2432. pKnownHeaders[HeaderID].RawValueLength =
  2433. (USHORT)HeaderValueLength;
  2434. pKnownHeaders[HeaderID].pRawValue = (PCSTR) pBuffer;
  2435. pRequest->CurrentBuffer.pOutBufferTail = pBuffer;
  2436. pRequest->CurrentBuffer.BytesAvailable -= AlignLength;
  2437. }
  2438. else
  2439. {
  2440. Status = STATUS_INSUFFICIENT_RESOURCES;
  2441. goto end;
  2442. }
  2443. }
  2444. else
  2445. {
  2446. ULONG OldHeaderLength;
  2447. ULONG CombinedHeaderLength;
  2448. PUCHAR pBuffer;
  2449. // Have an existing header, append this one.
  2450. OldHeaderLength = pKnownHeaders[HeaderID].RawValueLength;
  2451. CombinedHeaderLength = OldHeaderLength + HeaderValueLength + 1;
  2452. AlignLength = ALIGN_UP(CombinedHeaderLength, PVOID);
  2453. //
  2454. // UC_BUGBUG:
  2455. //
  2456. if(pRequest->CurrentBuffer.BytesAvailable >= AlignLength)
  2457. {
  2458. pBuffer = pBufferTail-AlignLength;
  2459. pRequest->CurrentBuffer.pOutBufferTail = pBuffer;
  2460. ASSERT(pBuffer >= pBufferHead);
  2461. // Copy the old header.
  2462. RtlCopyMemory(pBuffer,
  2463. pKnownHeaders[HeaderID].pRawValue,
  2464. pKnownHeaders[HeaderID].RawValueLength);
  2465. //
  2466. // Save pointers to the new values.
  2467. //
  2468. pKnownHeaders[HeaderID].pRawValue = (PCSTR) pBuffer;
  2469. // advance the buffer.
  2470. pBuffer += pKnownHeaders[HeaderID].RawValueLength;
  2471. // Add a ','
  2472. *pBuffer = ',';
  2473. pBuffer ++;
  2474. //
  2475. // append the new header
  2476. //
  2477. RtlCopyMemory(pBuffer, pHeader, HeaderValueLength);
  2478. //
  2479. // Account for the new header + a ','
  2480. //
  2481. pKnownHeaders[HeaderID].RawValueLength +=
  2482. ((USHORT)HeaderValueLength + 1);
  2483. pRequest->CurrentBuffer.BytesAvailable -= AlignLength;
  2484. }
  2485. else
  2486. {
  2487. Status = STATUS_INSUFFICIENT_RESOURCES;
  2488. goto end;
  2489. }
  2490. }
  2491. end:
  2492. return Status;
  2493. } // UcMultipleHeaderHandler
  2494. /****************************************************************************++
  2495. Routine Description:
  2496. The default routine for handling ProxyAuthenticte & WwwAuthenticate
  2497. Arguments:
  2498. pRequest - pointer to internal request.
  2499. pHeader - Pointer to the header value.
  2500. HeaderLength - Length of data pointed to by pHeader.
  2501. HeaderID - ID of the header.
  2502. pBytesTaken - BytesTaken
  2503. Return Value:
  2504. STATUS_SUCCESS if success, else failure.
  2505. --****************************************************************************/
  2506. NTSTATUS
  2507. UcAuthenticateHeaderHandler(
  2508. IN PUC_HTTP_REQUEST pRequest,
  2509. IN PUCHAR pHeader,
  2510. IN ULONG HeaderLength,
  2511. IN HTTP_HEADER_ID HeaderID
  2512. )
  2513. {
  2514. NTSTATUS Status;
  2515. PCSTR pBuffer;
  2516. ULONG BufLen;
  2517. PHTTP_KNOWN_HEADER pKnownHeaders;
  2518. pKnownHeaders = pRequest->CurrentBuffer.pResponse->Headers.KnownHeaders;
  2519. ASSERT(HeaderID == HttpHeaderProxyAuthenticate ||
  2520. HeaderID == HttpHeaderWwwAuthenticate);
  2521. Status = UcMultipleHeaderHandler(pRequest,
  2522. pHeader,
  2523. HeaderLength,
  2524. HeaderID
  2525. );
  2526. if(NT_SUCCESS(Status))
  2527. {
  2528. //
  2529. // First detect the auth scheme.
  2530. //
  2531. pBuffer = pKnownHeaders[HeaderID].pRawValue;
  2532. BufLen = pKnownHeaders[HeaderID].RawValueLength;
  2533. if(HeaderID == HttpHeaderWwwAuthenticate)
  2534. {
  2535. if((Status = UcParseAuthChallenge(
  2536. pRequest->pAuthInfo,
  2537. pBuffer,
  2538. BufLen,
  2539. pRequest,
  2540. &pRequest->CurrentBuffer.pResponse->Flags
  2541. )) != STATUS_SUCCESS)
  2542. {
  2543. return Status;
  2544. }
  2545. }
  2546. else
  2547. {
  2548. if((Status = UcParseAuthChallenge(
  2549. pRequest->pProxyAuthInfo,
  2550. pBuffer,
  2551. BufLen,
  2552. pRequest,
  2553. &pRequest->CurrentBuffer.pResponse->Flags
  2554. )) != STATUS_SUCCESS)
  2555. {
  2556. return Status;
  2557. }
  2558. }
  2559. }
  2560. return Status;
  2561. }
  2562. /****************************************************************************++
  2563. Routine Description:
  2564. The default routine for handling Content-Length header
  2565. Arguments:
  2566. pRequest - pointer to internal request.
  2567. pHeader - Pointer to the header value.
  2568. HeaderLength - Length of data pointed to by pHeader.
  2569. HeaderID - ID of the header.
  2570. pBytesTaken - BytesTaken
  2571. Return Value:
  2572. STATUS_SUCCESS if success, else failure.
  2573. --****************************************************************************/
  2574. NTSTATUS
  2575. UcContentLengthHeaderHandler(
  2576. IN PUC_HTTP_REQUEST pRequest,
  2577. IN PUCHAR pHeader,
  2578. IN ULONG HeaderLength,
  2579. IN HTTP_HEADER_ID HeaderID
  2580. )
  2581. {
  2582. NTSTATUS Status;
  2583. PUCHAR pBuffer;
  2584. USHORT Length;
  2585. PHTTP_KNOWN_HEADER pKnownHeaders;
  2586. pKnownHeaders = pRequest->CurrentBuffer.pResponse->Headers.KnownHeaders;
  2587. ASSERT(HeaderID == HttpHeaderContentLength);
  2588. Status = UcSingleHeaderHandler(pRequest,
  2589. pHeader,
  2590. HeaderLength,
  2591. HeaderID
  2592. );
  2593. if(Status == STATUS_SUCCESS)
  2594. {
  2595. //
  2596. // Convert to ULONG
  2597. //
  2598. pRequest->ResponseContentLengthSpecified = TRUE;
  2599. pRequest->ResponseContentLength = 0;
  2600. pBuffer = (PUCHAR) pKnownHeaders[HttpHeaderContentLength].pRawValue;
  2601. Length = pKnownHeaders[HttpHeaderContentLength].RawValueLength;
  2602. Status = UlAnsiToULongLong(
  2603. pBuffer,
  2604. Length,
  2605. 10,
  2606. &pRequest->ResponseContentLength
  2607. );
  2608. if(!NT_SUCCESS(Status))
  2609. {
  2610. // Eat the error code that is returned by UlAnsiToULongLong
  2611. Status = STATUS_INVALID_NETWORK_RESPONSE;
  2612. }
  2613. }
  2614. return Status;
  2615. }
  2616. /****************************************************************************++
  2617. Routine Description:
  2618. The default routine for handling transfer encoding header
  2619. Arguments:
  2620. pRequest - pointer to internal request.
  2621. pHeader - Pointer to the header value.
  2622. HeaderLength - Length of data pointed to by pHeader.
  2623. HeaderID - ID of the header.
  2624. pBytesTaken - BytesTaken
  2625. Return Value:
  2626. STATUS_SUCCESS if success, else failure.
  2627. --****************************************************************************/
  2628. NTSTATUS
  2629. UcTransferEncodingHeaderHandler(
  2630. IN PUC_HTTP_REQUEST pRequest,
  2631. IN PUCHAR pHeader,
  2632. IN ULONG HeaderLength,
  2633. IN HTTP_HEADER_ID HeaderID
  2634. )
  2635. {
  2636. NTSTATUS Status;
  2637. PHTTP_KNOWN_HEADER pKnownHeaders;
  2638. pKnownHeaders = pRequest->CurrentBuffer.pResponse->Headers.KnownHeaders;
  2639. ASSERT(HeaderID == HttpHeaderTransferEncoding);
  2640. Status = UcMultipleHeaderHandler(pRequest,
  2641. pHeader,
  2642. HeaderLength,
  2643. HeaderID
  2644. );
  2645. if(Status == STATUS_SUCCESS)
  2646. {
  2647. //
  2648. // Since this is a multiple header, we have to do a strstr.
  2649. // We can't do strstr, since input string is not NULL terminated.
  2650. // so, we use our internal function
  2651. //
  2652. if(UxStriStr(
  2653. pKnownHeaders[HttpHeaderTransferEncoding].pRawValue,
  2654. "identity",
  2655. pKnownHeaders[HttpHeaderTransferEncoding].RawValueLength
  2656. ))
  2657. {
  2658. pRequest->ResponseEncodingChunked = FALSE;
  2659. }
  2660. else
  2661. {
  2662. pRequest->ResponseEncodingChunked = TRUE;
  2663. pRequest->ResponseContentLength = 0;
  2664. pRequest->ParsedFirstChunk = 0;
  2665. }
  2666. }
  2667. return Status;
  2668. }
  2669. /****************************************************************************++
  2670. Routine Description:
  2671. The default routine for handling Connection close header
  2672. Arguments:
  2673. pRequest - pointer to internal request.
  2674. pHeader - Pointer to the header value.
  2675. HeaderLength - Length of data pointed to by pHeader.
  2676. HeaderID - ID of the header.
  2677. pBytesTaken - BytesTaken
  2678. Return Value:
  2679. STATUS_SUCCESS if success, else failure.
  2680. --****************************************************************************/
  2681. NTSTATUS
  2682. UcConnectionHeaderHandler(
  2683. IN PUC_HTTP_REQUEST pRequest,
  2684. IN PUCHAR pHeader,
  2685. IN ULONG HeaderLength,
  2686. IN HTTP_HEADER_ID HeaderID
  2687. )
  2688. {
  2689. NTSTATUS Status;
  2690. PHTTP_KNOWN_HEADER pKnownHeaders;
  2691. pKnownHeaders = pRequest->CurrentBuffer.pResponse->Headers.KnownHeaders;
  2692. ASSERT(HeaderID == HttpHeaderConnection);
  2693. Status = UcMultipleHeaderHandler(pRequest,
  2694. pHeader,
  2695. HeaderLength,
  2696. HeaderID
  2697. );
  2698. if(NT_SUCCESS(Status))
  2699. {
  2700. if(pRequest->ResponseVersion11)
  2701. {
  2702. ASSERT(pRequest->ResponseConnectionClose == FALSE);
  2703. // If it's a 1.1 response, we have to look for the
  2704. // Connection:Close header
  2705. if(UxStriStr(
  2706. pKnownHeaders[HttpHeaderConnection].pRawValue,
  2707. "close",
  2708. pKnownHeaders[HttpHeaderConnection].RawValueLength))
  2709. {
  2710. pRequest->ResponseConnectionClose = TRUE;
  2711. }
  2712. }
  2713. else
  2714. {
  2715. // If it's a 1.0 server, by default we close the connection.
  2716. // unless we see a Keepalive
  2717. ASSERT(pRequest->ResponseConnectionClose == TRUE);
  2718. if(UxStriStr(
  2719. pKnownHeaders[HttpHeaderConnection].pRawValue,
  2720. "keep-alive",
  2721. pKnownHeaders[HttpHeaderConnection].RawValueLength))
  2722. {
  2723. pRequest->ResponseConnectionClose = FALSE;
  2724. }
  2725. }
  2726. }
  2727. return Status;
  2728. }
  2729. /****************************************************************************++
  2730. Routine Description:
  2731. The default routine for handling Content-Type header (used for byte range)
  2732. Arguments:
  2733. pRequest - pointer to internal request.
  2734. pHeader - Pointer to the header value.
  2735. HeaderLength - Length of data pointed to by pHeader.
  2736. HeaderID - ID of the header.
  2737. pBytesTaken - BytesTaken
  2738. Return Value:
  2739. STATUS_SUCCESS if success, else failure.
  2740. --****************************************************************************/
  2741. NTSTATUS
  2742. UcContentTypeHeaderHandler(
  2743. IN PUC_HTTP_REQUEST pRequest,
  2744. IN PUCHAR pHeader,
  2745. IN ULONG HeaderLength,
  2746. IN HTTP_HEADER_ID HeaderID
  2747. )
  2748. {
  2749. NTSTATUS Status;
  2750. PHTTP_KNOWN_HEADER pKnownHeaders;
  2751. BOOLEAN bEndQuote;
  2752. pKnownHeaders = pRequest->CurrentBuffer.pResponse->Headers.KnownHeaders;
  2753. Status = UcSingleHeaderHandler(
  2754. pRequest,
  2755. pHeader,
  2756. HeaderLength,
  2757. HeaderID
  2758. );
  2759. if(NT_SUCCESS(Status))
  2760. {
  2761. if(pKnownHeaders[HttpHeaderContentType].RawValueLength <
  2762. (STRLEN_LIT("multipart/byteranges")))
  2763. {
  2764. return Status;
  2765. }
  2766. if(_strnicmp((const char *)
  2767. (pKnownHeaders[HttpHeaderContentType].pRawValue),
  2768. "multipart/byteranges",
  2769. STRLEN_LIT("multipart/byteranges")) == 0)
  2770. {
  2771. PCSTR s;
  2772. USHORT l;
  2773. // Now, we need to store the string separator in the internal
  2774. // request structure, so that it can be used for parsing out
  2775. // individual ranges.
  2776. //
  2777. // The content-type header is encoded as follows:
  2778. // multipart/byteranges; boundary=THIS_STRING_SEPERATES.
  2779. // Can't use UcFindKeyValuePair as the string separator might have
  2780. // a space (quoted string).
  2781. //
  2782. s = pKnownHeaders[HttpHeaderContentType].pRawValue;
  2783. l = pKnownHeaders[HttpHeaderContentType].RawValueLength;
  2784. bEndQuote = FALSE;
  2785. // Walk up to the '='
  2786. while(l)
  2787. {
  2788. if(*s == '=')
  2789. {
  2790. s++; l--;
  2791. // Ignore the quote after the string
  2792. if(l && *s == '"')
  2793. {
  2794. bEndQuote = TRUE;
  2795. s++; l--;
  2796. }
  2797. break;
  2798. }
  2799. s++; l--;
  2800. }
  2801. if(l == 0)
  2802. {
  2803. // We have reached the end with no boundary separator!
  2804. return STATUS_INVALID_NETWORK_RESPONSE;
  2805. }
  2806. pRequest->MultipartStringSeparatorLength = 2 + l;
  2807. if(pRequest->MultipartStringSeparatorLength+1 <
  2808. MULTIPART_SEPARATOR_SIZE)
  2809. {
  2810. pRequest->pMultipartStringSeparator =
  2811. pRequest->MultipartStringSeparatorBuffer;
  2812. }
  2813. else
  2814. {
  2815. // The string separator is too big, allocate a buffer
  2816. pRequest->pMultipartStringSeparator = (PSTR)
  2817. UL_ALLOCATE_POOL_WITH_QUOTA(
  2818. NonPagedPool,
  2819. pRequest->MultipartStringSeparatorLength+1,
  2820. UC_MULTIPART_STRING_BUFFER_POOL_TAG,
  2821. pRequest->pServerInfo->pProcess
  2822. );
  2823. if(!pRequest->pMultipartStringSeparator)
  2824. return STATUS_INVALID_NETWORK_RESPONSE;
  2825. }
  2826. pRequest->pMultipartStringSeparator[0] = '-';
  2827. pRequest->pMultipartStringSeparator[1] = '-';
  2828. RtlCopyMemory(pRequest->pMultipartStringSeparator+2,
  2829. s,
  2830. pRequest->MultipartStringSeparatorLength-2
  2831. );
  2832. // If there was an end quote, then the trailing quote
  2833. // should be ignored.
  2834. if(bEndQuote)
  2835. {
  2836. pRequest->pMultipartStringSeparator[
  2837. pRequest->MultipartStringSeparatorLength-1] = 0;
  2838. pRequest->MultipartStringSeparatorLength --;
  2839. }
  2840. pRequest->pMultipartStringSeparator[
  2841. pRequest->MultipartStringSeparatorLength] = ANSI_NULL;
  2842. pRequest->ResponseMultipartByteranges = TRUE;
  2843. }
  2844. }
  2845. return Status;
  2846. }