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.

4139 lines
104 KiB

  1. /*++
  2. Copyright (c) 1998-1999 Microsoft Corporation
  3. Module Name:
  4. parse.c
  5. Abstract:
  6. Contains all of the kernel mode HTTP parsing code.
  7. Author:
  8. Henry Sanders (henrysa) 27-Apr-1998
  9. Revision History:
  10. Paul McDaniel (paulmcd) 3-March-1998 finished up
  11. --*/
  12. #include "precomp.h"
  13. #include "parsep.h"
  14. #include "rcvhdrs.h"
  15. //
  16. // The fast verb translation table
  17. //
  18. FAST_VERB_ENTRY FastVerbTable[] =
  19. {
  20. CREATE_FAST_VERB_ENTRY(GET),
  21. CREATE_FAST_VERB_ENTRY(PUT),
  22. CREATE_FAST_VERB_ENTRY(HEAD),
  23. CREATE_FAST_VERB_ENTRY(POST),
  24. CREATE_FAST_VERB_ENTRY(DELETE),
  25. CREATE_FAST_VERB_ENTRY(TRACE),
  26. CREATE_FAST_VERB_ENTRY(TRACK),
  27. CREATE_FAST_VERB_ENTRY(OPTIONS),
  28. CREATE_FAST_VERB_ENTRY(MOVE),
  29. CREATE_FAST_VERB_ENTRY(COPY),
  30. CREATE_FAST_VERB_ENTRY(MKCOL),
  31. CREATE_FAST_VERB_ENTRY(LOCK)
  32. };
  33. //
  34. // The long verb translation table. All verbs more than 7 characters long
  35. // belong in this table.
  36. //
  37. LONG_VERB_ENTRY LongVerbTable[] =
  38. {
  39. CREATE_LONG_VERB_ENTRY(PROPFIND),
  40. CREATE_LONG_VERB_ENTRY(PROPPATCH)
  41. };
  42. //
  43. // The header map table. These entries don't need to be in strict
  44. // alphabetical order, but they do need to be grouped by the first character
  45. // of the header - all A's together, all C's together, etc. They also need
  46. // to be entered in uppercase, since we upcase incoming verbs before we do
  47. // the compare.
  48. //
  49. // for nice perf, group unused headers low in the sub-sort order
  50. //
  51. // it's important that the header name is <= 24 characters (3 ULONGLONG's).
  52. //
  53. // response headers are in here also for ResponseHeaderMap. their handler
  54. // is NULL and they must be at the end of the sort order for that letter.
  55. //
  56. HEADER_MAP_ENTRY HeaderMapTable[] =
  57. {
  58. CREATE_HEADER_MAP_ENTRY(Accept:,
  59. HttpHeaderAccept,
  60. MultipleHeaderHandler),
  61. CREATE_HEADER_MAP_ENTRY(Accept-Language:,
  62. HttpHeaderAcceptLanguage,
  63. MultipleHeaderHandler),
  64. CREATE_HEADER_MAP_ENTRY(Accept-Encoding:,
  65. HttpHeaderAcceptEncoding,
  66. MultipleHeaderHandler),
  67. CREATE_HEADER_MAP_ENTRY(Accept-Charset:,
  68. HttpHeaderAcceptCharset,
  69. MultipleHeaderHandler),
  70. CREATE_HEADER_MAP_ENTRY(Authorization:,
  71. HttpHeaderAuthorization,
  72. SingleHeaderHandler),
  73. CREATE_HEADER_MAP_ENTRY(Allow:,
  74. HttpHeaderAllow,
  75. MultipleHeaderHandler),
  76. CREATE_HEADER_MAP_ENTRY(Accept-Ranges:,
  77. HttpHeaderAcceptRanges,
  78. NULL),
  79. CREATE_HEADER_MAP_ENTRY(Age:,
  80. HttpHeaderAge,
  81. NULL),
  82. CREATE_HEADER_MAP_ENTRY(Connection:,
  83. HttpHeaderConnection,
  84. MultipleHeaderHandler),
  85. CREATE_HEADER_MAP_ENTRY(Cache-Control:,
  86. HttpHeaderCacheControl,
  87. MultipleHeaderHandler),
  88. CREATE_HEADER_MAP_ENTRY(Cookie:,
  89. HttpHeaderCookie,
  90. MultipleHeaderHandler),
  91. CREATE_HEADER_MAP_ENTRY(Content-Length:,
  92. HttpHeaderContentLength,
  93. SingleHeaderHandler),
  94. CREATE_HEADER_MAP_ENTRY(Content-Type:,
  95. HttpHeaderContentType,
  96. SingleHeaderHandler),
  97. CREATE_HEADER_MAP_ENTRY(Content-Encoding:,
  98. HttpHeaderContentEncoding,
  99. MultipleHeaderHandler),
  100. CREATE_HEADER_MAP_ENTRY(Content-Language:,
  101. HttpHeaderContentLanguage,
  102. MultipleHeaderHandler),
  103. CREATE_HEADER_MAP_ENTRY(Content-Location:,
  104. HttpHeaderContentLocation,
  105. SingleHeaderHandler),
  106. CREATE_HEADER_MAP_ENTRY(Content-MD5:,
  107. HttpHeaderContentMd5,
  108. SingleHeaderHandler),
  109. CREATE_HEADER_MAP_ENTRY(Content-Range:,
  110. HttpHeaderContentRange,
  111. SingleHeaderHandler),
  112. CREATE_HEADER_MAP_ENTRY(Date:,
  113. HttpHeaderDate,
  114. SingleHeaderHandler),
  115. CREATE_HEADER_MAP_ENTRY(ETag:,
  116. HttpHeaderEtag,
  117. NULL),
  118. CREATE_HEADER_MAP_ENTRY(Expect:,
  119. HttpHeaderExpect,
  120. MultipleHeaderHandler),
  121. CREATE_HEADER_MAP_ENTRY(Expires:,
  122. HttpHeaderExpires,
  123. SingleHeaderHandler),
  124. CREATE_HEADER_MAP_ENTRY(From:,
  125. HttpHeaderFrom,
  126. SingleHeaderHandler),
  127. CREATE_HEADER_MAP_ENTRY(Host:,
  128. HttpHeaderHost,
  129. SingleHeaderHandler),
  130. CREATE_HEADER_MAP_ENTRY(If-Modified-Since:,
  131. HttpHeaderIfModifiedSince,
  132. SingleHeaderHandler),
  133. CREATE_HEADER_MAP_ENTRY(If-None-Match:,
  134. HttpHeaderIfNoneMatch,
  135. MultipleHeaderHandler),
  136. CREATE_HEADER_MAP_ENTRY(If-Match:,
  137. HttpHeaderIfMatch,
  138. MultipleHeaderHandler),
  139. CREATE_HEADER_MAP_ENTRY(If-Unmodified-Since:,
  140. HttpHeaderIfUnmodifiedSince,
  141. SingleHeaderHandler),
  142. CREATE_HEADER_MAP_ENTRY(If-Range:,
  143. HttpHeaderIfRange,
  144. SingleHeaderHandler),
  145. CREATE_HEADER_MAP_ENTRY(Last-Modified:,
  146. HttpHeaderLastModified,
  147. SingleHeaderHandler),
  148. CREATE_HEADER_MAP_ENTRY(Location:,
  149. HttpHeaderLocation,
  150. NULL),
  151. CREATE_HEADER_MAP_ENTRY(Max-Forwards:,
  152. HttpHeaderMaxForwards,
  153. SingleHeaderHandler),
  154. CREATE_HEADER_MAP_ENTRY(Pragma:,
  155. HttpHeaderPragma,
  156. MultipleHeaderHandler),
  157. CREATE_HEADER_MAP_ENTRY(Proxy-Authorization:,
  158. HttpHeaderProxyAuthorization,
  159. SingleHeaderHandler),
  160. CREATE_HEADER_MAP_ENTRY(Proxy-Authenticate:,
  161. HttpHeaderProxyAuthenticate,
  162. NULL),
  163. CREATE_HEADER_MAP_ENTRY(Referer:,
  164. HttpHeaderReferer,
  165. SingleHeaderHandler),
  166. CREATE_HEADER_MAP_ENTRY(Range:,
  167. HttpHeaderRange,
  168. SingleHeaderHandler),
  169. CREATE_HEADER_MAP_ENTRY(Retry-After:,
  170. HttpHeaderRetryAfter,
  171. NULL),
  172. CREATE_HEADER_MAP_ENTRY(Server:,
  173. HttpHeaderServer,
  174. NULL),
  175. CREATE_HEADER_MAP_ENTRY(Set-Cookie:,
  176. HttpHeaderSetCookie,
  177. NULL),
  178. CREATE_HEADER_MAP_ENTRY(Trailer:,
  179. HttpHeaderTrailer,
  180. MultipleHeaderHandler),
  181. CREATE_HEADER_MAP_ENTRY(Transfer-Encoding:,
  182. HttpHeaderTransferEncoding,
  183. MultipleHeaderHandler),
  184. CREATE_HEADER_MAP_ENTRY(TE:,
  185. HttpHeaderTe,
  186. MultipleHeaderHandler),
  187. CREATE_HEADER_MAP_ENTRY(Upgrade:,
  188. HttpHeaderUpgrade,
  189. MultipleHeaderHandler),
  190. CREATE_HEADER_MAP_ENTRY(User-Agent:,
  191. HttpHeaderUserAgent,
  192. SingleHeaderHandler),
  193. CREATE_HEADER_MAP_ENTRY(Via:,
  194. HttpHeaderVia,
  195. MultipleHeaderHandler),
  196. CREATE_HEADER_MAP_ENTRY(Vary:,
  197. HttpHeaderVary,
  198. NULL),
  199. CREATE_HEADER_MAP_ENTRY(Warning:,
  200. HttpHeaderWarning,
  201. MultipleHeaderHandler),
  202. CREATE_HEADER_MAP_ENTRY(WWW-Authenticate:,
  203. HttpHeaderWwwAuthenticate,
  204. NULL)
  205. };
  206. ULONG ResponseHeaderMap[HttpHeaderMaximum];
  207. //
  208. // The header index table. This is initialized by the init code.
  209. //
  210. HEADER_INDEX_ENTRY HeaderIndexTable[NUMBER_HEADER_INDICIES];
  211. #define NUMBER_FAST_VERB_ENTRIES (sizeof(FastVerbTable)/sizeof(FAST_VERB_ENTRY))
  212. #define NUMBER_LONG_VERB_ENTRIES (sizeof(LongVerbTable)/sizeof(LONG_VERB_ENTRY))
  213. #define NUMBER_HEADER_MAP_ENTRIES (sizeof(HeaderMapTable)/sizeof(HEADER_MAP_ENTRY))
  214. const char DefaultChar = '_';
  215. ULONG
  216. GenerateDateHeader(
  217. OUT PUCHAR pBuffer
  218. );
  219. /*++
  220. Routine Description:
  221. A utility routine to find a token. We take an input pointer, skip any
  222. preceding LWS, then scan the token until we find either LWS or a CRLF
  223. pair.
  224. Arguments:
  225. pBuffer - Buffer to search for token.
  226. BufferLength - Length of data pointed to by pBuffer.
  227. TokenLength - Where to return the length of the token.
  228. Return Value:
  229. A pointer to the token we found, as well as the length, or NULL if we
  230. don't find a delimited token.
  231. --*/
  232. PUCHAR
  233. FindWSToken(
  234. IN PUCHAR pBuffer,
  235. IN ULONG BufferLength,
  236. OUT ULONG *pTokenLength
  237. )
  238. {
  239. PUCHAR pTokenStart;
  240. //
  241. // Sanity check.
  242. //
  243. PAGED_CODE();
  244. //
  245. // First, skip any preceding LWS.
  246. //
  247. while (BufferLength > 0 && IS_HTTP_LWS(*pBuffer))
  248. {
  249. pBuffer++;
  250. BufferLength--;
  251. }
  252. // If we stopped because we ran out of buffer, fail.
  253. if (BufferLength == 0)
  254. {
  255. return NULL;
  256. }
  257. pTokenStart = pBuffer;
  258. // Now skip over the token, until we see either LWS or a CR or LF.
  259. while (BufferLength != 0 &&
  260. (*pBuffer != CR &&
  261. *pBuffer != SP &&
  262. *pBuffer != LF &&
  263. *pBuffer != HT)
  264. )
  265. {
  266. pBuffer++;
  267. BufferLength--;
  268. }
  269. // See why we stopped.
  270. if (BufferLength == 0)
  271. {
  272. // Ran out of buffer before end of token.
  273. return NULL;
  274. }
  275. // Success. Set the token length and return the start of the token.
  276. *pTokenLength = DIFF(pBuffer - pTokenStart);
  277. return pTokenStart;
  278. } // FindWSToken
  279. /*++
  280. Routine Description:
  281. The slower way to look up a verb. We find the verb in the request and then
  282. look for it in the LongVerbTable. If it's not found, we'll return
  283. UnknownVerb. If it can't be parsed we return UnparsedVerb. Otherwise
  284. we return the verb type.
  285. Arguments:
  286. pHttpRequest - Pointer to the incoming HTTP request.
  287. HttpRequestLength - Length of data pointed to by pHttpRequest.
  288. pVerb - Where we return a pointer to the verb, if it's an
  289. unknown ver.
  290. ppVerbLength - Where we return the length of the verb
  291. pBytesTaken - The total length consumed, including the length of
  292. the verb plus preceding & 1 trailing whitespace.
  293. Return Value:
  294. The verb we found, or the appropriate error.
  295. --*/
  296. NTSTATUS
  297. LookupVerb(
  298. IN PUL_INTERNAL_REQUEST pRequest,
  299. IN PUCHAR pHttpRequest,
  300. IN ULONG HttpRequestLength,
  301. OUT ULONG * pBytesTaken
  302. )
  303. {
  304. ULONG TokenLength;
  305. PUCHAR pToken;
  306. PUCHAR pTempRequest;
  307. ULONG TempLength;
  308. ULONG i;
  309. //
  310. // Sanity check.
  311. //
  312. PAGED_CODE();
  313. // Since we may have gotten here due to a extraneous CRLF pair, skip
  314. // any of those now. Need to use a temporary variable since
  315. // the original input pointer and length are used below.
  316. pTempRequest = pHttpRequest;
  317. TempLength = HttpRequestLength;
  318. while ( TempLength != 0 &&
  319. ((*pTempRequest == CR) || (*pTempRequest == LF)) )
  320. {
  321. pTempRequest++;
  322. TempLength--;
  323. }
  324. // First find the verb.
  325. pToken = FindWSToken(pTempRequest, TempLength, &TokenLength);
  326. if (pToken == NULL)
  327. {
  328. // Didn't find it, let's get more buffer
  329. //
  330. pRequest->Verb = HttpVerbUnparsed;
  331. *pBytesTaken = 0;
  332. return STATUS_SUCCESS;
  333. }
  334. // Make sure we stopped because of a SP.
  335. if (*(pToken + TokenLength) != SP)
  336. {
  337. // Bad verb section!
  338. //
  339. pRequest->Verb = HttpVerbInvalid;
  340. pRequest->ErrorCode = UlErrorVerb;
  341. pRequest->ParseState = ParseErrorState;
  342. UlTrace(PARSER, (
  343. "ul!LookupVerb(pRequest = %p) ERROR: no space after verb\n",
  344. pRequest
  345. ));
  346. return STATUS_INVALID_DEVICE_REQUEST;
  347. }
  348. // Otherwise, we found one, so update bytes taken and look up up in
  349. // the table.
  350. *pBytesTaken = DIFF(pToken - pHttpRequest) + TokenLength + 1;
  351. for (i = 0; i < NUMBER_LONG_VERB_ENTRIES; i++)
  352. {
  353. if (LongVerbTable[i].RawVerbLength == TokenLength &&
  354. RtlEqualMemory(pToken, LongVerbTable[i].RawVerb, TokenLength))
  355. {
  356. // Found it.
  357. //
  358. pRequest->Verb = LongVerbTable[i].TranslatedVerb;
  359. return STATUS_SUCCESS;
  360. }
  361. }
  362. // The only other things this could be are an unknown verb or a very
  363. // small 0.9 request. Since 0.9 requests can only be GETs, check that
  364. // now.
  365. if (HttpRequestLength >= (sizeof("GET ") - 1))
  366. {
  367. if (RtlEqualMemory(pHttpRequest, "GET ", sizeof("GET ") - 1))
  368. {
  369. // This is a GET request.
  370. //
  371. pRequest->Verb = HttpVerbGET;
  372. return STATUS_SUCCESS;
  373. }
  374. }
  375. //
  376. // If we got here, we searched the table and didn't find it.
  377. //
  378. //
  379. // It's a raw verb
  380. //
  381. pRequest->Verb = HttpVerbUnknown;
  382. pRequest->pRawVerb = pToken;
  383. pRequest->RawVerbLength = TokenLength;
  384. //
  385. // include room for the terminator
  386. //
  387. pRequest->TotalRequestSize += (TokenLength + 1) * sizeof(WCHAR);
  388. return STATUS_SUCCESS;
  389. } // LookupVerb
  390. /*++
  391. Routine Description:
  392. A utility routine to parse an absolute URL in a URL string. When this
  393. is called we already have loaded the entire url into RawUrl.pUrl and
  394. know that it start with "http".
  395. this functions job is to set RawUrl.pHost + RawUrl.pAbsPath.
  396. Arguments:
  397. pRequest - Pointer to the HTTP_REQUEST
  398. Return Value:
  399. NTSTATUS
  400. Author:
  401. Henry Sanders () 1998
  402. Paul McDaniel (paulmcd) 6-Mar-1999
  403. --*/
  404. NTSTATUS
  405. ParseFullUrl(
  406. IN PUL_INTERNAL_REQUEST pRequest
  407. )
  408. {
  409. PUCHAR pURL;
  410. ULONG UrlLength;
  411. PUCHAR pUrlStart;
  412. //
  413. // Sanity check.
  414. //
  415. PAGED_CODE();
  416. pURL = pRequest->RawUrl.pUrl;
  417. UrlLength = pRequest->RawUrl.Length;
  418. //
  419. // When we're called, we know that the start of the URL must point at
  420. // an absolute scheme prefix. Adjust for that now.
  421. //
  422. pUrlStart = pURL + HTTP_PREFIX_SIZE;
  423. UrlLength -= HTTP_PREFIX_SIZE;
  424. //
  425. // Now check the second half of the absolute URL prefix. We use the larger
  426. // of the two possible prefix length here to do the check, because even if
  427. // it's the smaller of the two we'll need the extra bytes after the prefix
  428. // anyway for the host name.
  429. //
  430. if (UrlLength < HTTP_PREFIX2_SIZE)
  431. {
  432. pRequest->ErrorCode = UlErrorUrl;
  433. pRequest->ParseState = ParseErrorState;
  434. UlTrace(PARSER, (
  435. "ul!ParseFullUrl(pRequest = %p) ERROR: no room for URL scheme name\n",
  436. pRequest
  437. ));
  438. return STATUS_INVALID_DEVICE_REQUEST;
  439. }
  440. if ( (*(PULONG)pUrlStart & HTTP_PREFIX1_MASK) == HTTP_PREFIX1)
  441. {
  442. // Valid absolute URL.
  443. pUrlStart += HTTP_PREFIX1_SIZE;
  444. UrlLength -= HTTP_PREFIX1_SIZE;
  445. }
  446. else
  447. {
  448. if ( (*(PULONG)pUrlStart & HTTP_PREFIX2_MASK) == HTTP_PREFIX2)
  449. {
  450. // Valid absolute URL.
  451. pUrlStart += HTTP_PREFIX2_SIZE;
  452. UrlLength -= HTTP_PREFIX2_SIZE;
  453. }
  454. else
  455. {
  456. pRequest->ErrorCode = UlErrorUrl;
  457. pRequest->ParseState = ParseErrorState;
  458. UlTrace(PARSER, (
  459. "ul!ParseFullUrl(pRequest = %p) ERROR: invalid URL scheme name\n",
  460. pRequest
  461. ));
  462. return STATUS_INVALID_DEVICE_REQUEST;
  463. }
  464. }
  465. //
  466. // OK, we've got a valid absolute URL, and we've skipped over
  467. // the prefix part of it. Save a pointer to the host, and
  468. // search the host string until we find the trailing slash,
  469. // which signifies the end of the host/start of the absolute
  470. // path.
  471. //
  472. pRequest->RawUrl.pHost = pUrlStart;
  473. //
  474. // scan the host looking for the terminator
  475. //
  476. while (UrlLength > 0 && pUrlStart[0] != '/')
  477. {
  478. pUrlStart++;
  479. UrlLength--;
  480. }
  481. if (UrlLength == 0)
  482. {
  483. //
  484. // Ran out of buffer, can't happen, we get the full url passed in
  485. //
  486. pRequest->ErrorCode = UlErrorUrl;
  487. pRequest->ParseState = ParseErrorState;
  488. UlTrace(PARSER, (
  489. "ul!ParseFullUrl(pRequest = %p) ERROR: end of host name not found\n",
  490. pRequest
  491. ));
  492. return STATUS_INVALID_DEVICE_REQUEST;
  493. }
  494. //
  495. // Otherwise, pUrlStart points to the start of the absolute path portion.
  496. //
  497. pRequest->RawUrl.pAbsPath = pUrlStart;
  498. return STATUS_SUCCESS;
  499. } // ParseFullUrl
  500. /*++
  501. Routine Description:
  502. Look up a header that we don't have in our fast lookup table. This
  503. could be because it's a header we don't understand, or because we
  504. couldn't use the fast lookup table due to insufficient buffer length.
  505. The latter reason is uncommon, but we'll check the input table anyway
  506. if we're given one. If we find a header match in our mapping table,
  507. we'll call the header handler. Otherwise we'll try to allocate an
  508. unknown header element, fill it in and chain it on the http connection.
  509. Arguments:
  510. pHttpConn - Pointer to the current connection on which the
  511. request arrived.
  512. pHttpRequest - Pointer to the current request.
  513. HttpRequestLength - Bytes left in the request.
  514. pHeaderMap - Pointer to start of an array of header map entries
  515. (may be NULL).
  516. HeaderMapCount - Number of entries in array pointed to by pHeaderMap.
  517. Return Value:
  518. Number of bytes in the header (including CRLF), or 0 if we couldn't
  519. parse the header.
  520. --*/
  521. NTSTATUS
  522. LookupHeader(
  523. IN PUL_INTERNAL_REQUEST pRequest,
  524. IN PUCHAR pHttpRequest,
  525. IN ULONG HttpRequestLength,
  526. IN PHEADER_MAP_ENTRY pHeaderMap,
  527. IN ULONG HeaderMapCount,
  528. OUT ULONG * pBytesTaken
  529. )
  530. {
  531. NTSTATUS Status = STATUS_SUCCESS;
  532. ULONG CurrentOffset;
  533. ULONG HeaderNameLength;
  534. ULONG i;
  535. ULONG BytesTaken;
  536. ULONG HeaderValueLength;
  537. UCHAR CurrentChar;
  538. BOOLEAN EncodedWord;
  539. PUL_HTTP_UNKNOWN_HEADER pUnknownHeader;
  540. PLIST_ENTRY pListStart;
  541. PLIST_ENTRY pCurrentListEntry;
  542. ULONG OldHeaderLength;
  543. PUCHAR pHeaderValue;
  544. //
  545. // Sanity check.
  546. //
  547. PAGED_CODE();
  548. //
  549. // First, let's find the terminating : of the header name, if there is one.
  550. // This will also give us the length of the header, which we can then
  551. // use to search the header map table if we have one.
  552. //
  553. for (CurrentOffset = 0; CurrentOffset < HttpRequestLength; CurrentOffset++)
  554. {
  555. CurrentChar = *(pHttpRequest + CurrentOffset);
  556. if (CurrentChar == ':')
  557. {
  558. // We've found the end of the header.
  559. break;
  560. }
  561. else
  562. {
  563. if (!IS_HTTP_TOKEN(CurrentChar))
  564. {
  565. // Uh-oh, this isn't a valid header. What do we do now?
  566. //
  567. pRequest->ErrorCode = UlErrorHeader;
  568. pRequest->ParseState = ParseErrorState;
  569. UlTrace(PARSER, (
  570. "ul!LookupHeader(pRequest = %p) CurrentChar = 0x%x\n"
  571. " ERROR: invalid header char\n",
  572. pRequest,
  573. CurrentChar
  574. ));
  575. Status = STATUS_INVALID_DEVICE_REQUEST;
  576. goto end;
  577. }
  578. }
  579. }
  580. // Find out why we got out. If the current offset is less than the
  581. // header length, we got out because we found the :.
  582. if (CurrentOffset < HttpRequestLength)
  583. {
  584. // Found the terminator.
  585. CurrentOffset++; // Update to point beyond termintor.
  586. HeaderNameLength = CurrentOffset;
  587. }
  588. else
  589. {
  590. // Didn't find the :, need more.
  591. //
  592. *pBytesTaken = 0;
  593. goto end;
  594. }
  595. // See if we have a header map array we need to search.
  596. //
  597. if (pHeaderMap != NULL)
  598. {
  599. // We do have an array to search.
  600. for (i = 0; i < HeaderMapCount; i++)
  601. {
  602. ASSERT(pHeaderMap->pHandler != NULL);
  603. if (HeaderNameLength == pHeaderMap->HeaderLength &&
  604. _strnicmp(
  605. (const char *)(pHttpRequest),
  606. (const char *)(pHeaderMap->Header.HeaderChar),
  607. HeaderNameLength
  608. ) == 0 &&
  609. pHeaderMap->pHandler != NULL)
  610. {
  611. // This header matches. Call the handling function for it.
  612. Status = (*(pHeaderMap->pHandler))(
  613. pRequest,
  614. pHttpRequest + HeaderNameLength,
  615. HttpRequestLength - HeaderNameLength,
  616. pHeaderMap->HeaderID,
  617. &BytesTaken
  618. );
  619. if (NT_SUCCESS(Status) == FALSE)
  620. goto end;
  621. // If the handler consumed a non-zero number of bytes, it
  622. // worked, so return that number plus the header length.
  623. //
  624. // BUGBUG - it might be possible for a header handler to
  625. // encounter an error, for example being unable to
  626. // allocate memory, or a bad syntax in some header. We
  627. // need a more sophisticated method to detect this than
  628. // just checking bytes taken.
  629. //
  630. if (BytesTaken != 0)
  631. {
  632. *pBytesTaken = HeaderNameLength + BytesTaken;
  633. goto end;
  634. }
  635. // Otherwise he didn't take anything, so return 0.
  636. // we need more buffer
  637. //
  638. *pBytesTaken = 0;
  639. goto end;
  640. }
  641. pHeaderMap++;
  642. }
  643. }
  644. // OK, at this point either we had no header map array or none of them
  645. // matched. We have an unknown header. Just make sure this header is
  646. // terminated and save a pointer to it.
  647. // Find the end of the header value
  648. //
  649. Status = FindHeaderEnd(
  650. pRequest,
  651. pHttpRequest + HeaderNameLength,
  652. HttpRequestLength - HeaderNameLength,
  653. &EncodedWord,
  654. &BytesTaken
  655. );
  656. if (NT_SUCCESS(Status) == FALSE)
  657. goto end;
  658. if (BytesTaken == 0)
  659. {
  660. *pBytesTaken = 0;
  661. goto end;
  662. }
  663. //
  664. // Strip of the trailing CRLF from the header value length
  665. //
  666. HeaderValueLength = BytesTaken - CRLF_SIZE;
  667. pHeaderValue = pHttpRequest + HeaderNameLength;
  668. //
  669. // skip any preceding LWS.
  670. //
  671. while ( HeaderValueLength > 0 && IS_HTTP_LWS(*pHeaderValue) )
  672. {
  673. pHeaderValue++;
  674. HeaderValueLength--;
  675. }
  676. // Have an unknown header. Search our list of unknown headers,
  677. // and if we've already seen one instance of this header add this
  678. // on. Otherwise allocate an unknown header structure and set it
  679. // to point at this header.
  680. pListStart = &pRequest->UnknownHeaderList;
  681. for (pCurrentListEntry = pRequest->UnknownHeaderList.Flink;
  682. pCurrentListEntry != pListStart;
  683. pCurrentListEntry = pCurrentListEntry->Flink
  684. )
  685. {
  686. pUnknownHeader = CONTAINING_RECORD(
  687. pCurrentListEntry,
  688. UL_HTTP_UNKNOWN_HEADER,
  689. List
  690. );
  691. //
  692. // somehow HeaderNameLength includes the ':' character,
  693. // which is not the case of pUnknownHeader->HeaderNameLength.
  694. //
  695. // so we need to adjust for this here
  696. //
  697. if ((HeaderNameLength-1) == pUnknownHeader->HeaderNameLength &&
  698. _strnicmp(
  699. (const char *)(pHttpRequest),
  700. (const char *)(pUnknownHeader->pHeaderName),
  701. (HeaderNameLength-1)
  702. ) == 0)
  703. {
  704. // This header matches.
  705. OldHeaderLength = pUnknownHeader->HeaderValue.HeaderLength;
  706. Status = AppendHeaderValue(
  707. &pUnknownHeader->HeaderValue,
  708. pHeaderValue,
  709. HeaderValueLength
  710. );
  711. if (NT_SUCCESS(Status) == FALSE)
  712. goto end;
  713. //
  714. // encoded?
  715. //
  716. pUnknownHeader->HeaderValue.Encoded = EncodedWord ? 1 : 0;
  717. //
  718. // Successfully appended it. Update the total request
  719. // length for the length added. no need to add 1 for
  720. // the terminator, just add our new char count.
  721. //
  722. pRequest->TotalRequestSize +=
  723. (pUnknownHeader->HeaderValue.HeaderLength
  724. - OldHeaderLength) * sizeof(WCHAR);
  725. //
  726. // don't subtract for the ':' character, as that character
  727. // was "taken"
  728. //
  729. *pBytesTaken = HeaderNameLength + BytesTaken;
  730. goto end;
  731. } // if (headermatch)
  732. } // for (walk list)
  733. //
  734. // Didn't find a match. Allocate a new unknown header structure, set
  735. // it up and add it to the list.
  736. //
  737. pUnknownHeader = UL_ALLOCATE_STRUCT(
  738. NonPagedPool,
  739. UL_HTTP_UNKNOWN_HEADER,
  740. UL_UNKNOWN_HEADER_POOL_TAG
  741. );
  742. if (pUnknownHeader == NULL)
  743. {
  744. Status = STATUS_NO_MEMORY;
  745. goto end;
  746. }
  747. //
  748. // subtract the : from the header name length
  749. //
  750. pUnknownHeader->HeaderNameLength = HeaderNameLength - 1;
  751. pUnknownHeader->pHeaderName = pHttpRequest;
  752. //
  753. // header value
  754. //
  755. pUnknownHeader->HeaderValue.HeaderLength = HeaderValueLength;
  756. pUnknownHeader->HeaderValue.pHeader = pHeaderValue;
  757. //
  758. // null terminate our copy, the terminating CRLF gives
  759. // us space for this
  760. //
  761. pHeaderValue[HeaderValueLength] = ANSI_NULL;
  762. //
  763. // flags
  764. //
  765. pUnknownHeader->HeaderValue.OurBuffer = 0;
  766. pUnknownHeader->HeaderValue.Valid = 1;
  767. pUnknownHeader->HeaderValue.Encoded = EncodedWord ? 1 : 0;
  768. InsertTailList(&pRequest->UnknownHeaderList, &pUnknownHeader->List);
  769. pRequest->UnknownHeaderCount++;
  770. //
  771. // subtract 1 for the ':' and add space for the 2 terminiators
  772. //
  773. pRequest->TotalRequestSize +=
  774. ((HeaderNameLength - 1 + 1) + HeaderValueLength + 1) * sizeof(WCHAR);
  775. *pBytesTaken = HeaderNameLength + BytesTaken;
  776. end:
  777. return Status;
  778. } // LookupHeader
  779. /*++
  780. Routine Description:
  781. The routine to parse an individual header. We take in a pointer to the
  782. header and the bytes remaining in the request, and try to find
  783. the header in our lookup table. We try first the fast way, and then
  784. try again the slow way in case there wasn't quite enough data the first
  785. time.
  786. On input, HttpRequestLength is at least CRLF_SIZE.
  787. Arguments:
  788. pRequest - Pointer to the current connection on which the
  789. request arrived.
  790. pHttpRequest - Pointer to the current request.
  791. HttpRequestLength - Bytes left in the request.
  792. Return Value:
  793. Number of bytes in the header (including CRLF), or 0 if we couldn't
  794. parse the header.
  795. --*/
  796. NTSTATUS
  797. ParseHeader(
  798. IN PUL_INTERNAL_REQUEST pRequest,
  799. IN PUCHAR pHttpRequest,
  800. IN ULONG HttpRequestLength,
  801. OUT ULONG * pBytesTaken
  802. )
  803. {
  804. NTSTATUS Status = STATUS_SUCCESS;
  805. ULONG i;
  806. ULONG j;
  807. ULONG BytesTaken;
  808. ULONGLONG Temp;
  809. UCHAR c;
  810. PHEADER_MAP_ENTRY pCurrentHeaderMap;
  811. ULONG HeaderMapCount;
  812. PUL_HTTP_HEADER pFoundHeader;
  813. BOOLEAN SmallHeader = FALSE;
  814. //
  815. // Sanity check.
  816. //
  817. PAGED_CODE();
  818. ASSERT(HttpRequestLength >= CRLF_SIZE);
  819. c = *pHttpRequest;
  820. // message-headers start with field-name [= token]
  821. //
  822. if (IS_HTTP_TOKEN(c) == FALSE)
  823. {
  824. pRequest->ErrorCode = UlErrorHeader;
  825. pRequest->ParseState = ParseErrorState;
  826. UlTrace(PARSER, (
  827. "ul!ParseHeader(pRequest = %p) c = 0x%x ERROR: invalid header char\n",
  828. pRequest,
  829. c
  830. ));
  831. return STATUS_INVALID_DEVICE_REQUEST;
  832. }
  833. // Does the header start with an alpha?
  834. //
  835. if (IS_HTTP_ALPHA(c))
  836. {
  837. // Uppercase the character, and find the appropriate set of header map
  838. // entries.
  839. //
  840. c = UPCASE_CHAR(c);
  841. c -= 'A';
  842. pCurrentHeaderMap = HeaderIndexTable[c].pHeaderMap;
  843. HeaderMapCount = HeaderIndexTable[c].Count;
  844. // Loop through all the header map entries that might match
  845. // this header, and check them. The count will be 0 if there
  846. // are no entries that might match and we'll skip the loop.
  847. for (i = 0; i < HeaderMapCount; i++)
  848. {
  849. ASSERT(pCurrentHeaderMap->pHandler != NULL);
  850. // If we have enough bytes to do the fast check, do it.
  851. // Otherwise skip this. We may skip a valid match, but if
  852. // so we'll catch it later.
  853. if (HttpRequestLength >= pCurrentHeaderMap->MinBytesNeeded)
  854. {
  855. for (j = 0; j < pCurrentHeaderMap->ArrayCount; j++)
  856. {
  857. Temp = *(PULONGLONG)(pHttpRequest +
  858. (j * sizeof(ULONGLONG)));
  859. if ((Temp & pCurrentHeaderMap->HeaderMask[j]) !=
  860. pCurrentHeaderMap->Header.HeaderLong[j] )
  861. {
  862. break;
  863. }
  864. }
  865. // See why we exited out.
  866. if (j == pCurrentHeaderMap->ArrayCount &&
  867. pCurrentHeaderMap->pHandler != NULL)
  868. {
  869. // Exited because we found a match. Call the
  870. // handler for this header to take cake of this.
  871. Status = (*(pCurrentHeaderMap->pHandler))(
  872. pRequest,
  873. pHttpRequest +
  874. pCurrentHeaderMap->HeaderLength,
  875. HttpRequestLength -
  876. pCurrentHeaderMap->HeaderLength,
  877. pCurrentHeaderMap->HeaderID,
  878. &BytesTaken
  879. );
  880. if (NT_SUCCESS(Status) == FALSE)
  881. goto end;
  882. // If the handler consumed a non-zero number of
  883. // bytes, it worked, so return that number plus
  884. // the header length.
  885. if (BytesTaken != 0)
  886. {
  887. *pBytesTaken = pCurrentHeaderMap->HeaderLength +
  888. BytesTaken;
  889. goto end;
  890. }
  891. // Otherwise need more buffer
  892. //
  893. *pBytesTaken = 0;
  894. goto end;
  895. }
  896. // If we get here, we exited out early because a match
  897. // failed, so keep going.
  898. }
  899. else if (SmallHeader == FALSE)
  900. {
  901. //
  902. // Remember that we didn't check a header map entry
  903. // because the bytes in the buffer was not LONGLONG
  904. // aligned
  905. //
  906. SmallHeader = TRUE;
  907. }
  908. // Either didn't match or didn't have enough bytes for the
  909. // check. In either case, check the next header map entry.
  910. pCurrentHeaderMap++;
  911. }
  912. // Got all the way through the appropriate header map entries
  913. // without a match. This could be because we're dealing with a
  914. // header we don't know about or because it's a header we
  915. // care about that was too small to do the fast check. The
  916. // latter case should be very rare, but we still need to
  917. // handle it.
  918. // Update the current header map pointer to point back to the
  919. // first of the possibles. If there were no possibles,
  920. // the pointer will be NULL and the HeaderMapCount 0, so it'll
  921. // stay NULL. Otherwise the subtraction will back it up the
  922. // appropriate amount.
  923. if (SmallHeader)
  924. {
  925. pCurrentHeaderMap -= HeaderMapCount;
  926. }
  927. else
  928. {
  929. pCurrentHeaderMap = NULL;
  930. HeaderMapCount = 0;
  931. }
  932. }
  933. else
  934. {
  935. pCurrentHeaderMap = NULL;
  936. HeaderMapCount = 0;
  937. }
  938. // At this point either the header starts with a non-alphabetic
  939. // character or we don't have a set of header map entries for it.
  940. Status = LookupHeader(
  941. pRequest,
  942. pHttpRequest,
  943. HttpRequestLength,
  944. pCurrentHeaderMap,
  945. HeaderMapCount,
  946. &BytesTaken
  947. );
  948. if (NT_SUCCESS(Status) == FALSE)
  949. goto end;
  950. // Lookup header returns the total bytes taken, including the header name
  951. //
  952. *pBytesTaken = BytesTaken;
  953. end:
  954. return Status;
  955. } // ParseHeader
  956. NTSTATUS
  957. ParseHeaders(
  958. PUL_INTERNAL_REQUEST pRequest,
  959. PUCHAR pBuffer,
  960. ULONG BufferLength,
  961. PULONG pBytesTaken
  962. )
  963. {
  964. NTSTATUS Status;
  965. ULONG BytesTaken;
  966. *pBytesTaken = 0;
  967. //
  968. // loop over all headers
  969. //
  970. while (BufferLength >= CRLF_SIZE)
  971. {
  972. //
  973. // If this is an empty header, we're done with this stage
  974. //
  975. if (*(PUSHORT)pBuffer == CRLF ||
  976. *(PUSHORT)pBuffer == LFLF)
  977. {
  978. //
  979. // consume it
  980. //
  981. pBuffer += CRLF_SIZE;
  982. *pBytesTaken += CRLF_SIZE;
  983. BufferLength -= CRLF_SIZE;
  984. Status = STATUS_SUCCESS;
  985. goto end;
  986. }
  987. // Otherwise call our header parse routine to deal with this.
  988. Status = ParseHeader(
  989. pRequest,
  990. pBuffer,
  991. BufferLength,
  992. &BytesTaken
  993. );
  994. if (NT_SUCCESS(Status) == FALSE)
  995. goto end;
  996. //
  997. // If no bytes were consumed, the header must be incomplete, so
  998. // bail out until we get more data on this connection.
  999. //
  1000. if (BytesTaken == 0)
  1001. {
  1002. Status = STATUS_MORE_PROCESSING_REQUIRED;
  1003. goto end;
  1004. }
  1005. //
  1006. // Otherwise we parsed a header, so update and continue.
  1007. //
  1008. pBuffer += BytesTaken;
  1009. *pBytesTaken += BytesTaken;
  1010. BufferLength -= BytesTaken;
  1011. }
  1012. //
  1013. // we only get here if we didn't see the CRLF headers terminator
  1014. //
  1015. // we need more data
  1016. //
  1017. Status = STATUS_MORE_PROCESSING_REQUIRED;
  1018. end:
  1019. return Status;
  1020. } // ParseHeaders
  1021. NTSTATUS
  1022. ParseChunkLength(
  1023. IN PUL_INTERNAL_REQUEST pRequest,
  1024. IN PUCHAR pBuffer,
  1025. IN ULONG BufferLength,
  1026. OUT PULONG pBytesTaken,
  1027. OUT PULONGLONG pChunkLength
  1028. )
  1029. {
  1030. NTSTATUS Status;
  1031. PUCHAR pToken;
  1032. UCHAR SaveChar;
  1033. ULONG TokenLength;
  1034. BOOLEAN Encoded;
  1035. ULONG BytesTaken;
  1036. ULONG TotalBytesTaken = 0;
  1037. ASSERT(pBytesTaken != NULL);
  1038. ASSERT(pChunkLength != NULL);
  1039. //
  1040. // 2 cases:
  1041. //
  1042. // 1) the first chunk where the length follows the headers
  1043. // 2) subsequent chunks where the length follows a previous chunk
  1044. //
  1045. // in case 1 pBuffer will point straight to the chunk length.
  1046. //
  1047. // in case 2 pBuffer will point to the CRLF that terminated the previous
  1048. // chunk, this needs to be consumed, skipped, and then the chunk length
  1049. // read.
  1050. //
  1051. // BUGBUG: need to handle chunk-extensions embedded in the length field
  1052. //
  1053. //
  1054. // if we are case 2 (see above)
  1055. //
  1056. if (pRequest->ParsedFirstChunk == 1)
  1057. {
  1058. //
  1059. // make sure there is enough space first
  1060. //
  1061. if (BufferLength < CRLF_SIZE)
  1062. {
  1063. Status = STATUS_MORE_PROCESSING_REQUIRED;
  1064. goto end;
  1065. }
  1066. //
  1067. // now it better be a terminator
  1068. //
  1069. if (*(PUSHORT)pBuffer != CRLF &&
  1070. *(PUSHORT)pBuffer != LFLF)
  1071. {
  1072. UlTrace(PARSER, (
  1073. "ul!ParseChunkLength(pRequest = %p) ERROR: No CRLF at the end of chunk-data\n",
  1074. pRequest
  1075. ));
  1076. Status = STATUS_INVALID_DEVICE_REQUEST;
  1077. goto end;
  1078. }
  1079. //
  1080. // update our book-keeping
  1081. //
  1082. pBuffer += CRLF_SIZE;
  1083. TotalBytesTaken += CRLF_SIZE;
  1084. BufferLength -= CRLF_SIZE;
  1085. }
  1086. pToken = FindWSToken(pBuffer, BufferLength, &TokenLength);
  1087. if (pToken == NULL)
  1088. {
  1089. //
  1090. // not enough buffer
  1091. //
  1092. Status = STATUS_MORE_PROCESSING_REQUIRED;
  1093. goto end;
  1094. }
  1095. //
  1096. // Was there any token ?
  1097. //
  1098. if (TokenLength == 0)
  1099. {
  1100. UlTrace(PARSER, (
  1101. "ul!ParseChunkLength(pRequest = %p) ERROR: No length!\n",
  1102. pRequest
  1103. ));
  1104. Status = STATUS_INVALID_DEVICE_REQUEST;
  1105. goto end;
  1106. }
  1107. //
  1108. // Add the bytes consumed by FindWSToken
  1109. // (the token bytes plus preceding bytes)
  1110. //
  1111. TotalBytesTaken += DIFF((pToken + TokenLength) - pBuffer);
  1112. //
  1113. // and find the end
  1114. //
  1115. Status = FindHeaderEnd(
  1116. pRequest,
  1117. pToken + TokenLength,
  1118. BufferLength - DIFF((pToken + TokenLength) - pBuffer),
  1119. &Encoded,
  1120. &BytesTaken
  1121. );
  1122. if (NT_SUCCESS(Status) == FALSE)
  1123. goto end;
  1124. if (BytesTaken == 0)
  1125. {
  1126. Status = STATUS_MORE_PROCESSING_REQUIRED;
  1127. goto end;
  1128. }
  1129. TotalBytesTaken += BytesTaken;
  1130. //
  1131. // now update the HTTP_REQUEST
  1132. //
  1133. SaveChar = pToken[TokenLength];
  1134. pToken[TokenLength] = ANSI_NULL;
  1135. Status = UlAnsiToULongLong(
  1136. pToken,
  1137. 16, // Base
  1138. pChunkLength
  1139. );
  1140. pToken[TokenLength] = SaveChar;
  1141. //
  1142. // Did the number conversion fail ?
  1143. //
  1144. if (NT_SUCCESS(Status) == FALSE)
  1145. {
  1146. if (Status == STATUS_SECTION_TOO_BIG)
  1147. {
  1148. pRequest->ErrorCode = UlErrorEntityTooLarge;
  1149. }
  1150. else
  1151. {
  1152. pRequest->ErrorCode = UlErrorNum;
  1153. }
  1154. pRequest->ParseState = ParseErrorState;
  1155. UlTrace(PARSER, (
  1156. "ul!ParseChunkLength(pRequest = %p) ERROR: didn't grok chunk length\n",
  1157. pRequest
  1158. ));
  1159. goto end;
  1160. }
  1161. //
  1162. // all done, return the bytes consumed
  1163. //
  1164. *pBytesTaken = TotalBytesTaken;
  1165. end:
  1166. RETURN(Status);
  1167. } // ParseChunkLength
  1168. /*++
  1169. Routine Description:
  1170. This is the core HTTP protocol request engine. It takes a stream of bytes
  1171. and parses them as an HTTP request.
  1172. Arguments:
  1173. pHttpRequest - Pointer to the incoming HTTP request.
  1174. HttpRequestLength - Length of data pointed to by HttpRequest.
  1175. Return Value:
  1176. Status of parse attempt.
  1177. --*/
  1178. NTSTATUS
  1179. ParseHttp(
  1180. IN PUL_INTERNAL_REQUEST pRequest,
  1181. IN PUCHAR pHttpRequest,
  1182. IN ULONG HttpRequestLength,
  1183. OUT ULONG *pBytesTaken
  1184. )
  1185. {
  1186. ULONG OriginalBufferLength;
  1187. ULONG TokenLength;
  1188. ULONG CurrentBytesTaken;
  1189. ULONG TotalBytesTaken;
  1190. ULONG i;
  1191. NTSTATUS ReturnStatus;
  1192. PUCHAR pStart;
  1193. //
  1194. // Sanity check.
  1195. //
  1196. PAGED_CODE();
  1197. ASSERT( IS_VALID_HTTP_REQUEST( pRequest ) );
  1198. ReturnStatus = STATUS_SUCCESS;
  1199. TotalBytesTaken = 0;
  1200. //
  1201. // remember the original buffer length
  1202. //
  1203. OriginalBufferLength = HttpRequestLength;
  1204. //
  1205. // put this label here to allow for a manual re-pump of the
  1206. // parser. this is currently used for 0.9 requests.
  1207. //
  1208. parse_it:
  1209. //
  1210. // what state are we in ?
  1211. //
  1212. switch (pRequest->ParseState)
  1213. {
  1214. case ParseVerbState:
  1215. // Look through the fast verb table for the verb. We can only do
  1216. // this if the input data is big enough.
  1217. if (HttpRequestLength >= sizeof(ULONGLONG))
  1218. {
  1219. ULONGLONG RawInputVerb;
  1220. RawInputVerb = *(ULONGLONG *)pHttpRequest;
  1221. // Loop through the fast verb table, looking for the verb.
  1222. for (i = 0; i < NUMBER_FAST_VERB_ENTRIES;i++)
  1223. {
  1224. // Mask out the raw input verb and compare against this
  1225. // entry.
  1226. if ((RawInputVerb & FastVerbTable[i].RawVerbMask) ==
  1227. FastVerbTable[i].RawVerb.LongLong)
  1228. {
  1229. // It matched. Save the translated verb from the
  1230. // table, update the request pointer and length,
  1231. // switch states and get out.
  1232. pRequest->Verb = FastVerbTable[i].TranslatedVerb;
  1233. CurrentBytesTaken = FastVerbTable[i].RawVerbLength;
  1234. pRequest->ParseState = ParseUrlState;
  1235. break;
  1236. }
  1237. }
  1238. }
  1239. if (pRequest->ParseState != ParseUrlState)
  1240. {
  1241. // Didn't switch states yet, because we haven't found the
  1242. // verb yet. This could be because a) the incoming request
  1243. // was too small to allow us to use our fast lookup (which
  1244. // might be OK in an HTTP/0.9 request), or b) the incoming
  1245. // verb is a PROPFIND or such that is too big to fit into
  1246. // our fast find table, or c) this is an unknown verb. In
  1247. // any of these cases call our slower verb parser to try
  1248. // again.
  1249. ReturnStatus = LookupVerb(
  1250. pRequest,
  1251. pHttpRequest,
  1252. HttpRequestLength,
  1253. &CurrentBytesTaken
  1254. );
  1255. if (NT_SUCCESS(ReturnStatus) == FALSE)
  1256. goto end;
  1257. if (CurrentBytesTaken == 0)
  1258. {
  1259. ReturnStatus = STATUS_MORE_PROCESSING_REQUIRED;
  1260. goto end;
  1261. }
  1262. //
  1263. // we finished parsing the custom verb
  1264. //
  1265. pRequest->ParseState = ParseUrlState;
  1266. }
  1267. //
  1268. // now fall through to ParseUrlState
  1269. //
  1270. pHttpRequest += CurrentBytesTaken;
  1271. HttpRequestLength -= CurrentBytesTaken;
  1272. TotalBytesTaken += CurrentBytesTaken;
  1273. case ParseUrlState:
  1274. //
  1275. // We're parsing the URL. pHTTPRequest points to the incoming URL,
  1276. // HttpRequestLength is the length of this request that is left.
  1277. //
  1278. //
  1279. // Find the WS terminating the URL.
  1280. //
  1281. pRequest->RawUrl.pUrl = FindWSToken(
  1282. pHttpRequest,
  1283. HttpRequestLength,
  1284. &TokenLength
  1285. );
  1286. if (pRequest->RawUrl.pUrl == NULL)
  1287. {
  1288. ReturnStatus = STATUS_MORE_PROCESSING_REQUIRED;
  1289. goto end;
  1290. }
  1291. //
  1292. // Bytes taken includes WS in front of URL
  1293. //
  1294. CurrentBytesTaken = DIFF(pRequest->RawUrl.pUrl - pHttpRequest) + TokenLength;
  1295. //
  1296. // set url length
  1297. //
  1298. pRequest->RawUrl.Length = TokenLength;
  1299. //
  1300. // Now, let's see if this is an absolute URL.
  1301. //
  1302. // BUGBUG: this is not case-insens.
  1303. if (pRequest->RawUrl.Length >= HTTP_PREFIX_SIZE &&
  1304. (*(PULONG)(pRequest->RawUrl.pUrl) & HTTP_PREFIX_MASK) ==
  1305. HTTP_PREFIX)
  1306. {
  1307. //
  1308. // It is. let's parse it and find the host.
  1309. //
  1310. ReturnStatus = ParseFullUrl(pRequest);
  1311. if (NT_SUCCESS(ReturnStatus) == FALSE)
  1312. goto end;
  1313. }
  1314. else
  1315. {
  1316. pRequest->RawUrl.pHost = NULL;
  1317. pRequest->RawUrl.pAbsPath = pRequest->RawUrl.pUrl;
  1318. }
  1319. //
  1320. // count the space it needs in the user's buffer, including terminator.
  1321. //
  1322. pRequest->TotalRequestSize +=
  1323. (pRequest->RawUrl.Length + 1) * sizeof(WCHAR);
  1324. //
  1325. // adjust our book keeping vars
  1326. //
  1327. pHttpRequest += CurrentBytesTaken;
  1328. HttpRequestLength -= CurrentBytesTaken;
  1329. TotalBytesTaken += CurrentBytesTaken;
  1330. //
  1331. // fall through to parsing the version.
  1332. //
  1333. pRequest->ParseState = ParseVersionState;
  1334. case ParseVersionState:
  1335. //
  1336. // skip lws
  1337. //
  1338. pStart = pHttpRequest;
  1339. while (HttpRequestLength > 0 && IS_HTTP_LWS(*pHttpRequest))
  1340. {
  1341. pHttpRequest++;
  1342. HttpRequestLength--;
  1343. }
  1344. //
  1345. // is this a 0.9 request (no version) ?
  1346. //
  1347. if (HttpRequestLength >= CRLF_SIZE)
  1348. {
  1349. if (*(PUSHORT)(pHttpRequest) == CRLF ||
  1350. *(PUSHORT)(pHttpRequest) == LFLF)
  1351. {
  1352. // This IS a 0.9 request. No need to go any further,
  1353. // since by definition there are no more headers.
  1354. // Just update things and get out.
  1355. TotalBytesTaken += DIFF(pHttpRequest - pStart) + CRLF_SIZE;
  1356. HTTP_SET_VERSION(pRequest->Version, 0, 9);
  1357. //
  1358. // set the state to CookState so that we parse the url
  1359. //
  1360. pRequest->ParseState = ParseCookState;
  1361. //
  1362. // manually restart the parse switch, we changed the
  1363. // parse state
  1364. //
  1365. goto parse_it;
  1366. }
  1367. }
  1368. //
  1369. // do we have enough buffer to strcmp the version?
  1370. //
  1371. if (HttpRequestLength < MIN_VERSION_SIZE)
  1372. {
  1373. ReturnStatus = STATUS_MORE_PROCESSING_REQUIRED;
  1374. goto end;
  1375. }
  1376. //
  1377. // let's compare it
  1378. //
  1379. if (*(PULONGLONG)pHttpRequest == HTTP_11_VERSION)
  1380. {
  1381. HTTP_SET_VERSION(pRequest->Version, 1, 1);
  1382. }
  1383. else
  1384. {
  1385. if (*(PULONGLONG)pHttpRequest == HTTP_10_VERSION)
  1386. {
  1387. HTTP_SET_VERSION(pRequest->Version, 1, 0);
  1388. }
  1389. else
  1390. {
  1391. // BUGBUG for now this is OK. In the future need to add code
  1392. // to check the major version number and handle as a 1.1
  1393. // request if we can.
  1394. pRequest->ErrorCode = UlErrorVersion;
  1395. pRequest->ParseState = ParseErrorState;
  1396. UlTrace(PARSER, (
  1397. "ul!ParseHttp(pRequest = %p) ERROR: unknown HTTP version\n",
  1398. pRequest
  1399. ));
  1400. ReturnStatus = STATUS_INVALID_DEVICE_REQUEST;
  1401. goto end;
  1402. }
  1403. }
  1404. HttpRequestLength -= MIN_VERSION_SIZE;
  1405. pHttpRequest += MIN_VERSION_SIZE;
  1406. //
  1407. // skip lws
  1408. //
  1409. while (HttpRequestLength > 0 && IS_HTTP_LWS(*pHttpRequest))
  1410. {
  1411. pHttpRequest++;
  1412. HttpRequestLength--;
  1413. }
  1414. //
  1415. // Make sure we're terminated on this line.
  1416. //
  1417. if (HttpRequestLength < CRLF_SIZE)
  1418. {
  1419. ReturnStatus = STATUS_MORE_PROCESSING_REQUIRED;
  1420. goto end;
  1421. }
  1422. if (*(PUSHORT)pHttpRequest != CRLF && *(PUSHORT)pHttpRequest != LFLF)
  1423. {
  1424. // Might want to be more liberal, and see if there's space
  1425. // after the version. This also could be a sub-version withing
  1426. // HTTP/1.1, ie HTTP/1.11 or something like that.
  1427. pRequest->ErrorCode = UlErrorVersion;
  1428. pRequest->ParseState = ParseErrorState;
  1429. UlTrace(PARSER, (
  1430. "ul!ParseHttp(pRequest = %p) ERROR: HTTP version not terminated right\n",
  1431. pRequest
  1432. ));
  1433. ReturnStatus = STATUS_INVALID_DEVICE_REQUEST;
  1434. goto end;
  1435. }
  1436. pHttpRequest += CRLF_SIZE;
  1437. HttpRequestLength -= CRLF_SIZE;
  1438. TotalBytesTaken += DIFF(pHttpRequest - pStart);
  1439. pRequest->ParseState = ParseHeadersState;
  1440. case ParseHeadersState:
  1441. ReturnStatus = ParseHeaders(
  1442. pRequest,
  1443. pHttpRequest,
  1444. HttpRequestLength,
  1445. &CurrentBytesTaken
  1446. );
  1447. pHttpRequest += CurrentBytesTaken;
  1448. HttpRequestLength -= CurrentBytesTaken;
  1449. TotalBytesTaken += CurrentBytesTaken;
  1450. if (NT_SUCCESS(ReturnStatus) == FALSE)
  1451. goto end;
  1452. //
  1453. // fall through, this is the only way to get here, we never return pending
  1454. // in this state
  1455. //
  1456. pRequest->ParseState = ParseCookState;
  1457. case ParseCookState:
  1458. //
  1459. // time for post processing. cook it up!
  1460. //
  1461. {
  1462. //
  1463. // First cook up the url, unicode it + such.
  1464. //
  1465. ReturnStatus = UlpCookUrl(pRequest);
  1466. if (NT_SUCCESS(ReturnStatus) == FALSE)
  1467. goto end;
  1468. //
  1469. // mark if we are chunk encoded
  1470. //
  1471. if (pRequest->Headers[HttpHeaderTransferEncoding].Valid == 1)
  1472. {
  1473. ASSERT(pRequest->Headers[HttpHeaderTransferEncoding].pHeader != NULL);
  1474. //
  1475. // CODEWORK, there can be more than 1 encoding
  1476. //
  1477. if (_stricmp(
  1478. (const char *)(
  1479. pRequest->Headers[HttpHeaderTransferEncoding].pHeader
  1480. ),
  1481. "chunked"
  1482. ) == 0)
  1483. {
  1484. pRequest->Chunked = 1;
  1485. }
  1486. else
  1487. {
  1488. //
  1489. // CODEWORK: temp hack for bug#352
  1490. //
  1491. UlTrace(PARSER, (
  1492. "ul!ParseHttp(pRequest = %p)"
  1493. " ERROR: unknown Transfer-Encoding!\n",
  1494. pRequest
  1495. ));
  1496. pRequest->ErrorCode = UlErrorNotImplemented;
  1497. pRequest->ParseState = ParseErrorState;
  1498. ReturnStatus = STATUS_INVALID_DEVICE_REQUEST;
  1499. goto end;
  1500. }
  1501. }
  1502. //
  1503. // Now let's decode the content length header
  1504. //
  1505. if (pRequest->Headers[HttpHeaderContentLength].Valid == 1)
  1506. {
  1507. ASSERT(pRequest->Headers[HttpHeaderContentLength].pHeader != NULL);
  1508. ReturnStatus = UlAnsiToULongLong(
  1509. pRequest->Headers[HttpHeaderContentLength].pHeader,
  1510. 10,
  1511. &pRequest->ContentLength
  1512. );
  1513. if (NT_SUCCESS(ReturnStatus) == FALSE)
  1514. {
  1515. if (ReturnStatus == STATUS_SECTION_TOO_BIG)
  1516. {
  1517. pRequest->ErrorCode = UlErrorEntityTooLarge;
  1518. }
  1519. else
  1520. {
  1521. pRequest->ErrorCode = UlErrorNum;
  1522. }
  1523. pRequest->ParseState = ParseErrorState;
  1524. UlTrace(PARSER, (
  1525. "ul!ParseHttp(pRequest = %p) ERROR: couldn't decode Content-Length\n",
  1526. pRequest
  1527. ));
  1528. goto end;
  1529. }
  1530. if (pRequest->Chunked == 0)
  1531. {
  1532. //
  1533. // prime the first (and only) chunk size
  1534. //
  1535. pRequest->ChunkBytesToParse = pRequest->ContentLength;
  1536. pRequest->ChunkBytesToRead = pRequest->ContentLength;
  1537. }
  1538. }
  1539. }
  1540. pRequest->ParseState = ParseEntityBodyState;
  1541. //
  1542. // fall through
  1543. //
  1544. case ParseEntityBodyState:
  1545. //
  1546. // the only parsing we do here is chunk length calculation,
  1547. // and that is not necessary if we have no more bytes to parse
  1548. //
  1549. if (pRequest->ChunkBytesToParse == 0)
  1550. {
  1551. //
  1552. // no more bytes left to parse, let's see if there are any
  1553. // more in the request
  1554. //
  1555. if (pRequest->Chunked == 1)
  1556. {
  1557. //
  1558. // the request is chunk encoded
  1559. //
  1560. //
  1561. // attempt to read the size of the next chunk
  1562. //
  1563. ReturnStatus = ParseChunkLength(
  1564. pRequest,
  1565. pHttpRequest,
  1566. HttpRequestLength,
  1567. &CurrentBytesTaken,
  1568. &(pRequest->ChunkBytesToParse)
  1569. );
  1570. if (NT_SUCCESS(ReturnStatus) == FALSE)
  1571. goto end;
  1572. //
  1573. // Otherwise we parsed it, so update and continue.
  1574. //
  1575. pHttpRequest += CurrentBytesTaken;
  1576. TotalBytesTaken += CurrentBytesTaken;
  1577. HttpRequestLength -= CurrentBytesTaken;
  1578. //
  1579. // was this the first chunk?
  1580. //
  1581. if (pRequest->ParsedFirstChunk == 0)
  1582. {
  1583. //
  1584. // Prime the reader, let it read the first chunk
  1585. // even though we haven't quite parsed it yet....
  1586. //
  1587. pRequest->ChunkBytesToRead = pRequest->ChunkBytesToParse;
  1588. pRequest->ParsedFirstChunk = 1;
  1589. }
  1590. //
  1591. // is this the last chunk (denoted with a 0 byte chunk)?
  1592. //
  1593. if (pRequest->ChunkBytesToParse == 0)
  1594. {
  1595. //
  1596. // time to parse the trailer
  1597. //
  1598. pRequest->ParseState = ParseTrailerState;
  1599. }
  1600. }
  1601. else // if (pRequest->Chunked == 1)
  1602. {
  1603. //
  1604. // not chunk-encoded , all done
  1605. //
  1606. pRequest->ParseState = ParseDoneState;
  1607. }
  1608. } // if (pRequest->ChunkBytesToParse == 0)
  1609. //
  1610. // looks all good
  1611. //
  1612. if (pRequest->ParseState != ParseTrailerState)
  1613. {
  1614. break;
  1615. }
  1616. case ParseTrailerState:
  1617. //
  1618. // parse any existing trailer
  1619. //
  1620. // ParseHeaders will bail immediately if CRLF is
  1621. // next in the buffer (no trailer)
  1622. //
  1623. ReturnStatus = ParseHeaders(
  1624. pRequest,
  1625. pHttpRequest,
  1626. HttpRequestLength,
  1627. &CurrentBytesTaken
  1628. );
  1629. pHttpRequest += CurrentBytesTaken;
  1630. HttpRequestLength -= CurrentBytesTaken;
  1631. TotalBytesTaken += CurrentBytesTaken;
  1632. if (NT_SUCCESS(ReturnStatus) == FALSE)
  1633. goto end;
  1634. //
  1635. // all done
  1636. //
  1637. pRequest->ParseState = ParseDoneState;
  1638. break;
  1639. default:
  1640. //
  1641. // this should never happen!
  1642. //
  1643. ASSERT(FALSE);
  1644. break;
  1645. } // switch (pRequest->ParseState)
  1646. end:
  1647. *pBytesTaken = TotalBytesTaken;
  1648. if (ReturnStatus == STATUS_MORE_PROCESSING_REQUIRED &&
  1649. TotalBytesTaken == OriginalBufferLength)
  1650. {
  1651. //
  1652. // convert this to success, we consumed the entire buffer
  1653. //
  1654. ReturnStatus = STATUS_SUCCESS;
  1655. }
  1656. UlTrace(PARSER, (
  1657. "ul!ParseHttp returning 0x%x, (%p)->ParseState = %d, bytesTaken = %d\n",
  1658. ReturnStatus,
  1659. pRequest,
  1660. pRequest->ParseState,
  1661. TotalBytesTaken
  1662. ));
  1663. return ReturnStatus;
  1664. } // ParseHttp
  1665. /*++
  1666. Routine Description:
  1667. Routine to initialize the parse code.
  1668. Arguments:
  1669. Return Value:
  1670. --*/
  1671. NTSTATUS
  1672. InitializeParser(
  1673. VOID
  1674. )
  1675. {
  1676. ULONG i;
  1677. ULONG j;
  1678. PHEADER_MAP_ENTRY pHeaderMap;
  1679. PHEADER_INDEX_ENTRY pHeaderIndex;
  1680. UCHAR c;
  1681. //
  1682. // Make sure the entire table starts life as zero
  1683. //
  1684. RtlZeroMemory(&HeaderIndexTable, sizeof(HeaderIndexTable));
  1685. for (i = 0; i < NUMBER_HEADER_MAP_ENTRIES;i++)
  1686. {
  1687. pHeaderMap = &HeaderMapTable[i];
  1688. //
  1689. // Map the header to upper-case.
  1690. //
  1691. for (j = 0 ; j < pHeaderMap->HeaderLength ; j++)
  1692. {
  1693. c = pHeaderMap->Header.HeaderChar[j];
  1694. if ((c >= 'a') && (c <= 'z'))
  1695. {
  1696. pHeaderMap->Header.HeaderChar[j] = c - ('a' - 'A');
  1697. }
  1698. }
  1699. //
  1700. // response headers are hidden in here, leave them untouched
  1701. // at the end of a letter-run.
  1702. //
  1703. if (pHeaderMap->pHandler != NULL)
  1704. {
  1705. c = pHeaderMap->Header.HeaderChar[0];
  1706. pHeaderIndex = &HeaderIndexTable[c - 'A'];
  1707. if (pHeaderIndex->pHeaderMap == NULL)
  1708. {
  1709. pHeaderIndex->pHeaderMap = pHeaderMap;
  1710. pHeaderIndex->Count = 1;
  1711. }
  1712. else
  1713. {
  1714. pHeaderIndex->Count++;
  1715. }
  1716. }
  1717. // Now go through the mask fields for this header map structure and
  1718. // initialize them. We set them to default values first, and then
  1719. // go through the header itself and convert the mask for any
  1720. // non-alphabetic characters.
  1721. for (j = 0; j < MAX_HEADER_LONG_COUNT; j++)
  1722. {
  1723. pHeaderMap->HeaderMask[j] = CREATE_HEADER_MASK(
  1724. pHeaderMap->HeaderLength,
  1725. sizeof(ULONGLONG) * (j+1)
  1726. );
  1727. }
  1728. for (j = 0; j < pHeaderMap->HeaderLength; j++)
  1729. {
  1730. c = pHeaderMap->Header.HeaderChar[j];
  1731. if (c < 'A' || c > 'Z')
  1732. {
  1733. pHeaderMap->HeaderMask[j/sizeof(ULONGLONG)] |=
  1734. (ULONGLONG)0xff << ((j % sizeof(ULONGLONG)) * (ULONGLONG)8);
  1735. }
  1736. }
  1737. //
  1738. // setup the mapping from header id to map table index
  1739. //
  1740. ResponseHeaderMap[pHeaderMap->HeaderID] = i;
  1741. }
  1742. return STATUS_SUCCESS;
  1743. } // InitializeParser
  1744. ULONG
  1745. UlpFormatPort(
  1746. OUT PWSTR pString,
  1747. IN ULONG Port
  1748. )
  1749. {
  1750. PWSTR p1;
  1751. PWSTR p2;
  1752. WCHAR ch;
  1753. ULONG digit;
  1754. ULONG length;
  1755. //
  1756. // Sanity check.
  1757. //
  1758. PAGED_CODE();
  1759. //
  1760. // Fast-path common ports. While we're at it, special case port 0,
  1761. // which is definitely not common, but handling it specially makes
  1762. // the general conversion code a bit simpler.
  1763. //
  1764. switch (Port)
  1765. {
  1766. case 0:
  1767. *pString++ = L'0';
  1768. *pString = UNICODE_NULL;
  1769. return 1;
  1770. case 80:
  1771. *pString++ = L'8';
  1772. *pString++ = L'0';
  1773. *pString = UNICODE_NULL;
  1774. return 2;
  1775. case 443:
  1776. *pString++ = L'4';
  1777. *pString++ = L'4';
  1778. *pString++ = L'3';
  1779. *pString = UNICODE_NULL;
  1780. return 3;
  1781. }
  1782. //
  1783. // Pull the least signifigant digits off the port value and store them
  1784. // into the pString. Note that this will store the digits in reverse
  1785. // order.
  1786. //
  1787. p1 = p2 = pString;
  1788. while (Port != 0)
  1789. {
  1790. digit = Port % 10;
  1791. Port = Port / 10;
  1792. *p1++ = L'0' + (WCHAR)digit;
  1793. }
  1794. length = DIFF(p1 - pString);
  1795. //
  1796. // Reverse the digits in the pString.
  1797. //
  1798. *p1-- = UNICODE_NULL;
  1799. while (p1 > p2)
  1800. {
  1801. ch = *p1;
  1802. *p1 = *p2;
  1803. *p2 = ch;
  1804. p2++;
  1805. p1--;
  1806. }
  1807. return length;
  1808. } // UlpFormatPort
  1809. NTSTATUS
  1810. UlpCookUrl(
  1811. PUL_INTERNAL_REQUEST pRequest
  1812. )
  1813. {
  1814. NTSTATUS Status;
  1815. PUCHAR pHost;
  1816. ULONG HostLength;
  1817. PUCHAR pAbsPath;
  1818. ULONG AbsPathLength;
  1819. ULONG UrlLength;
  1820. ULONG PortLength;
  1821. ULONG LengthCopied;
  1822. PWSTR pUrl = NULL;
  1823. PWSTR pCurrent;
  1824. ULONG Index;
  1825. BOOLEAN PortInUrl;
  1826. CHAR IpAddressString[MAX_ADDRESS_LENGTH+1];
  1827. USHORT IpPortNum;
  1828. BOOLEAN HostFromTransport = FALSE;
  1829. //
  1830. // Sanity check.
  1831. //
  1832. PAGED_CODE();
  1833. //
  1834. // We must have already parsed the entire headers + such
  1835. //
  1836. if (pRequest->ParseState < ParseCookState)
  1837. return STATUS_INVALID_DEVICE_STATE;
  1838. //
  1839. // better have an absolute url .
  1840. //
  1841. if (pRequest->RawUrl.pAbsPath[0] != '/')
  1842. {
  1843. //
  1844. // allow * for Verb = OPTIONS
  1845. //
  1846. if (pRequest->RawUrl.pAbsPath[0] == '*' &&
  1847. pRequest->Verb == HttpVerbOPTIONS)
  1848. {
  1849. // ok
  1850. }
  1851. else
  1852. {
  1853. Status = STATUS_INVALID_DEVICE_REQUEST;
  1854. goto end;
  1855. }
  1856. }
  1857. //
  1858. // collect the host + abspath sections
  1859. //
  1860. if (pRequest->RawUrl.pHost != NULL)
  1861. {
  1862. pHost = pRequest->RawUrl.pHost;
  1863. HostLength = DIFF(pRequest->RawUrl.pAbsPath - pRequest->RawUrl.pHost);
  1864. pAbsPath = pRequest->RawUrl.pAbsPath;
  1865. AbsPathLength = pRequest->RawUrl.Length - DIFF(pAbsPath - pRequest->RawUrl.pUrl);
  1866. }
  1867. else
  1868. {
  1869. pHost = NULL;
  1870. HostLength = 0;
  1871. pAbsPath = pRequest->RawUrl.pAbsPath;
  1872. AbsPathLength = pRequest->RawUrl.Length;
  1873. }
  1874. //
  1875. // found a host yet?
  1876. //
  1877. if (pHost == NULL)
  1878. {
  1879. //
  1880. // do we have a host header?
  1881. //
  1882. if (pRequest->Headers[HttpHeaderHost].pHeader != NULL )
  1883. {
  1884. pHost = pRequest->Headers[HttpHeaderHost].pHeader;
  1885. HostLength = pRequest->Headers[HttpHeaderHost].HeaderLength;
  1886. }
  1887. else
  1888. {
  1889. TA_IP_ADDRESS RawAddress = { 0 };
  1890. ULONG CharCopied;
  1891. ULONG IpAddress;
  1892. //
  1893. // first, if this was a 1.1 client, it's an invalid request
  1894. // to not have a host header, fail it.
  1895. //
  1896. if (HTTP_EQUAL_VERSION(pRequest->Version, 1, 1))
  1897. {
  1898. pRequest->ErrorCode = UlErrorHost;
  1899. pRequest->ParseState = ParseErrorState;
  1900. UlTrace(PARSER, (
  1901. "ul!UlpCookUrl(pRequest = %p) ERROR: 1.1 request w/o host header\n",
  1902. pRequest
  1903. ));
  1904. Status = STATUS_INVALID_DEVICE_REQUEST;
  1905. goto end;
  1906. }
  1907. //
  1908. // get the ip address from the transport
  1909. //
  1910. /*
  1911. UlLocalAddressFromConnection(
  1912. pRequest->pHttpConn->pConnection,
  1913. &RawAddress
  1914. );
  1915. IpPortNum = SWAP_SHORT(RawAddress.Address[0].Address[0].sin_port);
  1916. IpAddress = SWAP_LONG(RawAddress.Address[0].Address[0].in_addr);
  1917. */
  1918. IpAddress = SWAP_LONG( (ULONG) (pRequest->ConnectionId >> 32) );
  1919. IpPortNum = SWAP_SHORT( (USHORT) (pRequest->ConnectionId & 0x000000FF) );
  1920. //
  1921. // format it into a string
  1922. //
  1923. pHost = (PUCHAR)(IpAddressString);
  1924. HostLength = sprintf(
  1925. IpAddressString,
  1926. "%d.%d.%d.%d:%d",
  1927. (UCHAR)(IpAddress >> 24),
  1928. (UCHAR)(IpAddress >> 16),
  1929. (UCHAR)(IpAddress >> 8),
  1930. (UCHAR)(IpAddress >> 0),
  1931. IpPortNum
  1932. );
  1933. ASSERT(HostLength < sizeof(IpAddressString) - 1);
  1934. HostFromTransport = TRUE;
  1935. PortInUrl = TRUE;
  1936. }
  1937. }
  1938. if (HostFromTransport == FALSE)
  1939. {
  1940. //
  1941. // is there a port # already there ?
  1942. //
  1943. Index = HostLength;
  1944. while (Index > 0)
  1945. {
  1946. Index -= 1;
  1947. if (pHost[Index] == ':')
  1948. break;
  1949. }
  1950. if (Index == 0)
  1951. {
  1952. TA_IP_ADDRESS RawAddress = { 0 };
  1953. PortInUrl = FALSE;
  1954. //
  1955. // no port number, get the port number from the transport
  1956. //
  1957. // we could simply assume port 80 at this point, but some
  1958. // browsers don't sent the port number in the host header
  1959. // even when their supposed to
  1960. //
  1961. /*
  1962. UlLocalAddressFromConnection(
  1963. pRequest->pHttpConn->pConnection,
  1964. &RawAddress
  1965. );
  1966. IpPortNum = SWAP_SHORT(RawAddress.Address[0].Address[0].sin_port);
  1967. */
  1968. IpPortNum = SWAP_SHORT( (USHORT) (pRequest->ConnectionId & 0x000000FF) );
  1969. }
  1970. else
  1971. {
  1972. PortInUrl = TRUE;
  1973. }
  1974. }
  1975. UrlLength = (HTTP_PREFIX_SIZE+HTTP_PREFIX2_SIZE) +
  1976. HostLength +
  1977. (sizeof(":")-1) +
  1978. MAX_PORT_LENGTH +
  1979. AbsPathLength;
  1980. UrlLength *= sizeof(WCHAR);
  1981. //
  1982. // allocate a new buffer to hold this guy
  1983. //
  1984. pUrl = UL_ALLOCATE_ARRAY(
  1985. NonPagedPool,
  1986. WCHAR,
  1987. (UrlLength/sizeof(WCHAR)) + 1,
  1988. URL_POOL_TAG
  1989. );
  1990. if (pUrl == NULL)
  1991. {
  1992. Status = STATUS_NO_MEMORY;
  1993. goto end;
  1994. }
  1995. pRequest->CookedUrl.pUrl = pCurrent = pUrl;
  1996. //
  1997. // compute the scheme
  1998. //
  1999. if (FALSE)
  2000. {
  2001. //
  2002. // yep, ssl
  2003. //
  2004. // CODEWORK
  2005. // copy the NULL for the hash function to work
  2006. //
  2007. RtlCopyMemory(pCurrent, L"https://", sizeof(L"https://"));
  2008. pRequest->CookedUrl.Hash = HashStringW(pCurrent, 0);
  2009. pCurrent += (sizeof(L"https://")-sizeof(WCHAR)) / sizeof(WCHAR);
  2010. pRequest->CookedUrl.Length = (sizeof(L"https://")-sizeof(WCHAR));
  2011. }
  2012. else
  2013. {
  2014. //
  2015. // not ssl
  2016. //
  2017. RtlCopyMemory(pCurrent, L"http://", sizeof(L"http://"));
  2018. pRequest->CookedUrl.Hash = HashStringW(pCurrent, 0);
  2019. pCurrent += (sizeof(L"http://")-sizeof(WCHAR)) / sizeof(WCHAR);
  2020. pRequest->CookedUrl.Length = (sizeof(L"http://")-sizeof(WCHAR));
  2021. }
  2022. //
  2023. // assemble the rest of the url
  2024. //
  2025. //
  2026. // host
  2027. //
  2028. Status = UlpCleanAndCopyUrl(
  2029. HostName,
  2030. pCurrent,
  2031. pHost,
  2032. HostLength,
  2033. &LengthCopied,
  2034. NULL,
  2035. &pRequest->CookedUrl.Hash
  2036. );
  2037. if (NT_SUCCESS(Status) == FALSE)
  2038. goto end;
  2039. pRequest->CookedUrl.pHost = pCurrent;
  2040. pRequest->CookedUrl.Length += LengthCopied;
  2041. pCurrent += LengthCopied / sizeof(WCHAR);
  2042. //
  2043. // port
  2044. //
  2045. if (PortInUrl == FALSE)
  2046. {
  2047. *pCurrent = L':';
  2048. PortLength = UlpFormatPort( pCurrent+1, IpPortNum ) + 1;
  2049. //
  2050. // update the running hash
  2051. //
  2052. pRequest->CookedUrl.Hash = HashStringW(pCurrent, pRequest->CookedUrl.Hash);
  2053. pCurrent += PortLength;
  2054. //
  2055. // swprintf returns char not byte count
  2056. //
  2057. pRequest->CookedUrl.Length += PortLength * sizeof(WCHAR);
  2058. }
  2059. // abs_path
  2060. //
  2061. Status = UlpCleanAndCopyUrl(
  2062. AbsPath,
  2063. pCurrent,
  2064. pAbsPath,
  2065. AbsPathLength,
  2066. &LengthCopied,
  2067. &pRequest->CookedUrl.pQueryString,
  2068. &pRequest->CookedUrl.Hash
  2069. );
  2070. if (NT_SUCCESS(Status) == FALSE)
  2071. goto end;
  2072. pRequest->CookedUrl.pAbsPath = pCurrent;
  2073. pRequest->CookedUrl.Length += LengthCopied;
  2074. ASSERT(pRequest->CookedUrl.Length <= UrlLength);
  2075. //
  2076. // Update pRequest, include space for the terminator
  2077. //
  2078. pRequest->TotalRequestSize += pRequest->CookedUrl.Length + sizeof(WCHAR);
  2079. //
  2080. // let's just make sure the hash is the right value
  2081. //
  2082. ASSERT(pRequest->CookedUrl.Hash == HashStringW(pRequest->CookedUrl.pUrl, 0));
  2083. //
  2084. // Scramble it
  2085. //
  2086. pRequest->CookedUrl.Hash = HashScramble(pRequest->CookedUrl.Hash);
  2087. ASSERT(pRequest->CookedUrl.pHost != NULL);
  2088. ASSERT(pRequest->CookedUrl.pAbsPath != NULL);
  2089. //
  2090. // validate the host part of the url
  2091. //
  2092. pCurrent = wcschr(pRequest->CookedUrl.pHost, L':');
  2093. //
  2094. // Check for :
  2095. //
  2096. // No colon ||
  2097. //
  2098. // no hostname ||
  2099. //
  2100. // No colon in the host OR no port number.
  2101. //
  2102. if (pCurrent == NULL ||
  2103. pCurrent == pRequest->CookedUrl.pHost ||
  2104. pCurrent >= (pRequest->CookedUrl.pAbsPath-1))
  2105. {
  2106. //
  2107. // bad. no colon for the port.
  2108. //
  2109. Status = STATUS_INVALID_DEVICE_REQUEST;
  2110. goto end;
  2111. }
  2112. //
  2113. // skip the colon
  2114. //
  2115. pCurrent += 1;
  2116. //
  2117. // now make sure the port number is in good shape
  2118. //
  2119. while (pCurrent < pRequest->CookedUrl.pAbsPath)
  2120. {
  2121. if (IS_HTTP_DIGIT(pCurrent[0]) == FALSE)
  2122. {
  2123. //
  2124. // bad. non digit.
  2125. //
  2126. Status = STATUS_INVALID_DEVICE_REQUEST;
  2127. goto end;
  2128. }
  2129. pCurrent += 1;
  2130. }
  2131. Status = STATUS_SUCCESS;
  2132. end:
  2133. if (NT_SUCCESS(Status) == FALSE)
  2134. {
  2135. if (pUrl != NULL)
  2136. {
  2137. UL_FREE_POOL(pUrl, URL_POOL_TAG);
  2138. RtlZeroMemory(&pRequest->CookedUrl, sizeof(pRequest->CookedUrl));
  2139. }
  2140. //
  2141. // has a specific error code been set?
  2142. //
  2143. if (pRequest->ErrorCode == UlError)
  2144. {
  2145. pRequest->ErrorCode = UlErrorUrl;
  2146. pRequest->ParseState = ParseErrorState;
  2147. }
  2148. UlTrace(PARSER, (
  2149. "ul!UlpCookUrl(pRequest = %p) ERROR: unhappy. Status = 0x%x\n",
  2150. pRequest,
  2151. Status
  2152. ));
  2153. }
  2154. return Status;
  2155. } // UlpCookUrl
  2156. NTSTATUS
  2157. Unescape(
  2158. IN PUCHAR pChar,
  2159. OUT PUCHAR pOutChar
  2160. )
  2161. {
  2162. UCHAR Result, Digit;
  2163. //
  2164. // Sanity check.
  2165. //
  2166. PAGED_CODE();
  2167. if (pChar[0] != '%' ||
  2168. IS_HTTP_HEX(pChar[1]) == FALSE ||
  2169. IS_HTTP_HEX(pChar[2]) == FALSE)
  2170. {
  2171. UlTrace(PARSER, (
  2172. "ul!Unescape( %c%c%c ) not HTTP_HEX format\n",
  2173. pChar[0],
  2174. pChar[1],
  2175. pChar[2]
  2176. ));
  2177. return STATUS_OBJECT_PATH_SYNTAX_BAD;
  2178. }
  2179. //
  2180. // HexToChar() inlined
  2181. //
  2182. // uppercase #1
  2183. //
  2184. if (IS_HTTP_ALPHA(pChar[1]))
  2185. Digit = UPCASE_CHAR(pChar[1]);
  2186. else
  2187. Digit = pChar[1];
  2188. Result = ((Digit >= 'A') ? (Digit - 'A' + 0xA) : (Digit - '0')) << 4;
  2189. // uppercase #2
  2190. //
  2191. if (IS_HTTP_ALPHA(pChar[2]))
  2192. Digit = UPCASE_CHAR(pChar[2]);
  2193. else
  2194. Digit = pChar[2];
  2195. Result |= (Digit >= 'A') ? (Digit - 'A' + 0xA) : (Digit - '0');
  2196. *pOutChar = Result;
  2197. return STATUS_SUCCESS;
  2198. } // Unescape
  2199. NTSTATUS
  2200. PopChar(
  2201. IN URL_PART UrlPart,
  2202. IN PUCHAR pChar,
  2203. OUT WCHAR * pUnicodeChar,
  2204. OUT PULONG pCharToSkip
  2205. )
  2206. {
  2207. NTSTATUS Status;
  2208. WCHAR UnicodeChar;
  2209. UCHAR Char;
  2210. UCHAR Trail1;
  2211. UCHAR Trail2;
  2212. ULONG CharToSkip;
  2213. //
  2214. // Sanity check.
  2215. //
  2216. PAGED_CODE();
  2217. //
  2218. // validate it as a valid url character
  2219. //
  2220. if (IS_URL_TOKEN(pChar[0]) == FALSE)
  2221. {
  2222. Status = STATUS_OBJECT_PATH_SYNTAX_BAD;
  2223. UlTrace(PARSER, (
  2224. "ul!PopChar(pChar = %p) first char isn't URL token\n",
  2225. pChar
  2226. ));
  2227. goto end;
  2228. }
  2229. //
  2230. // need to unescape ?
  2231. //
  2232. // can't decode the query string. that would be lossy decodeing
  2233. // as '=' and '&' characters might be encoded, but have meaning
  2234. // to the usermode parser.
  2235. //
  2236. if (UrlPart != QueryString && pChar[0] == '%')
  2237. {
  2238. Status = Unescape(pChar, &Char);
  2239. if (NT_SUCCESS(Status) == FALSE)
  2240. goto end;
  2241. CharToSkip = 3;
  2242. }
  2243. else
  2244. {
  2245. Char = pChar[0];
  2246. CharToSkip = 1;
  2247. }
  2248. //
  2249. // convert to unicode, checking for utf8 .
  2250. //
  2251. // 3 byte runs are the largest we can have. 16 bits in UCS-2 =
  2252. // 3 bytes of (4+4,2+6,2+6) where it's code + char.
  2253. // for a total of 6+6+4 char bits = 16 bits.
  2254. //
  2255. //
  2256. // NOTE: we'll only bother to decode utf if it was escaped
  2257. // thus the (CharToSkip == 3)
  2258. //
  2259. if ((CharToSkip == 3) && ((Char & 0xf0) == 0xe0))
  2260. {
  2261. // 3 byte run
  2262. //
  2263. // Unescape the next 2 trail bytes
  2264. //
  2265. Status = Unescape(pChar+CharToSkip, &Trail1);
  2266. if (NT_SUCCESS(Status) == FALSE)
  2267. goto end;
  2268. CharToSkip += 3; // %xx
  2269. Status = Unescape(pChar+CharToSkip, &Trail2);
  2270. if (NT_SUCCESS(Status) == FALSE)
  2271. goto end;
  2272. CharToSkip += 3; // %xx
  2273. if (IS_UTF8_TRAILBYTE(Trail1) == FALSE ||
  2274. IS_UTF8_TRAILBYTE(Trail2) == FALSE)
  2275. {
  2276. // bad utf!
  2277. //
  2278. Status = STATUS_OBJECT_PATH_SYNTAX_BAD;
  2279. UlTrace(PARSER, (
  2280. "ul!PopChar( 0x%x 0x%x ) bad trail bytes\n",
  2281. Trail1,
  2282. Trail2
  2283. ));
  2284. goto end;
  2285. }
  2286. // handle three byte case
  2287. // 1110xxxx 10xxxxxx 10xxxxxx
  2288. UnicodeChar = (USHORT) (((Char & 0x0f) << 12) |
  2289. ((Trail1 & 0x3f) << 6) |
  2290. (Trail2 & 0x3f));
  2291. }
  2292. else if ((CharToSkip == 3) && ((Char & 0xe0) == 0xc0))
  2293. {
  2294. // 2 byte run
  2295. //
  2296. // Unescape the next 1 trail byte
  2297. //
  2298. Status = Unescape(pChar+CharToSkip, &Trail1);
  2299. if (NT_SUCCESS(Status) == FALSE)
  2300. goto end;
  2301. CharToSkip += 3; // %xx
  2302. if (IS_UTF8_TRAILBYTE(Trail1) == FALSE)
  2303. {
  2304. // bad utf!
  2305. //
  2306. Status = STATUS_OBJECT_PATH_SYNTAX_BAD;
  2307. UlTrace(PARSER, (
  2308. "ul!PopChar( 0x%x ) bad trail byte\n",
  2309. Trail1
  2310. ));
  2311. goto end;
  2312. }
  2313. // handle two byte case
  2314. // 110xxxxx 10xxxxxx
  2315. UnicodeChar = (USHORT) (((Char & 0x0f) << 6) |
  2316. (Trail1 & 0x3f));
  2317. }
  2318. // now this can either be unescaped high-bit (bad)
  2319. // or escaped high-bit. (also bad)
  2320. //
  2321. // thus not checking CharToSkip
  2322. //
  2323. else if ((Char & 0x80) == 0x80)
  2324. {
  2325. // high bit set ! bad utf!
  2326. //
  2327. Status = STATUS_OBJECT_PATH_SYNTAX_BAD;
  2328. UlTrace(PARSER, (
  2329. "ul!PopChar( 0x%x ) ERROR: high bit set! bad utf!\n",
  2330. Char
  2331. ));
  2332. goto end;
  2333. }
  2334. //
  2335. // Normal character (again either escaped or unescaped)
  2336. //
  2337. else
  2338. {
  2339. //
  2340. // Simple conversion to unicode, it's 7-bit ascii.
  2341. //
  2342. UnicodeChar = (USHORT)Char;
  2343. }
  2344. //
  2345. // turn backslashes into forward slashes
  2346. //
  2347. if (UnicodeChar == L'\\')
  2348. {
  2349. UnicodeChar = L'/';
  2350. }
  2351. else if (UnicodeChar == 0)
  2352. {
  2353. //
  2354. // we pop'd a NULL. bad!
  2355. //
  2356. Status = STATUS_OBJECT_PATH_SYNTAX_BAD;
  2357. goto end;
  2358. }
  2359. *pCharToSkip = CharToSkip;
  2360. *pUnicodeChar = UnicodeChar;
  2361. Status = STATUS_SUCCESS;
  2362. end:
  2363. return Status;
  2364. } // PopChar
  2365. //
  2366. // PAULMCD(2/99): stolen from iisrtl\string.cxx and incorporated
  2367. // and added more comments
  2368. //
  2369. //
  2370. // Private constants.
  2371. //
  2372. #define ACTION_NOTHING 0x00000000
  2373. #define ACTION_EMIT_CH 0x00010000
  2374. #define ACTION_EMIT_DOT_CH 0x00020000
  2375. #define ACTION_EMIT_DOT_DOT_CH 0x00030000
  2376. #define ACTION_BACKUP 0x00040000
  2377. #define ACTION_MASK 0xFFFF0000
  2378. //
  2379. // Private globals
  2380. //
  2381. //
  2382. // this table says what to do based on the current state and the current
  2383. // character
  2384. //
  2385. ULONG pActionTable[16] =
  2386. {
  2387. //
  2388. // state 0 = fresh, seen nothing exciting yet
  2389. //
  2390. ACTION_EMIT_CH, // other = emit it state = 0
  2391. ACTION_EMIT_CH, // "." = emit it state = 0
  2392. ACTION_NOTHING, // EOS = normal finish state = 4
  2393. ACTION_EMIT_CH, // "/" = we saw the "/", emit it state = 1
  2394. //
  2395. // state 1 = we saw a "/" !
  2396. //
  2397. ACTION_EMIT_CH, // other = emit it, state = 0
  2398. ACTION_NOTHING, // "." = eat it, state = 2
  2399. ACTION_NOTHING, // EOS = normal finish state = 4
  2400. ACTION_NOTHING, // "/" = extra slash, eat it, state = 1
  2401. //
  2402. // state 2 = we saw a "/" and ate a "." !
  2403. //
  2404. ACTION_EMIT_DOT_CH, // other = emit the dot we ate. state = 0
  2405. ACTION_NOTHING, // "." = eat it, a .. state = 3
  2406. ACTION_NOTHING, // EOS = normal finish state = 4
  2407. ACTION_NOTHING, // "/" = we ate a "/./", swallow it state = 1
  2408. //
  2409. // state 3 = we saw a "/" and ate a ".." !
  2410. //
  2411. ACTION_EMIT_DOT_DOT_CH, // other = emit the "..". state = 0
  2412. ACTION_EMIT_DOT_DOT_CH, // "." = 3 dots, emit the ".." state = 0
  2413. ACTION_BACKUP, // EOS = we have a "/..\0", backup! state = 4
  2414. ACTION_BACKUP // "/" = we have a "/../", backup! state = 1
  2415. };
  2416. //
  2417. // this table says which newstate to be in given the current state and the
  2418. // character we saw
  2419. //
  2420. ULONG pNextStateTable[16] =
  2421. {
  2422. // state 0
  2423. 0 , // other
  2424. 0 , // "."
  2425. 4 , // EOS
  2426. 1 , // "\"
  2427. // state 1
  2428. 0 , // other
  2429. 2 , // "."
  2430. 4 , // EOS
  2431. 1 , // "\"
  2432. // state 2
  2433. 0 , // other
  2434. 3 , // "."
  2435. 4 , // EOS
  2436. 1 , // "\"
  2437. // state 3
  2438. 0 , // other
  2439. 0 , // "."
  2440. 4 , // EOS
  2441. 1 // "\"
  2442. };
  2443. //
  2444. // this says how to index into pNextStateTable given our current state.
  2445. //
  2446. // since max states = 4, we calculate the index by multiplying with 4.
  2447. //
  2448. #define IndexFromState( st) ( (st) * 4)
  2449. /***************************************************************************++
  2450. Routine Description:
  2451. Unescape
  2452. Convert backslash to forward slash
  2453. Remove double slashes (empty directiories names) - e.g. // or \\
  2454. Handle /./
  2455. Handle /../
  2456. Convert to unicode
  2457. computes the case insensitive hash
  2458. Arguments:
  2459. Return Value:
  2460. NTSTATUS - Completion status.
  2461. --***************************************************************************/
  2462. NTSTATUS
  2463. UlpCleanAndCopyUrl(
  2464. IN URL_PART UrlPart,
  2465. IN OUT PWSTR pDestination,
  2466. IN PUCHAR pSource,
  2467. IN ULONG SourceLength,
  2468. OUT PULONG pBytesCopied,
  2469. OUT PWSTR * ppQueryString OPTIONAL,
  2470. OUT PULONG pUrlHash
  2471. )
  2472. {
  2473. NTSTATUS Status;
  2474. PWSTR pDest;
  2475. PUCHAR pChar;
  2476. ULONG CharToSkip;
  2477. UCHAR Char;
  2478. BOOLEAN HashValid;
  2479. ULONG UrlHash;
  2480. ULONG BytesCopied;
  2481. PWSTR pQueryString;
  2482. ULONG StateIndex;
  2483. WCHAR UnicodeChar;
  2484. BOOLEAN MakeCanonical;
  2485. //
  2486. // Sanity check.
  2487. //
  2488. PAGED_CODE();
  2489. //
  2490. // a cool local helper macro
  2491. //
  2492. #define EMIT_CHAR(ch) \
  2493. do { \
  2494. pDest[0] = (ch); \
  2495. pDest += 1; \
  2496. BytesCopied += 2; \
  2497. if (HashValid) \
  2498. UrlHash = HashCharW((ch), UrlHash); \
  2499. } while (0)
  2500. pDest = pDestination;
  2501. pQueryString = NULL;
  2502. BytesCopied = 0;
  2503. pChar = pSource;
  2504. CharToSkip = 0;
  2505. HashValid = TRUE;
  2506. UrlHash = *pUrlHash;
  2507. StateIndex = 0;
  2508. MakeCanonical = (UrlPart == AbsPath) ? TRUE : FALSE;
  2509. while (SourceLength > 0)
  2510. {
  2511. //
  2512. // advance ! it's at the top of the loop to enable ANSI_NULL to
  2513. // come through ONCE
  2514. //
  2515. pChar += CharToSkip;
  2516. SourceLength -= CharToSkip;
  2517. //
  2518. // well? have we hit the end?
  2519. //
  2520. if (SourceLength == 0)
  2521. {
  2522. UnicodeChar = UNICODE_NULL;
  2523. }
  2524. else
  2525. {
  2526. //
  2527. // Nope. Peek briefly to see if we hit the query string
  2528. //
  2529. if (UrlPart == AbsPath && pChar[0] == '?')
  2530. {
  2531. ASSERT(pQueryString == NULL);
  2532. //
  2533. // remember it's location
  2534. //
  2535. pQueryString = pDest;
  2536. //
  2537. // let it fall through ONCE to the canonical
  2538. // in order to handle a trailing "/.." like
  2539. // "http://foobar:80/foo/bar/..?v=1&v2"
  2540. //
  2541. UnicodeChar = L'?';
  2542. CharToSkip = 1;
  2543. //
  2544. // now we are cleaning the query string
  2545. //
  2546. UrlPart = QueryString;
  2547. }
  2548. else
  2549. {
  2550. //
  2551. // grab the next char
  2552. //
  2553. Status = PopChar(UrlPart, pChar, &UnicodeChar, &CharToSkip);
  2554. if (NT_SUCCESS(Status) == FALSE)
  2555. goto end;
  2556. }
  2557. }
  2558. if (MakeCanonical)
  2559. {
  2560. //
  2561. // now use the state machine to make it canonical .
  2562. //
  2563. //
  2564. // from the old value of StateIndex, figure out our new base StateIndex
  2565. //
  2566. StateIndex = IndexFromState(pNextStateTable[StateIndex]);
  2567. //
  2568. // did we just hit the query string? this will only happen once
  2569. // that we take this branch after hitting it, as we stop
  2570. // processing after hitting it.
  2571. //
  2572. if (UrlPart == QueryString)
  2573. {
  2574. //
  2575. // treat this just like we hit a NULL, EOS.
  2576. //
  2577. StateIndex += 2;
  2578. }
  2579. else
  2580. {
  2581. //
  2582. // otherwise based the new state off of the char we
  2583. // just popped.
  2584. //
  2585. switch (UnicodeChar)
  2586. {
  2587. case UNICODE_NULL: StateIndex += 2; break;
  2588. case L'.': StateIndex += 1; break;
  2589. case L'/': StateIndex += 3; break;
  2590. default: StateIndex += 0; break;
  2591. }
  2592. }
  2593. }
  2594. else
  2595. {
  2596. StateIndex = (UnicodeChar == UNICODE_NULL) ? 2 : 0;
  2597. }
  2598. //
  2599. // Perform the action associated with the state.
  2600. //
  2601. switch (pActionTable[StateIndex])
  2602. {
  2603. case ACTION_EMIT_DOT_DOT_CH:
  2604. EMIT_CHAR(L'.');
  2605. // fall through
  2606. case ACTION_EMIT_DOT_CH:
  2607. EMIT_CHAR(L'.');
  2608. // fall through
  2609. case ACTION_EMIT_CH:
  2610. EMIT_CHAR(UnicodeChar);
  2611. // fall through
  2612. case ACTION_NOTHING:
  2613. break;
  2614. case ACTION_BACKUP:
  2615. //
  2616. // pDest currently points 1 past the last '/'. backup over it and
  2617. // find the preceding '/', set pDest to 1 past that one.
  2618. //
  2619. //
  2620. // backup to the '/'
  2621. //
  2622. pDest -= 1;
  2623. BytesCopied -= 2;
  2624. ASSERT(pDest[0] == L'/');
  2625. //
  2626. // are we at the start of the string? that's bad, can't go back!
  2627. //
  2628. if (pDest == pDestination)
  2629. {
  2630. ASSERT(BytesCopied == 0);
  2631. UlTrace(PARSER, (
  2632. "ul!UlpCleanAndCopyUrl() Can't back up for ..\n"
  2633. ));
  2634. Status = STATUS_OBJECT_PATH_SYNTAX_BAD;
  2635. goto end;
  2636. }
  2637. //
  2638. // back up over the '/'
  2639. //
  2640. pDest -= 1;
  2641. BytesCopied -= 2;
  2642. ASSERT(pDest > pDestination);
  2643. //
  2644. // now find the previous slash
  2645. //
  2646. while (pDest > pDestination && pDest[0] != L'/')
  2647. {
  2648. pDest -= 1;
  2649. BytesCopied -= 2;
  2650. }
  2651. //
  2652. // we already have a slash, so don't have to store 1.
  2653. //
  2654. ASSERT(pDest[0] == L'/');
  2655. //
  2656. // simply skip it, as if we had emitted it just now
  2657. //
  2658. pDest += 1;
  2659. BytesCopied += 2;
  2660. //
  2661. // mark our running hash invalid
  2662. //
  2663. HashValid = FALSE;
  2664. break;
  2665. default:
  2666. ASSERT(!"UL!UlpCleanAndCopyUrl: Invalid action code in state table!");
  2667. Status = STATUS_OBJECT_PATH_SYNTAX_BAD;
  2668. goto end;
  2669. }
  2670. //
  2671. // Just hit the query string ?
  2672. //
  2673. if (MakeCanonical && UrlPart == QueryString)
  2674. {
  2675. //
  2676. // Stop canonical processing
  2677. //
  2678. MakeCanonical = FALSE;
  2679. //
  2680. // Need to emit the '?', it wasn't emitted above
  2681. //
  2682. ASSERT(pActionTable[StateIndex] != ACTION_EMIT_CH);
  2683. EMIT_CHAR(L'?');
  2684. }
  2685. }
  2686. //
  2687. // terminate the string, it hasn't been done in the loop
  2688. //
  2689. ASSERT((pDest-1)[0] != UNICODE_NULL);
  2690. pDest[0] = UNICODE_NULL;
  2691. //
  2692. // need to recompute the hash?
  2693. //
  2694. if (HashValid == FALSE)
  2695. {
  2696. //
  2697. // this can happen if we had to backtrack due to /../
  2698. //
  2699. UrlHash = HashStringW(pDestination, *pUrlHash);
  2700. }
  2701. *pUrlHash = UrlHash;
  2702. *pBytesCopied = BytesCopied;
  2703. if (ppQueryString != NULL)
  2704. {
  2705. *ppQueryString = pQueryString;
  2706. }
  2707. Status = STATUS_SUCCESS;
  2708. end:
  2709. return Status;
  2710. } // UlpCleanAndCopyUrl
  2711. /***************************************************************************++
  2712. Routine Description:
  2713. Figures out how big the fixed headers are. Fixed headers include the
  2714. status line, and any headers that don't have to be generated for
  2715. every request (such as Date and Connection).
  2716. The final CRLF separating headers from body is considered part of
  2717. the variable headers.
  2718. Arguments:
  2719. pResponse - the response containing the headers
  2720. Return Values:
  2721. The number of bytes in the fixed headers.
  2722. --***************************************************************************/
  2723. ULONG
  2724. UlComputeFixedHeaderSize(
  2725. IN HTTP_VERSION Version,
  2726. IN PHTTP_RESPONSE pResponse
  2727. )
  2728. {
  2729. ULONG HeaderLength;
  2730. ULONG i;
  2731. PHTTP_UNKNOWN_HEADER pUnknownHeaders;
  2732. //
  2733. // Sanity check.
  2734. //
  2735. PAGED_CODE();
  2736. if ((pResponse == NULL) || HTTP_EQUAL_VERSION(Version, 0, 9)) {
  2737. return 0;
  2738. }
  2739. HeaderLength = 0;
  2740. HeaderLength += VERSION_SIZE + // HTTP-Version
  2741. 1 + // SP
  2742. 3 + // Status-Code
  2743. 1 + // SP
  2744. pResponse->ReasonLength / sizeof(WCHAR) + // Reason-Phrase
  2745. CRLF_SIZE; // CRLF
  2746. //
  2747. // Loop through the known headers.
  2748. //
  2749. for (i = 0; i < HttpHeaderResponseMaximum; ++i)
  2750. {
  2751. USHORT RawValueLength = pResponse->Headers.pKnownHeaders[i].RawValueLength;
  2752. // skip some headers we'll generate
  2753. if ((i == HttpHeaderDate) || (i == HttpHeaderConnection)) {
  2754. continue;
  2755. }
  2756. if (RawValueLength > 0)
  2757. {
  2758. HeaderLength += HeaderMapTable[
  2759. ResponseHeaderMap[i]
  2760. ].HeaderLength + // Header-Name
  2761. 1 + // SP
  2762. RawValueLength / sizeof(WCHAR) + // Header-Value
  2763. CRLF_SIZE; // CRLF
  2764. }
  2765. }
  2766. //
  2767. // Include default headers we may need to generate for the application.
  2768. //
  2769. if (pResponse->Headers.pKnownHeaders[HttpHeaderServer].RawValueLength == 0)
  2770. {
  2771. HeaderLength += HeaderMapTable[
  2772. ResponseHeaderMap[HttpHeaderServer]
  2773. ].HeaderLength + // Header-Name
  2774. 1 + // SP
  2775. DEFAULT_SERVER_HDR_LENGTH + // Header-Value
  2776. CRLF_SIZE; // CRLF
  2777. }
  2778. //
  2779. // And the unknown headers (this might throw an exception).
  2780. //
  2781. pUnknownHeaders = pResponse->Headers.pUnknownHeaders;
  2782. if (pUnknownHeaders != NULL)
  2783. {
  2784. for (i = 0 ; i < pResponse->Headers.UnknownHeaderCount; ++i)
  2785. {
  2786. USHORT Length;
  2787. if (pUnknownHeaders[i].NameLength > 0)
  2788. {
  2789. HeaderLength += pUnknownHeaders[i].NameLength /
  2790. sizeof(WCHAR) + // Header-Name
  2791. 1 + // ':'
  2792. 1 + // SP
  2793. pUnknownHeaders[i].RawValueLength /
  2794. sizeof(WCHAR) + // Header-Value
  2795. CRLF_SIZE; // CRLF
  2796. }
  2797. }
  2798. }
  2799. return HeaderLength;
  2800. } // UlComputeFixedHeaderSize
  2801. /***************************************************************************++
  2802. Routine Description:
  2803. Figures out how big the variable headers are. Variable headers include
  2804. Date and Connection.
  2805. The final CRLF separating headers from body is considered part of
  2806. the variable headers.
  2807. Arguments:
  2808. ConnHeader - Tells us which connection header to generate
  2809. Return Values:
  2810. The number of bytes in the fixed headers.
  2811. --***************************************************************************/
  2812. ULONG
  2813. UlComputeVariableHeaderSize(
  2814. IN UL_CONN_HDR ConnHeader
  2815. )
  2816. {
  2817. ULONG Length;
  2818. PHEADER_MAP_ENTRY pEntry;
  2819. Length = 0;
  2820. //
  2821. // Date: header
  2822. //
  2823. pEntry = &(HeaderMapTable[ResponseHeaderMap[HttpHeaderDate]]);
  2824. Length += pEntry->HeaderLength; // header name
  2825. Length += 1; // SP
  2826. Length += DATE_HDR_LENGTH; // header value
  2827. Length += CRLF_SIZE; // CRLF
  2828. //
  2829. // Connection: header
  2830. //
  2831. pEntry = &(HeaderMapTable[ResponseHeaderMap[HttpHeaderConnection]]);
  2832. switch (ConnHeader) {
  2833. case ConnHdrNone:
  2834. // no header
  2835. break;
  2836. case ConnHdrClose:
  2837. Length += pEntry->HeaderLength;
  2838. Length += 1;
  2839. Length += CONN_CLOSE_HDR_LENGTH;
  2840. Length += CRLF_SIZE;
  2841. break;
  2842. case ConnHdrKeepAlive:
  2843. Length += pEntry->HeaderLength;
  2844. Length += 1;
  2845. Length += CONN_KEEPALIVE_HDR_LENGTH;
  2846. Length += CRLF_SIZE;
  2847. break;
  2848. default:
  2849. ASSERT( ConnHeader < ConnHdrMax );
  2850. break;
  2851. }
  2852. //
  2853. // final CRLF
  2854. //
  2855. Length += CRLF_SIZE;
  2856. return Length;
  2857. } // UlComputeVariableHeaderSize
  2858. /***************************************************************************++
  2859. Routine Description:
  2860. Generates the varaible part of the header, including Date:, Connection:,
  2861. and final CRLF.
  2862. Arguments:
  2863. ConnHeader - tells us what Connection: value to send
  2864. BufferLength - length of pBuffer
  2865. pBuffer - generate the headers here
  2866. pBytesCopied - gets the number of bytes generated
  2867. --***************************************************************************/
  2868. VOID
  2869. UlGenerateVariableHeaders(
  2870. IN UL_CONN_HDR ConnHeader,
  2871. IN ULONG BufferLength,
  2872. OUT PUCHAR pBuffer,
  2873. OUT PULONG pBytesCopied
  2874. )
  2875. {
  2876. PHEADER_MAP_ENTRY pEntry;
  2877. PUCHAR pStartBuffer;
  2878. PUCHAR pCloseHeaderValue;
  2879. ULONG CloseHeaderValueLength;
  2880. ULONG BytesCopied;
  2881. ASSERT( pBuffer );
  2882. ASSERT( pBytesCopied );
  2883. ASSERT( BufferLength >= UlComputeVariableHeaderSize(ConnHeader) );
  2884. pStartBuffer = pBuffer;
  2885. //
  2886. // generate Date: header
  2887. //
  2888. pEntry = &(HeaderMapTable[ResponseHeaderMap[HttpHeaderDate]]);
  2889. RtlCopyMemory(
  2890. pBuffer,
  2891. pEntry->MixedCaseHeader,
  2892. pEntry->HeaderLength
  2893. );
  2894. pBuffer += pEntry->HeaderLength;
  2895. pBuffer[0] = SP;
  2896. pBuffer += 1;
  2897. BytesCopied = GenerateDateHeader( pBuffer );
  2898. pBuffer += BytesCopied;
  2899. ((PUSHORT)pBuffer)[0] = CRLF;
  2900. pBuffer += sizeof(USHORT);
  2901. //
  2902. // generate Connection: header
  2903. //
  2904. switch (ConnHeader) {
  2905. case ConnHdrNone:
  2906. pCloseHeaderValue = NULL;
  2907. CloseHeaderValueLength = 0;
  2908. break;
  2909. case ConnHdrClose:
  2910. pCloseHeaderValue = CONN_CLOSE_HDR;
  2911. CloseHeaderValueLength = CONN_CLOSE_HDR_LENGTH;
  2912. break;
  2913. case ConnHdrKeepAlive:
  2914. pCloseHeaderValue = CONN_KEEPALIVE_HDR;
  2915. CloseHeaderValueLength = CONN_KEEPALIVE_HDR_LENGTH;
  2916. break;
  2917. default:
  2918. ASSERT(ConnHeader < ConnHdrMax);
  2919. pCloseHeaderValue = NULL;
  2920. CloseHeaderValueLength = 0;
  2921. break;
  2922. }
  2923. if (pCloseHeaderValue != NULL) {
  2924. pEntry = &(HeaderMapTable[ResponseHeaderMap[HttpHeaderConnection]]);
  2925. RtlCopyMemory(
  2926. pBuffer,
  2927. pEntry->MixedCaseHeader,
  2928. pEntry->HeaderLength
  2929. );
  2930. pBuffer += pEntry->HeaderLength;
  2931. pBuffer[0] = SP;
  2932. pBuffer += 1;
  2933. RtlCopyMemory(
  2934. pBuffer,
  2935. pCloseHeaderValue,
  2936. CloseHeaderValueLength
  2937. );
  2938. pBuffer += CloseHeaderValueLength;
  2939. ((PUSHORT)pBuffer)[0] = CRLF;
  2940. pBuffer += sizeof(USHORT);
  2941. }
  2942. //
  2943. // generate final CRLF
  2944. //
  2945. ((PUSHORT)pBuffer)[0] = CRLF;
  2946. pBuffer += sizeof(USHORT);
  2947. //
  2948. // make sure we didn't use too much
  2949. //
  2950. BytesCopied = DIFF(pBuffer - pStartBuffer);
  2951. *pBytesCopied = BytesCopied;
  2952. ASSERT( BytesCopied <= BufferLength );
  2953. }
  2954. ULONG
  2955. _WideCharToMultiByte(
  2956. ULONG uCodePage,
  2957. ULONG dwFlags,
  2958. PCWSTR lpWideCharStr,
  2959. int cchWideChar,
  2960. PSTR lpMultiByteStr,
  2961. int cchMultiByte,
  2962. PCSTR lpDefaultChar,
  2963. BOOLEAN *lpfUsedDefaultChar
  2964. )
  2965. {
  2966. int i;
  2967. //
  2968. // simply strip the upper byte, it's supposed to be ascii already
  2969. //
  2970. for (i = 0; i < cchWideChar; ++i)
  2971. {
  2972. if ((lpWideCharStr[i] & 0xff00) != 0 || IS_HTTP_CTL(lpWideCharStr[i]))
  2973. {
  2974. lpMultiByteStr[0] = *lpDefaultChar;
  2975. }
  2976. else
  2977. {
  2978. lpMultiByteStr[0] = (UCHAR)(lpWideCharStr[i]);
  2979. }
  2980. lpMultiByteStr += 1;
  2981. }
  2982. return (ULONG)(i);
  2983. } // _WideCharToMultiByte
  2984. ULONG
  2985. _MultiByteToWideChar(
  2986. ULONG uCodePage,
  2987. ULONG dwFlags,
  2988. PCSTR lpMultiByteStr,
  2989. int cchMultiByte,
  2990. PWSTR lpWideCharStr,
  2991. int cchWideChar
  2992. )
  2993. {
  2994. int i;
  2995. //
  2996. // simply add a 0 upper byte, it's supposed to be ascii
  2997. //
  2998. for (i = 0; i < cchMultiByte; ++i)
  2999. {
  3000. if (lpMultiByteStr[i] > 128)
  3001. {
  3002. lpWideCharStr[i] = (WCHAR)(DefaultChar);
  3003. }
  3004. else
  3005. {
  3006. lpWideCharStr[i] = (WCHAR)(lpMultiByteStr[i]);
  3007. }
  3008. }
  3009. return (ULONG)(i);
  3010. } // _MultiByteToWideChar
  3011. /***************************************************************************++
  3012. Routine Description:
  3013. Generates the fixed part of the header. Fixed headers include the
  3014. status line, and any headers that don't have to be generated for
  3015. every request (such as Date and Connection).
  3016. The final CRLF separating headers from body is considered part of
  3017. the variable headers.
  3018. Arguments:
  3019. Version - the http version for the status line
  3020. pResponse - the user specified response
  3021. BufferLength - length of pBuffer
  3022. pBuffer - generate the headers here
  3023. pBytesCopied - gets the number of bytes generated
  3024. --***************************************************************************/
  3025. VOID
  3026. UlGenerateFixedHeaders(
  3027. IN HTTP_VERSION Version,
  3028. IN PHTTP_RESPONSE pResponse,
  3029. IN ULONG BufferLength,
  3030. OUT PUCHAR pBuffer,
  3031. OUT PULONG pBytesCopied
  3032. )
  3033. {
  3034. PUCHAR pStartBuffer;
  3035. PUCHAR pEndOfNumber;
  3036. ULONG BytesCopied;
  3037. ULONG i;
  3038. PHTTP_UNKNOWN_HEADER pUnknownHeaders;
  3039. //
  3040. // Sanity check.
  3041. //
  3042. PAGED_CODE();
  3043. ASSERT(pResponse != NULL);
  3044. ASSERT(pBuffer != NULL && BufferLength > 0);
  3045. ASSERT(pBytesCopied != NULL);
  3046. pStartBuffer = pBuffer;
  3047. //
  3048. // Build the response headers.
  3049. //
  3050. if (HTTP_NOT_EQUAL_VERSION(Version, 0, 9))
  3051. {
  3052. //
  3053. // Always send back 1.1 in the response.
  3054. //
  3055. RtlCopyMemory(pBuffer, "HTTP/1.1 ", sizeof("HTTP/1.1 ") - 1);
  3056. pBuffer += sizeof("HTTP/1.1 ") - 1;
  3057. //
  3058. // Status code.
  3059. //
  3060. pBuffer[0] = '0' + ((pResponse->StatusCode / 100) % 10);
  3061. pBuffer[1] = '0' + ((pResponse->StatusCode / 10) % 10);
  3062. pBuffer[2] = '0' + ((pResponse->StatusCode / 1) % 10);
  3063. pBuffer[3] = SP;
  3064. pBuffer += 4;
  3065. //
  3066. // Copy the reason, converting from widechar.
  3067. //
  3068. RtlCopyMemory(
  3069. pBuffer,
  3070. pResponse->pReason,
  3071. pResponse->ReasonLength
  3072. );
  3073. pBuffer += pResponse->ReasonLength;
  3074. //
  3075. // Terminate with the CRLF.
  3076. //
  3077. ((PUSHORT)pBuffer)[0] = CRLF;
  3078. pBuffer += sizeof(USHORT);
  3079. //
  3080. // Loop through the known headers.
  3081. //
  3082. for (i = 0; i < HttpHeaderResponseMaximum; ++i)
  3083. {
  3084. // skip some headers we'll generate
  3085. if ((i == HttpHeaderDate) || (i == HttpHeaderConnection)) {
  3086. continue;
  3087. }
  3088. if (pResponse->Headers.pKnownHeaders[i].RawValueLength > 0)
  3089. {
  3090. PHEADER_MAP_ENTRY pEntry;
  3091. pEntry = &(HeaderMapTable[ResponseHeaderMap[i]]);
  3092. RtlCopyMemory(
  3093. pBuffer,
  3094. pEntry->MixedCaseHeader,
  3095. pEntry->HeaderLength
  3096. );
  3097. pBuffer += pEntry->HeaderLength;
  3098. pBuffer[0] = SP;
  3099. pBuffer += 1;
  3100. RtlCopyMemory(
  3101. pBuffer,
  3102. pResponse->Headers.pKnownHeaders[i].pRawValue,
  3103. pResponse->Headers.pKnownHeaders[i].RawValueLength
  3104. );
  3105. pBuffer += pResponse->Headers.pKnownHeaders[i].RawValueLength;
  3106. ((PUSHORT)pBuffer)[0] = CRLF;
  3107. pBuffer += sizeof(USHORT);
  3108. }
  3109. }
  3110. //
  3111. // Append some default headers if not provided by the application.
  3112. //
  3113. if (pResponse->Headers.pKnownHeaders[HttpHeaderServer].RawValueLength == 0)
  3114. {
  3115. PHEADER_MAP_ENTRY pEntry;
  3116. pEntry = &(HeaderMapTable[ResponseHeaderMap[HttpHeaderServer]]);
  3117. RtlCopyMemory(
  3118. pBuffer,
  3119. pEntry->MixedCaseHeader,
  3120. pEntry->HeaderLength
  3121. );
  3122. pBuffer += pEntry->HeaderLength;
  3123. pBuffer[0] = SP;
  3124. pBuffer += 1;
  3125. RtlCopyMemory(
  3126. pBuffer,
  3127. DEFAULT_SERVER_HDR,
  3128. DEFAULT_SERVER_HDR_LENGTH
  3129. );
  3130. pBuffer += DEFAULT_SERVER_HDR_LENGTH;
  3131. ((PUSHORT)pBuffer)[0] = CRLF;
  3132. pBuffer += sizeof(USHORT);
  3133. }
  3134. //
  3135. // And now the unknown headers (this might throw an exception).
  3136. //
  3137. pUnknownHeaders = pResponse->Headers.pUnknownHeaders;
  3138. if (pUnknownHeaders != NULL)
  3139. {
  3140. for (i = 0 ; i < pResponse->Headers.UnknownHeaderCount; ++i)
  3141. {
  3142. if (pUnknownHeaders[i].NameLength > 0)
  3143. {
  3144. RtlCopyMemory(
  3145. pBuffer,
  3146. pUnknownHeaders[i].pName,
  3147. pUnknownHeaders[i].NameLength
  3148. );
  3149. pBuffer += pUnknownHeaders[i].NameLength;
  3150. *pBuffer++ = ':';
  3151. *pBuffer++ = SP;
  3152. RtlCopyMemory(
  3153. pBuffer,
  3154. pUnknownHeaders[i].pRawValue,
  3155. pUnknownHeaders[i].RawValueLength
  3156. );
  3157. pBuffer += pUnknownHeaders[i].RawValueLength;
  3158. ((PUSHORT)pBuffer)[0] = CRLF;
  3159. pBuffer += sizeof(USHORT);
  3160. } // if (pUnknownHeaders[i].NameLength > 0)
  3161. }
  3162. } // if (pUnknownHeaders != NULL)
  3163. *pBytesCopied = DIFF(pBuffer - pStartBuffer);
  3164. } // if (Version > UlHttpVersion09)
  3165. else
  3166. {
  3167. *pBytesCopied = 0;
  3168. }
  3169. //
  3170. // Ensure we didn't use too much.
  3171. //
  3172. ASSERT(DIFF(pBuffer - pStartBuffer) <= BufferLength);
  3173. } // UlGenerateFixedHeaders
  3174. PSTR Weekdays[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
  3175. PSTR Months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  3176. "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
  3177. ULONG
  3178. GenerateDateHeader(
  3179. OUT PUCHAR pBuffer
  3180. )
  3181. {
  3182. LARGE_INTEGER systemTime;
  3183. TIME_FIELDS timeFields;
  3184. int length;
  3185. //
  3186. // CODEWORK: Cache this stuff, don't regenerate on EVERY request.
  3187. //
  3188. KeQuerySystemTime( &systemTime );
  3189. RtlTimeToTimeFields( &systemTime, &timeFields );
  3190. length = sprintf(
  3191. pBuffer,
  3192. "%s, %02d %s %04d %02d:%02d:%02d GMT",
  3193. Weekdays[timeFields.Weekday],
  3194. timeFields.Day,
  3195. Months[timeFields.Month - 1],
  3196. timeFields.Year,
  3197. timeFields.Hour,
  3198. timeFields.Minute,
  3199. timeFields.Second
  3200. );
  3201. ASSERT( length <= DATE_HDR_LENGTH );
  3202. return (ULONG)length;
  3203. } // GenerateDateHeader