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.

2352 lines
70 KiB

  1. /*++
  2. Copyright (c) 1998-2002 Microsoft Corporation
  3. Module Name:
  4. parse.c
  5. Abstract:
  6. Contains all of the kernel mode HTTP parsing code.
  7. Author:
  8. Henry Sanders (henrysa) 27-Apr-1998
  9. Revision History:
  10. Paul McDaniel (paulmcd) 3-Mar-1998 finished up
  11. Rajesh Sundaram (rajeshsu) 10-Oct-2000 Implemented client parser
  12. --*/
  13. #include "precomp.h"
  14. // Internal (private) status codes
  15. //
  16. // Values are 32 bit values layed out as follows:
  17. //
  18. // 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
  19. // 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
  20. // +---+-+-+-----------------------+-------------------------------+
  21. // |Sev|C|R| Facility | Code |
  22. // +---+-+-+-----------------------+-------------------------------+
  23. //
  24. // where
  25. //
  26. // Sev - is the severity code
  27. //
  28. // 00 - Success
  29. // 01 - Informational
  30. // 10 - Warning
  31. // 11 - Error
  32. //
  33. // C - is the Customer code flag - 0
  34. //
  35. // R - is a reserved bit - 0
  36. //
  37. // Facility - is the facility code - 0x16
  38. //
  39. // Code - is the facility's status code
  40. #define STATUS_QDSTRING_TERMINATED_BY_CRLF 0xC0160001
  41. //
  42. //
  43. // The request header map table. These entries don't need to be in strict
  44. // alphabetical order, but they do need to be grouped by the first character
  45. // of the header - all A's together, all C's together, etc. They also need
  46. // to be entered in uppercase, since we upcase incoming verbs before we do
  47. // the compare.
  48. //
  49. // for nice perf, group unused headers low in the sub-sort order
  50. //
  51. // it's important that the header name is <= 24 characters (3 ULONGLONG's).
  52. //
  53. HEADER_MAP_ENTRY g_RequestHeaderMapTable[] =
  54. {
  55. CREATE_HEADER_MAP_ENTRY(Accept:,
  56. HttpHeaderAccept,
  57. FALSE,
  58. UlAcceptHeaderHandler,
  59. NULL,
  60. 0),
  61. CREATE_HEADER_MAP_ENTRY(Accept-Language:,
  62. HttpHeaderAcceptLanguage,
  63. FALSE,
  64. UlMultipleHeaderHandler,
  65. NULL,
  66. 2),
  67. CREATE_HEADER_MAP_ENTRY(Accept-Encoding:,
  68. HttpHeaderAcceptEncoding,
  69. FALSE,
  70. UlMultipleHeaderHandler,
  71. NULL,
  72. 3),
  73. CREATE_HEADER_MAP_ENTRY(Accept-Charset:,
  74. HttpHeaderAcceptCharset,
  75. FALSE,
  76. UlMultipleHeaderHandler,
  77. NULL,
  78. -1),
  79. CREATE_HEADER_MAP_ENTRY(Authorization:,
  80. HttpHeaderAuthorization,
  81. FALSE,
  82. UlSingleHeaderHandler,
  83. NULL,
  84. -1),
  85. CREATE_HEADER_MAP_ENTRY(Allow:,
  86. HttpHeaderAllow,
  87. FALSE,
  88. UlMultipleHeaderHandler,
  89. NULL,
  90. -1),
  91. CREATE_HEADER_MAP_ENTRY(Connection:,
  92. HttpHeaderConnection,
  93. FALSE,
  94. UlMultipleHeaderHandler,
  95. NULL,
  96. 8),
  97. CREATE_HEADER_MAP_ENTRY(Cache-Control:,
  98. HttpHeaderCacheControl,
  99. FALSE,
  100. UlMultipleHeaderHandler,
  101. NULL,
  102. -1),
  103. CREATE_HEADER_MAP_ENTRY(Cookie:,
  104. HttpHeaderCookie,
  105. FALSE,
  106. UlMultipleHeaderHandler,
  107. NULL,
  108. -1),
  109. CREATE_HEADER_MAP_ENTRY(Content-Length:,
  110. HttpHeaderContentLength,
  111. TRUE,
  112. UlSingleHeaderHandler,
  113. NULL,
  114. -1),
  115. CREATE_HEADER_MAP_ENTRY(Content-Type:,
  116. HttpHeaderContentType,
  117. FALSE,
  118. UlSingleHeaderHandler,
  119. NULL,
  120. -1),
  121. CREATE_HEADER_MAP_ENTRY(Content-Encoding:,
  122. HttpHeaderContentEncoding,
  123. FALSE,
  124. UlMultipleHeaderHandler,
  125. NULL,
  126. -1),
  127. CREATE_HEADER_MAP_ENTRY(Content-Language:,
  128. HttpHeaderContentLanguage,
  129. FALSE,
  130. UlMultipleHeaderHandler,
  131. NULL,
  132. -1),
  133. CREATE_HEADER_MAP_ENTRY(Content-Location:,
  134. HttpHeaderContentLocation,
  135. FALSE,
  136. UlSingleHeaderHandler,
  137. NULL,
  138. -1),
  139. CREATE_HEADER_MAP_ENTRY(Content-MD5:,
  140. HttpHeaderContentMd5,
  141. FALSE,
  142. UlSingleHeaderHandler,
  143. NULL,
  144. -1),
  145. CREATE_HEADER_MAP_ENTRY(Content-Range:,
  146. HttpHeaderContentRange,
  147. FALSE,
  148. UlSingleHeaderHandler,
  149. NULL,
  150. -1),
  151. CREATE_HEADER_MAP_ENTRY(Date:,
  152. HttpHeaderDate,
  153. FALSE,
  154. UlSingleHeaderHandler,
  155. NULL,
  156. -1),
  157. CREATE_HEADER_MAP_ENTRY(Expect:,
  158. HttpHeaderExpect,
  159. FALSE,
  160. UlMultipleHeaderHandler,
  161. NULL,
  162. -1),
  163. CREATE_HEADER_MAP_ENTRY(Expires:,
  164. HttpHeaderExpires,
  165. FALSE,
  166. UlSingleHeaderHandler,
  167. NULL,
  168. -1),
  169. CREATE_HEADER_MAP_ENTRY(From:,
  170. HttpHeaderFrom,
  171. FALSE,
  172. UlSingleHeaderHandler,
  173. NULL,
  174. -1),
  175. CREATE_HEADER_MAP_ENTRY(Host:,
  176. HttpHeaderHost,
  177. TRUE,
  178. UlHostHeaderHandler,
  179. NULL,
  180. 7),
  181. CREATE_HEADER_MAP_ENTRY(If-Modified-Since:,
  182. HttpHeaderIfModifiedSince,
  183. FALSE,
  184. UlSingleHeaderHandler,
  185. NULL,
  186. 4),
  187. CREATE_HEADER_MAP_ENTRY(If-None-Match:,
  188. HttpHeaderIfNoneMatch,
  189. FALSE,
  190. UlMultipleHeaderHandler,
  191. NULL,
  192. 5),
  193. CREATE_HEADER_MAP_ENTRY(If-Match:,
  194. HttpHeaderIfMatch,
  195. FALSE,
  196. UlMultipleHeaderHandler,
  197. NULL,
  198. -1),
  199. CREATE_HEADER_MAP_ENTRY(If-Unmodified-Since:,
  200. HttpHeaderIfUnmodifiedSince,
  201. FALSE,
  202. UlSingleHeaderHandler,
  203. NULL,
  204. -1),
  205. CREATE_HEADER_MAP_ENTRY(If-Range:,
  206. HttpHeaderIfRange,
  207. FALSE,
  208. UlSingleHeaderHandler,
  209. NULL,
  210. -1),
  211. CREATE_HEADER_MAP_ENTRY(Keep-Alive:,
  212. HttpHeaderKeepAlive,
  213. FALSE,
  214. UlSingleHeaderHandler,
  215. NULL,
  216. -1),
  217. CREATE_HEADER_MAP_ENTRY(Last-Modified:,
  218. HttpHeaderLastModified,
  219. FALSE,
  220. UlSingleHeaderHandler,
  221. NULL,
  222. -1),
  223. CREATE_HEADER_MAP_ENTRY(Max-Forwards:,
  224. HttpHeaderMaxForwards,
  225. FALSE,
  226. UlSingleHeaderHandler,
  227. NULL,
  228. -1),
  229. CREATE_HEADER_MAP_ENTRY(Pragma:,
  230. HttpHeaderPragma,
  231. FALSE,
  232. UlMultipleHeaderHandler,
  233. NULL,
  234. -1),
  235. CREATE_HEADER_MAP_ENTRY(Proxy-Authorization:,
  236. HttpHeaderProxyAuthorization,
  237. FALSE,
  238. UlSingleHeaderHandler,
  239. NULL,
  240. -1),
  241. CREATE_HEADER_MAP_ENTRY(Referer:,
  242. HttpHeaderReferer,
  243. FALSE,
  244. UlSingleHeaderHandler,
  245. NULL,
  246. 1),
  247. CREATE_HEADER_MAP_ENTRY(Range:,
  248. HttpHeaderRange,
  249. FALSE,
  250. UlSingleHeaderHandler,
  251. NULL,
  252. -1),
  253. CREATE_HEADER_MAP_ENTRY(Trailer:,
  254. HttpHeaderTrailer,
  255. FALSE,
  256. UlMultipleHeaderHandler,
  257. NULL,
  258. -1),
  259. CREATE_HEADER_MAP_ENTRY(Transfer-Encoding:,
  260. HttpHeaderTransferEncoding,
  261. FALSE,
  262. UlMultipleHeaderHandler,
  263. NULL,
  264. -1),
  265. CREATE_HEADER_MAP_ENTRY(TE:,
  266. HttpHeaderTe,
  267. FALSE,
  268. UlMultipleHeaderHandler,
  269. NULL,
  270. -1),
  271. CREATE_HEADER_MAP_ENTRY(Translate:,
  272. HttpHeaderTranslate,
  273. FALSE,
  274. UlSingleHeaderHandler,
  275. NULL,
  276. -1),
  277. CREATE_HEADER_MAP_ENTRY(User-Agent:,
  278. HttpHeaderUserAgent,
  279. FALSE,
  280. UlSingleHeaderHandler,
  281. NULL,
  282. 6),
  283. CREATE_HEADER_MAP_ENTRY(Upgrade:,
  284. HttpHeaderUpgrade,
  285. FALSE,
  286. UlMultipleHeaderHandler,
  287. NULL,
  288. -1),
  289. CREATE_HEADER_MAP_ENTRY(Via:,
  290. HttpHeaderVia,
  291. FALSE,
  292. UlMultipleHeaderHandler,
  293. NULL,
  294. -1),
  295. CREATE_HEADER_MAP_ENTRY(Warning:,
  296. HttpHeaderWarning,
  297. FALSE,
  298. UlMultipleHeaderHandler,
  299. NULL,
  300. -1),
  301. };
  302. // The response header map table. These entries don't need to be in strict
  303. // alphabetical order, but they do need to be grouped by the first character
  304. // of the header - all A's together, all C's together, etc. They also need
  305. // to be entered in uppercase, since we upcase incoming verbs before we do
  306. // the compare.
  307. //
  308. // for nice perf, group unused headers low in the sub-sort order
  309. //
  310. // it's important that the header name is <= 24 characters (3 ULONGLONG's).
  311. //
  312. HEADER_MAP_ENTRY g_ResponseHeaderMapTable[] =
  313. {
  314. CREATE_HEADER_MAP_ENTRY(Accept-Ranges:,
  315. HttpHeaderAcceptRanges,
  316. FALSE,
  317. NULL,
  318. UcMultipleHeaderHandler,
  319. -1),
  320. CREATE_HEADER_MAP_ENTRY(Age:,
  321. HttpHeaderAge,
  322. FALSE,
  323. NULL,
  324. UcSingleHeaderHandler,
  325. -1),
  326. CREATE_HEADER_MAP_ENTRY(Allow:,
  327. HttpHeaderAllow,
  328. FALSE,
  329. NULL,
  330. UcMultipleHeaderHandler,
  331. -1),
  332. CREATE_HEADER_MAP_ENTRY(Cache-Control:,
  333. HttpHeaderCacheControl,
  334. FALSE,
  335. NULL,
  336. UcMultipleHeaderHandler,
  337. -1),
  338. CREATE_HEADER_MAP_ENTRY(Connection:,
  339. HttpHeaderConnection,
  340. FALSE,
  341. NULL,
  342. UcConnectionHeaderHandler,
  343. -1),
  344. CREATE_HEADER_MAP_ENTRY(Content-Encoding:,
  345. HttpHeaderContentEncoding,
  346. FALSE,
  347. NULL,
  348. UcMultipleHeaderHandler,
  349. -1),
  350. CREATE_HEADER_MAP_ENTRY(Content-Language:,
  351. HttpHeaderContentLanguage,
  352. FALSE,
  353. NULL,
  354. UcMultipleHeaderHandler,
  355. -1),
  356. CREATE_HEADER_MAP_ENTRY(Content-Length:,
  357. HttpHeaderContentLength,
  358. FALSE,
  359. NULL,
  360. UcContentLengthHeaderHandler,
  361. -1),
  362. CREATE_HEADER_MAP_ENTRY(Content-Location:,
  363. HttpHeaderContentLocation,
  364. FALSE,
  365. NULL,
  366. UcSingleHeaderHandler,
  367. -1),
  368. CREATE_HEADER_MAP_ENTRY(Content-MD5:,
  369. HttpHeaderContentMd5,
  370. FALSE,
  371. NULL,
  372. UcSingleHeaderHandler,
  373. -1),
  374. CREATE_HEADER_MAP_ENTRY(Content-Range:,
  375. HttpHeaderContentRange,
  376. FALSE,
  377. NULL,
  378. UcSingleHeaderHandler,
  379. -1),
  380. CREATE_HEADER_MAP_ENTRY(Content-Type:,
  381. HttpHeaderContentType,
  382. FALSE,
  383. NULL,
  384. UcContentTypeHeaderHandler,
  385. -1),
  386. CREATE_HEADER_MAP_ENTRY(Date:,
  387. HttpHeaderDate,
  388. FALSE,
  389. NULL,
  390. UcSingleHeaderHandler,
  391. -1),
  392. CREATE_HEADER_MAP_ENTRY(ETag:,
  393. HttpHeaderEtag,
  394. FALSE,
  395. NULL,
  396. UcSingleHeaderHandler,
  397. -1),
  398. CREATE_HEADER_MAP_ENTRY(Expires:,
  399. HttpHeaderExpires,
  400. FALSE,
  401. NULL,
  402. UcSingleHeaderHandler,
  403. -1),
  404. CREATE_HEADER_MAP_ENTRY(Keep-Alive:,
  405. HttpHeaderKeepAlive,
  406. FALSE,
  407. NULL,
  408. UcSingleHeaderHandler,
  409. -1),
  410. CREATE_HEADER_MAP_ENTRY(Last-Modified:,
  411. HttpHeaderLastModified,
  412. FALSE,
  413. NULL,
  414. UcSingleHeaderHandler,
  415. -1),
  416. CREATE_HEADER_MAP_ENTRY(Location:,
  417. HttpHeaderLocation,
  418. FALSE,
  419. NULL,
  420. UcSingleHeaderHandler,
  421. -1),
  422. CREATE_HEADER_MAP_ENTRY(Pragma:,
  423. HttpHeaderPragma,
  424. FALSE,
  425. NULL,
  426. UcMultipleHeaderHandler,
  427. -1),
  428. CREATE_HEADER_MAP_ENTRY(Proxy-Authenticate:,
  429. HttpHeaderProxyAuthenticate,
  430. FALSE,
  431. NULL,
  432. UcAuthenticateHeaderHandler,
  433. -1),
  434. CREATE_HEADER_MAP_ENTRY(Retry-After:,
  435. HttpHeaderRetryAfter,
  436. FALSE,
  437. NULL,
  438. UcSingleHeaderHandler,
  439. -1),
  440. CREATE_HEADER_MAP_ENTRY(Server:,
  441. HttpHeaderServer,
  442. FALSE,
  443. NULL,
  444. UcSingleHeaderHandler,
  445. -1),
  446. CREATE_HEADER_MAP_ENTRY(Set-Cookie:,
  447. HttpHeaderSetCookie,
  448. FALSE,
  449. NULL,
  450. UcMultipleHeaderHandler,
  451. -1),
  452. CREATE_HEADER_MAP_ENTRY(Trailer:,
  453. HttpHeaderTrailer,
  454. FALSE,
  455. NULL,
  456. UcMultipleHeaderHandler,
  457. -1),
  458. CREATE_HEADER_MAP_ENTRY(Transfer-Encoding:,
  459. HttpHeaderTransferEncoding,
  460. FALSE,
  461. NULL,
  462. UcTransferEncodingHeaderHandler,
  463. -1),
  464. CREATE_HEADER_MAP_ENTRY(Upgrade:,
  465. HttpHeaderUpgrade,
  466. FALSE,
  467. NULL,
  468. UcMultipleHeaderHandler,
  469. -1),
  470. CREATE_HEADER_MAP_ENTRY(Vary:,
  471. HttpHeaderVary,
  472. FALSE,
  473. NULL,
  474. UcMultipleHeaderHandler,
  475. -1),
  476. CREATE_HEADER_MAP_ENTRY(Via:,
  477. HttpHeaderVia,
  478. FALSE,
  479. NULL,
  480. UcMultipleHeaderHandler,
  481. -1),
  482. CREATE_HEADER_MAP_ENTRY(Warning:,
  483. HttpHeaderWarning,
  484. FALSE,
  485. NULL,
  486. UcMultipleHeaderHandler,
  487. -1),
  488. CREATE_HEADER_MAP_ENTRY(WWW-Authenticate:,
  489. HttpHeaderWwwAuthenticate,
  490. FALSE,
  491. NULL,
  492. UcAuthenticateHeaderHandler,
  493. -1)
  494. };
  495. ULONG g_RequestHeaderMap[HttpHeaderMaximum];
  496. ULONG g_ResponseHeaderMap[HttpHeaderMaximum];
  497. //
  498. // The header index table. This is initialized by the init code.
  499. //
  500. HEADER_INDEX_ENTRY g_RequestHeaderIndexTable[NUMBER_HEADER_INDICES];
  501. HEADER_INDEX_ENTRY g_ResponseHeaderIndexTable[NUMBER_HEADER_INDICES];
  502. HEADER_HINT_INDEX_ENTRY g_RequestHeaderHintIndexTable[NUMBER_HEADER_HINT_INDICES];
  503. #define NUMBER_REQUEST_HEADER_MAP_ENTRIES \
  504. (sizeof(g_RequestHeaderMapTable)/sizeof(HEADER_MAP_ENTRY))
  505. #define NUMBER_RESPONSE_HEADER_MAP_ENTRIES \
  506. (sizeof(g_ResponseHeaderMapTable)/sizeof(HEADER_MAP_ENTRY))
  507. /*++
  508. Routine Description:
  509. A utility routine to find a hex value token. We take an input pointer,
  510. skip any preceding LWS, then scan the token until we find a non-hex char,
  511. LWS or a CRLF pair.
  512. Arguments:
  513. pBuffer - Buffer to search for token.
  514. BufferLength - Length of data pointed to by pBuffer.
  515. TokenLength - Where to return the length of the token.
  516. Return Value:
  517. A pointer to the token we found, as well as the length, or NULL if we
  518. don't find a delimited token.
  519. --*/
  520. PUCHAR
  521. FindHexToken(
  522. IN PUCHAR pBuffer,
  523. IN ULONG BufferLength,
  524. OUT ULONG *pTokenLength
  525. )
  526. {
  527. PUCHAR pTokenStart;
  528. //
  529. // First, skip any preceding LWS.
  530. //
  531. while (BufferLength > 0 && IS_HTTP_LWS(*pBuffer))
  532. {
  533. pBuffer++;
  534. BufferLength--;
  535. }
  536. // If we stopped because we ran out of buffer, fail.
  537. if (BufferLength == 0)
  538. {
  539. return NULL;
  540. }
  541. pTokenStart = pBuffer;
  542. // Now skip over the token, until we see either LWS or a CR or LF.
  543. while (
  544. ( BufferLength != 0 ) &&
  545. ( IS_HTTP_HEX(*pBuffer) )
  546. )
  547. {
  548. pBuffer++;
  549. BufferLength--;
  550. }
  551. // See why we stopped.
  552. if (BufferLength == 0)
  553. {
  554. // Ran out of buffer before end of token.
  555. return NULL;
  556. }
  557. // Success. Set the token length and return the start of the token.
  558. *pTokenLength = DIFF(pBuffer - pTokenStart);
  559. return pTokenStart;
  560. } // FindHexToken
  561. /*++
  562. Routine Description:
  563. Routine to initialize the parse code.
  564. Arguments:
  565. Return Value:
  566. --*/
  567. NTSTATUS
  568. InitializeParser(
  569. VOID
  570. )
  571. {
  572. ULONG i;
  573. ULONG j;
  574. PHEADER_MAP_ENTRY pHeaderMap;
  575. PHEADER_INDEX_ENTRY pHeaderIndex;
  576. UCHAR c;
  577. //
  578. // Make sure the entire table starts life as zero
  579. //
  580. RtlZeroMemory(
  581. &g_RequestHeaderIndexTable,
  582. sizeof(g_RequestHeaderIndexTable)
  583. );
  584. RtlZeroMemory(
  585. &g_ResponseHeaderIndexTable,
  586. sizeof(g_ResponseHeaderIndexTable)
  587. );
  588. RtlZeroMemory(
  589. &g_RequestHeaderHintIndexTable,
  590. sizeof(g_RequestHeaderHintIndexTable)
  591. );
  592. #if DBG
  593. //
  594. // Initialize g_RequestHeaderMap & g_ResponseHeaderMap to 0xFFFFFFFF
  595. // so that we can catch un-initialized entries.
  596. //
  597. RtlFillMemory(
  598. &g_RequestHeaderMap,
  599. sizeof(g_RequestHeaderMap),
  600. 0xFF);
  601. RtlFillMemory(
  602. &g_ResponseHeaderMap,
  603. sizeof(g_ResponseHeaderMap),
  604. 0xFF);
  605. #endif
  606. for (i = 0; i < NUMBER_REQUEST_HEADER_MAP_ENTRIES;i++)
  607. {
  608. pHeaderMap = &g_RequestHeaderMapTable[i];
  609. //
  610. // Map the header to upper-case.
  611. //
  612. for (j = 0 ; j < pHeaderMap->HeaderLength ; j++)
  613. {
  614. c = pHeaderMap->Header.HeaderChar[j];
  615. if ((c >= 'a') && (c <= 'z'))
  616. {
  617. pHeaderMap->Header.HeaderChar[j] = c - ('a' - 'A');
  618. }
  619. }
  620. ASSERT(pHeaderMap->pServerHandler != NULL);
  621. c = pHeaderMap->Header.HeaderChar[0];
  622. pHeaderIndex = &g_RequestHeaderIndexTable[c - 'A'];
  623. if (pHeaderIndex->pHeaderMap == NULL)
  624. {
  625. pHeaderIndex->pHeaderMap = pHeaderMap;
  626. pHeaderIndex->Count = 1;
  627. }
  628. else
  629. {
  630. pHeaderIndex->Count++;
  631. }
  632. // Now go through the mask fields for this header map structure and
  633. // initialize them. We set them to default values first, and then
  634. // go through the header itself and convert the mask for any
  635. // non-alphabetic characters.
  636. for (j = 0; j < MAX_HEADER_LONG_COUNT; j++)
  637. {
  638. pHeaderMap->HeaderMask[j] = CREATE_HEADER_MASK(
  639. pHeaderMap->HeaderLength,
  640. sizeof(ULONGLONG) * (j+1)
  641. );
  642. }
  643. for (j = 0; j < pHeaderMap->HeaderLength; j++)
  644. {
  645. c = pHeaderMap->Header.HeaderChar[j];
  646. if (c < 'A' || c > 'Z')
  647. {
  648. pHeaderMap->HeaderMask[j/sizeof(ULONGLONG)] |=
  649. (ULONGLONG)0xff << ((j % sizeof(ULONGLONG)) * (ULONGLONG)8);
  650. }
  651. }
  652. //
  653. // setup the mapping from header id to map table index
  654. //
  655. g_RequestHeaderMap[pHeaderMap->HeaderID] = i;
  656. //
  657. // Save the Header Map and first char in the hint table if the entry
  658. // is part of the hints
  659. //
  660. if ((pHeaderMap->HintIndex >= 0)
  661. && (pHeaderMap->HintIndex < NUMBER_HEADER_HINT_INDICES))
  662. {
  663. g_RequestHeaderHintIndexTable[pHeaderMap->HintIndex].pHeaderMap
  664. = pHeaderMap;
  665. g_RequestHeaderHintIndexTable[pHeaderMap->HintIndex].c
  666. = pHeaderMap->Header.HeaderChar[0];
  667. }
  668. }
  669. for (i = 0; i < NUMBER_RESPONSE_HEADER_MAP_ENTRIES;i++)
  670. {
  671. pHeaderMap = &g_ResponseHeaderMapTable[i];
  672. //
  673. // Map the header to upper-case.
  674. //
  675. for (j = 0 ; j < pHeaderMap->HeaderLength ; j++)
  676. {
  677. c = pHeaderMap->Header.HeaderChar[j];
  678. if ((c >= 'a') && (c <= 'z'))
  679. {
  680. pHeaderMap->Header.HeaderChar[j] = c - ('a' - 'A');
  681. }
  682. }
  683. ASSERT(pHeaderMap->pClientHandler != NULL);
  684. c = pHeaderMap->Header.HeaderChar[0];
  685. pHeaderIndex = &g_ResponseHeaderIndexTable[c - 'A'];
  686. if (pHeaderIndex->pHeaderMap == NULL)
  687. {
  688. pHeaderIndex->pHeaderMap = pHeaderMap;
  689. pHeaderIndex->Count = 1;
  690. }
  691. else
  692. {
  693. pHeaderIndex->Count++;
  694. }
  695. // Now go through the mask fields for this header map structure and
  696. // initialize them. We set them to default values first, and then
  697. // go through the header itself and convert the mask for any
  698. // non-alphabetic characters.
  699. for (j = 0; j < MAX_HEADER_LONG_COUNT; j++)
  700. {
  701. pHeaderMap->HeaderMask[j] = CREATE_HEADER_MASK(
  702. pHeaderMap->HeaderLength,
  703. sizeof(ULONGLONG) * (j+1)
  704. );
  705. }
  706. for (j = 0; j < pHeaderMap->HeaderLength; j++)
  707. {
  708. c = pHeaderMap->Header.HeaderChar[j];
  709. if (c < 'A' || c > 'Z')
  710. {
  711. pHeaderMap->HeaderMask[j/sizeof(ULONGLONG)] |=
  712. (ULONGLONG)0xff << ((j % sizeof(ULONGLONG)) * (ULONGLONG)8);
  713. }
  714. }
  715. //
  716. // setup the mapping from header id to map table index
  717. //
  718. g_ResponseHeaderMap[pHeaderMap->HeaderID] = i;
  719. }
  720. #if DBG
  721. for(i=0; i<HttpHeaderRequestMaximum; i++)
  722. {
  723. ASSERT(g_RequestHeaderMap[i] != 0xFFFFFFFF);
  724. }
  725. for(i=0; i<HttpHeaderResponseMaximum; i++)
  726. {
  727. ASSERT(g_ResponseHeaderMap[i] != 0xFFFFFFFF);
  728. }
  729. #endif
  730. return STATUS_SUCCESS;
  731. } // InitializeParser
  732. /***************************************************************************++
  733. Routine Description:
  734. Parses the http version from a string. Assumes string begins with "HTTP/".
  735. Eats leading zeros. Puts resulting version in HTTP_VERSION structure
  736. passed into function.
  737. Arguments:
  738. pString array of chars to parse
  739. StringLength number of bytes in pString
  740. pVersion where to put parsed version.
  741. Returns:
  742. Number of bytes parsed out of string. Zero indicates parse failure,
  743. and no version information was found. Number of bytes does not include
  744. trailing linear white space nor CRLF line terminator.
  745. --***************************************************************************/
  746. ULONG
  747. UlpParseHttpVersion(
  748. PUCHAR pString,
  749. ULONG StringLength,
  750. PHTTP_VERSION pVersion
  751. )
  752. {
  753. ULONG BytesRemaining = StringLength;
  754. ULONG NumberLength;
  755. USHORT VersionNumber;
  756. C_ASSERT(sizeof(VersionNumber) == sizeof(pVersion->MajorVersion));
  757. C_ASSERT(sizeof(VersionNumber) == sizeof(pVersion->MinorVersion));
  758. BOOLEAN Done = FALSE;
  759. ASSERT( pString );
  760. ASSERT( StringLength > HTTP_PREFIX_SIZE + 1 );
  761. ASSERT( pVersion );
  762. pVersion->MajorVersion = 0;
  763. pVersion->MinorVersion = 0;
  764. //
  765. // compare 'HTTP' away
  766. //
  767. if ( *(UNALIGNED64 ULONG *)pString == (ULONG) HTTP_PREFIX )
  768. {
  769. BytesRemaining -= HTTP_PREFIX_SIZE;
  770. pString += HTTP_PREFIX_SIZE;
  771. }
  772. else
  773. {
  774. goto End;
  775. }
  776. if ( '/' == *pString )
  777. {
  778. BytesRemaining--;
  779. pString++;
  780. }
  781. else
  782. {
  783. goto End;
  784. }
  785. //
  786. // Parse major version
  787. //
  788. //
  789. // Skip leading zeros.
  790. //
  791. NumberLength = 0;
  792. while ( (0 != BytesRemaining) && (*pString == ZERO) )
  793. {
  794. BytesRemaining--;
  795. pString++;
  796. NumberLength++;
  797. }
  798. while ( (0 != BytesRemaining) && IS_HTTP_DIGIT(*pString) )
  799. {
  800. VersionNumber = pVersion->MajorVersion;
  801. pVersion->MajorVersion *= 10;
  802. pVersion->MajorVersion += (*pString - '0');
  803. // Guard against wrapping around.
  804. if ( VersionNumber > pVersion->MajorVersion )
  805. {
  806. goto End;
  807. }
  808. BytesRemaining--;
  809. pString++;
  810. NumberLength++;
  811. }
  812. // Must disallow version numbers less than 1.0
  813. if ((0 == pVersion->MajorVersion) ||
  814. (0 == BytesRemaining) ||
  815. (0 == NumberLength) )
  816. {
  817. goto End;
  818. }
  819. //
  820. // find '.'
  821. //
  822. if ( '.' != *pString )
  823. {
  824. // Error: No decimal place; bail out.
  825. goto End;
  826. }
  827. else
  828. {
  829. BytesRemaining--;
  830. pString++;
  831. }
  832. if ( 0 == BytesRemaining || !IS_HTTP_DIGIT(*pString) )
  833. {
  834. goto End;
  835. }
  836. //
  837. // Parse minor version
  838. //
  839. //
  840. // Skip leading zeros.
  841. //
  842. NumberLength = 0;
  843. while ( (0 != BytesRemaining) && (*pString == ZERO) )
  844. {
  845. BytesRemaining--;
  846. pString++;
  847. NumberLength++;
  848. }
  849. while ( (0 != BytesRemaining) && IS_HTTP_DIGIT(*pString) )
  850. {
  851. VersionNumber = pVersion->MinorVersion;
  852. pVersion->MinorVersion *= 10;
  853. pVersion->MinorVersion += (*pString - '0');
  854. // Guard against wrapping around.
  855. if ( VersionNumber > pVersion->MinorVersion )
  856. {
  857. goto End;
  858. }
  859. BytesRemaining--;
  860. pString++;
  861. NumberLength++;
  862. }
  863. if ( 0 == NumberLength )
  864. {
  865. goto End;
  866. }
  867. Done = TRUE;
  868. End:
  869. if (!Done)
  870. {
  871. return 0;
  872. }
  873. else
  874. {
  875. UlTrace(PARSER, (
  876. "http!UlpParseHttpVersion: found version HTTP/%hu.%hu\n",
  877. pVersion->MajorVersion,
  878. pVersion->MinorVersion
  879. ));
  880. return (StringLength - BytesRemaining);
  881. }
  882. } // UlpParseHttpVersion
  883. /****************************************************************************++
  884. Routine Description:
  885. A utility routine, to find the terminating CRLF or LFLF of a header's
  886. field content, if present. This routine does not perform line folding
  887. (hence read-only) but returns an error if it realizes that it has to do
  888. so.
  889. The user is supposed to allocate memory & call FindHeaderEnd to do the real
  890. folding.
  891. NOTE: NOTE: If this function is fixed, the corresponding FindHeaderEnd
  892. also needs to be fixed.
  893. Arguments:
  894. pHeader - Header whose end is to be found.
  895. HeaderLength - Length of data pointed to by pHeader.
  896. pBytesTaken - Where to return the total number of bytes traversed.
  897. We return 0 if we couldn't locate the end of the header
  898. Return Value:
  899. STATUS_SUCCESS - if no parsing errors were encountered
  900. (including not being able to locate the
  901. end of the header)
  902. STATUS_INVALID_DEVICE_REQUEST - Illegal response
  903. STATUS_MORE_PROCESSING_REQUIRED - Need to do header folding
  904. --****************************************************************************/
  905. NTSTATUS
  906. FindHeaderEndReadOnly(
  907. IN PUCHAR pHeader,
  908. IN ULONG HeaderLength,
  909. OUT PULONG pBytesTaken
  910. )
  911. {
  912. NTSTATUS Status;
  913. UCHAR CurrentChar;
  914. ULONG CurrentOffset;
  915. HFC_PARSER_STATE ParserState, OldState;
  916. ULONG QuotedStringLen;
  917. ParserState = HFCStart;
  918. CurrentOffset = 0;
  919. //
  920. // The field-content of a header contains *TEXT or combinations
  921. // token, separators and quoted-string. It is terminated by a CRLF.
  922. //
  923. // Folding - if one or more LWS follows a CRLF, replace the entire
  924. // sequence with a single SP, and treat this as a continuation of
  925. // header field content.
  926. //
  927. for (/* NOTHING */; CurrentOffset < HeaderLength; CurrentOffset++)
  928. {
  929. CurrentChar = *(pHeader + CurrentOffset);
  930. OldState = ParserState;
  931. switch (ParserState)
  932. {
  933. case HFCStart:
  934. if (CurrentChar == CR)
  935. {
  936. ParserState = HFCSeenCR;
  937. }
  938. else if (CurrentChar == LF)
  939. {
  940. ParserState = HFCSeenLF;
  941. }
  942. else if (CurrentChar == DOUBLE_QUOTE)
  943. {
  944. ParserState = HFCInQuotedString;
  945. }
  946. else if (!IS_HTTP_CTL(CurrentChar) ||
  947. IS_HTTP_WS_TOKEN(CurrentChar))
  948. {
  949. ;
  950. }
  951. else
  952. {
  953. UlTraceError(PARSER, (
  954. "FindHeaderEndReadOnly: ERROR parsing char (%x) at "
  955. "Offset %d in state %d\n",
  956. CurrentChar,
  957. CurrentOffset,
  958. OldState
  959. ));
  960. return STATUS_INVALID_DEVICE_REQUEST;
  961. }
  962. break;
  963. case HFCSeenCR:
  964. if (CurrentChar == LF)
  965. {
  966. ParserState = HFCSeenCRLF;
  967. }
  968. else
  969. {
  970. UlTraceError(PARSER, (
  971. "FindHeaderEndReadOnly: ERROR parsing char (%x) at "
  972. "Offset %d in state %d\n",
  973. CurrentChar,
  974. CurrentOffset,
  975. OldState
  976. ));
  977. return STATUS_INVALID_DEVICE_REQUEST;
  978. }
  979. break;
  980. case HFCSeenLF:
  981. if (CurrentChar == LF)
  982. {
  983. ParserState = HFCSeenCRLF; // LFLF considered = CRLF
  984. }
  985. else
  986. {
  987. UlTraceError(PARSER, (
  988. "FindHeaderEndReadOnly: ERROR parsing char (%x) at "
  989. "Offset %d in state %d\n",
  990. CurrentChar,
  991. CurrentOffset,
  992. OldState
  993. ));
  994. return STATUS_INVALID_DEVICE_REQUEST;
  995. }
  996. break;
  997. case HFCSeenCRLF:
  998. if (IS_HTTP_LWS(CurrentChar))
  999. {
  1000. // We have to fold the header value. We can't use the
  1001. // TDI indicated buffers for this, because we'll have
  1002. // to change the indicated data.
  1003. return STATUS_MORE_PROCESSING_REQUIRED;
  1004. }
  1005. else
  1006. {
  1007. // Found a non-continuation char immediately
  1008. // following CRLF; must be end of header content.
  1009. //
  1010. // All done!
  1011. //
  1012. *pBytesTaken = CurrentOffset;
  1013. return STATUS_SUCCESS;
  1014. }
  1015. break;
  1016. case HFCInQuotedString:
  1017. Status = ParseQuotedString(pHeader + CurrentOffset,
  1018. HeaderLength - CurrentOffset,
  1019. NULL,
  1020. &QuotedStringLen);
  1021. if (Status == STATUS_SUCCESS)
  1022. {
  1023. if (QuotedStringLen == 0)
  1024. {
  1025. //
  1026. // Ran out of header buffer while parsing quotes
  1027. // string. Setting QuotedStringLen to whatever
  1028. // available will get us out of the for loop.
  1029. //
  1030. QuotedStringLen = HeaderLength - CurrentOffset;
  1031. }
  1032. else
  1033. {
  1034. // Found a quoted string. Change the parser state.
  1035. ParserState = HFCStart;
  1036. }
  1037. //
  1038. // Increment the offset by the length of quoted string-1.
  1039. // One less because the for loop will increment it by 1.
  1040. //
  1041. CurrentOffset += (QuotedStringLen - 1);
  1042. }
  1043. else if (Status == STATUS_QDSTRING_TERMINATED_BY_CRLF)
  1044. {
  1045. //
  1046. // Reparse the current character as an HTTP Char
  1047. //
  1048. ParserState = HFCStart;
  1049. //
  1050. // Decrement the offset because the for loop will
  1051. // increment it by 1.
  1052. //
  1053. CurrentOffset--;
  1054. }
  1055. else if (Status == STATUS_MORE_PROCESSING_REQUIRED)
  1056. {
  1057. //
  1058. // The quoted string is folded. Let the caller know.
  1059. //
  1060. return Status;
  1061. }
  1062. else
  1063. {
  1064. UlTraceError(PARSER, (
  1065. "FindHeaderEndReadOnly: ERROR parsing char (%x) at "
  1066. "Offset %d in state %d\n",
  1067. CurrentChar,
  1068. CurrentOffset,
  1069. OldState
  1070. ));
  1071. return STATUS_INVALID_DEVICE_REQUEST;
  1072. }
  1073. break;
  1074. default:
  1075. ASSERT(!"Invalid ParserState value!");
  1076. break;
  1077. }
  1078. }
  1079. //
  1080. // Did not find the end of a header, let's get more buffer.
  1081. //
  1082. *pBytesTaken = 0;
  1083. return STATUS_SUCCESS;
  1084. } // FindHeaderEndReadOnly
  1085. /****************************************************************************++
  1086. Routine Description:
  1087. A utility routine, to find the terminating CRLF or LFLF of a header's
  1088. field content, if present. This routine also performs line folding,
  1089. which may "compress" the content. We don't actually shrink the length
  1090. of the buffer, but simply move the content up, and fill up extra bytes
  1091. at the end with spaces.
  1092. Example: "<CR><LF><SP><TAB><SP>Field<CR><LF><SP>Content<SP><CR><LF>" becomes
  1093. "<SP>Field<SP>Content<SP><SP><SP><SP><SP><SP><SP><CR><LF>"
  1094. NOTE: NOTE: If this function is fixed, the corresponding
  1095. FindHeaderEndReadOnly also needs to be fixed.
  1096. Arguments:
  1097. pHeader - Header whose end is to be found.
  1098. HeaderLength - Length of data pointed to by pHeader.
  1099. pBytesTaken - Where to return the total number of bytes traversed.
  1100. We return 0 if we couldn't locate the end of the header
  1101. Return Value:
  1102. STATUS_SUCCESS if no parsing errors were encountered (including not
  1103. being able to locate the end of the header),
  1104. STATUS_INVALID_DATA_REQUEST otherwise.
  1105. --****************************************************************************/
  1106. NTSTATUS
  1107. FindHeaderEnd(
  1108. IN PUCHAR pHeader,
  1109. IN ULONG HeaderLength,
  1110. OUT PULONG pBytesTaken
  1111. )
  1112. {
  1113. UCHAR CurrentChar;
  1114. PUCHAR pDest;
  1115. ULONG CurrentOffset;
  1116. HFC_PARSER_STATE ParserState, OldState;
  1117. ULONG QuotedStringLen;
  1118. NTSTATUS Status;
  1119. ParserState = HFCStart;
  1120. CurrentOffset = 0;
  1121. pDest = pHeader + CurrentOffset;
  1122. //
  1123. // The field-content of a header contains *TEXT or combinations
  1124. // token, separators and quoted-string. It is terminated by a CRLF.
  1125. //
  1126. // Folding - if one or more LWS follows a CRLF, replace the entire
  1127. // sequence with a single SP, and treat this as a continuation of
  1128. // header field content.
  1129. //
  1130. for (/* NOTHING */; CurrentOffset < HeaderLength; CurrentOffset++)
  1131. {
  1132. CurrentChar = *(pHeader + CurrentOffset);
  1133. OldState = ParserState;
  1134. switch (ParserState)
  1135. {
  1136. case HFCFolding:
  1137. if (IS_HTTP_LWS(CurrentChar))
  1138. {
  1139. // Do nothing - eat this char.
  1140. break;
  1141. }
  1142. // Else fall through.
  1143. ParserState = HFCStart;
  1144. case HFCStart:
  1145. if (CurrentChar == CR)
  1146. {
  1147. ParserState = HFCSeenCR;
  1148. }
  1149. else if (CurrentChar == LF)
  1150. {
  1151. ParserState = HFCSeenLF;
  1152. }
  1153. else if (CurrentChar == DOUBLE_QUOTE)
  1154. {
  1155. *pDest++ = CurrentChar;
  1156. ParserState = HFCInQuotedString;
  1157. }
  1158. else if (!IS_HTTP_CTL(CurrentChar) ||
  1159. IS_HTTP_WS_TOKEN(CurrentChar))
  1160. {
  1161. *pDest++ = CurrentChar;
  1162. }
  1163. else
  1164. {
  1165. UlTraceError(PARSER, (
  1166. "FindHeaderEnd: ERROR parsing char (%x) at "
  1167. "Offset %d in state %d\n",
  1168. CurrentChar,
  1169. CurrentOffset,
  1170. OldState
  1171. ));
  1172. return STATUS_INVALID_DEVICE_REQUEST;
  1173. }
  1174. break;
  1175. case HFCSeenCR:
  1176. if (CurrentChar == LF)
  1177. {
  1178. ParserState = HFCSeenCRLF;
  1179. }
  1180. else
  1181. {
  1182. UlTraceError(PARSER, (
  1183. "FindHeaderEnd: ERROR parsing char (%x) at "
  1184. "Offset %d in state %d\n",
  1185. CurrentChar,
  1186. CurrentOffset,
  1187. OldState
  1188. ));
  1189. return STATUS_INVALID_DEVICE_REQUEST;
  1190. }
  1191. break;
  1192. case HFCSeenLF:
  1193. if (CurrentChar == LF)
  1194. {
  1195. ParserState = HFCSeenCRLF; // LFLF considered = CRLF
  1196. }
  1197. else
  1198. {
  1199. UlTraceError(PARSER, (
  1200. "FindHeaderEnd: ERROR parsing char (%x) at "
  1201. "Offset %d in state %d\n",
  1202. CurrentChar,
  1203. CurrentOffset,
  1204. OldState
  1205. ));
  1206. return STATUS_INVALID_DEVICE_REQUEST;
  1207. }
  1208. break;
  1209. case HFCSeenCRLF:
  1210. if (IS_HTTP_LWS(CurrentChar))
  1211. {
  1212. //
  1213. // Replace one or more LWS following CRLF
  1214. // with a single SP.
  1215. //
  1216. *pDest++ = SP;
  1217. ParserState = HFCFolding;
  1218. }
  1219. else
  1220. {
  1221. // Found a non-continuation char immediately
  1222. // following CRLF; must be end of header content.
  1223. //
  1224. // Fill up any trailing bytes with spaces. This is to
  1225. // account for any compression occurring due to folding.
  1226. //
  1227. ASSERT(CurrentOffset >= CRLF_SIZE);
  1228. while (pDest < (pHeader + CurrentOffset - CRLF_SIZE))
  1229. {
  1230. *pDest++ = SP;
  1231. }
  1232. //
  1233. // Calling routines expect to find
  1234. // one terminating CRLF for the header.
  1235. // Attach it back.
  1236. //
  1237. *pDest++ = CR;
  1238. *pDest++ = LF;
  1239. //
  1240. // All done!
  1241. //
  1242. *pBytesTaken = CurrentOffset;
  1243. return STATUS_SUCCESS;
  1244. }
  1245. break;
  1246. case HFCInQuotedString:
  1247. Status = ParseQuotedString(pHeader + CurrentOffset,
  1248. HeaderLength - CurrentOffset,
  1249. pDest,
  1250. &QuotedStringLen);
  1251. if (Status == STATUS_SUCCESS)
  1252. {
  1253. if (QuotedStringLen == 0)
  1254. {
  1255. //
  1256. // Ran out of header buffer while parsing quoted
  1257. // string. Setting QuotedStringLen to whatever
  1258. // available will get us out of the for loop.
  1259. //
  1260. QuotedStringLen = HeaderLength - CurrentOffset;
  1261. }
  1262. else
  1263. {
  1264. // Found a quoted string. Change the parser state.
  1265. ParserState = HFCStart;
  1266. }
  1267. //
  1268. // Skip the destination pointer by the length of quoted
  1269. // string.
  1270. //
  1271. pDest += QuotedStringLen;
  1272. //
  1273. // Increment the offset by the length of quoted string-1.
  1274. // One less because the for loop will increment it by 1.
  1275. //
  1276. CurrentOffset += (QuotedStringLen - 1);
  1277. }
  1278. else if (Status == STATUS_QDSTRING_TERMINATED_BY_CRLF)
  1279. {
  1280. //
  1281. // Reparse the current character as an HTTP Char
  1282. //
  1283. ParserState = HFCStart;
  1284. //
  1285. // Decrement the offset because the for loop will
  1286. // increment it by 1.
  1287. //
  1288. CurrentOffset--;
  1289. }
  1290. else
  1291. {
  1292. UlTraceError(PARSER, (
  1293. "FindHeaderEnd: ERROR parsing char (%x) at "
  1294. "Offset %d in state %d\n",
  1295. CurrentChar,
  1296. CurrentOffset,
  1297. OldState
  1298. ));
  1299. return STATUS_INVALID_DEVICE_REQUEST;
  1300. }
  1301. break;
  1302. default:
  1303. ASSERT(!"Invalid ParserState value!");
  1304. break;
  1305. }
  1306. }
  1307. //
  1308. // Did not find the end of a header, let's get more buffer.
  1309. //
  1310. *pBytesTaken = 0;
  1311. return STATUS_SUCCESS;
  1312. } // FindHeaderEnd
  1313. /*++
  1314. Routine Description:
  1315. A wrapper around FindHeaderEnd that enforces a maximum length
  1316. for request headers.
  1317. Arguments:
  1318. pRequest - The request object
  1319. pHeader - Header whose end is to be found.
  1320. HeaderLength - Length of data pointed to by pHeader.
  1321. pBytesTaken - Where to return the total number of bytes traversed.
  1322. We return 0 if we couldn't locate the end of the header
  1323. Return Value:
  1324. As FindHeaderEnd. Returns STATUS_INVALID_DEVICE_REQUEST if too long.
  1325. --*/
  1326. NTSTATUS
  1327. FindRequestHeaderEnd(
  1328. IN PUL_INTERNAL_REQUEST pRequest,
  1329. IN PUCHAR pHeader,
  1330. IN ULONG HeaderLength,
  1331. OUT PULONG pBytesTaken
  1332. )
  1333. {
  1334. NTSTATUS Status = FindHeaderEnd(pHeader, HeaderLength, pBytesTaken);
  1335. if (NT_SUCCESS(Status))
  1336. {
  1337. if (*pBytesTaken > ANSI_STRING_MAX_CHAR_LEN)
  1338. {
  1339. UlTraceError(PARSER, (
  1340. "FindRequestHeaderEnd(pRequest = %p) "
  1341. "Header too long: %lu\n",
  1342. pRequest,
  1343. *pBytesTaken
  1344. ));
  1345. UlSetErrorCode(pRequest, UlErrorHeader, NULL);
  1346. Status = STATUS_INVALID_DEVICE_REQUEST;
  1347. }
  1348. }
  1349. return Status;
  1350. } // FindHeaderEndMax
  1351. /*++
  1352. Routine Description:
  1353. A utility routine, to find the terminating CRLF or LFLF of a
  1354. chunk header.
  1355. NOTE: This skips any chunk-extension fields, if present. It is
  1356. OK to ignore any chunk-extension extensions that we don't understand,
  1357. and the current code understands none.
  1358. TODO: modify the API to pass up chunk-extension fields to the user.
  1359. Arguments:
  1360. pHeader - Header whose end is to be found.
  1361. HeaderLength - Length of data pointed to by pHeader.
  1362. TokenLength - Where to return the length of the token.
  1363. Return Value:
  1364. Length of the header, or 0 if we couldn't find the end.
  1365. --*/
  1366. NTSTATUS
  1367. FindChunkHeaderEnd(
  1368. IN PUCHAR pHeader,
  1369. IN ULONG HeaderLength,
  1370. OUT PULONG pBytesTaken
  1371. )
  1372. {
  1373. UCHAR CurrentChar;
  1374. ULONG CurrentOffset;
  1375. ULONG ChunkExtNameLength = 0;
  1376. ULONG ChunkExtValLength = 0;
  1377. BOOLEAN SeenSingleCharQuote;
  1378. CH_PARSER_STATE ParserState;
  1379. ULONG QuotedStringLen;
  1380. NTSTATUS Status;
  1381. CurrentOffset = 0;
  1382. ParserState = CHStart;
  1383. SeenSingleCharQuote = FALSE;
  1384. //
  1385. // While we still have data, loop through looking for the end of
  1386. // the chunk header.
  1387. //
  1388. // The following loop implements parsing for:
  1389. // chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
  1390. //
  1391. for (; CurrentOffset < HeaderLength; CurrentOffset++)
  1392. {
  1393. CurrentChar = *(pHeader + CurrentOffset);
  1394. switch (ParserState)
  1395. {
  1396. case CHStart:
  1397. if (CurrentChar == CR || CurrentChar == LF)
  1398. {
  1399. ParserState = CHSeenCR;
  1400. }
  1401. else if (CurrentChar == SEMI_COLON)
  1402. {
  1403. ParserState = CHInChunkExtName;
  1404. ChunkExtNameLength = 0;
  1405. }
  1406. else if (IS_HTTP_LWS(CurrentChar))
  1407. {
  1408. // Ignore leading linear white spaces.
  1409. ;
  1410. }
  1411. else
  1412. {
  1413. ParserState = CHError;
  1414. }
  1415. break;
  1416. case CHSeenCR:
  1417. if (CurrentChar == LF)
  1418. {
  1419. ParserState = CHSuccess;
  1420. }
  1421. else
  1422. {
  1423. ParserState = CHError;
  1424. }
  1425. break;
  1426. case CHInChunkExtName:
  1427. if (CurrentChar == EQUALS)
  1428. {
  1429. ParserState = CHSeenChunkExtNameAndEquals;
  1430. }
  1431. else if (CurrentChar == SEMI_COLON)
  1432. {
  1433. ChunkExtNameLength = 0;
  1434. ParserState = CHInChunkExtName;
  1435. }
  1436. else if (CurrentChar == CR ||
  1437. CurrentChar == LF)
  1438. {
  1439. ParserState = CHSeenCR;
  1440. }
  1441. else if (IS_HTTP_TOKEN(CurrentChar))
  1442. {
  1443. ChunkExtNameLength++;
  1444. }
  1445. else
  1446. {
  1447. ParserState = CHError;
  1448. }
  1449. break;
  1450. case CHSeenChunkExtNameAndEquals:
  1451. if (CurrentChar == DOUBLE_QUOTE)
  1452. {
  1453. ChunkExtValLength = 0;
  1454. ParserState = CHInChunkExtValQuotedString;
  1455. }
  1456. else if (IS_HTTP_TOKEN(CurrentChar))
  1457. {
  1458. ChunkExtValLength = 1; // including this one
  1459. ParserState = CHInChunkExtValToken;
  1460. }
  1461. else
  1462. {
  1463. ParserState = CHError;
  1464. }
  1465. break;
  1466. case CHInChunkExtValToken:
  1467. if (IS_HTTP_TOKEN(CurrentChar))
  1468. {
  1469. ChunkExtValLength++;
  1470. }
  1471. else if (CurrentChar == SEMI_COLON)
  1472. {
  1473. ParserState = CHInChunkExtName;
  1474. ChunkExtNameLength = 0;
  1475. }
  1476. else if (CurrentChar == CR)
  1477. {
  1478. ParserState = CHSeenCR;
  1479. }
  1480. else
  1481. {
  1482. ParserState = CHError;
  1483. }
  1484. break;
  1485. case CHInChunkExtValQuotedString:
  1486. Status = ParseQuotedString(pHeader + CurrentOffset,
  1487. HeaderLength - CurrentOffset,
  1488. NULL,
  1489. &QuotedStringLen);
  1490. if (Status == STATUS_SUCCESS ||
  1491. Status == STATUS_MORE_PROCESSING_REQUIRED)
  1492. {
  1493. if (QuotedStringLen == 0)
  1494. {
  1495. //
  1496. // Ran out of header buffer while parsing quotes
  1497. // string. Setting QuotedStringLen to whatever
  1498. // available will get us out of the for loop.
  1499. //
  1500. QuotedStringLen = HeaderLength - CurrentOffset;
  1501. }
  1502. else
  1503. {
  1504. // Found a quoted string. Change the parser state.
  1505. ParserState = CHSeenChunkExtValQuotedStringTerminator;
  1506. }
  1507. // Do not count the closing <">.
  1508. ChunkExtValLength = QuotedStringLen - 1;
  1509. // One less because the for loop will increment it by 1.
  1510. CurrentOffset += (QuotedStringLen - 1);
  1511. }
  1512. else
  1513. {
  1514. ParserState = CHError;
  1515. }
  1516. break;
  1517. case CHSeenChunkExtValQuotedStringTerminator:
  1518. if (CurrentChar == SEMI_COLON)
  1519. {
  1520. ParserState = CHInChunkExtName;
  1521. }
  1522. else if (CurrentChar == CR)
  1523. {
  1524. ParserState = CHSeenCR;
  1525. }
  1526. else
  1527. {
  1528. ParserState = CHError;
  1529. }
  1530. break;
  1531. case CHSuccess:
  1532. break;
  1533. case CHError:
  1534. break;
  1535. default:
  1536. ASSERT(!"Invalid CH parser state!");
  1537. break;
  1538. }
  1539. if ((ParserState == CHError) ||
  1540. (ParserState == CHSuccess))
  1541. {
  1542. break;
  1543. }
  1544. }
  1545. if (ParserState == CHSuccess)
  1546. {
  1547. ASSERT(CurrentOffset < HeaderLength);
  1548. //
  1549. // All done!
  1550. //
  1551. *pBytesTaken = CurrentOffset + 1;
  1552. return STATUS_SUCCESS;
  1553. }
  1554. else if (ParserState == CHError)
  1555. {
  1556. *pBytesTaken = 0;
  1557. return STATUS_INVALID_DEVICE_REQUEST;
  1558. }
  1559. //
  1560. // Else, more data is required to find the end.
  1561. //
  1562. *pBytesTaken = 0;
  1563. return STATUS_SUCCESS;
  1564. }
  1565. /****************************************************************************++
  1566. Routine Description:
  1567. A utility routine, to parse the chunk length of a chunked response.
  1568. Arguments:
  1569. FirstChunkParsed - Whether we are in the first chunk or a subsequent chunk
  1570. pBuffer - pointer to the indicated data
  1571. BufferLength - Length of data pointed to by pBuffer.
  1572. pBytesTaken - Bytes consumed by this routine.
  1573. pChunkLength - Parsed Chunk length
  1574. Return Value:
  1575. Length of the header, or 0 if we couldn't find the end.
  1576. --****************************************************************************/
  1577. NTSTATUS
  1578. ParseChunkLength(
  1579. IN ULONG FirstChunkParsed,
  1580. IN PUCHAR pBuffer,
  1581. IN ULONG BufferLength,
  1582. OUT PULONG pBytesTaken,
  1583. OUT PULONGLONG pChunkLength
  1584. )
  1585. {
  1586. NTSTATUS Status;
  1587. PUCHAR pToken;
  1588. ULONG TokenLength;
  1589. ULONG BytesTaken;
  1590. ULONG TotalBytesTaken = 0;
  1591. ULONG HeaderLength;
  1592. ASSERT(pBytesTaken != NULL);
  1593. ASSERT(pChunkLength != NULL);
  1594. *pBytesTaken = 0;
  1595. //
  1596. // 2 cases:
  1597. //
  1598. // 1) the first chunk where the length follows the headers
  1599. // 2) subsequent chunks where the length follows a previous chunk
  1600. //
  1601. // in case 1 pBuffer will point straight to the chunk length.
  1602. //
  1603. // in case 2 pBuffer will point to the CRLF that terminated the previous
  1604. // chunk, this needs to be consumed, skipped, and then the chunk length
  1605. // read.
  1606. //
  1607. // if we are case 2 (see above)
  1608. //
  1609. if (FirstChunkParsed == 1)
  1610. {
  1611. //
  1612. // make sure there is enough space first
  1613. //
  1614. if (BufferLength < CRLF_SIZE)
  1615. {
  1616. Status = STATUS_MORE_PROCESSING_REQUIRED;
  1617. goto end;
  1618. }
  1619. //
  1620. // now it better be a terminator
  1621. //
  1622. if (*(UNALIGNED64 USHORT *)pBuffer != CRLF &&
  1623. *(UNALIGNED64 USHORT *)pBuffer != LFLF)
  1624. {
  1625. UlTraceError(PARSER, (
  1626. "http!ParseChunkLength ERROR: No CRLF at the end of "
  1627. "chunk-data\n"));
  1628. Status = STATUS_INVALID_DEVICE_REQUEST;
  1629. goto end;
  1630. }
  1631. //
  1632. // update our book-keeping
  1633. //
  1634. pBuffer += CRLF_SIZE;
  1635. TotalBytesTaken += CRLF_SIZE;
  1636. BufferLength -= CRLF_SIZE;
  1637. }
  1638. pToken = FindHexToken(pBuffer, BufferLength, &TokenLength);
  1639. if (pToken == NULL ||
  1640. ((BufferLength - TokenLength) < CRLF_SIZE))
  1641. {
  1642. //
  1643. // not enough buffer
  1644. //
  1645. Status = STATUS_MORE_PROCESSING_REQUIRED;
  1646. goto end;
  1647. }
  1648. //
  1649. // Was there any token ?
  1650. //
  1651. if (TokenLength == 0)
  1652. {
  1653. UlTraceError(PARSER, ("[ParseChunkLength]: No length!\n"));
  1654. Status = STATUS_INVALID_DEVICE_REQUEST;
  1655. goto end;
  1656. }
  1657. //
  1658. // Add the bytes consumed by FindHexToken
  1659. // (the token bytes plus preceding bytes)
  1660. //
  1661. TotalBytesTaken += DIFF((pToken + TokenLength) - pBuffer);
  1662. //
  1663. // and find the end
  1664. //
  1665. HeaderLength = BufferLength - DIFF((pToken + TokenLength) - pBuffer);
  1666. Status = FindChunkHeaderEnd(
  1667. pToken + TokenLength,
  1668. HeaderLength,
  1669. &BytesTaken
  1670. );
  1671. if (NT_SUCCESS(Status) == FALSE)
  1672. {
  1673. UlTraceError(PARSER, ("[ParseChunkLength]: FindChunkHeaderEnd failed!\n"));
  1674. goto end;
  1675. }
  1676. if (BytesTaken == 0)
  1677. {
  1678. Status = STATUS_MORE_PROCESSING_REQUIRED;
  1679. goto end;
  1680. }
  1681. TotalBytesTaken += BytesTaken;
  1682. //
  1683. // now update the HTTP_REQUEST
  1684. //
  1685. Status = UlAnsiToULongLong(
  1686. pToken,
  1687. (USHORT) TokenLength,
  1688. 16, // Base
  1689. pChunkLength
  1690. );
  1691. //
  1692. // Did the number conversion fail ?
  1693. //
  1694. if (NT_SUCCESS(Status) == FALSE)
  1695. {
  1696. UlTraceError(PARSER, ("[ParseChunkLength]: Failed number conversion \n"));
  1697. goto end;
  1698. }
  1699. //
  1700. // all done, return the bytes consumed
  1701. //
  1702. *pBytesTaken = TotalBytesTaken;
  1703. end:
  1704. RETURN(Status);
  1705. } // ParseChunkLength
  1706. /*++
  1707. Routine Description:
  1708. This routine parses a quoted string. The grammar of quoted string is
  1709. quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
  1710. qdtext = <any TEXT except <">>
  1711. quoted-pair = "\" CHAR
  1712. TEXT = <any OCTET except CTLs, but including LWS>
  1713. CHAR = <any US-ASCII character (octets 0 - 127)>
  1714. LWS = [CRLF] 1*( SP | HT )
  1715. Upon seeing a header folding, this routine overwrites the folding CRLF
  1716. with SPSP and does not replace LWS with a single SP. (This is done
  1717. to handle read only buffer cases.)
  1718. STATUS_QDSTRING_TERMINATED_BY_CRLF is *not* a success code! It is up
  1719. to the caller to handle the input buffer.
  1720. Arguments:
  1721. pInput - Supplies pointer to input buffer. pInput must point to
  1722. the first char after the opening <"> char.
  1723. pInputLength - Supplies the length of the input buffer in bytes.
  1724. pOutput - Supplied pointer where output will be written.
  1725. (Can be same as pInput.)
  1726. pBytesTaken - Returns the length of the quoted string in bytes.
  1727. (It includs the closing double quote char.)
  1728. Return Value:
  1729. STATUS_INVALID_PARAMETER - Input is malformed.
  1730. STATUS_MORE_PROCESSING_REQUIRED - Same as STATUS_SUCCESS except it is
  1731. returned when pOutput is NULL.
  1732. STATUS_QDSTRING_TERMINATED_BY_CRLF - Indicates that the buffer contained
  1733. an unmatched quote and was terminated
  1734. by a CRLF
  1735. STATUS_SUCCESS - Either parsed a quoted string
  1736. successfully (when *pBytesTaken != 0)
  1737. or more data is required to proceed
  1738. (when *pBytesTaken == 0).
  1739. --*/
  1740. NTSTATUS
  1741. ParseQuotedString(
  1742. IN PUCHAR pInput,
  1743. IN ULONG InputLength,
  1744. IN PUCHAR pOutput, OPTIONAL
  1745. OUT PULONG pBytesTaken
  1746. )
  1747. {
  1748. ULONG CurrentOffset;
  1749. UCHAR CurrentChar;
  1750. QS_PARSER_STATE ParserState;
  1751. BOOLEAN bFolded;
  1752. // Sanity check.
  1753. ASSERT(pInput && InputLength);
  1754. ASSERT(pBytesTaken);
  1755. UlTrace(PARSER, (
  1756. "ParseQuotedString %.*s\n",
  1757. InputLength,
  1758. pInput
  1759. ));
  1760. // Initialize output argument.
  1761. *pBytesTaken = 0;
  1762. // Initially, there is no folding.
  1763. bFolded = FALSE;
  1764. // Initialize parser state.
  1765. ParserState = QSInString;
  1766. //
  1767. // Loop through all input chars.
  1768. //
  1769. for (CurrentOffset = 0; CurrentOffset < InputLength; CurrentOffset++)
  1770. {
  1771. static PCHAR StateName[] =
  1772. {
  1773. "QSInString",
  1774. "QSSeenBackSlash",
  1775. "QSSeenCR",
  1776. "QSSeenLF",
  1777. "QSSeenCRLF",
  1778. "QSFolding",
  1779. "Default"
  1780. };
  1781. CurrentChar = pInput[CurrentOffset];
  1782. if (ARGUMENT_PRESENT(pOutput))
  1783. {
  1784. pOutput[CurrentOffset] = CurrentChar;
  1785. }
  1786. UlTraceVerbose(PARSER, (
  1787. "\t%-15.15s [0x%02X] '%c'\n",
  1788. StateName[ParserState],
  1789. CurrentChar,
  1790. ((IS_HTTP_PRINT(CurrentChar)) ? CurrentChar : '?')
  1791. ));
  1792. switch(ParserState)
  1793. {
  1794. case QSFolding:
  1795. if (IS_HTTP_LWS(CurrentChar))
  1796. {
  1797. // Skip LWS.
  1798. break;
  1799. }
  1800. // Fall through.
  1801. ParserState = QSInString;
  1802. case QSInString:
  1803. if (CurrentChar == DOUBLE_QUOTE)
  1804. {
  1805. //
  1806. // We are done parsing! Update the bytes consumed.
  1807. //
  1808. *pBytesTaken = CurrentOffset + 1;
  1809. ASSERT(*pBytesTaken <= InputLength);
  1810. //
  1811. // If the string was folded and the input was readonly,
  1812. // let the caller know that the string was folded.
  1813. //
  1814. if (ARGUMENT_PRESENT(pOutput) == FALSE && bFolded == TRUE)
  1815. {
  1816. return STATUS_MORE_PROCESSING_REQUIRED;
  1817. }
  1818. return STATUS_SUCCESS;
  1819. }
  1820. else if (CurrentChar == BACK_SLASH)
  1821. {
  1822. ParserState = QSSeenBackSlash;
  1823. }
  1824. else if (CurrentChar == CR)
  1825. {
  1826. ParserState = QSSeenCR;
  1827. }
  1828. else if (CurrentChar == LF)
  1829. {
  1830. ParserState = QSSeenLF;
  1831. }
  1832. else if (!IS_HTTP_CTL(CurrentChar) || IS_HTTP_LWS(CurrentChar))
  1833. {
  1834. ;
  1835. }
  1836. else
  1837. {
  1838. return STATUS_INVALID_PARAMETER;
  1839. }
  1840. break;
  1841. case QSSeenCR:
  1842. if (CurrentChar == LF)
  1843. {
  1844. ParserState = QSSeenCRLF;
  1845. }
  1846. else
  1847. {
  1848. return STATUS_INVALID_PARAMETER;
  1849. }
  1850. break;
  1851. case QSSeenLF:
  1852. if (CurrentChar == LF)
  1853. {
  1854. ParserState = QSSeenCRLF;
  1855. break;
  1856. }
  1857. if (!IS_HTTP_LWS(CurrentChar) &&
  1858. (CurrentOffset >= 2) &&
  1859. ((pInput[CurrentOffset-2] == CR) ||
  1860. (pInput[CurrentOffset-2] == LF)))
  1861. {
  1862. // The only way to enter this block is if the
  1863. // first character of a CRLF or LFLF pair is
  1864. // preceded by a '\' effectively escape encoding
  1865. // the first character.
  1866. UlTraceVerbose(PARSER, (
  1867. "ParseQuotedString: Unmatched Quote 0x%02X 0x%02X 0x%02X\n",
  1868. pInput[CurrentOffset-2],
  1869. pInput[CurrentOffset-1],
  1870. pInput[CurrentOffset]
  1871. ));
  1872. // Allow single double quote in field value.
  1873. // Field value now ends immediately prior to CRLF.
  1874. *pBytesTaken = CurrentOffset - 2;
  1875. ASSERT(*pBytesTaken <= InputLength);
  1876. ASSERT(pInput[CurrentOffset-1] == LF);
  1877. ASSERT(CurrentOffset > 2);
  1878. ASSERT(pInput[CurrentOffset-3] == BACK_SLASH);
  1879. return STATUS_QDSTRING_TERMINATED_BY_CRLF;
  1880. }
  1881. return STATUS_INVALID_PARAMETER;
  1882. break;
  1883. case QSSeenCRLF:
  1884. if (IS_HTTP_LWS(CurrentChar))
  1885. {
  1886. bFolded = TRUE;
  1887. ParserState = QSFolding;
  1888. if (ARGUMENT_PRESENT(pOutput))
  1889. {
  1890. //
  1891. // Overwrite prior CRLF with SPSP.
  1892. //
  1893. ASSERT(CurrentOffset >= 2);
  1894. ASSERT((pOutput[CurrentOffset-2] == CR) ||
  1895. (pOutput[CurrentOffset-2] == LF));
  1896. ASSERT(pOutput[CurrentOffset-1] == LF);
  1897. pOutput[CurrentOffset-2] = SP;
  1898. pOutput[CurrentOffset-1] = SP;
  1899. }
  1900. }
  1901. else
  1902. {
  1903. UlTraceVerbose(PARSER, (
  1904. "ParseQuotedString: Unmatched Quote 0x%02X 0x%02X 0x%02X\n",
  1905. pInput[CurrentOffset-2],
  1906. pInput[CurrentOffset-1],
  1907. pInput[CurrentOffset]
  1908. ));
  1909. // Allow single double quote in field value.
  1910. // Field value now ends immediately prior to CRLF.
  1911. *pBytesTaken = CurrentOffset - 2;
  1912. ASSERT(*pBytesTaken <= InputLength);
  1913. ASSERT(CurrentOffset >= 2);
  1914. ASSERT(pInput[CurrentOffset-1] == LF);
  1915. return STATUS_QDSTRING_TERMINATED_BY_CRLF;
  1916. }
  1917. break;
  1918. case QSSeenBackSlash:
  1919. // Accept any CHAR in this state.
  1920. if (IS_HTTP_CHAR(CurrentChar))
  1921. {
  1922. ParserState = QSInString;
  1923. }
  1924. else
  1925. {
  1926. return STATUS_INVALID_PARAMETER;
  1927. }
  1928. break;
  1929. default:
  1930. ASSERT(!"ParseQuotedString: Invalid parser state!");
  1931. break;
  1932. }
  1933. }
  1934. // We ran out of data to parse, get more.
  1935. return STATUS_SUCCESS;
  1936. }