Source code of Windows XP (NT5)
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.

5439 lines
141 KiB

  1. /*++
  2. Copyright (c) 1998-2001 Microsoft Corporation
  3. Module Name:
  4. parse.cxx
  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. --*/
  12. #include "precomp.h"
  13. #include "parsep.h"
  14. #include "rcvhdrs.h"
  15. //
  16. // Global initialization flag.
  17. //
  18. BOOLEAN g_DateCacheInitialized = FALSE;
  19. //
  20. // The fast verb translation table
  21. //
  22. FAST_VERB_ENTRY FastVerbTable[] =
  23. {
  24. CREATE_FAST_VERB_ENTRY(GET),
  25. CREATE_FAST_VERB_ENTRY(HEAD),
  26. CREATE_FAST_VERB_ENTRY(PUT),
  27. CREATE_FAST_VERB_ENTRY(POST),
  28. CREATE_FAST_VERB_ENTRY(DELETE),
  29. CREATE_FAST_VERB_ENTRY(TRACE),
  30. CREATE_FAST_VERB_ENTRY(TRACK),
  31. CREATE_FAST_VERB_ENTRY(OPTIONS),
  32. CREATE_FAST_VERB_ENTRY(CONNECT),
  33. CREATE_FAST_VERB_ENTRY(MOVE),
  34. CREATE_FAST_VERB_ENTRY(COPY),
  35. CREATE_FAST_VERB_ENTRY(MKCOL),
  36. CREATE_FAST_VERB_ENTRY(LOCK),
  37. CREATE_FAST_VERB_ENTRY(UNLOCK),
  38. CREATE_FAST_VERB_ENTRY(SEARCH)
  39. };
  40. //
  41. // The long verb translation table. All verbs more than 7 characters long
  42. // belong in this table.
  43. //
  44. LONG_VERB_ENTRY LongVerbTable[] =
  45. {
  46. CREATE_LONG_VERB_ENTRY(PROPFIND),
  47. CREATE_LONG_VERB_ENTRY(PROPPATCH)
  48. };
  49. //
  50. // The request header map table. These entries don't need to be in strict
  51. // alphabetical order, but they do need to be grouped by the first character
  52. // of the header - all A's together, all C's together, etc. They also need
  53. // to be entered in uppercase, since we upcase incoming verbs before we do
  54. // the compare.
  55. //
  56. // for nice perf, group unused headers low in the sub-sort order
  57. //
  58. // it's important that the header name is <= 24 characters (3 ULONGLONG's).
  59. //
  60. HEADER_MAP_ENTRY g_RequestHeaderMapTable[] =
  61. {
  62. CREATE_HEADER_MAP_ENTRY(Accept:,
  63. HttpHeaderAccept,
  64. FALSE,
  65. UlAcceptHeaderHandler,
  66. NULL,
  67. 0),
  68. CREATE_HEADER_MAP_ENTRY(Accept-Language:,
  69. HttpHeaderAcceptLanguage,
  70. FALSE,
  71. UlMultipleHeaderHandler,
  72. NULL,
  73. 1),
  74. CREATE_HEADER_MAP_ENTRY(Accept-Encoding:,
  75. HttpHeaderAcceptEncoding,
  76. FALSE,
  77. UlMultipleHeaderHandler,
  78. NULL,
  79. 3),
  80. CREATE_HEADER_MAP_ENTRY(Accept-Charset:,
  81. HttpHeaderAcceptCharset,
  82. FALSE,
  83. UlMultipleHeaderHandler,
  84. NULL,
  85. -1),
  86. CREATE_HEADER_MAP_ENTRY(Authorization:,
  87. HttpHeaderAuthorization,
  88. FALSE,
  89. UlSingleHeaderHandler,
  90. NULL,
  91. -1),
  92. CREATE_HEADER_MAP_ENTRY(Allow:,
  93. HttpHeaderAllow,
  94. FALSE,
  95. UlMultipleHeaderHandler,
  96. NULL,
  97. -1),
  98. CREATE_HEADER_MAP_ENTRY(Connection:,
  99. HttpHeaderConnection,
  100. FALSE,
  101. UlMultipleHeaderHandler,
  102. NULL,
  103. 7),
  104. CREATE_HEADER_MAP_ENTRY(Cache-Control:,
  105. HttpHeaderCacheControl,
  106. FALSE,
  107. UlMultipleHeaderHandler,
  108. NULL,
  109. -1),
  110. CREATE_HEADER_MAP_ENTRY(Cookie:,
  111. HttpHeaderCookie,
  112. FALSE,
  113. UlMultipleHeaderHandler,
  114. NULL,
  115. -1),
  116. CREATE_HEADER_MAP_ENTRY(Content-Length:,
  117. HttpHeaderContentLength,
  118. FALSE,
  119. UlSingleHeaderHandler,
  120. NULL,
  121. -1),
  122. CREATE_HEADER_MAP_ENTRY(Content-Type:,
  123. HttpHeaderContentType,
  124. FALSE,
  125. UlSingleHeaderHandler,
  126. NULL,
  127. -1),
  128. CREATE_HEADER_MAP_ENTRY(Content-Encoding:,
  129. HttpHeaderContentEncoding,
  130. FALSE,
  131. UlMultipleHeaderHandler,
  132. NULL,
  133. -1),
  134. CREATE_HEADER_MAP_ENTRY(Content-Language:,
  135. HttpHeaderContentLanguage,
  136. FALSE,
  137. UlMultipleHeaderHandler,
  138. NULL,
  139. -1),
  140. CREATE_HEADER_MAP_ENTRY(Content-Location:,
  141. HttpHeaderContentLocation,
  142. FALSE,
  143. UlSingleHeaderHandler,
  144. NULL,
  145. -1),
  146. CREATE_HEADER_MAP_ENTRY(Content-MD5:,
  147. HttpHeaderContentMd5,
  148. FALSE,
  149. UlSingleHeaderHandler,
  150. NULL,
  151. -1),
  152. CREATE_HEADER_MAP_ENTRY(Content-Range:,
  153. HttpHeaderContentRange,
  154. FALSE,
  155. UlSingleHeaderHandler,
  156. NULL,
  157. -1),
  158. CREATE_HEADER_MAP_ENTRY(Date:,
  159. HttpHeaderDate,
  160. FALSE,
  161. UlSingleHeaderHandler,
  162. NULL,
  163. -1),
  164. CREATE_HEADER_MAP_ENTRY(Expect:,
  165. HttpHeaderExpect,
  166. FALSE,
  167. UlMultipleHeaderHandler,
  168. NULL,
  169. -1),
  170. CREATE_HEADER_MAP_ENTRY(Expires:,
  171. HttpHeaderExpires,
  172. FALSE,
  173. UlSingleHeaderHandler,
  174. NULL,
  175. -1),
  176. CREATE_HEADER_MAP_ENTRY(From:,
  177. HttpHeaderFrom,
  178. FALSE,
  179. UlSingleHeaderHandler,
  180. NULL,
  181. -1),
  182. CREATE_HEADER_MAP_ENTRY(Host:,
  183. HttpHeaderHost,
  184. FALSE,
  185. UlSingleHeaderHandler,
  186. NULL,
  187. 6),
  188. CREATE_HEADER_MAP_ENTRY(If-Modified-Since:,
  189. HttpHeaderIfModifiedSince,
  190. FALSE,
  191. UlSingleHeaderHandler,
  192. NULL,
  193. 4),
  194. CREATE_HEADER_MAP_ENTRY(If-None-Match:,
  195. HttpHeaderIfNoneMatch,
  196. FALSE,
  197. UlMultipleHeaderHandler,
  198. NULL,
  199. 5),
  200. CREATE_HEADER_MAP_ENTRY(If-Match:,
  201. HttpHeaderIfMatch,
  202. FALSE,
  203. UlMultipleHeaderHandler,
  204. NULL,
  205. -1),
  206. CREATE_HEADER_MAP_ENTRY(If-Unmodified-Since:,
  207. HttpHeaderIfUnmodifiedSince,
  208. FALSE,
  209. UlSingleHeaderHandler,
  210. NULL,
  211. -1),
  212. CREATE_HEADER_MAP_ENTRY(If-Range:,
  213. HttpHeaderIfRange,
  214. FALSE,
  215. UlSingleHeaderHandler,
  216. NULL,
  217. -1),
  218. CREATE_HEADER_MAP_ENTRY(Last-Modified:,
  219. HttpHeaderLastModified,
  220. FALSE,
  221. UlSingleHeaderHandler,
  222. NULL,
  223. -1),
  224. CREATE_HEADER_MAP_ENTRY(Max-Forwards:,
  225. HttpHeaderMaxForwards,
  226. FALSE,
  227. UlSingleHeaderHandler,
  228. NULL,
  229. -1),
  230. CREATE_HEADER_MAP_ENTRY(Pragma:,
  231. HttpHeaderPragma,
  232. FALSE,
  233. UlMultipleHeaderHandler,
  234. NULL,
  235. -1),
  236. CREATE_HEADER_MAP_ENTRY(Proxy-Authorization:,
  237. HttpHeaderProxyAuthorization,
  238. FALSE,
  239. UlSingleHeaderHandler,
  240. NULL,
  241. -1),
  242. CREATE_HEADER_MAP_ENTRY(Referer:,
  243. HttpHeaderReferer,
  244. FALSE,
  245. UlSingleHeaderHandler,
  246. NULL,
  247. -1),
  248. CREATE_HEADER_MAP_ENTRY(Range:,
  249. HttpHeaderRange,
  250. FALSE,
  251. UlSingleHeaderHandler,
  252. NULL,
  253. -1),
  254. CREATE_HEADER_MAP_ENTRY(Trailer:,
  255. HttpHeaderTrailer,
  256. FALSE,
  257. UlMultipleHeaderHandler,
  258. NULL,
  259. -1),
  260. CREATE_HEADER_MAP_ENTRY(Transfer-Encoding:,
  261. HttpHeaderTransferEncoding,
  262. FALSE,
  263. UlMultipleHeaderHandler,
  264. NULL,
  265. -1),
  266. CREATE_HEADER_MAP_ENTRY(TE:,
  267. HttpHeaderTe,
  268. FALSE,
  269. UlMultipleHeaderHandler,
  270. NULL,
  271. -1),
  272. CREATE_HEADER_MAP_ENTRY(Translate:,
  273. HttpHeaderTranslate,
  274. FALSE,
  275. UlSingleHeaderHandler,
  276. NULL,
  277. -1),
  278. CREATE_HEADER_MAP_ENTRY(User-Agent:,
  279. HttpHeaderUserAgent,
  280. FALSE,
  281. UlSingleHeaderHandler,
  282. NULL,
  283. 2),
  284. CREATE_HEADER_MAP_ENTRY(Upgrade:,
  285. HttpHeaderUpgrade,
  286. FALSE,
  287. UlMultipleHeaderHandler,
  288. NULL,
  289. -1),
  290. CREATE_HEADER_MAP_ENTRY(Via:,
  291. HttpHeaderVia,
  292. FALSE,
  293. UlMultipleHeaderHandler,
  294. NULL,
  295. -1),
  296. CREATE_HEADER_MAP_ENTRY(Warning:,
  297. HttpHeaderWarning,
  298. FALSE,
  299. UlMultipleHeaderHandler,
  300. NULL,
  301. -1),
  302. };
  303. // The response header map table. These entries don't need to be in strict
  304. // alphabetical order, but they do need to be grouped by the first character
  305. // of the header - all A's together, all C's together, etc. They also need
  306. // to be entered in uppercase, since we upcase incoming verbs before we do
  307. // the compare.
  308. //
  309. // for nice perf, group unused headers low in the sub-sort order
  310. //
  311. // it's important that the header name is <= 24 characters (3 ULONGLONG's).
  312. //
  313. // BUGBUG: Fix the AUTO-GENERATE fields for the ones we auto-generate!!!
  314. //
  315. #define UcSingleHeaderHandler NULL
  316. #define UcMultipleHeaderHandler NULL
  317. HEADER_MAP_ENTRY g_ResponseHeaderMapTable[] =
  318. {
  319. CREATE_HEADER_MAP_ENTRY(Accept-Ranges:,
  320. HttpHeaderAcceptRanges,
  321. FALSE,
  322. NULL,
  323. UcMultipleHeaderHandler,
  324. -1),
  325. CREATE_HEADER_MAP_ENTRY(Age:,
  326. HttpHeaderAge,
  327. FALSE,
  328. NULL,
  329. UcSingleHeaderHandler,
  330. -1),
  331. CREATE_HEADER_MAP_ENTRY(Allow:,
  332. HttpHeaderAllow,
  333. FALSE,
  334. NULL,
  335. UcMultipleHeaderHandler,
  336. -1),
  337. CREATE_HEADER_MAP_ENTRY(Cache-Control:,
  338. HttpHeaderCacheControl,
  339. FALSE,
  340. NULL,
  341. UcMultipleHeaderHandler,
  342. -1),
  343. CREATE_HEADER_MAP_ENTRY(Connection:,
  344. HttpHeaderConnection,
  345. FALSE,
  346. NULL,
  347. UcMultipleHeaderHandler,
  348. -1),
  349. CREATE_HEADER_MAP_ENTRY(Content-Encoding:,
  350. HttpHeaderContentEncoding,
  351. FALSE,
  352. NULL,
  353. UcMultipleHeaderHandler,
  354. -1),
  355. CREATE_HEADER_MAP_ENTRY(Content-Language:,
  356. HttpHeaderContentLanguage,
  357. FALSE,
  358. NULL,
  359. UcMultipleHeaderHandler,
  360. -1),
  361. CREATE_HEADER_MAP_ENTRY(Content-Length:,
  362. HttpHeaderContentLength,
  363. TRUE,
  364. NULL,
  365. UcSingleHeaderHandler,
  366. -1),
  367. CREATE_HEADER_MAP_ENTRY(Content-Location:,
  368. HttpHeaderContentLocation,
  369. FALSE,
  370. NULL,
  371. UcSingleHeaderHandler,
  372. -1),
  373. CREATE_HEADER_MAP_ENTRY(Content-MD5:,
  374. HttpHeaderContentMd5,
  375. FALSE,
  376. NULL,
  377. UcSingleHeaderHandler,
  378. -1),
  379. CREATE_HEADER_MAP_ENTRY(Content-Range:,
  380. HttpHeaderContentRange,
  381. FALSE,
  382. NULL,
  383. UcSingleHeaderHandler,
  384. -1),
  385. CREATE_HEADER_MAP_ENTRY(Content-Type:,
  386. HttpHeaderContentType,
  387. FALSE,
  388. NULL,
  389. UcSingleHeaderHandler,
  390. -1),
  391. CREATE_HEADER_MAP_ENTRY(Date:,
  392. HttpHeaderDate,
  393. FALSE,
  394. NULL,
  395. UcSingleHeaderHandler,
  396. -1),
  397. CREATE_HEADER_MAP_ENTRY(ETag:,
  398. HttpHeaderEtag,
  399. FALSE,
  400. NULL,
  401. UcSingleHeaderHandler,
  402. -1),
  403. CREATE_HEADER_MAP_ENTRY(Expires:,
  404. HttpHeaderExpires,
  405. FALSE,
  406. NULL,
  407. UcSingleHeaderHandler,
  408. -1),
  409. CREATE_HEADER_MAP_ENTRY(Last-Modified:,
  410. HttpHeaderLastModified,
  411. FALSE,
  412. NULL,
  413. UcSingleHeaderHandler,
  414. -1),
  415. CREATE_HEADER_MAP_ENTRY(Location:,
  416. HttpHeaderLocation,
  417. FALSE,
  418. NULL,
  419. UcSingleHeaderHandler,
  420. -1),
  421. CREATE_HEADER_MAP_ENTRY(Pragma:,
  422. HttpHeaderPragma,
  423. FALSE,
  424. NULL,
  425. UcMultipleHeaderHandler,
  426. -1),
  427. CREATE_HEADER_MAP_ENTRY(Proxy-Authenticate:,
  428. HttpHeaderProxyAuthenticate,
  429. FALSE,
  430. NULL,
  431. UcMultipleHeaderHandler,
  432. -1),
  433. CREATE_HEADER_MAP_ENTRY(Retry-After:,
  434. HttpHeaderRetryAfter,
  435. FALSE,
  436. NULL,
  437. UcSingleHeaderHandler,
  438. -1),
  439. CREATE_HEADER_MAP_ENTRY(Server:,
  440. HttpHeaderServer,
  441. FALSE,
  442. NULL,
  443. UcSingleHeaderHandler,
  444. -1),
  445. #if 0
  446. CREATE_HEADER_MAP_ENTRY(Set-Cookie:,
  447. HttpHeaderSetCookie,
  448. FALSE,
  449. NULL,
  450. -1),
  451. #endif
  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. TRUE,
  461. NULL,
  462. UcMultipleHeaderHandler,
  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. UcMultipleHeaderHandler,
  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_INDICIES];
  501. HEADER_INDEX_ENTRY g_ResponseHeaderIndexTable[NUMBER_HEADER_INDICIES];
  502. HEADER_HINT_INDEX_ENTRY g_RequestHeaderHintIndexTable[NUMBER_HEADER_HINT_INDICIES];
  503. #define NUMBER_FAST_VERB_ENTRIES (sizeof(FastVerbTable)/sizeof(FAST_VERB_ENTRY))
  504. #define NUMBER_LONG_VERB_ENTRIES (sizeof(LongVerbTable)/sizeof(LONG_VERB_ENTRY))
  505. #define NUMBER_REQUEST_HEADER_MAP_ENTRIES \
  506. (sizeof(g_RequestHeaderMapTable)/sizeof(HEADER_MAP_ENTRY))
  507. #define NUMBER_RESPONSE_HEADER_MAP_ENTRIES \
  508. (sizeof(g_ResponseHeaderMapTable)/sizeof(HEADER_MAP_ENTRY))
  509. const char DefaultChar = '_';
  510. /*++
  511. Routine Description:
  512. A utility routine to find a token. We take an input pointer, skip any
  513. preceding LWS, then scan the token until we find either LWS or a CRLF
  514. pair.
  515. Arguments:
  516. pBuffer - Buffer to search for token.
  517. BufferLength - Length of data pointed to by pBuffer.
  518. TokenLength - Where to return the length of the token.
  519. Return Value:
  520. A pointer to the token we found, as well as the length, or NULL if we
  521. don't find a delimited token.
  522. --*/
  523. PUCHAR
  524. FindWSToken(
  525. IN PUCHAR pBuffer,
  526. IN ULONG BufferLength,
  527. OUT ULONG *pTokenLength
  528. )
  529. {
  530. PUCHAR pTokenStart;
  531. //
  532. // Sanity check.
  533. //
  534. PAGED_CODE();
  535. //
  536. // First, skip any preceding LWS.
  537. //
  538. while (BufferLength > 0 && IS_HTTP_LWS(*pBuffer))
  539. {
  540. pBuffer++;
  541. BufferLength--;
  542. }
  543. // If we stopped because we ran out of buffer, fail.
  544. if (BufferLength == 0)
  545. {
  546. return NULL;
  547. }
  548. pTokenStart = pBuffer;
  549. // Now skip over the token, until we see either LWS or a CR or LF.
  550. while (BufferLength != 0 &&
  551. (*pBuffer != CR &&
  552. *pBuffer != SP &&
  553. *pBuffer != LF &&
  554. *pBuffer != HT)
  555. )
  556. {
  557. pBuffer++;
  558. BufferLength--;
  559. }
  560. // See why we stopped.
  561. if (BufferLength == 0)
  562. {
  563. // Ran out of buffer before end of token.
  564. return NULL;
  565. }
  566. // Success. Set the token length and return the start of the token.
  567. *pTokenLength = DIFF(pBuffer - pTokenStart);
  568. return pTokenStart;
  569. } // FindWSToken
  570. /*++
  571. Routine Description:
  572. The slower way to look up a verb. We find the verb in the request and then
  573. look for it in the LongVerbTable. If it's not found, we'll return
  574. UnknownVerb. If it can't be parsed we return UnparsedVerb. Otherwise
  575. we return the verb type.
  576. Arguments:
  577. pHttpRequest - Pointer to the incoming HTTP request.
  578. HttpRequestLength - Length of data pointed to by pHttpRequest.
  579. pVerb - Where we return a pointer to the verb, if it's an
  580. unknown ver.
  581. ppVerbLength - Where we return the length of the verb
  582. pBytesTaken - The total length consumed, including the length of
  583. the verb plus preceding & 1 trailing whitespace.
  584. Return Value:
  585. The verb we found, or the appropriate error.
  586. --*/
  587. NTSTATUS
  588. LookupVerb(
  589. IN PUL_INTERNAL_REQUEST pRequest,
  590. IN PUCHAR pHttpRequest,
  591. IN ULONG HttpRequestLength,
  592. OUT ULONG * pBytesTaken
  593. )
  594. {
  595. ULONG TokenLength;
  596. PUCHAR pToken;
  597. PUCHAR pTempRequest;
  598. ULONG TempLength;
  599. ULONG i;
  600. //
  601. // Sanity check.
  602. //
  603. PAGED_CODE();
  604. // Since we may have gotten here due to a extraneous CRLF pair, skip
  605. // any of those now. Need to use a temporary variable since
  606. // the original input pointer and length are used below.
  607. pTempRequest = pHttpRequest;
  608. TempLength = HttpRequestLength;
  609. while ( TempLength != 0 &&
  610. ((*pTempRequest == CR) || (*pTempRequest == LF)) )
  611. {
  612. pTempRequest++;
  613. TempLength--;
  614. }
  615. // First find the verb.
  616. pToken = FindWSToken(pTempRequest, TempLength, &TokenLength);
  617. if (pToken == NULL)
  618. {
  619. // Didn't find it, let's get more buffer
  620. //
  621. pRequest->Verb = HttpVerbUnparsed;
  622. *pBytesTaken = 0;
  623. return STATUS_SUCCESS;
  624. }
  625. // Make sure we stopped because of a SP.
  626. if (*(pToken + TokenLength) != SP)
  627. {
  628. // Bad verb section!
  629. //
  630. pRequest->Verb = HttpVerbInvalid;
  631. pRequest->ErrorCode = UlErrorVerb;
  632. pRequest->ParseState = ParseErrorState;
  633. UlTrace(PARSER, (
  634. "ul!LookupVerb(pRequest = %p) ERROR: no space after verb\n",
  635. pRequest
  636. ));
  637. return STATUS_INVALID_DEVICE_REQUEST;
  638. }
  639. // Otherwise, we found one, so update bytes taken and look up up in
  640. // the tables.
  641. *pBytesTaken = DIFF(pToken - pHttpRequest) + TokenLength + 1;
  642. //
  643. // If we ate some leading whitespace, or if the HttpRequestLength is less than
  644. // sizeof(ULONGLONG), we must look through the "fast" verb table again, but do
  645. // it the "slow" way.
  646. //
  647. for (i = 0; i < NUMBER_FAST_VERB_ENTRIES; i++)
  648. {
  649. if ((FastVerbTable[i].RawVerbLength == (TokenLength + 1)) &&
  650. RtlEqualMemory(pToken, FastVerbTable[i].RawVerb.Char, TokenLength))
  651. {
  652. // It matched. Save the translated verb from the
  653. // table, and bail out.
  654. //
  655. pRequest->Verb = FastVerbTable[i].TranslatedVerb;
  656. return STATUS_SUCCESS;
  657. }
  658. }
  659. //
  660. // Now look through the "long" verb table
  661. //
  662. for (i = 0; i < NUMBER_LONG_VERB_ENTRIES; i++)
  663. {
  664. if (LongVerbTable[i].RawVerbLength == TokenLength &&
  665. RtlEqualMemory(pToken, LongVerbTable[i].RawVerb, TokenLength))
  666. {
  667. // Found it.
  668. //
  669. pRequest->Verb = LongVerbTable[i].TranslatedVerb;
  670. return STATUS_SUCCESS;
  671. }
  672. }
  673. //
  674. // If we got here, we searched both tables and didn't find it.
  675. //
  676. //
  677. // It's a raw verb
  678. //
  679. pRequest->Verb = HttpVerbUnknown;
  680. pRequest->pRawVerb = pToken;
  681. pRequest->RawVerbLength = TokenLength;
  682. //
  683. // include room for the terminator
  684. //
  685. pRequest->TotalRequestSize += (TokenLength + 1) * sizeof(CHAR);
  686. ASSERT( !(pRequest->RawVerbLength==3 && RtlEqualMemory(pRequest->pRawVerb,"GET",3)));
  687. return STATUS_SUCCESS;
  688. } // LookupVerb
  689. /*++
  690. Routine Description:
  691. A utility routine to parse an absolute URL in a URL string. When this
  692. is called we already have loaded the entire url into RawUrl.pUrl and
  693. know that it start with "http".
  694. this functions job is to set RawUrl.pHost + RawUrl.pAbsPath.
  695. Arguments:
  696. pRequest - Pointer to the HTTP_REQUEST
  697. Return Value:
  698. NTSTATUS
  699. Author:
  700. Henry Sanders () 1998
  701. Paul McDaniel (paulmcd) 6-Mar-1999
  702. --*/
  703. NTSTATUS
  704. ParseFullUrl(
  705. IN PUL_INTERNAL_REQUEST pRequest
  706. )
  707. {
  708. PUCHAR pURL;
  709. ULONG UrlLength;
  710. PUCHAR pUrlStart;
  711. //
  712. // Sanity check.
  713. //
  714. PAGED_CODE();
  715. pURL = pRequest->RawUrl.pUrl;
  716. UrlLength = pRequest->RawUrl.Length;
  717. //
  718. // When we're called, we know that the start of the URL must point at
  719. // an absolute scheme prefix. Adjust for that now.
  720. //
  721. pUrlStart = pURL + HTTP_PREFIX_SIZE;
  722. UrlLength -= HTTP_PREFIX_SIZE;
  723. //
  724. // Now check the second half of the absolute URL prefix. We use the larger
  725. // of the two possible prefix length here to do the check, because even if
  726. // it's the smaller of the two we'll need the extra bytes after the prefix
  727. // anyway for the host name.
  728. //
  729. if (UrlLength < HTTP_PREFIX2_SIZE)
  730. {
  731. pRequest->ErrorCode = UlErrorUrl;
  732. pRequest->ParseState = ParseErrorState;
  733. UlTrace(PARSER, (
  734. "ul!ParseFullUrl(pRequest = %p) ERROR: no room for URL scheme name\n",
  735. pRequest
  736. ));
  737. return STATUS_INVALID_DEVICE_REQUEST;
  738. }
  739. if ( (*(UNALIGNED64 ULONG *)pUrlStart & HTTP_PREFIX1_MASK) == HTTP_PREFIX1)
  740. {
  741. // Valid absolute URL.
  742. pUrlStart += HTTP_PREFIX1_SIZE;
  743. UrlLength -= HTTP_PREFIX1_SIZE;
  744. }
  745. else
  746. {
  747. if ( (*(UNALIGNED64 ULONG *)pUrlStart & HTTP_PREFIX2_MASK) == HTTP_PREFIX2)
  748. {
  749. // Valid absolute URL.
  750. pUrlStart += HTTP_PREFIX2_SIZE;
  751. UrlLength -= HTTP_PREFIX2_SIZE;
  752. }
  753. else
  754. {
  755. pRequest->ErrorCode = UlErrorUrl;
  756. pRequest->ParseState = ParseErrorState;
  757. UlTrace(PARSER, (
  758. "ul!ParseFullUrl(pRequest = %p) ERROR: invalid URL scheme name\n",
  759. pRequest
  760. ));
  761. return STATUS_INVALID_DEVICE_REQUEST;
  762. }
  763. }
  764. //
  765. // OK, we've got a valid absolute URL, and we've skipped over
  766. // the prefix part of it. Save a pointer to the host, and
  767. // search the host string until we find the trailing slash,
  768. // which signifies the end of the host/start of the absolute
  769. // path.
  770. //
  771. pRequest->RawUrl.pHost = pUrlStart;
  772. //
  773. // scan the host looking for the terminator
  774. //
  775. while (UrlLength > 0 && pUrlStart[0] != '/')
  776. {
  777. pUrlStart++;
  778. UrlLength--;
  779. }
  780. if (UrlLength == 0)
  781. {
  782. //
  783. // Ran out of buffer, can't happen, we get the full url passed in
  784. //
  785. pRequest->ErrorCode = UlErrorUrl;
  786. pRequest->ParseState = ParseErrorState;
  787. UlTrace(PARSER, (
  788. "ul!ParseFullUrl(pRequest = %p) ERROR: end of host name not found\n",
  789. pRequest
  790. ));
  791. return STATUS_INVALID_DEVICE_REQUEST;
  792. }
  793. //
  794. // Otherwise, pUrlStart points to the start of the absolute path portion.
  795. //
  796. pRequest->RawUrl.pAbsPath = pUrlStart;
  797. return STATUS_SUCCESS;
  798. } // ParseFullUrl
  799. /*++
  800. Routine Description:
  801. Look up a header that we don't have in our fast lookup table. This
  802. could be because it's a header we don't understand, or because we
  803. couldn't use the fast lookup table due to insufficient buffer length.
  804. The latter reason is uncommon, but we'll check the input table anyway
  805. if we're given one. If we find a header match in our mapping table,
  806. we'll call the header handler. Otherwise we'll try to allocate an
  807. unknown header element, fill it in and chain it on the http connection.
  808. Arguments:
  809. pHttpConn - Pointer to the current connection on which the
  810. request arrived.
  811. pHttpRequest - Pointer to the current request.
  812. HttpRequestLength - Bytes left in the request.
  813. pHeaderMap - Pointer to start of an array of header map entries
  814. (may be NULL).
  815. HeaderMapCount - Number of entries in array pointed to by pHeaderMap.
  816. Return Value:
  817. Number of bytes in the header (including CRLF), or 0 if we couldn't
  818. parse the header.
  819. --*/
  820. NTSTATUS
  821. UlLookupHeader(
  822. IN PUL_INTERNAL_REQUEST pRequest,
  823. IN PUCHAR pHttpRequest,
  824. IN ULONG HttpRequestLength,
  825. IN PHEADER_MAP_ENTRY pHeaderMap,
  826. IN ULONG HeaderMapCount,
  827. OUT ULONG * pBytesTaken
  828. )
  829. {
  830. NTSTATUS Status = STATUS_SUCCESS;
  831. ULONG CurrentOffset;
  832. ULONG HeaderNameLength;
  833. ULONG i;
  834. ULONG BytesTaken;
  835. ULONG HeaderValueLength;
  836. UCHAR CurrentChar;
  837. PUL_HTTP_UNKNOWN_HEADER pUnknownHeader;
  838. PLIST_ENTRY pListStart;
  839. PLIST_ENTRY pCurrentListEntry;
  840. ULONG OldHeaderLength;
  841. PUCHAR pHeaderValue;
  842. BOOLEAN ExternalAllocated;
  843. //
  844. // Sanity check.
  845. //
  846. PAGED_CODE();
  847. //
  848. // First, let's find the terminating : of the header name, if there is one.
  849. // This will also give us the length of the header, which we can then
  850. // use to search the header map table if we have one.
  851. //
  852. for (CurrentOffset = 0; CurrentOffset < HttpRequestLength; CurrentOffset++)
  853. {
  854. CurrentChar = *(pHttpRequest + CurrentOffset);
  855. if (CurrentChar == ':')
  856. {
  857. // We've found the end of the header.
  858. break;
  859. }
  860. else
  861. {
  862. if (!IS_HTTP_TOKEN(CurrentChar))
  863. {
  864. // Uh-oh, this isn't a valid header. What do we do now?
  865. //
  866. pRequest->ErrorCode = UlErrorHeader;
  867. pRequest->ParseState = ParseErrorState;
  868. UlTrace(PARSER, (
  869. "UlLookupHeader(pRequest = %p) CurrentChar = 0x%x\n"
  870. " ERROR: invalid header char\n",
  871. pRequest,
  872. CurrentChar
  873. ));
  874. Status = STATUS_INVALID_DEVICE_REQUEST;
  875. goto end;
  876. }
  877. }
  878. }
  879. // Find out why we got out. If the current offset is less than the
  880. // header length, we got out because we found the :.
  881. if (CurrentOffset < HttpRequestLength)
  882. {
  883. // Found the terminator.
  884. CurrentOffset++; // Update to point beyond termintor.
  885. HeaderNameLength = CurrentOffset;
  886. }
  887. else
  888. {
  889. // Didn't find the :, need more.
  890. //
  891. *pBytesTaken = 0;
  892. goto end;
  893. }
  894. // See if we have a header map array we need to search.
  895. //
  896. if (pHeaderMap != NULL)
  897. {
  898. // We do have an array to search.
  899. for (i = 0; i < HeaderMapCount; i++)
  900. {
  901. ASSERT(pHeaderMap->pServerHandler != NULL);
  902. if (HeaderNameLength == pHeaderMap->HeaderLength &&
  903. _strnicmp(
  904. (const char *)(pHttpRequest),
  905. (const char *)(pHeaderMap->Header.HeaderChar),
  906. HeaderNameLength
  907. ) == 0 &&
  908. pHeaderMap->pServerHandler != NULL)
  909. {
  910. // This header matches. Call the handling function for it.
  911. Status = (*(pHeaderMap->pServerHandler))(
  912. pRequest,
  913. pHttpRequest + HeaderNameLength,
  914. HttpRequestLength - HeaderNameLength,
  915. pHeaderMap->HeaderID,
  916. &BytesTaken
  917. );
  918. if (NT_SUCCESS(Status) == FALSE)
  919. goto end;
  920. // If the handler consumed a non-zero number of bytes, it
  921. // worked, so return that number plus the header length.
  922. //
  923. // BUGBUG - it might be possible for a header handler to
  924. // encounter an error, for example being unable to
  925. // allocate memory, or a bad syntax in some header. We
  926. // need a more sophisticated method to detect this than
  927. // just checking bytes taken.
  928. //
  929. if (BytesTaken != 0)
  930. {
  931. *pBytesTaken = HeaderNameLength + BytesTaken;
  932. goto end;
  933. }
  934. // Otherwise he didn't take anything, so return 0.
  935. // we need more buffer
  936. //
  937. *pBytesTaken = 0;
  938. goto end;
  939. }
  940. pHeaderMap++;
  941. }
  942. }
  943. // OK, at this point either we had no header map array or none of them
  944. // matched. We have an unknown header. Just make sure this header is
  945. // terminated and save a pointer to it.
  946. // Find the end of the header value
  947. //
  948. Status = FindHeaderEnd(
  949. pHttpRequest + HeaderNameLength,
  950. HttpRequestLength - HeaderNameLength,
  951. &BytesTaken
  952. );
  953. if (NT_SUCCESS(Status) == FALSE)
  954. goto end;
  955. if (BytesTaken == 0)
  956. {
  957. *pBytesTaken = 0;
  958. goto end;
  959. }
  960. //
  961. // Strip of the trailing CRLF from the header value length
  962. //
  963. HeaderValueLength = BytesTaken - CRLF_SIZE;
  964. pHeaderValue = pHttpRequest + HeaderNameLength;
  965. //
  966. // skip any preceding LWS.
  967. //
  968. while ( HeaderValueLength > 0 && IS_HTTP_LWS(*pHeaderValue) )
  969. {
  970. pHeaderValue++;
  971. HeaderValueLength--;
  972. }
  973. // Have an unknown header. Search our list of unknown headers,
  974. // and if we've already seen one instance of this header add this
  975. // on. Otherwise allocate an unknown header structure and set it
  976. // to point at this header.
  977. pListStart = &pRequest->UnknownHeaderList;
  978. for (pCurrentListEntry = pRequest->UnknownHeaderList.Flink;
  979. pCurrentListEntry != pListStart;
  980. pCurrentListEntry = pCurrentListEntry->Flink
  981. )
  982. {
  983. pUnknownHeader = CONTAINING_RECORD(
  984. pCurrentListEntry,
  985. UL_HTTP_UNKNOWN_HEADER,
  986. List
  987. );
  988. //
  989. // somehow HeaderNameLength includes the ':' character,
  990. // which is not the case of pUnknownHeader->HeaderNameLength.
  991. //
  992. // so we need to adjust for this here
  993. //
  994. if ((HeaderNameLength-1) == pUnknownHeader->HeaderNameLength &&
  995. _strnicmp(
  996. (const char *)(pHttpRequest),
  997. (const char *)(pUnknownHeader->pHeaderName),
  998. (HeaderNameLength-1)
  999. ) == 0)
  1000. {
  1001. // This header matches.
  1002. OldHeaderLength = pUnknownHeader->HeaderValue.HeaderLength;
  1003. Status = UlAppendHeaderValue(
  1004. pRequest,
  1005. &pUnknownHeader->HeaderValue,
  1006. pHeaderValue,
  1007. HeaderValueLength
  1008. );
  1009. if (NT_SUCCESS(Status) == FALSE)
  1010. goto end;
  1011. //
  1012. // Successfully appended it. Update the total request
  1013. // length for the length added. no need to add 1 for
  1014. // the terminator, just add our new char count.
  1015. //
  1016. pRequest->TotalRequestSize +=
  1017. (pUnknownHeader->HeaderValue.HeaderLength
  1018. - OldHeaderLength) * sizeof(CHAR);
  1019. //
  1020. // don't subtract for the ':' character, as that character
  1021. // was "taken"
  1022. //
  1023. *pBytesTaken = HeaderNameLength + BytesTaken;
  1024. goto end;
  1025. } // if (headermatch)
  1026. } // for (walk list)
  1027. //
  1028. // Didn't find a match. Allocate a new unknown header structure, set
  1029. // it up and add it to the list.
  1030. //
  1031. if (pRequest->NextUnknownHeaderIndex < DEFAULT_MAX_UNKNOWN_HEADERS)
  1032. {
  1033. ExternalAllocated = FALSE;
  1034. pUnknownHeader = &pRequest->UnknownHeaders[pRequest->NextUnknownHeaderIndex];
  1035. pRequest->NextUnknownHeaderIndex++;
  1036. }
  1037. else
  1038. {
  1039. ExternalAllocated = TRUE;
  1040. pUnknownHeader = UL_ALLOCATE_STRUCT(
  1041. NonPagedPool,
  1042. UL_HTTP_UNKNOWN_HEADER,
  1043. UL_HTTP_UNKNOWN_HEADER_POOL_TAG
  1044. );
  1045. //
  1046. // Assume the memory allocation will succeed so just blindly set the
  1047. // flag below to TRUE which forces us to take a slow path in
  1048. // UlpFreeHttpRequest.
  1049. //
  1050. pRequest->HeadersAppended = TRUE;
  1051. }
  1052. if (pUnknownHeader == NULL)
  1053. {
  1054. Status = STATUS_NO_MEMORY;
  1055. goto end;
  1056. }
  1057. //
  1058. // subtract the : from the header name length
  1059. //
  1060. pUnknownHeader->HeaderNameLength = HeaderNameLength - 1;
  1061. pUnknownHeader->pHeaderName = pHttpRequest;
  1062. //
  1063. // header value
  1064. //
  1065. pUnknownHeader->HeaderValue.HeaderLength = HeaderValueLength;
  1066. pUnknownHeader->HeaderValue.pHeader = pHeaderValue;
  1067. //
  1068. // null terminate our copy, the terminating CRLF gives
  1069. // us space for this
  1070. //
  1071. pHeaderValue[HeaderValueLength] = ANSI_NULL;
  1072. //
  1073. // flags
  1074. //
  1075. pUnknownHeader->HeaderValue.OurBuffer = 0;
  1076. pUnknownHeader->HeaderValue.ExternalAllocated = ExternalAllocated;
  1077. InsertTailList(&pRequest->UnknownHeaderList, &pUnknownHeader->List);
  1078. pRequest->UnknownHeaderCount++;
  1079. //
  1080. // subtract 1 for the ':' and add space for the 2 terminiators
  1081. //
  1082. pRequest->TotalRequestSize +=
  1083. ((HeaderNameLength - 1 + 1) + HeaderValueLength + 1) * sizeof(CHAR);
  1084. *pBytesTaken = HeaderNameLength + BytesTaken;
  1085. end:
  1086. return Status;
  1087. } // UlLookupHeader
  1088. /*++
  1089. Routine Description:
  1090. The routine to parse an individual header based on a hint. We take in a pointer to the
  1091. header and the bytes remaining in the request, and try to find
  1092. the header based on the hint passed.
  1093. On input, HttpRequestLength is at least CRLF_SIZE.
  1094. Arguments:
  1095. pRequest - Pointer to the current connection on which the
  1096. request arrived.
  1097. pHttpRequest - Pointer to the current request.
  1098. HttpRequestLength - Bytes left in the request.
  1099. pHeaderHintMap - Hint to the Map that may match the current request
  1100. Return Value:
  1101. Number of bytes in the header (including CRLF), or 0 if we couldn't
  1102. parse the header.
  1103. --*/
  1104. __inline
  1105. NTSTATUS
  1106. UlParseHeaderWithHint(
  1107. IN PUL_INTERNAL_REQUEST pRequest,
  1108. IN PUCHAR pHttpRequest,
  1109. IN ULONG HttpRequestLength,
  1110. IN PHEADER_MAP_ENTRY pHeaderHintMap,
  1111. OUT ULONG * pBytesTaken
  1112. )
  1113. {
  1114. NTSTATUS Status = STATUS_SUCCESS;
  1115. ULONG BytesTaken;
  1116. ULONGLONG Temp;
  1117. ULONG i;
  1118. ULONG j;
  1119. ASSERT(pHeaderHintMap != NULL);
  1120. if (HttpRequestLength >= pHeaderHintMap->MinBytesNeeded)
  1121. {
  1122. for (j = 0; j < pHeaderHintMap->ArrayCount; j++)
  1123. {
  1124. Temp = *(UNALIGNED64 ULONGLONG *)(pHttpRequest +
  1125. (j * sizeof(ULONGLONG)));
  1126. if ((Temp & pHeaderHintMap->HeaderMask[j]) !=
  1127. pHeaderHintMap->Header.HeaderLong[j] )
  1128. {
  1129. break;
  1130. }
  1131. }
  1132. // See why we exited out.
  1133. if (j == pHeaderHintMap->ArrayCount &&
  1134. pHeaderHintMap->pServerHandler != NULL)
  1135. {
  1136. // Exited because we found a match. Call the
  1137. // handler for this header to take cake of this.
  1138. Status = (*(pHeaderHintMap->pServerHandler))(
  1139. pRequest,
  1140. pHttpRequest +
  1141. pHeaderHintMap->HeaderLength,
  1142. HttpRequestLength -
  1143. pHeaderHintMap->HeaderLength,
  1144. pHeaderHintMap->HeaderID,
  1145. &BytesTaken
  1146. );
  1147. if (NT_SUCCESS(Status) == FALSE)
  1148. goto end;
  1149. // If the handler consumed a non-zero number of
  1150. // bytes, it worked, so return that number plus
  1151. // the header length.
  1152. if (BytesTaken != 0)
  1153. {
  1154. *pBytesTaken = pHeaderHintMap->HeaderLength +
  1155. BytesTaken;
  1156. goto end;
  1157. }
  1158. // Otherwise need more buffer
  1159. *pBytesTaken = 0;
  1160. }
  1161. else
  1162. {
  1163. // No match
  1164. *pBytesTaken = -1;
  1165. }
  1166. }
  1167. else
  1168. {
  1169. // No match
  1170. *pBytesTaken = -1;
  1171. }
  1172. end:
  1173. return Status;
  1174. } // UlParseHeaderWithHint
  1175. /*++
  1176. Routine Description:
  1177. The routine to parse an individual header. We take in a pointer to the
  1178. header and the bytes remaining in the request, and try to find
  1179. the header in our lookup table. We try first the fast way, and then
  1180. try again the slow way in case there wasn't quite enough data the first
  1181. time.
  1182. On input, HttpRequestLength is at least CRLF_SIZE.
  1183. Arguments:
  1184. pRequest - Pointer to the current connection on which the
  1185. request arrived.
  1186. pHttpRequest - Pointer to the current request.
  1187. HttpRequestLength - Bytes left in the request.
  1188. Return Value:
  1189. Number of bytes in the header (including CRLF), or 0 if we couldn't
  1190. parse the header.
  1191. --*/
  1192. NTSTATUS
  1193. UlParseHeader(
  1194. IN PUL_INTERNAL_REQUEST pRequest,
  1195. IN PUCHAR pHttpRequest,
  1196. IN ULONG HttpRequestLength,
  1197. OUT ULONG * pBytesTaken
  1198. )
  1199. {
  1200. NTSTATUS Status = STATUS_SUCCESS;
  1201. ULONG i;
  1202. ULONG j;
  1203. ULONG BytesTaken;
  1204. ULONGLONG Temp;
  1205. UCHAR c;
  1206. PHEADER_MAP_ENTRY pCurrentHeaderMap;
  1207. ULONG HeaderMapCount;
  1208. PUL_HTTP_HEADER pFoundHeader;
  1209. BOOLEAN SmallHeader = FALSE;
  1210. //
  1211. // Sanity check.
  1212. //
  1213. PAGED_CODE();
  1214. ASSERT(HttpRequestLength >= CRLF_SIZE);
  1215. c = *pHttpRequest;
  1216. // message-headers start with field-name [= token]
  1217. //
  1218. if (IS_HTTP_TOKEN(c) == FALSE)
  1219. {
  1220. pRequest->ErrorCode = UlErrorHeader;
  1221. pRequest->ParseState = ParseErrorState;
  1222. UlTrace(PARSER, (
  1223. "UlParseHeader (pRequest = %p) c = 0x%02x ERROR: invalid header char \n",
  1224. pRequest,
  1225. c
  1226. ));
  1227. return STATUS_INVALID_DEVICE_REQUEST;
  1228. }
  1229. // Does the header start with an alpha?
  1230. //
  1231. if (IS_HTTP_ALPHA(c))
  1232. {
  1233. // Uppercase the character, and find the appropriate set of header map
  1234. // entries.
  1235. //
  1236. c = UPCASE_CHAR(c);
  1237. c -= 'A';
  1238. pCurrentHeaderMap = g_RequestHeaderIndexTable[c].pHeaderMap;
  1239. HeaderMapCount = g_RequestHeaderIndexTable[c].Count;
  1240. // Loop through all the header map entries that might match
  1241. // this header, and check them. The count will be 0 if there
  1242. // are no entries that might match and we'll skip the loop.
  1243. for (i = 0; i < HeaderMapCount; i++)
  1244. {
  1245. ASSERT(pCurrentHeaderMap->pServerHandler != NULL);
  1246. // If we have enough bytes to do the fast check, do it.
  1247. // Otherwise skip this. We may skip a valid match, but if
  1248. // so we'll catch it later.
  1249. if (HttpRequestLength >= pCurrentHeaderMap->MinBytesNeeded)
  1250. {
  1251. for (j = 0; j < pCurrentHeaderMap->ArrayCount; j++)
  1252. {
  1253. Temp = *(UNALIGNED64 ULONGLONG *)(pHttpRequest +
  1254. (j * sizeof(ULONGLONG)));
  1255. if ((Temp & pCurrentHeaderMap->HeaderMask[j]) !=
  1256. pCurrentHeaderMap->Header.HeaderLong[j] )
  1257. {
  1258. break;
  1259. }
  1260. }
  1261. // See why we exited out.
  1262. if (j == pCurrentHeaderMap->ArrayCount &&
  1263. pCurrentHeaderMap->pServerHandler != NULL)
  1264. {
  1265. // Exited because we found a match. Call the
  1266. // handler for this header to take cake of this.
  1267. Status = (*(pCurrentHeaderMap->pServerHandler))(
  1268. pRequest,
  1269. pHttpRequest +
  1270. pCurrentHeaderMap->HeaderLength,
  1271. HttpRequestLength -
  1272. pCurrentHeaderMap->HeaderLength,
  1273. pCurrentHeaderMap->HeaderID,
  1274. &BytesTaken
  1275. );
  1276. if (NT_SUCCESS(Status) == FALSE)
  1277. goto end;
  1278. // If the handler consumed a non-zero number of
  1279. // bytes, it worked, so return that number plus
  1280. // the header length.
  1281. if (BytesTaken != 0)
  1282. {
  1283. *pBytesTaken = pCurrentHeaderMap->HeaderLength +
  1284. BytesTaken;
  1285. goto end;
  1286. }
  1287. // Otherwise need more buffer
  1288. //
  1289. *pBytesTaken = 0;
  1290. goto end;
  1291. }
  1292. // If we get here, we exited out early because a match
  1293. // failed, so keep going.
  1294. }
  1295. else if (SmallHeader == FALSE)
  1296. {
  1297. //
  1298. // Remember that we didn't check a header map entry
  1299. // because the bytes in the buffer was not LONGLONG
  1300. // aligned
  1301. //
  1302. SmallHeader = TRUE;
  1303. }
  1304. // Either didn't match or didn't have enough bytes for the
  1305. // check. In either case, check the next header map entry.
  1306. pCurrentHeaderMap++;
  1307. }
  1308. // Got all the way through the appropriate header map entries
  1309. // without a match. This could be because we're dealing with a
  1310. // header we don't know about or because it's a header we
  1311. // care about that was too small to do the fast check. The
  1312. // latter case should be very rare, but we still need to
  1313. // handle it.
  1314. // Update the current header map pointer to point back to the
  1315. // first of the possibles. If there were no possibles,
  1316. // the pointer will be NULL and the HeaderMapCount 0, so it'll
  1317. // stay NULL. Otherwise the subtraction will back it up the
  1318. // appropriate amount.
  1319. if (SmallHeader)
  1320. {
  1321. pCurrentHeaderMap -= HeaderMapCount;
  1322. }
  1323. else
  1324. {
  1325. pCurrentHeaderMap = NULL;
  1326. HeaderMapCount = 0;
  1327. }
  1328. }
  1329. else
  1330. {
  1331. pCurrentHeaderMap = NULL;
  1332. HeaderMapCount = 0;
  1333. }
  1334. // At this point either the header starts with a non-alphabetic
  1335. // character or we don't have a set of header map entries for it.
  1336. Status = UlLookupHeader(
  1337. pRequest,
  1338. pHttpRequest,
  1339. HttpRequestLength,
  1340. pCurrentHeaderMap,
  1341. HeaderMapCount,
  1342. &BytesTaken
  1343. );
  1344. if (NT_SUCCESS(Status) == FALSE)
  1345. goto end;
  1346. // Lookup header returns the total bytes taken, including the header name
  1347. //
  1348. *pBytesTaken = BytesTaken;
  1349. end:
  1350. return Status;
  1351. } // UlParseHeader
  1352. NTSTATUS
  1353. UlParseHeaders(
  1354. PUL_INTERNAL_REQUEST pRequest,
  1355. PUCHAR pBuffer,
  1356. ULONG BufferLength,
  1357. PULONG pBytesTaken
  1358. )
  1359. {
  1360. NTSTATUS Status;
  1361. ULONG BytesTaken;
  1362. LONG HintIndex;
  1363. PHEADER_MAP_ENTRY pHeaderHintMap;
  1364. *pBytesTaken = 0;
  1365. HintIndex = 0;
  1366. //
  1367. // loop over all headers
  1368. //
  1369. while (BufferLength >= CRLF_SIZE)
  1370. {
  1371. //
  1372. // If this is an empty header, we're done with this stage
  1373. //
  1374. if (*(UNALIGNED64 USHORT *)pBuffer == CRLF ||
  1375. *(UNALIGNED64 USHORT *)pBuffer == LFLF)
  1376. {
  1377. //
  1378. // consume it
  1379. //
  1380. pBuffer += CRLF_SIZE;
  1381. *pBytesTaken += CRLF_SIZE;
  1382. BufferLength -= CRLF_SIZE;
  1383. Status = STATUS_SUCCESS;
  1384. goto end;
  1385. }
  1386. // Otherwise call our header parse routine to deal with this.
  1387. //
  1388. // Try to find a header hint based on the first char and certain order
  1389. //
  1390. pHeaderHintMap = NULL;
  1391. while (1)
  1392. {
  1393. if (
  1394. (HintIndex >= 0) &&
  1395. (HintIndex < NUMBER_HEADER_HINT_INDICIES) &&
  1396. (g_RequestHeaderHintIndexTable[HintIndex].c == UPCASE_CHAR(*pBuffer))
  1397. )
  1398. {
  1399. pHeaderHintMap = g_RequestHeaderHintIndexTable[HintIndex].pHeaderMap;
  1400. break;
  1401. }
  1402. if (HintIndex >= NUMBER_HEADER_HINT_INDICIES)
  1403. {
  1404. break;
  1405. }
  1406. ++HintIndex;
  1407. }
  1408. if (NULL != pHeaderHintMap)
  1409. {
  1410. Status = UlParseHeaderWithHint(
  1411. pRequest,
  1412. pBuffer,
  1413. BufferLength,
  1414. pHeaderHintMap,
  1415. &BytesTaken
  1416. );
  1417. if (-1 == BytesTaken)
  1418. {
  1419. // hint failed
  1420. Status = UlParseHeader(
  1421. pRequest,
  1422. pBuffer,
  1423. BufferLength,
  1424. &BytesTaken
  1425. );
  1426. }
  1427. }
  1428. else
  1429. {
  1430. Status = UlParseHeader(
  1431. pRequest,
  1432. pBuffer,
  1433. BufferLength,
  1434. &BytesTaken
  1435. );
  1436. }
  1437. if (NT_SUCCESS(Status) == FALSE)
  1438. goto end;
  1439. //
  1440. // Check if the parsed headers are longer than maximum allowed.
  1441. //
  1442. if ( (*pBytesTaken+BytesTaken) > g_UlMaxFieldLength )
  1443. {
  1444. pRequest->ErrorCode = UlErrorFieldLength;
  1445. pRequest->ParseState = ParseErrorState;
  1446. UlTrace(PARSER, (
  1447. "UlParseHeaders(pRequest = %p) ERROR: Header field is too big\n",
  1448. pRequest
  1449. ));
  1450. Status = STATUS_SECTION_TOO_BIG;
  1451. goto end;
  1452. }
  1453. //
  1454. // If no bytes were consumed, the header must be incomplete, so
  1455. // bail out until we get more data on this connection.
  1456. //
  1457. if (BytesTaken == 0)
  1458. {
  1459. Status = STATUS_MORE_PROCESSING_REQUIRED;
  1460. goto end;
  1461. }
  1462. //
  1463. // Otherwise we parsed a header, so update and continue.
  1464. //
  1465. pBuffer += BytesTaken;
  1466. *pBytesTaken += BytesTaken;
  1467. BufferLength -= BytesTaken;
  1468. ++HintIndex;
  1469. }
  1470. //
  1471. // we only get here if we didn't see the CRLF headers terminator
  1472. //
  1473. // we need more data
  1474. //
  1475. Status = STATUS_MORE_PROCESSING_REQUIRED;
  1476. end:
  1477. //
  1478. // Terminate the header index table when we are done with parsing headers.
  1479. //
  1480. pRequest->HeaderIndex[pRequest->HeaderCount] = HttpHeaderRequestMaximum;
  1481. return Status;
  1482. } // UlParseHeaders
  1483. NTSTATUS
  1484. UlParseChunkLength(
  1485. IN PUL_INTERNAL_REQUEST pRequest,
  1486. IN PUCHAR pBuffer,
  1487. IN ULONG BufferLength,
  1488. OUT PULONG pBytesTaken,
  1489. OUT PULONGLONG pChunkLength
  1490. )
  1491. {
  1492. NTSTATUS Status;
  1493. PUCHAR pToken;
  1494. UCHAR SaveChar;
  1495. ULONG TokenLength;
  1496. ULONG BytesTaken;
  1497. ULONG TotalBytesTaken = 0;
  1498. ASSERT(pBytesTaken != NULL);
  1499. ASSERT(pChunkLength != NULL);
  1500. //
  1501. // 2 cases:
  1502. //
  1503. // 1) the first chunk where the length follows the headers
  1504. // 2) subsequent chunks where the length follows a previous chunk
  1505. //
  1506. // in case 1 pBuffer will point straight to the chunk length.
  1507. //
  1508. // in case 2 pBuffer will point to the CRLF that terminated the previous
  1509. // chunk, this needs to be consumed, skipped, and then the chunk length
  1510. // read.
  1511. //
  1512. // BUGBUG: need to handle chunk-extensions embedded in the length field
  1513. //
  1514. //
  1515. // if we are case 2 (see above)
  1516. //
  1517. if (pRequest->ParsedFirstChunk == 1)
  1518. {
  1519. //
  1520. // make sure there is enough space first
  1521. //
  1522. if (BufferLength < CRLF_SIZE)
  1523. {
  1524. Status = STATUS_MORE_PROCESSING_REQUIRED;
  1525. goto end;
  1526. }
  1527. //
  1528. // now it better be a terminator
  1529. //
  1530. if (*(UNALIGNED64 USHORT *)pBuffer != CRLF &&
  1531. *(UNALIGNED64 USHORT *)pBuffer != LFLF)
  1532. {
  1533. UlTrace(PARSER, (
  1534. "http!UlParseChunkLength(pRequest = %p) "
  1535. "ERROR: No CRLF at the end of chunk-data\n",
  1536. pRequest
  1537. ));
  1538. Status = STATUS_INVALID_DEVICE_REQUEST;
  1539. goto end;
  1540. }
  1541. //
  1542. // update our book-keeping
  1543. //
  1544. pBuffer += CRLF_SIZE;
  1545. TotalBytesTaken += CRLF_SIZE;
  1546. BufferLength -= CRLF_SIZE;
  1547. }
  1548. pToken = FindWSToken(pBuffer, BufferLength, &TokenLength);
  1549. if (pToken == NULL)
  1550. {
  1551. //
  1552. // not enough buffer
  1553. //
  1554. Status = STATUS_MORE_PROCESSING_REQUIRED;
  1555. goto end;
  1556. }
  1557. //
  1558. // Was there any token ?
  1559. //
  1560. if (TokenLength == 0)
  1561. {
  1562. UlTrace(PARSER, (
  1563. "http!UlParseChunkLength(pRequest = %p) ERROR: No length!\n",
  1564. pRequest
  1565. ));
  1566. Status = STATUS_INVALID_DEVICE_REQUEST;
  1567. goto end;
  1568. }
  1569. //
  1570. // Add the bytes consumed by FindWSToken
  1571. // (the token bytes plus preceding bytes)
  1572. //
  1573. TotalBytesTaken += DIFF((pToken + TokenLength) - pBuffer);
  1574. //
  1575. // and find the end
  1576. //
  1577. Status = FindChunkHeaderEnd(
  1578. pToken + TokenLength,
  1579. BufferLength - DIFF((pToken + TokenLength) - pBuffer),
  1580. &BytesTaken
  1581. );
  1582. if (NT_SUCCESS(Status) == FALSE)
  1583. {
  1584. pRequest->ErrorCode = UlErrorCRLF;
  1585. pRequest->ParseState = ParseErrorState;
  1586. goto end;
  1587. }
  1588. if (BytesTaken == 0)
  1589. {
  1590. Status = STATUS_MORE_PROCESSING_REQUIRED;
  1591. goto end;
  1592. }
  1593. TotalBytesTaken += BytesTaken;
  1594. //
  1595. // now update the HTTP_REQUEST
  1596. //
  1597. SaveChar = pToken[TokenLength];
  1598. pToken[TokenLength] = ANSI_NULL;
  1599. Status = UlAnsiToULongLong(
  1600. pToken,
  1601. 16, // Base
  1602. pChunkLength
  1603. );
  1604. pToken[TokenLength] = SaveChar;
  1605. UlTrace(PARSER, (
  1606. "http!UlParseChunkLength(pRequest = %p) %sfirst chunk=0x%I64x bytes, "
  1607. "consumed %d bytes\n",
  1608. pRequest,
  1609. (pRequest->ParsedFirstChunk == 1) ? "non-" : "",
  1610. *pChunkLength,
  1611. TotalBytesTaken
  1612. ));
  1613. //
  1614. // Did the number conversion fail ?
  1615. //
  1616. if (NT_SUCCESS(Status) == FALSE)
  1617. {
  1618. if (Status == STATUS_SECTION_TOO_BIG)
  1619. {
  1620. pRequest->ErrorCode = UlErrorEntityTooLarge;
  1621. }
  1622. else
  1623. {
  1624. pRequest->ErrorCode = UlErrorNum;
  1625. }
  1626. pRequest->ParseState = ParseErrorState;
  1627. UlTrace(PARSER, (
  1628. "http!UlParseChunkLength(pRequest = %p) "
  1629. "ERROR: didn't grok chunk length\n",
  1630. pRequest
  1631. ));
  1632. goto end;
  1633. }
  1634. //
  1635. // all done, return the bytes consumed
  1636. //
  1637. *pBytesTaken = TotalBytesTaken;
  1638. end:
  1639. RETURN(Status);
  1640. } // UlParseChunkLength
  1641. /*++
  1642. Routine Description:
  1643. This is the core HTTP protocol request engine. It takes a stream of bytes
  1644. and parses them as an HTTP request.
  1645. Arguments:
  1646. pHttpRequest - Pointer to the incoming HTTP request.
  1647. HttpRequestLength - Length of data pointed to by HttpRequest.
  1648. Return Value:
  1649. Status of parse attempt.
  1650. --*/
  1651. NTSTATUS
  1652. UlParseHttp(
  1653. IN PUL_INTERNAL_REQUEST pRequest,
  1654. IN PUCHAR pHttpRequest,
  1655. IN ULONG HttpRequestLength,
  1656. OUT ULONG * pBytesTaken
  1657. )
  1658. {
  1659. ULONG OriginalBufferLength;
  1660. ULONG TokenLength;
  1661. ULONG CurrentBytesTaken;
  1662. ULONG TotalBytesTaken;
  1663. ULONG i;
  1664. NTSTATUS ReturnStatus;
  1665. PUCHAR pStart;
  1666. //
  1667. // Sanity check.
  1668. //
  1669. PAGED_CODE();
  1670. ASSERT( UL_IS_VALID_INTERNAL_REQUEST( pRequest ) );
  1671. ReturnStatus = STATUS_SUCCESS;
  1672. TotalBytesTaken = 0;
  1673. //
  1674. // remember the original buffer length
  1675. //
  1676. OriginalBufferLength = HttpRequestLength;
  1677. //
  1678. // put this label here to allow for a manual re-pump of the
  1679. // parser. this is currently used for 0.9 requests.
  1680. //
  1681. parse_it:
  1682. //
  1683. // what state are we in ?
  1684. //
  1685. switch (pRequest->ParseState)
  1686. {
  1687. case ParseVerbState:
  1688. // Look through the fast verb table for the verb. We can only do
  1689. // this if the input data is big enough.
  1690. if (HttpRequestLength >= sizeof(ULONGLONG))
  1691. {
  1692. ULONGLONG RawInputVerb;
  1693. RawInputVerb = *(UNALIGNED64 ULONGLONG *)pHttpRequest;
  1694. // Loop through the fast verb table, looking for the verb.
  1695. for (i = 0; i < NUMBER_FAST_VERB_ENTRIES;i++)
  1696. {
  1697. // Mask out the raw input verb and compare against this
  1698. // entry.
  1699. if ((RawInputVerb & FastVerbTable[i].RawVerbMask) ==
  1700. FastVerbTable[i].RawVerb.LongLong)
  1701. {
  1702. // It matched. Save the translated verb from the
  1703. // table, update the request pointer and length,
  1704. // switch states and get out.
  1705. pRequest->Verb = FastVerbTable[i].TranslatedVerb;
  1706. CurrentBytesTaken = FastVerbTable[i].RawVerbLength;
  1707. pRequest->ParseState = ParseUrlState;
  1708. break;
  1709. }
  1710. }
  1711. }
  1712. if (pRequest->ParseState != ParseUrlState)
  1713. {
  1714. // Didn't switch states yet, because we haven't found the
  1715. // verb yet. This could be because a) the incoming request
  1716. // was too small to allow us to use our fast lookup (which
  1717. // might be OK in an HTTP/0.9 request), or b) the incoming
  1718. // verb is a PROPFIND or such that is too big to fit into
  1719. // our fast find table, or c) this is an unknown verb, or
  1720. // d) there was leading white-space before the verb.
  1721. // In any of these cases call our slower verb parser to try
  1722. // again.
  1723. ReturnStatus = LookupVerb(
  1724. pRequest,
  1725. pHttpRequest,
  1726. HttpRequestLength,
  1727. &CurrentBytesTaken
  1728. );
  1729. if (NT_SUCCESS(ReturnStatus) == FALSE)
  1730. goto end;
  1731. if (CurrentBytesTaken == 0)
  1732. {
  1733. ReturnStatus = STATUS_MORE_PROCESSING_REQUIRED;
  1734. goto end;
  1735. }
  1736. //
  1737. // we finished parsing the custom verb
  1738. //
  1739. pRequest->ParseState = ParseUrlState;
  1740. }
  1741. //
  1742. // now fall through to ParseUrlState
  1743. //
  1744. pHttpRequest += CurrentBytesTaken;
  1745. HttpRequestLength -= CurrentBytesTaken;
  1746. TotalBytesTaken += CurrentBytesTaken;
  1747. case ParseUrlState:
  1748. //
  1749. // We're parsing the URL. pHTTPRequest points to the incoming URL,
  1750. // HttpRequestLength is the length of this request that is left.
  1751. //
  1752. //
  1753. // Find the WS terminating the URL.
  1754. //
  1755. pRequest->RawUrl.pUrl = FindWSToken(
  1756. pHttpRequest,
  1757. HttpRequestLength,
  1758. &TokenLength
  1759. );
  1760. if (pRequest->RawUrl.pUrl == NULL)
  1761. {
  1762. ReturnStatus = STATUS_MORE_PROCESSING_REQUIRED;
  1763. goto end;
  1764. }
  1765. if (TokenLength > g_UlMaxFieldLength)
  1766. {
  1767. //
  1768. // The URL is longer than maximum allowed size
  1769. //
  1770. pRequest->ErrorCode = UlErrorUrlLength;
  1771. pRequest->ParseState = ParseErrorState;
  1772. UlTrace(PARSER, (
  1773. "UlParseHttp(pRequest = %p) ERROR: URL is too big\n",
  1774. pRequest
  1775. ));
  1776. ReturnStatus = STATUS_SECTION_TOO_BIG;
  1777. goto end;
  1778. }
  1779. //
  1780. // Bytes taken includes WS in front of URL
  1781. //
  1782. CurrentBytesTaken = DIFF(pRequest->RawUrl.pUrl - pHttpRequest) + TokenLength;
  1783. //
  1784. // set url length
  1785. //
  1786. pRequest->RawUrl.Length = TokenLength;
  1787. //
  1788. // Now, let's see if this is an absolute URL.
  1789. //
  1790. // BUGBUG: this is not case-insens.
  1791. if (pRequest->RawUrl.Length >= HTTP_PREFIX_SIZE &&
  1792. (*(UNALIGNED64 ULONG *)pRequest->RawUrl.pUrl & HTTP_PREFIX_MASK) ==
  1793. HTTP_PREFIX)
  1794. {
  1795. //
  1796. // It is. let's parse it and find the host.
  1797. //
  1798. ReturnStatus = ParseFullUrl(pRequest);
  1799. if (NT_SUCCESS(ReturnStatus) == FALSE)
  1800. goto end;
  1801. }
  1802. else
  1803. {
  1804. pRequest->RawUrl.pHost = NULL;
  1805. pRequest->RawUrl.pAbsPath = pRequest->RawUrl.pUrl;
  1806. }
  1807. //
  1808. // count the space it needs in the user's buffer, including terminator.
  1809. //
  1810. pRequest->TotalRequestSize +=
  1811. (pRequest->RawUrl.Length + 1) * sizeof(CHAR);
  1812. //
  1813. // adjust our book keeping vars
  1814. //
  1815. pHttpRequest += CurrentBytesTaken;
  1816. HttpRequestLength -= CurrentBytesTaken;
  1817. TotalBytesTaken += CurrentBytesTaken;
  1818. //
  1819. // fall through to parsing the version.
  1820. //
  1821. pRequest->ParseState = ParseVersionState;
  1822. case ParseVersionState:
  1823. //
  1824. // skip lws
  1825. //
  1826. pStart = pHttpRequest;
  1827. while (HttpRequestLength > 0 && IS_HTTP_LWS(*pHttpRequest))
  1828. {
  1829. pHttpRequest++;
  1830. HttpRequestLength--;
  1831. }
  1832. //
  1833. // is this a 0.9 request (no version) ?
  1834. //
  1835. if (HttpRequestLength >= CRLF_SIZE)
  1836. {
  1837. if (*(UNALIGNED64 USHORT *)(pHttpRequest) == CRLF ||
  1838. *(UNALIGNED64 USHORT *)(pHttpRequest) == LFLF)
  1839. {
  1840. // This IS a 0.9 request. No need to go any further,
  1841. // since by definition there are no more headers.
  1842. // Just update things and get out.
  1843. TotalBytesTaken += DIFF(pHttpRequest - pStart) + CRLF_SIZE;
  1844. HTTP_SET_VERSION(pRequest->Version, 0, 9);
  1845. //
  1846. // set the state to CookState so that we parse the url
  1847. //
  1848. pRequest->ParseState = ParseCookState;
  1849. //
  1850. // manually restart the parse switch, we changed the
  1851. // parse state
  1852. //
  1853. goto parse_it;
  1854. }
  1855. }
  1856. //
  1857. // do we have enough buffer to strcmp the version?
  1858. //
  1859. if (HttpRequestLength < MIN_VERSION_SIZE)
  1860. {
  1861. ReturnStatus = STATUS_MORE_PROCESSING_REQUIRED;
  1862. goto end;
  1863. }
  1864. //
  1865. // let's compare it
  1866. //
  1867. if (*(UNALIGNED64 ULONGLONG *)pHttpRequest == HTTP_11_VERSION)
  1868. {
  1869. HTTP_SET_VERSION(pRequest->Version, 1, 1);
  1870. HttpRequestLength -= MIN_VERSION_SIZE;
  1871. pHttpRequest += MIN_VERSION_SIZE;
  1872. }
  1873. else
  1874. {
  1875. ULONG VersionBytes;
  1876. if (*(UNALIGNED64 ULONGLONG *)pHttpRequest == HTTP_10_VERSION)
  1877. {
  1878. HTTP_SET_VERSION(pRequest->Version, 1, 0);
  1879. HttpRequestLength -= MIN_VERSION_SIZE;
  1880. pHttpRequest += MIN_VERSION_SIZE;
  1881. }
  1882. else if ( 0 != (VersionBytes = UlpParseHttpVersion(
  1883. pHttpRequest,
  1884. HttpRequestLength,
  1885. &pRequest->Version )) )
  1886. {
  1887. pHttpRequest += VersionBytes;
  1888. HttpRequestLength -= VersionBytes;
  1889. }
  1890. else
  1891. {
  1892. // Bad version.
  1893. pRequest->ErrorCode = UlErrorVersion;
  1894. pRequest->ParseState = ParseErrorState;
  1895. UlTrace(PARSER, (
  1896. "UlParseHttp(pRequest = %p) ERROR: unknown HTTP version\n",
  1897. pRequest
  1898. ));
  1899. ReturnStatus = STATUS_INVALID_DEVICE_REQUEST;
  1900. goto end;
  1901. }
  1902. }
  1903. //
  1904. // skip lws
  1905. //
  1906. while (HttpRequestLength > 0 && IS_HTTP_LWS(*pHttpRequest))
  1907. {
  1908. pHttpRequest++;
  1909. HttpRequestLength--;
  1910. }
  1911. //
  1912. // Make sure we're terminated on this line.
  1913. //
  1914. if (HttpRequestLength < CRLF_SIZE)
  1915. {
  1916. ReturnStatus = STATUS_MORE_PROCESSING_REQUIRED;
  1917. goto end;
  1918. }
  1919. if (*(UNALIGNED64 USHORT *)pHttpRequest != CRLF && *(UNALIGNED64 USHORT *)pHttpRequest != LFLF)
  1920. {
  1921. // Bad line termination after successfully grabbing version.
  1922. pRequest->ErrorCode = UlError;
  1923. pRequest->ParseState = ParseErrorState;
  1924. UlTrace(PARSER, (
  1925. "UlParseHttp(pRequest = %p) ERROR: HTTP version not terminated correctly\n",
  1926. pRequest
  1927. ));
  1928. ReturnStatus = STATUS_INVALID_DEVICE_REQUEST;
  1929. goto end;
  1930. }
  1931. pHttpRequest += CRLF_SIZE;
  1932. HttpRequestLength -= CRLF_SIZE;
  1933. TotalBytesTaken += DIFF(pHttpRequest - pStart);
  1934. //
  1935. // Fall through to parsing the headers
  1936. //
  1937. pRequest->ParseState = ParseHeadersState;
  1938. case ParseHeadersState:
  1939. ReturnStatus = UlParseHeaders(
  1940. pRequest,
  1941. pHttpRequest,
  1942. HttpRequestLength,
  1943. &CurrentBytesTaken
  1944. );
  1945. pHttpRequest += CurrentBytesTaken;
  1946. HttpRequestLength -= CurrentBytesTaken;
  1947. TotalBytesTaken += CurrentBytesTaken;
  1948. if (NT_SUCCESS(ReturnStatus) == FALSE)
  1949. goto end;
  1950. //
  1951. // fall through, this is the only way to get here, we never return
  1952. // pending in this state
  1953. //
  1954. pRequest->ParseState = ParseCookState;
  1955. case ParseCookState:
  1956. //
  1957. // time for post processing. cook it up!
  1958. //
  1959. {
  1960. //
  1961. // First cook up the url, unicode it + such.
  1962. //
  1963. ReturnStatus = UlpCookUrl(pRequest);
  1964. if (NT_SUCCESS(ReturnStatus) == FALSE)
  1965. goto end;
  1966. //
  1967. // mark if we are chunk encoded (only possible for 1.1)
  1968. //
  1969. if ((HTTP_GREATER_EQUAL_VERSION(pRequest->Version, 1, 1)) &&
  1970. (pRequest->HeaderValid[HttpHeaderTransferEncoding]))
  1971. {
  1972. ASSERT(pRequest->Headers[HttpHeaderTransferEncoding].pHeader != NULL);
  1973. //
  1974. // CODEWORK, there can be more than 1 encoding
  1975. //
  1976. if (_stricmp(
  1977. (const char *)(
  1978. pRequest->Headers[HttpHeaderTransferEncoding].pHeader
  1979. ),
  1980. "chunked"
  1981. ) == 0)
  1982. {
  1983. pRequest->Chunked = 1;
  1984. }
  1985. else
  1986. {
  1987. //
  1988. // CODEWORK: temp hack for bug#352
  1989. //
  1990. UlTrace(PARSER, (
  1991. "UlParseHttp(pRequest = %p)"
  1992. " ERROR: unknown Transfer-Encoding!\n",
  1993. pRequest
  1994. ));
  1995. pRequest->ErrorCode = UlErrorNotImplemented;
  1996. pRequest->ParseState = ParseErrorState;
  1997. ReturnStatus = STATUS_INVALID_DEVICE_REQUEST;
  1998. goto end;
  1999. }
  2000. }
  2001. //
  2002. // Now let's decode the content length header
  2003. //
  2004. if (pRequest->HeaderValid[HttpHeaderContentLength])
  2005. {
  2006. ASSERT(pRequest->Headers[HttpHeaderContentLength].pHeader != NULL);
  2007. ReturnStatus = UlAnsiToULongLong(
  2008. pRequest->Headers[HttpHeaderContentLength].pHeader,
  2009. 10,
  2010. &pRequest->ContentLength
  2011. );
  2012. if (NT_SUCCESS(ReturnStatus) == FALSE)
  2013. {
  2014. if (ReturnStatus == STATUS_SECTION_TOO_BIG)
  2015. {
  2016. pRequest->ErrorCode = UlErrorEntityTooLarge;
  2017. }
  2018. else
  2019. {
  2020. pRequest->ErrorCode = UlErrorNum;
  2021. }
  2022. pRequest->ParseState = ParseErrorState;
  2023. UlTrace(PARSER, (
  2024. "UlParseHttp(pRequest = %p) ERROR: couldn't decode Content-Length\n",
  2025. pRequest
  2026. ));
  2027. goto end;
  2028. }
  2029. if (pRequest->Chunked == 0)
  2030. {
  2031. //
  2032. // prime the first (and only) chunk size
  2033. //
  2034. pRequest->ChunkBytesToParse = pRequest->ContentLength;
  2035. pRequest->ChunkBytesToRead = pRequest->ContentLength;
  2036. }
  2037. }
  2038. }
  2039. pRequest->ParseState = ParseEntityBodyState;
  2040. //
  2041. // fall through
  2042. //
  2043. case ParseEntityBodyState:
  2044. //
  2045. // the only parsing we do here is chunk length calculation,
  2046. // and that is not necessary if we have no more bytes to parse
  2047. //
  2048. if (pRequest->ChunkBytesToParse == 0)
  2049. {
  2050. //
  2051. // no more bytes left to parse, let's see if there are any
  2052. // more in the request
  2053. //
  2054. if (pRequest->Chunked == 1)
  2055. {
  2056. //
  2057. // the request is chunk encoded
  2058. //
  2059. //
  2060. // attempt to read the size of the next chunk
  2061. //
  2062. ReturnStatus = UlParseChunkLength(
  2063. pRequest,
  2064. pHttpRequest,
  2065. HttpRequestLength,
  2066. &CurrentBytesTaken,
  2067. &(pRequest->ChunkBytesToParse)
  2068. );
  2069. UlTraceVerbose(PARSER, (
  2070. "http!UlParseHttp(pRequest = %p): Status = 0x%x. "
  2071. "chunk length: %d bytes taken, 0x%I64x bytes to parse.\n",
  2072. pRequest, ReturnStatus, CurrentBytesTaken,
  2073. pRequest->ChunkBytesToParse
  2074. ));
  2075. if (NT_SUCCESS(ReturnStatus) == FALSE)
  2076. goto end;
  2077. //
  2078. // Otherwise we parsed it, so update and continue.
  2079. //
  2080. pHttpRequest += CurrentBytesTaken;
  2081. TotalBytesTaken += CurrentBytesTaken;
  2082. HttpRequestLength -= CurrentBytesTaken;
  2083. //
  2084. // was this the first chunk?
  2085. //
  2086. if (pRequest->ParsedFirstChunk == 0)
  2087. {
  2088. //
  2089. // Prime the reader, let it read the first chunk
  2090. // even though we haven't quite parsed it yet....
  2091. //
  2092. UlTraceVerbose(PARSER, (
  2093. "UlParseHttp (pRequest=%p) first-chunk seen\n",
  2094. pRequest
  2095. ));
  2096. pRequest->ChunkBytesToRead = pRequest->ChunkBytesToParse;
  2097. pRequest->ParsedFirstChunk = 1;
  2098. }
  2099. //
  2100. // is this the last chunk (denoted with a 0 byte chunk)?
  2101. //
  2102. if (pRequest->ChunkBytesToParse == 0)
  2103. {
  2104. //
  2105. // time to parse the trailer
  2106. //
  2107. UlTraceVerbose(PARSER, (
  2108. "UlParseHttp (pRequest=%p) last-chunk seen\n",
  2109. pRequest
  2110. ));
  2111. pRequest->ParseState = ParseTrailerState;
  2112. }
  2113. }
  2114. else // if (pRequest->Chunked == 1)
  2115. {
  2116. //
  2117. // not chunk-encoded , all done
  2118. //
  2119. UlTraceVerbose(PARSER, (
  2120. "UlParseHttp (pRequest=%p) State: EntityBody->Done\n",
  2121. pRequest
  2122. ));
  2123. pRequest->ParseState = ParseDoneState;
  2124. }
  2125. } // if (pRequest->ChunkBytesToParse == 0)
  2126. else
  2127. {
  2128. UlTraceVerbose(PARSER, (
  2129. "UlParseHttp (pRequest=%p) State: EntityBody, "
  2130. "ChunkBytesToParse=0x%I64x.\n",
  2131. pRequest, pRequest->ChunkBytesToParse
  2132. ));
  2133. }
  2134. //
  2135. // looks all good
  2136. //
  2137. if (pRequest->ParseState != ParseTrailerState)
  2138. {
  2139. break;
  2140. }
  2141. //
  2142. // fall through
  2143. //
  2144. case ParseTrailerState:
  2145. //
  2146. // parse any existing trailer
  2147. //
  2148. // ParseHeaders will bail immediately if CRLF is
  2149. // next in the buffer (no trailer)
  2150. //
  2151. ReturnStatus = UlParseHeaders(
  2152. pRequest,
  2153. pHttpRequest,
  2154. HttpRequestLength,
  2155. &CurrentBytesTaken
  2156. );
  2157. pHttpRequest += CurrentBytesTaken;
  2158. HttpRequestLength -= CurrentBytesTaken;
  2159. TotalBytesTaken += CurrentBytesTaken;
  2160. if (NT_SUCCESS(ReturnStatus) == FALSE)
  2161. goto end;
  2162. //
  2163. // all done
  2164. //
  2165. UlTrace(PARSER, (
  2166. "UlParseHttp (pRequest=%p) State: Trailer->Done\n",
  2167. pRequest
  2168. ));
  2169. pRequest->ParseState = ParseDoneState;
  2170. break;
  2171. default:
  2172. //
  2173. // this should never happen!
  2174. //
  2175. ASSERT(! "Unhandled ParseState");
  2176. break;
  2177. } // switch (pRequest->ParseState)
  2178. end:
  2179. *pBytesTaken = TotalBytesTaken;
  2180. if (ReturnStatus == STATUS_MORE_PROCESSING_REQUIRED &&
  2181. TotalBytesTaken == OriginalBufferLength)
  2182. {
  2183. //
  2184. // convert this to success, we consumed the entire buffer
  2185. //
  2186. ReturnStatus = STATUS_SUCCESS;
  2187. }
  2188. UlTrace(PARSER, (
  2189. "UlParseHttp returning 0x%x, (%p)->ParseState = %d, bytesTaken = %d\n",
  2190. ReturnStatus,
  2191. pRequest,
  2192. pRequest->ParseState,
  2193. TotalBytesTaken
  2194. ));
  2195. return ReturnStatus;
  2196. } // UlParseHttp
  2197. /*++
  2198. Routine Description:
  2199. Routine to initialize the parse code.
  2200. Arguments:
  2201. Return Value:
  2202. --*/
  2203. NTSTATUS
  2204. InitializeParser(
  2205. VOID
  2206. )
  2207. {
  2208. ULONG i;
  2209. ULONG j;
  2210. PHEADER_MAP_ENTRY pHeaderMap;
  2211. PHEADER_INDEX_ENTRY pHeaderIndex;
  2212. UCHAR c;
  2213. //
  2214. // Make sure the entire table starts life as zero
  2215. //
  2216. RtlZeroMemory(
  2217. &g_RequestHeaderIndexTable,
  2218. sizeof(g_RequestHeaderIndexTable)
  2219. );
  2220. RtlZeroMemory(
  2221. &g_ResponseHeaderIndexTable,
  2222. sizeof(g_ResponseHeaderIndexTable)
  2223. );
  2224. RtlZeroMemory(
  2225. &g_RequestHeaderHintIndexTable,
  2226. sizeof(g_RequestHeaderHintIndexTable)
  2227. );
  2228. for (i = 0; i < NUMBER_REQUEST_HEADER_MAP_ENTRIES;i++)
  2229. {
  2230. pHeaderMap = &g_RequestHeaderMapTable[i];
  2231. //
  2232. // Map the header to upper-case.
  2233. //
  2234. for (j = 0 ; j < pHeaderMap->HeaderLength ; j++)
  2235. {
  2236. c = pHeaderMap->Header.HeaderChar[j];
  2237. if ((c >= 'a') && (c <= 'z'))
  2238. {
  2239. pHeaderMap->Header.HeaderChar[j] = c - ('a' - 'A');
  2240. }
  2241. }
  2242. ASSERT(pHeaderMap->pServerHandler != NULL);
  2243. c = pHeaderMap->Header.HeaderChar[0];
  2244. pHeaderIndex = &g_RequestHeaderIndexTable[c - 'A'];
  2245. if (pHeaderIndex->pHeaderMap == NULL)
  2246. {
  2247. pHeaderIndex->pHeaderMap = pHeaderMap;
  2248. pHeaderIndex->Count = 1;
  2249. }
  2250. else
  2251. {
  2252. pHeaderIndex->Count++;
  2253. }
  2254. // Now go through the mask fields for this header map structure and
  2255. // initialize them. We set them to default values first, and then
  2256. // go through the header itself and convert the mask for any
  2257. // non-alphabetic characters.
  2258. for (j = 0; j < MAX_HEADER_LONG_COUNT; j++)
  2259. {
  2260. pHeaderMap->HeaderMask[j] = CREATE_HEADER_MASK(
  2261. pHeaderMap->HeaderLength,
  2262. sizeof(ULONGLONG) * (j+1)
  2263. );
  2264. }
  2265. for (j = 0; j < pHeaderMap->HeaderLength; j++)
  2266. {
  2267. c = pHeaderMap->Header.HeaderChar[j];
  2268. if (c < 'A' || c > 'Z')
  2269. {
  2270. pHeaderMap->HeaderMask[j/sizeof(ULONGLONG)] |=
  2271. (ULONGLONG)0xff << ((j % sizeof(ULONGLONG)) * (ULONGLONG)8);
  2272. }
  2273. }
  2274. //
  2275. // setup the mapping from header id to map table index
  2276. //
  2277. g_RequestHeaderMap[pHeaderMap->HeaderID] = i;
  2278. //
  2279. // Save the Header Map and first char in the hint table if the entry is part of the hints
  2280. //
  2281. if ((pHeaderMap->HintIndex >= 0) && (pHeaderMap->HintIndex < NUMBER_HEADER_HINT_INDICIES))
  2282. {
  2283. g_RequestHeaderHintIndexTable[pHeaderMap->HintIndex].pHeaderMap = pHeaderMap;
  2284. g_RequestHeaderHintIndexTable[pHeaderMap->HintIndex].c = pHeaderMap->Header.HeaderChar[0];
  2285. }
  2286. }
  2287. for (i = 0; i < NUMBER_RESPONSE_HEADER_MAP_ENTRIES;i++)
  2288. {
  2289. pHeaderMap = &g_ResponseHeaderMapTable[i];
  2290. //
  2291. // Map the header to upper-case.
  2292. //
  2293. for (j = 0 ; j < pHeaderMap->HeaderLength ; j++)
  2294. {
  2295. c = pHeaderMap->Header.HeaderChar[j];
  2296. if ((c >= 'a') && (c <= 'z'))
  2297. {
  2298. pHeaderMap->Header.HeaderChar[j] = c - ('a' - 'A');
  2299. }
  2300. }
  2301. ASSERT(pHeaderMap->pClientHandler == NULL);
  2302. c = pHeaderMap->Header.HeaderChar[0];
  2303. pHeaderIndex = &g_ResponseHeaderIndexTable[c - 'A'];
  2304. if (pHeaderIndex->pHeaderMap == NULL)
  2305. {
  2306. pHeaderIndex->pHeaderMap = pHeaderMap;
  2307. pHeaderIndex->Count = 1;
  2308. }
  2309. else
  2310. {
  2311. pHeaderIndex->Count++;
  2312. }
  2313. // Now go through the mask fields for this header map structure and
  2314. // initialize them. We set them to default values first, and then
  2315. // go through the header itself and convert the mask for any
  2316. // non-alphabetic characters.
  2317. for (j = 0; j < MAX_HEADER_LONG_COUNT; j++)
  2318. {
  2319. pHeaderMap->HeaderMask[j] = CREATE_HEADER_MASK(
  2320. pHeaderMap->HeaderLength,
  2321. sizeof(ULONGLONG) * (j+1)
  2322. );
  2323. }
  2324. for (j = 0; j < pHeaderMap->HeaderLength; j++)
  2325. {
  2326. c = pHeaderMap->Header.HeaderChar[j];
  2327. if (c < 'A' || c > 'Z')
  2328. {
  2329. pHeaderMap->HeaderMask[j/sizeof(ULONGLONG)] |=
  2330. (ULONGLONG)0xff << ((j % sizeof(ULONGLONG)) * (ULONGLONG)8);
  2331. }
  2332. }
  2333. //
  2334. // setup the mapping from header id to map table index
  2335. //
  2336. g_ResponseHeaderMap[pHeaderMap->HeaderID] = i;
  2337. }
  2338. return STATUS_SUCCESS;
  2339. } // InitializeParser
  2340. ULONG
  2341. UlpFormatPort(
  2342. OUT PWSTR pString,
  2343. IN ULONG Port
  2344. )
  2345. {
  2346. PWSTR p1;
  2347. PWSTR p2;
  2348. WCHAR ch;
  2349. ULONG digit;
  2350. ULONG length;
  2351. //
  2352. // Sanity check.
  2353. //
  2354. PAGED_CODE();
  2355. //
  2356. // Fast-path common ports. While we're at it, special case port 0,
  2357. // which is definitely not common, but handling it specially makes
  2358. // the general conversion code a bit simpler.
  2359. //
  2360. switch (Port)
  2361. {
  2362. case 0:
  2363. *pString++ = L'0';
  2364. *pString = UNICODE_NULL;
  2365. return 1;
  2366. case 80:
  2367. *pString++ = L'8';
  2368. *pString++ = L'0';
  2369. *pString = UNICODE_NULL;
  2370. return 2;
  2371. case 443:
  2372. *pString++ = L'4';
  2373. *pString++ = L'4';
  2374. *pString++ = L'3';
  2375. *pString = UNICODE_NULL;
  2376. return 3;
  2377. }
  2378. //
  2379. // Pull the least signifigant digits off the port value and store them
  2380. // into the pString. Note that this will store the digits in reverse
  2381. // order.
  2382. //
  2383. p1 = p2 = pString;
  2384. while (Port != 0)
  2385. {
  2386. digit = Port % 10;
  2387. Port = Port / 10;
  2388. *p1++ = L'0' + (WCHAR)digit;
  2389. }
  2390. length = DIFF(p1 - pString);
  2391. //
  2392. // Reverse the digits in the pString.
  2393. //
  2394. *p1-- = UNICODE_NULL;
  2395. while (p1 > p2)
  2396. {
  2397. ch = *p1;
  2398. *p1 = *p2;
  2399. *p2 = ch;
  2400. p2++;
  2401. p1--;
  2402. }
  2403. return length;
  2404. } // UlpFormatPort
  2405. ULONG
  2406. HostAddressAndPortToString(
  2407. PCHAR IpAddressString,
  2408. ULONG IpAddress,
  2409. USHORT IpPortNum
  2410. )
  2411. {
  2412. PCHAR psz = IpAddressString;
  2413. psz = UlStrPrintUlong(psz, (IpAddress >> 24) & 0xFF, '.');
  2414. psz = UlStrPrintUlong(psz, (IpAddress >> 16) & 0xFF, '.');
  2415. psz = UlStrPrintUlong(psz, (IpAddress >> 8) & 0xFF, '.');
  2416. psz = UlStrPrintUlong(psz, (IpAddress >> 0) & 0xFF, ':');
  2417. psz = UlStrPrintUlong(psz, IpPortNum, '\0');
  2418. return DIFF(psz - IpAddressString);
  2419. }
  2420. NTSTATUS
  2421. UlpCookUrl(
  2422. PUL_INTERNAL_REQUEST pRequest
  2423. )
  2424. {
  2425. NTSTATUS Status;
  2426. PUCHAR pHost;
  2427. ULONG HostLength;
  2428. PUCHAR pAbsPath;
  2429. ULONG AbsPathLength;
  2430. ULONG UrlLength;
  2431. ULONG PortLength;
  2432. ULONG LengthCopied;
  2433. PWSTR pUrl = NULL;
  2434. PWSTR pCurrent;
  2435. ULONG Index;
  2436. BOOLEAN PortInUrl;
  2437. CHAR IpAddressString[MAX_ADDRESS_LENGTH+1];
  2438. USHORT IpPortNum;
  2439. BOOLEAN HostFromTransport = FALSE;
  2440. //
  2441. // Sanity check.
  2442. //
  2443. PAGED_CODE();
  2444. //
  2445. // We must have already parsed the entire headers + such
  2446. //
  2447. if (pRequest->ParseState < ParseCookState)
  2448. return STATUS_INVALID_DEVICE_STATE;
  2449. //
  2450. // better have an absolute url .
  2451. //
  2452. if (pRequest->RawUrl.pAbsPath[0] != '/')
  2453. {
  2454. //
  2455. // allow * for Verb = OPTIONS
  2456. //
  2457. if (pRequest->RawUrl.pAbsPath[0] == '*' &&
  2458. pRequest->Verb == HttpVerbOPTIONS)
  2459. {
  2460. // ok
  2461. }
  2462. else
  2463. {
  2464. Status = STATUS_INVALID_DEVICE_REQUEST;
  2465. goto end;
  2466. }
  2467. }
  2468. //
  2469. // collect the host + abspath sections
  2470. //
  2471. if (pRequest->RawUrl.pHost != NULL)
  2472. {
  2473. pHost = pRequest->RawUrl.pHost;
  2474. HostLength = DIFF(pRequest->RawUrl.pAbsPath - pRequest->RawUrl.pHost);
  2475. pAbsPath = pRequest->RawUrl.pAbsPath;
  2476. AbsPathLength = pRequest->RawUrl.Length - DIFF(pAbsPath - pRequest->RawUrl.pUrl);
  2477. }
  2478. else
  2479. {
  2480. pHost = NULL;
  2481. HostLength = 0;
  2482. pAbsPath = pRequest->RawUrl.pAbsPath;
  2483. AbsPathLength = pRequest->RawUrl.Length;
  2484. }
  2485. //
  2486. // found a host yet?
  2487. //
  2488. if (pHost == NULL)
  2489. {
  2490. //
  2491. // do we have a host header?
  2492. //
  2493. if (pRequest->HeaderValid[HttpHeaderHost] &&
  2494. (pRequest->Headers[HttpHeaderHost].HeaderLength > 0) )
  2495. {
  2496. ASSERT(pRequest->Headers[HttpHeaderHost].pHeader != NULL);
  2497. pHost = pRequest->Headers[HttpHeaderHost].pHeader;
  2498. HostLength = pRequest->Headers[HttpHeaderHost].HeaderLength;
  2499. }
  2500. else
  2501. {
  2502. ULONG CharCopied;
  2503. ULONG IpAddress;
  2504. //
  2505. // first, if this was a 1.1 client, it's an invalid request
  2506. // to not have a host header, fail it.
  2507. //
  2508. if (HTTP_GREATER_EQUAL_VERSION(pRequest->Version, 1, 1))
  2509. {
  2510. pRequest->ErrorCode = UlErrorHost;
  2511. pRequest->ParseState = ParseErrorState;
  2512. UlTrace(PARSER, (
  2513. "http!UlpCookUrl(pRequest = %p) ERROR: 1.1 (or greater) request w/o host header\n",
  2514. pRequest
  2515. ));
  2516. Status = STATUS_INVALID_DEVICE_REQUEST;
  2517. goto end;
  2518. }
  2519. //
  2520. // get the ip address from the transport
  2521. //
  2522. IpAddress = pRequest->pHttpConn->pConnection->LocalAddress;
  2523. IpPortNum = pRequest->pHttpConn->pConnection->LocalPort;
  2524. //
  2525. // format it into a string
  2526. //
  2527. pHost = (PUCHAR)(IpAddressString);
  2528. HostLength = HostAddressAndPortToString(
  2529. IpAddressString,
  2530. IpAddress,
  2531. IpPortNum);
  2532. ASSERT(HostLength < sizeof(IpAddressString));
  2533. HostFromTransport = TRUE;
  2534. PortInUrl = TRUE;
  2535. }
  2536. }
  2537. if (HostFromTransport == FALSE)
  2538. {
  2539. //
  2540. // is there a port # already there ?
  2541. //
  2542. Index = HostLength;
  2543. while (Index > 0)
  2544. {
  2545. Index -= 1;
  2546. if (pHost[Index] == ':')
  2547. break;
  2548. }
  2549. if (Index == 0)
  2550. {
  2551. PortInUrl = FALSE;
  2552. //
  2553. // no port number, get the port number from the transport
  2554. //
  2555. // we could simply assume port 80 at this point, but some
  2556. // browsers don't sent the port number in the host header
  2557. // even when their supposed to
  2558. //
  2559. IpPortNum = pRequest->pHttpConn->pConnection->LocalPort;
  2560. }
  2561. else
  2562. {
  2563. PortInUrl = TRUE;
  2564. }
  2565. }
  2566. UrlLength = (HTTP_PREFIX_SIZE+HTTP_PREFIX2_SIZE) +
  2567. HostLength +
  2568. (sizeof(":")-1) +
  2569. MAX_PORT_LENGTH +
  2570. AbsPathLength;
  2571. UrlLength *= sizeof(WCHAR);
  2572. //
  2573. // allocate a new buffer to hold this guy
  2574. //
  2575. if (UrlLength > g_UlMaxInternalUrlLength)
  2576. {
  2577. pUrl = UL_ALLOCATE_ARRAY(
  2578. NonPagedPool,
  2579. WCHAR,
  2580. (UrlLength/sizeof(WCHAR)) + 1,
  2581. URL_POOL_TAG
  2582. );
  2583. }
  2584. else
  2585. {
  2586. pUrl = pRequest->pUrlBuffer;
  2587. }
  2588. if (pUrl == NULL)
  2589. {
  2590. Status = STATUS_NO_MEMORY;
  2591. goto end;
  2592. }
  2593. pRequest->CookedUrl.pUrl = pCurrent = pUrl;
  2594. //
  2595. // compute the scheme
  2596. //
  2597. if (pRequest->Secure)
  2598. {
  2599. //
  2600. // yep, ssl
  2601. //
  2602. // copy the NULL for the hash function to work
  2603. //
  2604. RtlCopyMemory(pCurrent, L"https://", sizeof(L"https://"));
  2605. pRequest->CookedUrl.Hash = HashStringNoCaseW(pCurrent, 0);
  2606. pCurrent += (sizeof(L"https://")-sizeof(WCHAR)) / sizeof(WCHAR);
  2607. pRequest->CookedUrl.Length = (sizeof(L"https://")-sizeof(WCHAR));
  2608. }
  2609. else
  2610. {
  2611. //
  2612. // not ssl
  2613. //
  2614. RtlCopyMemory(pCurrent, L"http://", sizeof(L"http://"));
  2615. pRequest->CookedUrl.Hash = HashStringNoCaseW(pCurrent, 0);
  2616. pCurrent += (sizeof(L"http://")-sizeof(WCHAR)) / sizeof(WCHAR);
  2617. pRequest->CookedUrl.Length = (sizeof(L"http://")-sizeof(WCHAR));
  2618. }
  2619. //
  2620. // assemble the rest of the url
  2621. //
  2622. //
  2623. // host
  2624. //
  2625. Status = UlpCleanAndCopyUrl(
  2626. HostName,
  2627. pCurrent,
  2628. pHost,
  2629. HostLength,
  2630. &LengthCopied,
  2631. NULL,
  2632. &pRequest->CookedUrl.Hash
  2633. );
  2634. if (NT_SUCCESS(Status) == FALSE)
  2635. goto end;
  2636. pRequest->CookedUrl.pHost = pCurrent;
  2637. pRequest->CookedUrl.Length += LengthCopied;
  2638. pCurrent += LengthCopied / sizeof(WCHAR);
  2639. //
  2640. // port
  2641. //
  2642. if (PortInUrl == FALSE)
  2643. {
  2644. *pCurrent = L':';
  2645. PortLength = UlpFormatPort( pCurrent+1, IpPortNum ) + 1;
  2646. //
  2647. // update the running hash
  2648. //
  2649. pRequest->CookedUrl.Hash = HashStringNoCaseW(pCurrent, pRequest->CookedUrl.Hash);
  2650. pCurrent += PortLength;
  2651. //
  2652. // swprintf returns char not byte count
  2653. //
  2654. pRequest->CookedUrl.Length += PortLength * sizeof(WCHAR);
  2655. }
  2656. // abs_path
  2657. //
  2658. Status = UlpCleanAndCopyUrl(
  2659. AbsPath,
  2660. pCurrent,
  2661. pAbsPath,
  2662. AbsPathLength,
  2663. &LengthCopied,
  2664. &pRequest->CookedUrl.pQueryString,
  2665. &pRequest->CookedUrl.Hash
  2666. );
  2667. if (NT_SUCCESS(Status) == FALSE)
  2668. {
  2669. if (STATUS_OBJECT_PATH_INVALID == Status)
  2670. {
  2671. pRequest->ErrorCode = UlErrorForbiddenUrl;
  2672. pRequest->ParseState = ParseErrorState;
  2673. }
  2674. goto end;
  2675. }
  2676. pRequest->CookedUrl.pAbsPath = pCurrent;
  2677. pRequest->CookedUrl.Length += LengthCopied;
  2678. ASSERT(pRequest->CookedUrl.Length <= UrlLength);
  2679. //
  2680. // Update pRequest, include space for the terminator
  2681. //
  2682. pRequest->TotalRequestSize += pRequest->CookedUrl.Length + sizeof(WCHAR);
  2683. //
  2684. // let's just make sure the hash is the right value
  2685. //
  2686. ASSERT(pRequest->CookedUrl.Hash
  2687. == HashStringNoCaseW(pRequest->CookedUrl.pUrl, 0));
  2688. //
  2689. // Scramble it
  2690. //
  2691. pRequest->CookedUrl.Hash = HashRandomizeBits(pRequest->CookedUrl.Hash);
  2692. ASSERT(pRequest->CookedUrl.pHost != NULL);
  2693. ASSERT(pRequest->CookedUrl.pAbsPath != NULL);
  2694. //
  2695. // validate the host part of the url
  2696. //
  2697. pCurrent = wcschr(pRequest->CookedUrl.pHost, L':');
  2698. //
  2699. // Check for :
  2700. //
  2701. // No colon ||
  2702. //
  2703. // no hostname ||
  2704. //
  2705. // No colon in the host OR no port number.
  2706. //
  2707. if (pCurrent == NULL ||
  2708. pCurrent == pRequest->CookedUrl.pHost ||
  2709. pCurrent >= (pRequest->CookedUrl.pAbsPath-1))
  2710. {
  2711. //
  2712. // bad. no colon for the port.
  2713. //
  2714. Status = STATUS_INVALID_DEVICE_REQUEST;
  2715. goto end;
  2716. }
  2717. //
  2718. // skip the colon
  2719. //
  2720. pCurrent += 1;
  2721. //
  2722. // now make sure the port number is in good shape
  2723. //
  2724. while (pCurrent < pRequest->CookedUrl.pAbsPath)
  2725. {
  2726. if (IS_HTTP_DIGIT(pCurrent[0]) == FALSE)
  2727. {
  2728. //
  2729. // bad. non digit.
  2730. //
  2731. Status = STATUS_INVALID_DEVICE_REQUEST;
  2732. goto end;
  2733. }
  2734. pCurrent += 1;
  2735. }
  2736. Status = STATUS_SUCCESS;
  2737. end:
  2738. if (NT_SUCCESS(Status) == FALSE)
  2739. {
  2740. if (pUrl != NULL)
  2741. {
  2742. if (pUrl != pRequest->pUrlBuffer)
  2743. {
  2744. UL_FREE_POOL(pUrl, URL_POOL_TAG);
  2745. }
  2746. RtlZeroMemory(&pRequest->CookedUrl, sizeof(pRequest->CookedUrl));
  2747. }
  2748. //
  2749. // has a specific error code been set?
  2750. //
  2751. if (pRequest->ErrorCode == UlError)
  2752. {
  2753. pRequest->ErrorCode = UlErrorUrl;
  2754. pRequest->ParseState = ParseErrorState;
  2755. }
  2756. UlTrace(PARSER, (
  2757. "ul!UlpCookUrl(pRequest = %p) ERROR: unhappy. Status = 0x%x\n",
  2758. pRequest,
  2759. Status
  2760. ));
  2761. }
  2762. return Status;
  2763. } // UlpCookUrl
  2764. NTSTATUS
  2765. Unescape(
  2766. IN PUCHAR pChar,
  2767. OUT PUCHAR pOutChar
  2768. )
  2769. {
  2770. UCHAR Result, Digit;
  2771. //
  2772. // Sanity check.
  2773. //
  2774. PAGED_CODE();
  2775. if (pChar[0] != '%' ||
  2776. IS_HTTP_HEX(pChar[1]) == FALSE ||
  2777. IS_HTTP_HEX(pChar[2]) == FALSE)
  2778. {
  2779. UlTrace(PARSER, (
  2780. "ul!Unescape( %c%c%c ) not HTTP_HEX format\n",
  2781. pChar[0],
  2782. pChar[1],
  2783. pChar[2]
  2784. ));
  2785. return STATUS_OBJECT_PATH_SYNTAX_BAD;
  2786. }
  2787. //
  2788. // HexToChar() inlined
  2789. //
  2790. // uppercase #1
  2791. //
  2792. if (IS_HTTP_ALPHA(pChar[1]))
  2793. Digit = UPCASE_CHAR(pChar[1]);
  2794. else
  2795. Digit = pChar[1];
  2796. Result = ((Digit >= 'A') ? (Digit - 'A' + 0xA) : (Digit - '0')) << 4;
  2797. // uppercase #2
  2798. //
  2799. if (IS_HTTP_ALPHA(pChar[2]))
  2800. Digit = UPCASE_CHAR(pChar[2]);
  2801. else
  2802. Digit = pChar[2];
  2803. Result |= (Digit >= 'A') ? (Digit - 'A' + 0xA) : (Digit - '0');
  2804. *pOutChar = Result;
  2805. return STATUS_SUCCESS;
  2806. } // Unescape
  2807. //
  2808. // PAULMCD(2/99): stolen from iisrtl\string.cxx and incorporated
  2809. // and added more comments
  2810. //
  2811. //
  2812. // Private constants.
  2813. //
  2814. #define ACTION_NOTHING 0x00000000
  2815. #define ACTION_EMIT_CH 0x00010000
  2816. #define ACTION_EMIT_DOT_CH 0x00020000
  2817. #define ACTION_EMIT_DOT_DOT_CH 0x00030000
  2818. #define ACTION_BACKUP 0x00040000
  2819. #define ACTION_MASK 0xFFFF0000
  2820. //
  2821. // Private globals
  2822. //
  2823. //
  2824. // this table says what to do based on the current state and the current
  2825. // character
  2826. //
  2827. ULONG pActionTable[16] =
  2828. {
  2829. //
  2830. // state 0 = fresh, seen nothing exciting yet
  2831. //
  2832. ACTION_EMIT_CH, // other = emit it state = 0
  2833. ACTION_EMIT_CH, // "." = emit it state = 0
  2834. ACTION_NOTHING, // EOS = normal finish state = 4
  2835. ACTION_EMIT_CH, // "/" = we saw the "/", emit it state = 1
  2836. //
  2837. // state 1 = we saw a "/" !
  2838. //
  2839. ACTION_EMIT_CH, // other = emit it, state = 0
  2840. ACTION_NOTHING, // "." = eat it, state = 2
  2841. ACTION_NOTHING, // EOS = normal finish state = 4
  2842. ACTION_NOTHING, // "/" = extra slash, eat it, state = 1
  2843. //
  2844. // state 2 = we saw a "/" and ate a "." !
  2845. //
  2846. ACTION_EMIT_DOT_CH, // other = emit the dot we ate. state = 0
  2847. ACTION_NOTHING, // "." = eat it, a .. state = 3
  2848. ACTION_NOTHING, // EOS = normal finish state = 4
  2849. ACTION_NOTHING, // "/" = we ate a "/./", swallow it state = 1
  2850. //
  2851. // state 3 = we saw a "/" and ate a ".." !
  2852. //
  2853. ACTION_EMIT_DOT_DOT_CH, // other = emit the "..". state = 0
  2854. ACTION_EMIT_DOT_DOT_CH, // "." = 3 dots, emit the ".." state = 0
  2855. ACTION_BACKUP, // EOS = we have a "/..\0", backup! state = 4
  2856. ACTION_BACKUP // "/" = we have a "/../", backup! state = 1
  2857. };
  2858. //
  2859. // this table says which newstate to be in given the current state and the
  2860. // character we saw
  2861. //
  2862. ULONG pNextStateTable[16] =
  2863. {
  2864. // state 0
  2865. 0 , // other
  2866. 0 , // "."
  2867. 4 , // EOS
  2868. 1 , // "\"
  2869. // state 1
  2870. 0 , // other
  2871. 2 , // "."
  2872. 4 , // EOS
  2873. 1 , // "\"
  2874. // state 2
  2875. 0 , // other
  2876. 3 , // "."
  2877. 4 , // EOS
  2878. 1 , // "\"
  2879. // state 3
  2880. 0 , // other
  2881. 0 , // "."
  2882. 4 , // EOS
  2883. 1 // "\"
  2884. };
  2885. //
  2886. // this says how to index into pNextStateTable given our current state.
  2887. //
  2888. // since max states = 4, we calculate the index by multiplying with 4.
  2889. //
  2890. #define IndexFromState( st) ( (st) * 4)
  2891. /***************************************************************************++
  2892. Routine Description:
  2893. Unescape
  2894. Convert backslash to forward slash
  2895. Remove double slashes (empty directiories names) - e.g. // or \\
  2896. Handle /./
  2897. Handle /../
  2898. Convert to unicode
  2899. computes the case insensitive hash
  2900. Arguments:
  2901. Return Value:
  2902. NTSTATUS - Completion status.
  2903. --***************************************************************************/
  2904. NTSTATUS
  2905. UlpCleanAndCopyUrl(
  2906. IN URL_PART UrlPart,
  2907. IN OUT PWSTR pDestination,
  2908. IN PUCHAR pSource,
  2909. IN ULONG SourceLength,
  2910. OUT PULONG pBytesCopied,
  2911. OUT PWSTR * ppQueryString OPTIONAL,
  2912. OUT PULONG pUrlHash
  2913. )
  2914. {
  2915. NTSTATUS Status;
  2916. URL_TYPE AnsiUrlType = g_UlEnableDBCS ? UrlTypeDbcs : UrlTypeAnsi;
  2917. if (!g_UlEnableNonUTF8)
  2918. {
  2919. //
  2920. // Only accept UTF-8 URLs.
  2921. //
  2922. Status = UlpCleanAndCopyUrlByType(
  2923. UrlTypeUtf8,
  2924. UrlPart,
  2925. pDestination,
  2926. pSource,
  2927. SourceLength,
  2928. pBytesCopied,
  2929. ppQueryString,
  2930. pUrlHash
  2931. );
  2932. }
  2933. else if (!g_UlFavorDBCS)
  2934. {
  2935. //
  2936. // The URL may be either UTF-8 or ANSI. First
  2937. // try UTF-8, and if that fails go for ANSI.
  2938. //
  2939. Status = UlpCleanAndCopyUrlByType(
  2940. UrlTypeUtf8,
  2941. UrlPart,
  2942. pDestination,
  2943. pSource,
  2944. SourceLength,
  2945. pBytesCopied,
  2946. ppQueryString,
  2947. pUrlHash
  2948. );
  2949. if (!NT_SUCCESS(Status))
  2950. {
  2951. Status = UlpCleanAndCopyUrlByType(
  2952. AnsiUrlType,
  2953. UrlPart,
  2954. pDestination,
  2955. pSource,
  2956. SourceLength,
  2957. pBytesCopied,
  2958. ppQueryString,
  2959. pUrlHash
  2960. );
  2961. }
  2962. }
  2963. else
  2964. {
  2965. //
  2966. // The URL may be either ANSI or UTF-8. First
  2967. // try the ANSI interpretation, and if that fails
  2968. // go for UTF-8.
  2969. //
  2970. Status = UlpCleanAndCopyUrlByType(
  2971. AnsiUrlType,
  2972. UrlPart,
  2973. pDestination,
  2974. pSource,
  2975. SourceLength,
  2976. pBytesCopied,
  2977. ppQueryString,
  2978. pUrlHash
  2979. );
  2980. if (!NT_SUCCESS(Status))
  2981. {
  2982. Status = UlpCleanAndCopyUrlByType(
  2983. UrlTypeUtf8,
  2984. UrlPart,
  2985. pDestination,
  2986. pSource,
  2987. SourceLength,
  2988. pBytesCopied,
  2989. ppQueryString,
  2990. pUrlHash
  2991. );
  2992. }
  2993. }
  2994. return Status;
  2995. }
  2996. /***************************************************************************++
  2997. Routine Description:
  2998. This function can be told to clean up UTF-8, ANSI, or DBCS URLs.
  2999. Unescape
  3000. Convert backslash to forward slash
  3001. Remove double slashes (empty directiories names) - e.g. // or \\
  3002. Handle /./
  3003. Handle /../
  3004. Convert to unicode
  3005. computes the case insensitive hash
  3006. Arguments:
  3007. Return Value:
  3008. NTSTATUS - Completion status.
  3009. --***************************************************************************/
  3010. NTSTATUS
  3011. UlpCleanAndCopyUrlByType(
  3012. IN URL_TYPE UrlType,
  3013. IN URL_PART UrlPart,
  3014. IN OUT PWSTR pDestination,
  3015. IN PUCHAR pSource,
  3016. IN ULONG SourceLength,
  3017. OUT PULONG pBytesCopied,
  3018. OUT PWSTR * ppQueryString OPTIONAL,
  3019. OUT PULONG pUrlHash
  3020. )
  3021. {
  3022. NTSTATUS Status;
  3023. PWSTR pDest;
  3024. PUCHAR pChar;
  3025. ULONG CharToSkip;
  3026. UCHAR Char;
  3027. BOOLEAN HashValid;
  3028. ULONG UrlHash;
  3029. ULONG BytesCopied;
  3030. PWSTR pQueryString;
  3031. ULONG StateIndex;
  3032. WCHAR UnicodeChar;
  3033. BOOLEAN MakeCanonical;
  3034. PUSHORT pFastPopChar;
  3035. //
  3036. // Sanity check.
  3037. //
  3038. PAGED_CODE();
  3039. //
  3040. // a cool local helper macro
  3041. //
  3042. #define EMIT_CHAR(ch) \
  3043. do { \
  3044. pDest[0] = (ch); \
  3045. pDest += 1; \
  3046. BytesCopied += 2; \
  3047. if (HashValid) \
  3048. UrlHash = HashCharNoCaseW((ch), UrlHash); \
  3049. } while (0)
  3050. pDest = pDestination;
  3051. pQueryString = NULL;
  3052. BytesCopied = 0;
  3053. pChar = pSource;
  3054. CharToSkip = 0;
  3055. HashValid = TRUE;
  3056. UrlHash = *pUrlHash;
  3057. StateIndex = 0;
  3058. MakeCanonical = (UrlPart == AbsPath) ? TRUE : FALSE;
  3059. if (UrlType == UrlTypeUtf8 && UrlPart != QueryString)
  3060. {
  3061. pFastPopChar = FastPopChars;
  3062. }
  3063. else
  3064. {
  3065. pFastPopChar = DummyPopChars;
  3066. }
  3067. while (SourceLength > 0)
  3068. {
  3069. //
  3070. // advance ! it's at the top of the loop to enable ANSI_NULL to
  3071. // come through ONCE
  3072. //
  3073. pChar += CharToSkip;
  3074. SourceLength -= CharToSkip;
  3075. //
  3076. // well? have we hit the end?
  3077. //
  3078. if (SourceLength == 0)
  3079. {
  3080. UnicodeChar = UNICODE_NULL;
  3081. }
  3082. else
  3083. {
  3084. //
  3085. // Nope. Peek briefly to see if we hit the query string
  3086. //
  3087. if (UrlPart == AbsPath && pChar[0] == '?')
  3088. {
  3089. ASSERT(pQueryString == NULL);
  3090. //
  3091. // remember it's location
  3092. //
  3093. pQueryString = pDest;
  3094. //
  3095. // let it fall through ONCE to the canonical
  3096. // in order to handle a trailing "/.." like
  3097. // "http://foobar:80/foo/bar/..?v=1&v2"
  3098. //
  3099. UnicodeChar = L'?';
  3100. CharToSkip = 1;
  3101. //
  3102. // now we are cleaning the query string
  3103. //
  3104. UrlPart = QueryString;
  3105. //
  3106. // cannot use fast path for PopChar anymore
  3107. //
  3108. pFastPopChar = DummyPopChars;
  3109. }
  3110. else
  3111. {
  3112. USHORT NextUnicodeChar = pFastPopChar[*pChar];
  3113. //
  3114. // Grab the next character. Try to be fast for the
  3115. // normal character case. Otherwise call PopChar.
  3116. //
  3117. if (NextUnicodeChar == 0)
  3118. {
  3119. Status = PopChar(
  3120. UrlType,
  3121. UrlPart,
  3122. pChar,
  3123. &UnicodeChar,
  3124. &CharToSkip
  3125. );
  3126. if (NT_SUCCESS(Status) == FALSE)
  3127. goto end;
  3128. }
  3129. else
  3130. {
  3131. #if DBG
  3132. Status = PopChar(
  3133. UrlType,
  3134. UrlPart,
  3135. pChar,
  3136. &UnicodeChar,
  3137. &CharToSkip
  3138. );
  3139. ASSERT(NT_SUCCESS(Status));
  3140. ASSERT(UnicodeChar == NextUnicodeChar);
  3141. ASSERT(CharToSkip == 1);
  3142. #endif
  3143. UnicodeChar = NextUnicodeChar;
  3144. CharToSkip = 1;
  3145. }
  3146. }
  3147. }
  3148. if (MakeCanonical)
  3149. {
  3150. //
  3151. // now use the state machine to make it canonical .
  3152. //
  3153. //
  3154. // from the old value of StateIndex, figure out our new base StateIndex
  3155. //
  3156. StateIndex = IndexFromState(pNextStateTable[StateIndex]);
  3157. //
  3158. // did we just hit the query string? this will only happen once
  3159. // that we take this branch after hitting it, as we stop
  3160. // processing after hitting it.
  3161. //
  3162. if (UrlPart == QueryString)
  3163. {
  3164. //
  3165. // treat this just like we hit a NULL, EOS.
  3166. //
  3167. StateIndex += 2;
  3168. }
  3169. else
  3170. {
  3171. //
  3172. // otherwise based the new state off of the char we
  3173. // just popped.
  3174. //
  3175. switch (UnicodeChar)
  3176. {
  3177. case UNICODE_NULL: StateIndex += 2; break;
  3178. case L'.': StateIndex += 1; break;
  3179. case L'/': StateIndex += 3; break;
  3180. default: StateIndex += 0; break;
  3181. }
  3182. }
  3183. }
  3184. else
  3185. {
  3186. StateIndex = (UnicodeChar == UNICODE_NULL) ? 2 : 0;
  3187. }
  3188. //
  3189. // Perform the action associated with the state.
  3190. //
  3191. switch (pActionTable[StateIndex])
  3192. {
  3193. case ACTION_EMIT_DOT_DOT_CH:
  3194. EMIT_CHAR(L'.');
  3195. // fall through
  3196. case ACTION_EMIT_DOT_CH:
  3197. EMIT_CHAR(L'.');
  3198. // fall through
  3199. case ACTION_EMIT_CH:
  3200. EMIT_CHAR(UnicodeChar);
  3201. // fall through
  3202. case ACTION_NOTHING:
  3203. break;
  3204. case ACTION_BACKUP:
  3205. //
  3206. // pDest currently points 1 past the last '/'. backup over it and
  3207. // find the preceding '/', set pDest to 1 past that one.
  3208. //
  3209. //
  3210. // backup to the '/'
  3211. //
  3212. pDest -= 1;
  3213. BytesCopied -= 2;
  3214. ASSERT(pDest[0] == L'/');
  3215. //
  3216. // are we at the start of the string? that's bad, can't go back!
  3217. //
  3218. if (pDest == pDestination)
  3219. {
  3220. ASSERT(BytesCopied == 0);
  3221. UlTrace(PARSER, (
  3222. "ul!UlpCleanAndCopyUrl() Can't back up for ..\n"
  3223. ));
  3224. Status = STATUS_OBJECT_PATH_INVALID;
  3225. goto end;
  3226. }
  3227. //
  3228. // back up over the '/'
  3229. //
  3230. pDest -= 1;
  3231. BytesCopied -= 2;
  3232. ASSERT(pDest > pDestination);
  3233. //
  3234. // now find the previous slash
  3235. //
  3236. while (pDest > pDestination && pDest[0] != L'/')
  3237. {
  3238. pDest -= 1;
  3239. BytesCopied -= 2;
  3240. }
  3241. //
  3242. // we already have a slash, so don't have to store 1.
  3243. //
  3244. ASSERT(pDest[0] == L'/');
  3245. //
  3246. // simply skip it, as if we had emitted it just now
  3247. //
  3248. pDest += 1;
  3249. BytesCopied += 2;
  3250. //
  3251. // mark our running hash invalid
  3252. //
  3253. HashValid = FALSE;
  3254. break;
  3255. default:
  3256. ASSERT(!"UL!UlpCleanAndCopyUrl: Invalid action code in state table!");
  3257. Status = STATUS_OBJECT_PATH_SYNTAX_BAD;
  3258. goto end;
  3259. }
  3260. //
  3261. // Just hit the query string ?
  3262. //
  3263. if (MakeCanonical && UrlPart == QueryString)
  3264. {
  3265. //
  3266. // Stop canonical processing
  3267. //
  3268. MakeCanonical = FALSE;
  3269. //
  3270. // Need to emit the '?', it wasn't emitted above
  3271. //
  3272. ASSERT(pActionTable[StateIndex] != ACTION_EMIT_CH);
  3273. EMIT_CHAR(L'?');
  3274. }
  3275. }
  3276. //
  3277. // terminate the string, it hasn't been done in the loop
  3278. //
  3279. ASSERT((pDest-1)[0] != UNICODE_NULL);
  3280. pDest[0] = UNICODE_NULL;
  3281. //
  3282. // need to recompute the hash?
  3283. //
  3284. if (HashValid == FALSE)
  3285. {
  3286. //
  3287. // this can happen if we had to backtrack due to /../
  3288. //
  3289. UrlHash = HashStringNoCaseW(pDestination, *pUrlHash);
  3290. }
  3291. *pUrlHash = UrlHash;
  3292. *pBytesCopied = BytesCopied;
  3293. if (ppQueryString != NULL)
  3294. {
  3295. *ppQueryString = pQueryString;
  3296. }
  3297. Status = STATUS_SUCCESS;
  3298. end:
  3299. return Status;
  3300. } // UlpCleanAndCopyUrlByType
  3301. /***************************************************************************++
  3302. Routine Description:
  3303. Figures out how big the fixed headers are. Fixed headers include the
  3304. status line, and any headers that don't have to be generated for
  3305. every request (such as Date and Connection).
  3306. The final CRLF separating headers from body is considered part of
  3307. the variable headers.
  3308. Arguments:
  3309. Version - HTTP Version of the request: 0.9, 1.0., 1.1
  3310. pUserResponse - the response containing the headers
  3311. pHeaderLength - result: the number of bytes in the fixed headers.
  3312. Return Value:
  3313. NTSTATUS - STATUS_SUCCESS or an error code (possibly from an exception)
  3314. --***************************************************************************/
  3315. NTSTATUS
  3316. UlComputeFixedHeaderSize(
  3317. IN HTTP_VERSION Version,
  3318. IN PHTTP_RESPONSE pUserResponse,
  3319. OUT PULONG pHeaderLength
  3320. )
  3321. {
  3322. NTSTATUS Status = STATUS_SUCCESS;
  3323. ULONG HeaderLength = 0;
  3324. ULONG i;
  3325. PHTTP_UNKNOWN_HEADER pUnknownHeaders;
  3326. //
  3327. // Sanity check.
  3328. //
  3329. PAGED_CODE();
  3330. ASSERT(pHeaderLength != NULL);
  3331. if ((pUserResponse == NULL) || (HTTP_EQUAL_VERSION(Version, 0, 9)))
  3332. {
  3333. *pHeaderLength = 0;
  3334. return STATUS_SUCCESS;
  3335. }
  3336. //
  3337. // The pUserResponse is user-mode data and cannot be trusted.
  3338. // Hence the try/except.
  3339. //
  3340. __try
  3341. {
  3342. HeaderLength
  3343. += (VERSION_SIZE + // HTTP-Version
  3344. 1 + // SP
  3345. 3 + // Status-Code
  3346. 1 + // SP
  3347. pUserResponse->ReasonLength / sizeof(CHAR) +// Reason-Phrase
  3348. CRLF_SIZE // CRLF
  3349. );
  3350. //
  3351. // Loop through the known headers.
  3352. //
  3353. for (i = 0; i < HttpHeaderResponseMaximum; ++i)
  3354. {
  3355. USHORT RawValueLength
  3356. = pUserResponse->Headers.pKnownHeaders[i].RawValueLength;
  3357. // skip some headers we'll generate
  3358. if ((i == HttpHeaderDate) || (i == HttpHeaderConnection)) {
  3359. continue;
  3360. }
  3361. if (RawValueLength > 0)
  3362. {
  3363. HeaderLength
  3364. += (g_ResponseHeaderMapTable[
  3365. g_ResponseHeaderMap[i]
  3366. ].HeaderLength + // Header-Name
  3367. 1 + // SP
  3368. RawValueLength / sizeof(CHAR) + // Header-Value
  3369. CRLF_SIZE // CRLF
  3370. );
  3371. }
  3372. }
  3373. //
  3374. // Include default headers we may need to generate for the application.
  3375. //
  3376. if (pUserResponse->Headers.pKnownHeaders[HttpHeaderServer
  3377. ].RawValueLength == 0)
  3378. {
  3379. HeaderLength += (g_ResponseHeaderMapTable[
  3380. g_ResponseHeaderMap[HttpHeaderServer]
  3381. ].HeaderLength + // Header-Name
  3382. 1 + // SP
  3383. DEFAULT_SERVER_HDR_LENGTH + // Header-Value
  3384. CRLF_SIZE // CRLF
  3385. );
  3386. }
  3387. //
  3388. // And the unknown headers (this might throw an exception).
  3389. //
  3390. pUnknownHeaders = pUserResponse->Headers.pUnknownHeaders;
  3391. if (pUnknownHeaders != NULL)
  3392. {
  3393. for (i = 0 ; i < pUserResponse->Headers.UnknownHeaderCount; ++i)
  3394. {
  3395. USHORT Length;
  3396. if (pUnknownHeaders[i].NameLength > 0)
  3397. {
  3398. HeaderLength += (pUnknownHeaders[i].NameLength /
  3399. sizeof(CHAR) + // Header-Name
  3400. 1 + // ':'
  3401. 1 + // SP
  3402. pUnknownHeaders[i].RawValueLength /
  3403. sizeof(CHAR) + // Header-Value
  3404. CRLF_SIZE // CRLF
  3405. );
  3406. }
  3407. }
  3408. }
  3409. }
  3410. __except( UL_EXCEPTION_FILTER() )
  3411. {
  3412. Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode());
  3413. HeaderLength = 0;
  3414. }
  3415. *pHeaderLength = HeaderLength;
  3416. return Status;
  3417. } // UlComputeFixedHeaderSize
  3418. /***************************************************************************++
  3419. Routine Description:
  3420. Figures out how big the variable headers are. Variable headers include
  3421. Date and Connection.
  3422. The final CRLF separating headers from body is considered part of
  3423. the variable headers.
  3424. Arguments:
  3425. pRequest - Supplies the request to query.
  3426. ForceDisconnect - Supplies TRUE if we'll be forcing a disconnect
  3427. at the end of the response.
  3428. pContentLengthString - Supplies a header value for an optional
  3429. Content-Length header. If this is the empty string "", then no
  3430. Content-Length header is generated.
  3431. ContentLengthStringLength - Supplies the length of the above string.
  3432. pConnHeader - Receives information on the Connection: header to
  3433. generate.
  3434. Return Values:
  3435. The number of bytes in the fixed headers.
  3436. --***************************************************************************/
  3437. ULONG
  3438. UlComputeVariableHeaderSize(
  3439. IN PUL_INTERNAL_REQUEST pRequest,
  3440. IN BOOLEAN ForceDisconnect,
  3441. IN PUCHAR pContentLengthString,
  3442. IN ULONG ContentLengthStringLength,
  3443. OUT PUL_CONN_HDR pConnHeader
  3444. )
  3445. {
  3446. ULONG Length;
  3447. PHEADER_MAP_ENTRY pEntry;
  3448. UL_CONN_HDR connHeader;
  3449. //
  3450. // Sanity check.
  3451. //
  3452. ASSERT( UL_IS_VALID_INTERNAL_REQUEST( pRequest ) );
  3453. ASSERT( pContentLengthString != NULL );
  3454. ASSERT( pConnHeader != NULL );
  3455. //
  3456. // Determine the appropriate connection: header.
  3457. //
  3458. Length = 0;
  3459. connHeader = UlChooseConnectionHeader( pRequest->Version, ForceDisconnect );
  3460. *pConnHeader = connHeader;
  3461. //
  3462. // Date: header
  3463. //
  3464. pEntry = &(g_ResponseHeaderMapTable[g_ResponseHeaderMap[HttpHeaderDate]]);
  3465. Length += pEntry->HeaderLength; // header name
  3466. Length += 1; // SP
  3467. Length += DATE_HDR_LENGTH; // header value
  3468. Length += CRLF_SIZE; // CRLF
  3469. //
  3470. // Connection: header
  3471. //
  3472. pEntry = &(g_ResponseHeaderMapTable[g_ResponseHeaderMap[HttpHeaderConnection]]);
  3473. switch (connHeader) {
  3474. case ConnHdrNone:
  3475. // no header
  3476. break;
  3477. case ConnHdrClose:
  3478. Length += pEntry->HeaderLength;
  3479. Length += 1;
  3480. Length += CONN_CLOSE_HDR_LENGTH;
  3481. Length += CRLF_SIZE;
  3482. break;
  3483. case ConnHdrKeepAlive:
  3484. Length += pEntry->HeaderLength;
  3485. Length += 1;
  3486. Length += CONN_KEEPALIVE_HDR_LENGTH;
  3487. Length += CRLF_SIZE;
  3488. break;
  3489. default:
  3490. ASSERT( connHeader < ConnHdrMax );
  3491. break;
  3492. }
  3493. //
  3494. // Any Content-Length: header we may need to generate.
  3495. //
  3496. if (pContentLengthString[0] != '\0')
  3497. {
  3498. ASSERT( ContentLengthStringLength > 0 );
  3499. pEntry = &(g_ResponseHeaderMapTable[g_ResponseHeaderMap[HttpHeaderContentLength]]);
  3500. Length += pEntry->HeaderLength;
  3501. Length += 1;
  3502. Length += ContentLengthStringLength;
  3503. Length += CRLF_SIZE;
  3504. }
  3505. else
  3506. {
  3507. ASSERT( ContentLengthStringLength == 0 );
  3508. }
  3509. //
  3510. // final CRLF
  3511. //
  3512. Length += CRLF_SIZE;
  3513. return Length;
  3514. } // UlComputeVariableHeaderSize
  3515. /***************************************************************************++
  3516. Routine Description:
  3517. Figures out how big the maximum default variable headers are. Variable
  3518. headers include Date, Content and Connection.
  3519. The final CRLF separating headers from body is considered part of
  3520. the variable headers.
  3521. Arguments:
  3522. Return Values:
  3523. The maximum number of bytes in the variable headers.
  3524. --***************************************************************************/
  3525. ULONG
  3526. UlComputeMaxVariableHeaderSize( VOID )
  3527. {
  3528. ULONG Length = 0;
  3529. PHEADER_MAP_ENTRY pEntry;
  3530. //
  3531. // Date: header
  3532. //
  3533. pEntry = &(g_ResponseHeaderMapTable[g_ResponseHeaderMap[HttpHeaderDate]]);
  3534. Length += pEntry->HeaderLength; // header name
  3535. Length += 1; // SP
  3536. Length += DATE_HDR_LENGTH; // header value
  3537. Length += CRLF_SIZE; // CRLF
  3538. //
  3539. // Connection: header
  3540. //
  3541. pEntry = &(g_ResponseHeaderMapTable[g_ResponseHeaderMap[HttpHeaderConnection]]);
  3542. Length += pEntry->HeaderLength;
  3543. Length += 1;
  3544. Length += MAX(CONN_CLOSE_HDR_LENGTH, CONN_KEEPALIVE_HDR_LENGTH);
  3545. Length += CRLF_SIZE;
  3546. //
  3547. // Content-Length: header
  3548. //
  3549. pEntry = &(g_ResponseHeaderMapTable[g_ResponseHeaderMap[HttpHeaderContentLength]]);
  3550. Length += pEntry->HeaderLength;
  3551. Length += 1;
  3552. Length += MAX_ULONGLONG_STR;
  3553. Length += CRLF_SIZE;
  3554. //
  3555. // final CRLF
  3556. //
  3557. Length += CRLF_SIZE;
  3558. return Length;
  3559. } // UlComputeMaxVariableHeaderSize
  3560. /***************************************************************************++
  3561. Routine Description:
  3562. Generates the varaible part of the header, including Date:, Connection:,
  3563. and final CRLF.
  3564. Arguments:
  3565. ConnHeader - Supplies the type of Connection: header to generate.
  3566. pContentLengthString - Supplies a header value for an optional
  3567. Content-Length header. If this is the empty string "", then no
  3568. Content-Length header is generated.
  3569. ContentLengthStringLength - Supplies the length of the above string.
  3570. pBuffer - Supplies the target buffer for the generated headers.
  3571. pBytesCopied - Receives the number of header bytes generated.
  3572. pDateTime - Receives the system time equivalent of the Date: header
  3573. --***************************************************************************/
  3574. VOID
  3575. UlGenerateVariableHeaders(
  3576. IN UL_CONN_HDR ConnHeader,
  3577. IN PUCHAR pContentLengthString,
  3578. IN ULONG ContentLengthStringLength,
  3579. OUT PUCHAR pBuffer,
  3580. OUT PULONG pBytesCopied,
  3581. OUT PLARGE_INTEGER pDateTime
  3582. )
  3583. {
  3584. PHEADER_MAP_ENTRY pEntry;
  3585. PUCHAR pStartBuffer;
  3586. PUCHAR pCloseHeaderValue;
  3587. ULONG CloseHeaderValueLength;
  3588. ULONG BytesCopied;
  3589. ASSERT( pContentLengthString != NULL );
  3590. ASSERT( pBuffer );
  3591. ASSERT( pBytesCopied );
  3592. ASSERT( pDateTime );
  3593. pStartBuffer = pBuffer;
  3594. //
  3595. // generate Date: header
  3596. //
  3597. pEntry = &(g_ResponseHeaderMapTable[g_ResponseHeaderMap[HttpHeaderDate]]);
  3598. RtlCopyMemory(
  3599. pBuffer,
  3600. pEntry->MixedCaseHeader,
  3601. pEntry->HeaderLength
  3602. );
  3603. pBuffer += pEntry->HeaderLength;
  3604. pBuffer[0] = SP;
  3605. pBuffer += 1;
  3606. BytesCopied = GenerateDateHeader( pBuffer, pDateTime );
  3607. pBuffer += BytesCopied;
  3608. ((UNALIGNED64 USHORT *)pBuffer)[0] = CRLF;
  3609. pBuffer += sizeof(USHORT);
  3610. //
  3611. // generate Connection: header
  3612. //
  3613. switch (ConnHeader) {
  3614. case ConnHdrNone:
  3615. pCloseHeaderValue = NULL;
  3616. CloseHeaderValueLength = 0;
  3617. break;
  3618. case ConnHdrClose:
  3619. pCloseHeaderValue = (PUCHAR) CONN_CLOSE_HDR;
  3620. CloseHeaderValueLength = CONN_CLOSE_HDR_LENGTH;
  3621. break;
  3622. case ConnHdrKeepAlive:
  3623. pCloseHeaderValue = (PUCHAR) CONN_KEEPALIVE_HDR;
  3624. CloseHeaderValueLength = CONN_KEEPALIVE_HDR_LENGTH;
  3625. break;
  3626. default:
  3627. ASSERT(ConnHeader < ConnHdrMax);
  3628. pCloseHeaderValue = NULL;
  3629. CloseHeaderValueLength = 0;
  3630. break;
  3631. }
  3632. if (pCloseHeaderValue != NULL) {
  3633. pEntry = &(g_ResponseHeaderMapTable[g_ResponseHeaderMap[HttpHeaderConnection]]);
  3634. RtlCopyMemory(
  3635. pBuffer,
  3636. pEntry->MixedCaseHeader,
  3637. pEntry->HeaderLength
  3638. );
  3639. pBuffer += pEntry->HeaderLength;
  3640. pBuffer[0] = SP;
  3641. pBuffer += 1;
  3642. RtlCopyMemory(
  3643. pBuffer,
  3644. pCloseHeaderValue,
  3645. CloseHeaderValueLength
  3646. );
  3647. pBuffer += CloseHeaderValueLength;
  3648. ((UNALIGNED64 USHORT *)pBuffer)[0] = CRLF;
  3649. pBuffer += sizeof(USHORT);
  3650. }
  3651. if (pContentLengthString[0] != '\0')
  3652. {
  3653. ASSERT( ContentLengthStringLength > 0 );
  3654. pEntry = &(g_ResponseHeaderMapTable[g_ResponseHeaderMap[HttpHeaderContentLength]]);
  3655. RtlCopyMemory(
  3656. pBuffer,
  3657. pEntry->MixedCaseHeader,
  3658. pEntry->HeaderLength
  3659. );
  3660. pBuffer += pEntry->HeaderLength;
  3661. pBuffer[0] = SP;
  3662. pBuffer += 1;
  3663. RtlCopyMemory(
  3664. pBuffer,
  3665. pContentLengthString,
  3666. ContentLengthStringLength
  3667. );
  3668. pBuffer += ContentLengthStringLength;
  3669. ((UNALIGNED64 USHORT *)pBuffer)[0] = CRLF;
  3670. pBuffer += sizeof(USHORT);
  3671. }
  3672. else
  3673. {
  3674. ASSERT( ContentLengthStringLength == 0 );
  3675. }
  3676. //
  3677. // generate final CRLF
  3678. //
  3679. ((UNALIGNED64 USHORT *)pBuffer)[0] = CRLF;
  3680. pBuffer += sizeof(USHORT);
  3681. //
  3682. // make sure we didn't use too much
  3683. //
  3684. BytesCopied = DIFF(pBuffer - pStartBuffer);
  3685. *pBytesCopied = BytesCopied;
  3686. }
  3687. ULONG
  3688. _WideCharToMultiByte(
  3689. ULONG uCodePage,
  3690. ULONG dwFlags,
  3691. PCWSTR lpWideCharStr,
  3692. int cchWideChar,
  3693. PSTR lpMultiByteStr,
  3694. int cchMultiByte,
  3695. PCSTR lpDefaultChar,
  3696. PBOOLEAN lpfUsedDefaultChar
  3697. )
  3698. {
  3699. int i;
  3700. //
  3701. // simply strip the upper byte, it's supposed to be ascii already
  3702. //
  3703. for (i = 0; i < cchWideChar; ++i)
  3704. {
  3705. if ((lpWideCharStr[i] & 0xff00) != 0 || IS_HTTP_CTL(lpWideCharStr[i]))
  3706. {
  3707. lpMultiByteStr[0] = *lpDefaultChar;
  3708. }
  3709. else
  3710. {
  3711. lpMultiByteStr[0] = (UCHAR)(lpWideCharStr[i]);
  3712. }
  3713. lpMultiByteStr += 1;
  3714. }
  3715. return (ULONG)(i);
  3716. } // _WideCharToMultiByte
  3717. ULONG
  3718. _MultiByteToWideChar(
  3719. ULONG uCodePage,
  3720. ULONG dwFlags,
  3721. PCSTR lpMultiByteStr,
  3722. int cchMultiByte,
  3723. PWSTR lpWideCharStr,
  3724. int cchWideChar
  3725. )
  3726. {
  3727. int i;
  3728. //
  3729. // simply add a 0 upper byte, it's supposed to be ascii
  3730. //
  3731. for (i = 0; i < cchMultiByte; ++i)
  3732. {
  3733. if (lpMultiByteStr[i] > 128)
  3734. {
  3735. lpWideCharStr[i] = (WCHAR)(DefaultChar);
  3736. }
  3737. else
  3738. {
  3739. lpWideCharStr[i] = (WCHAR)(lpMultiByteStr[i]);
  3740. }
  3741. }
  3742. return (ULONG)(i);
  3743. } // _MultiByteToWideChar
  3744. /***************************************************************************++
  3745. Routine Description:
  3746. Generates the fixed part of the header. Fixed headers include the
  3747. status line, and any headers that don't have to be generated for
  3748. every request (such as Date and Connection).
  3749. The final CRLF separating headers from body is considered part of
  3750. the variable headers.
  3751. Arguments:
  3752. Version - the http version for the status line
  3753. pUserResponse - the user specified response
  3754. BufferLength - length of pBuffer
  3755. pBuffer - generate the headers here
  3756. pBytesCopied - gets the number of bytes generated
  3757. --***************************************************************************/
  3758. NTSTATUS
  3759. UlGenerateFixedHeaders(
  3760. IN HTTP_VERSION Version,
  3761. IN PHTTP_RESPONSE pUserResponse,
  3762. IN ULONG BufferLength,
  3763. OUT PUCHAR pBuffer,
  3764. OUT PULONG pBytesCopied
  3765. )
  3766. {
  3767. PUCHAR pStartBuffer;
  3768. PUCHAR pEndBuffer;
  3769. ULONG BytesCopied;
  3770. ULONG BytesToCopy;
  3771. ULONG i;
  3772. PHTTP_UNKNOWN_HEADER pUnknownHeaders;
  3773. NTSTATUS Status = STATUS_SUCCESS;
  3774. //
  3775. // Sanity check.
  3776. //
  3777. PAGED_CODE();
  3778. ASSERT(pUserResponse != NULL);
  3779. ASSERT(pBuffer != NULL && BufferLength > 0);
  3780. ASSERT(pBytesCopied != NULL);
  3781. //
  3782. // The pUserResponse is user-mode data and cannot be trusted.
  3783. // Hence the try/except.
  3784. //
  3785. __try
  3786. {
  3787. pStartBuffer = pBuffer;
  3788. pEndBuffer = pBuffer + BufferLength;
  3789. //
  3790. // Build the response headers.
  3791. //
  3792. if (HTTP_NOT_EQUAL_VERSION(Version, 0, 9))
  3793. {
  3794. BytesToCopy =
  3795. sizeof("HTTP/1.1 ") - 1 +
  3796. 4 +
  3797. pUserResponse->ReasonLength / sizeof(CHAR) +
  3798. sizeof(USHORT);
  3799. if ((pBuffer + BytesToCopy) > pEndBuffer)
  3800. {
  3801. return STATUS_INSUFFICIENT_RESOURCES;
  3802. }
  3803. //
  3804. // Always send back 1.1 in the response.
  3805. //
  3806. RtlCopyMemory(pBuffer, "HTTP/1.1 ", sizeof("HTTP/1.1 ") - 1);
  3807. pBuffer += sizeof("HTTP/1.1 ") - 1;
  3808. //
  3809. // Status code.
  3810. //
  3811. pBuffer[0] = '0' + ((pUserResponse->StatusCode / 100) % 10);
  3812. pBuffer[1] = '0' + ((pUserResponse->StatusCode / 10) % 10);
  3813. pBuffer[2] = '0' + ((pUserResponse->StatusCode / 1) % 10);
  3814. pBuffer[3] = SP;
  3815. pBuffer += 4;
  3816. //
  3817. // Copy the reason, converting from widechar.
  3818. //
  3819. BytesCopied = pUserResponse->ReasonLength / sizeof(CHAR);
  3820. RtlCopyMemory(
  3821. pBuffer,
  3822. pUserResponse->pReason,
  3823. BytesCopied
  3824. );
  3825. pBuffer += BytesCopied;
  3826. //
  3827. // Terminate with the CRLF.
  3828. //
  3829. ((UNALIGNED64 USHORT *)pBuffer)[0] = CRLF;
  3830. pBuffer += sizeof(USHORT);
  3831. //
  3832. // Loop through the known headers.
  3833. //
  3834. for (i = 0; i < HttpHeaderResponseMaximum; ++i)
  3835. {
  3836. //
  3837. // Skip some headers we'll generate.
  3838. //
  3839. if ((i == HttpHeaderDate) || (i == HttpHeaderConnection))
  3840. {
  3841. continue;
  3842. }
  3843. if (pUserResponse->Headers.pKnownHeaders[i].RawValueLength > 0)
  3844. {
  3845. PHEADER_MAP_ENTRY pEntry;
  3846. pEntry = &(g_ResponseHeaderMapTable[g_ResponseHeaderMap[i]]);
  3847. BytesToCopy =
  3848. pEntry->HeaderLength +
  3849. 1 +
  3850. pUserResponse->Headers.pKnownHeaders[i].RawValueLength / sizeof(CHAR) +
  3851. sizeof(USHORT);
  3852. if ((pBuffer + BytesToCopy) > pEndBuffer)
  3853. {
  3854. return STATUS_INSUFFICIENT_RESOURCES;
  3855. }
  3856. RtlCopyMemory(
  3857. pBuffer,
  3858. pEntry->MixedCaseHeader,
  3859. pEntry->HeaderLength
  3860. );
  3861. pBuffer += pEntry->HeaderLength;
  3862. pBuffer[0] = SP;
  3863. pBuffer += 1;
  3864. BytesCopied = pUserResponse->Headers.pKnownHeaders[i].RawValueLength / sizeof(CHAR);
  3865. RtlCopyMemory(
  3866. pBuffer,
  3867. pUserResponse->Headers.pKnownHeaders[i].pRawValue,
  3868. BytesCopied
  3869. );
  3870. pBuffer += BytesCopied;
  3871. ((UNALIGNED64 USHORT *)pBuffer)[0] = CRLF;
  3872. pBuffer += sizeof(USHORT);
  3873. }
  3874. }
  3875. //
  3876. // Append some default headers if not provided by the application.
  3877. //
  3878. if (pUserResponse->Headers.pKnownHeaders[HttpHeaderServer].RawValueLength == 0)
  3879. {
  3880. PHEADER_MAP_ENTRY pEntry;
  3881. pEntry = &(g_ResponseHeaderMapTable[g_ResponseHeaderMap[HttpHeaderServer]]);
  3882. BytesToCopy =
  3883. pEntry->HeaderLength +
  3884. 1 +
  3885. DEFAULT_SERVER_HDR_LENGTH +
  3886. sizeof(USHORT);
  3887. if ((pBuffer + BytesToCopy) > pEndBuffer)
  3888. {
  3889. return STATUS_INSUFFICIENT_RESOURCES;
  3890. }
  3891. RtlCopyMemory(
  3892. pBuffer,
  3893. pEntry->MixedCaseHeader,
  3894. pEntry->HeaderLength
  3895. );
  3896. pBuffer += pEntry->HeaderLength;
  3897. pBuffer[0] = SP;
  3898. pBuffer += 1;
  3899. RtlCopyMemory(
  3900. pBuffer,
  3901. DEFAULT_SERVER_HDR,
  3902. DEFAULT_SERVER_HDR_LENGTH
  3903. );
  3904. pBuffer += DEFAULT_SERVER_HDR_LENGTH;
  3905. ((UNALIGNED64 USHORT *)pBuffer)[0] = CRLF;
  3906. pBuffer += sizeof(USHORT);
  3907. }
  3908. //
  3909. // And now the unknown headers (this might throw an exception).
  3910. //
  3911. pUnknownHeaders = pUserResponse->Headers.pUnknownHeaders;
  3912. if (pUnknownHeaders != NULL)
  3913. {
  3914. for (i = 0 ; i < pUserResponse->Headers.UnknownHeaderCount; ++i)
  3915. {
  3916. if (pUnknownHeaders[i].NameLength > 0)
  3917. {
  3918. BytesToCopy =
  3919. pUnknownHeaders[i].NameLength / sizeof(CHAR) +
  3920. 1 +
  3921. 1 +
  3922. pUnknownHeaders[i].RawValueLength / sizeof(CHAR) +
  3923. sizeof(USHORT);
  3924. if ((pBuffer + BytesToCopy) > pEndBuffer)
  3925. {
  3926. return STATUS_INSUFFICIENT_RESOURCES;
  3927. }
  3928. BytesCopied = pUnknownHeaders[i].NameLength / sizeof(CHAR);
  3929. RtlCopyMemory(
  3930. pBuffer,
  3931. pUnknownHeaders[i].pName,
  3932. BytesCopied
  3933. );
  3934. pBuffer += BytesCopied;
  3935. *pBuffer++ = ':';
  3936. *pBuffer++ = SP;
  3937. BytesCopied = pUnknownHeaders[i].RawValueLength / sizeof(CHAR);
  3938. RtlCopyMemory(
  3939. pBuffer,
  3940. pUnknownHeaders[i].pRawValue,
  3941. BytesCopied
  3942. );
  3943. pBuffer += BytesCopied;
  3944. ((UNALIGNED64 USHORT *)pBuffer)[0] = CRLF;
  3945. pBuffer += sizeof(USHORT);
  3946. } // if (pUnknownHeaders[i].NameLength > 0)
  3947. }
  3948. } // if (pUnknownHeaders != NULL)
  3949. *pBytesCopied = DIFF(pBuffer - pStartBuffer);
  3950. } // if (Version > UlHttpVersion09)
  3951. else
  3952. {
  3953. *pBytesCopied = 0;
  3954. }
  3955. //
  3956. // Ensure we didn't use too much.
  3957. //
  3958. ASSERT(DIFF(pBuffer - pStartBuffer) <= BufferLength);
  3959. }
  3960. __except( UL_EXCEPTION_FILTER() )
  3961. {
  3962. Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode());
  3963. }
  3964. return Status;
  3965. } // UlGenerateFixedHeaders
  3966. PSTR Weekdays[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
  3967. PSTR Months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  3968. "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
  3969. /***************************************************************************++
  3970. Routine Description:
  3971. Generates a date header string from a LARGE_INTEGER.
  3972. Arguments:
  3973. pBuffer: Buffer to store generated date string
  3974. systemTime: A 64 bit Time value to be converted
  3975. --***************************************************************************/
  3976. ULONG
  3977. GenerateDateHeaderString(
  3978. OUT PUCHAR pBuffer,
  3979. IN LARGE_INTEGER systemTime
  3980. )
  3981. {
  3982. TIME_FIELDS timeFields;
  3983. int length;
  3984. RtlTimeToTimeFields( &systemTime, &timeFields );
  3985. length = sprintf(
  3986. (char *) pBuffer,
  3987. "%s, %02d %s %04d %02d:%02d:%02d GMT",
  3988. Weekdays[timeFields.Weekday],
  3989. timeFields.Day,
  3990. Months[timeFields.Month - 1],
  3991. timeFields.Year,
  3992. timeFields.Hour,
  3993. timeFields.Minute,
  3994. timeFields.Second
  3995. );
  3996. ASSERT( length <= DATE_HDR_LENGTH );
  3997. return (ULONG)length;
  3998. } // GenerateDateHeaderString
  3999. /***************************************************************************++
  4000. Routine Description:
  4001. Generates a date header and updates cached value if required.
  4002. Arguments:
  4003. pBuffer: Buffer to store generated date header.
  4004. pSystemTime: caller allocated buffer to receive the SystemTime equivalent
  4005. of the generated string time.
  4006. --***************************************************************************/
  4007. ULONG
  4008. GenerateDateHeader(
  4009. OUT PUCHAR pBuffer,
  4010. OUT PLARGE_INTEGER pSystemTime
  4011. )
  4012. {
  4013. LARGE_INTEGER CacheTime;
  4014. int length;
  4015. LONGLONG timediff;
  4016. //
  4017. // Get the current time.
  4018. //
  4019. KeQuerySystemTime( pSystemTime );
  4020. CacheTime.QuadPart = g_UlSystemTime.QuadPart;
  4021. //
  4022. // Check the difference between the current time, and
  4023. // the cached time. Note that timediff is signed.
  4024. //
  4025. timediff = pSystemTime->QuadPart - CacheTime.QuadPart;
  4026. if (timediff < ONE_SECOND) {
  4027. //
  4028. // The entry hasn't gone stale yet. We can copy.
  4029. // Force a barrier around reading the string into memory.
  4030. //
  4031. UL_READMOSTLY_READ_BARRIER();
  4032. RtlCopyMemory(pBuffer, g_UlDateString, g_UlDateStringLength+1);
  4033. UL_READMOSTLY_READ_BARRIER();
  4034. //
  4035. // Inspect the global time value again in case it changed.
  4036. //
  4037. if (CacheTime.QuadPart == g_UlSystemTime.QuadPart) {
  4038. //
  4039. // Global value hasn't changed. We are all set.
  4040. //
  4041. pSystemTime->QuadPart = CacheTime.QuadPart;
  4042. return g_UlDateStringLength;
  4043. }
  4044. }
  4045. //
  4046. // The cached value is stale, or is/was being changed. We need to update or re-read it.
  4047. // Note that we could also spin trying to re-read and acquire the lock.
  4048. //
  4049. UlAcquireResourceExclusive(
  4050. &g_pUlNonpagedData->DateHeaderResource,
  4051. TRUE
  4052. );
  4053. //
  4054. // Has someone else updated the time while we were blocked?
  4055. //
  4056. CacheTime.QuadPart = g_UlSystemTime.QuadPart;
  4057. timediff = pSystemTime->QuadPart - CacheTime.QuadPart;
  4058. if (timediff >= ONE_SECOND)
  4059. {
  4060. g_UlSystemTime.QuadPart = 0;
  4061. KeQuerySystemTime( pSystemTime );
  4062. UL_READMOSTLY_WRITE_BARRIER();
  4063. g_UlDateStringLength = GenerateDateHeaderString(
  4064. g_UlDateString,
  4065. *pSystemTime
  4066. );
  4067. UL_READMOSTLY_WRITE_BARRIER();
  4068. g_UlSystemTime.QuadPart = pSystemTime->QuadPart;
  4069. }
  4070. else
  4071. {
  4072. // Capture the system time used to generate the buffer
  4073. pSystemTime->QuadPart = g_UlSystemTime.QuadPart;
  4074. }
  4075. //
  4076. // The time has been updated. Copy the new string into
  4077. // the caller's buffer.
  4078. //
  4079. RtlCopyMemory(
  4080. pBuffer,
  4081. g_UlDateString,
  4082. g_UlDateStringLength + 1
  4083. );
  4084. UlReleaseResource(&g_pUlNonpagedData->DateHeaderResource);
  4085. return g_UlDateStringLength;
  4086. } // GenerateDateHeader
  4087. /***************************************************************************++
  4088. Routine Description:
  4089. Initializes the date cache.
  4090. --***************************************************************************/
  4091. NTSTATUS
  4092. UlInitializeDateCache( VOID )
  4093. {
  4094. LARGE_INTEGER now;
  4095. NTSTATUS status;
  4096. KeQuerySystemTime(&now);
  4097. g_UlDateStringLength = GenerateDateHeaderString(g_UlDateString, now);
  4098. status = UlInitializeResource(
  4099. &g_pUlNonpagedData->DateHeaderResource,
  4100. "DateHeaderResource",
  4101. 0,
  4102. UL_DATE_HEADER_RESOURCE_TAG
  4103. );
  4104. if (NT_SUCCESS(status))
  4105. {
  4106. g_DateCacheInitialized = TRUE;
  4107. }
  4108. return status;
  4109. }
  4110. /***************************************************************************++
  4111. Routine Description:
  4112. Terminates the date header cache.
  4113. --***************************************************************************/
  4114. VOID
  4115. UlTerminateDateCache( VOID )
  4116. {
  4117. if (g_DateCacheInitialized)
  4118. {
  4119. UlDeleteResource(&g_pUlNonpagedData->DateHeaderResource);
  4120. }
  4121. }
  4122. /***************************************************************************++
  4123. Routine Description:
  4124. Parses the http version from a string. Assumes string begins with "HTTP/". Eats
  4125. leading zeros. Puts resulting version in HTTP_VERSION structure passed in to
  4126. function.
  4127. Arguments:
  4128. pString array of chars to parse
  4129. StringLength number o bytes in pString
  4130. pVersion where to put parsed version.
  4131. Returns:
  4132. Number of bytes parsed out of string. Zero indicates parse failure, and no version
  4133. information was found. Number of bytes does not include trailing linear white space nor
  4134. CRLF line terminator.
  4135. --***************************************************************************/
  4136. ULONG
  4137. UlpParseHttpVersion(
  4138. PUCHAR pString,
  4139. ULONG StringLength,
  4140. PHTTP_VERSION pVersion
  4141. )
  4142. {
  4143. ULONG BytesRemaining = StringLength;
  4144. BOOLEAN Done = FALSE;
  4145. ASSERT( pString );
  4146. ASSERT( StringLength > HTTP_PREFIX_SIZE + 1 );
  4147. ASSERT( pVersion );
  4148. // Deal with leading zeros. Buffer should look like "HTTP/00000n.n"...
  4149. pVersion->MajorVersion = 0;
  4150. pVersion->MinorVersion = 0;
  4151. //
  4152. // compare 'HTTP' away
  4153. //
  4154. if ( *(UNALIGNED64 ULONG *)pString == (ULONG) HTTP_PREFIX )
  4155. {
  4156. BytesRemaining -= HTTP_PREFIX_SIZE;
  4157. pString += HTTP_PREFIX_SIZE;
  4158. }
  4159. else
  4160. {
  4161. goto End;
  4162. }
  4163. if ( '/' == *pString )
  4164. {
  4165. BytesRemaining--;
  4166. pString++;
  4167. }
  4168. else
  4169. {
  4170. goto End;
  4171. }
  4172. //
  4173. // Parse major version
  4174. //
  4175. while ( (0 != BytesRemaining) && IS_HTTP_DIGIT(*pString) )
  4176. {
  4177. pVersion->MajorVersion *= 10;
  4178. pVersion->MajorVersion += (*pString - '0');
  4179. BytesRemaining--;
  4180. pString++;
  4181. }
  4182. if ( 0 == BytesRemaining )
  4183. {
  4184. goto End;
  4185. }
  4186. //
  4187. // find '.'
  4188. //
  4189. if ( '.' != *pString )
  4190. {
  4191. // Error: No decimal place; bail out.
  4192. goto End;
  4193. }
  4194. else
  4195. {
  4196. BytesRemaining--;
  4197. pString++;
  4198. }
  4199. if ( 0 == BytesRemaining || !IS_HTTP_DIGIT(*pString) )
  4200. {
  4201. goto End;
  4202. }
  4203. //
  4204. // Parse minor version
  4205. //
  4206. while ( (0 != BytesRemaining) && IS_HTTP_DIGIT(*pString) )
  4207. {
  4208. pVersion->MinorVersion *= 10;
  4209. pVersion->MinorVersion += (*pString - '0');
  4210. BytesRemaining--;
  4211. pString++;
  4212. }
  4213. Done = TRUE;
  4214. End:
  4215. if (!Done)
  4216. {
  4217. return 0;
  4218. }
  4219. else
  4220. {
  4221. UlTrace(PARSER, (
  4222. "http!UlpParseHttpVersion: found version HTTP/%d.%d\n",
  4223. pVersion->MajorVersion,
  4224. pVersion->MinorVersion
  4225. ));
  4226. return (StringLength - BytesRemaining);
  4227. }
  4228. } // UlpParseHttpVersion