Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

6017 lines
172 KiB

  1. /*++
  2. Copyright (c) 1998-2002 Microsoft Corporation
  3. Module Name:
  4. ulparse.c
  5. Abstract:
  6. Contains the kernel-mode server-side HTTP parsing code.
  7. Author:
  8. Henry Sanders (henrysa) 27-Apr-1998
  9. Revision History:
  10. Rajesh Sundaram (rajeshsu) 15-Feb-2002 Moved from parse.c
  11. --*/
  12. #include "precomp.h"
  13. #include "ulparsep.h"
  14. #ifdef ALLOC_PRAGMA
  15. #pragma alloc_text( PAGE, UlpFindWSToken )
  16. #pragma alloc_text( PAGE, UlpLookupVerb )
  17. #pragma alloc_text( PAGE, UlpParseFullUrl )
  18. #pragma alloc_text( PAGE, UlLookupHeader )
  19. #pragma alloc_text( PAGE, UlParseHeaderWithHint )
  20. #pragma alloc_text( PAGE, UlParseHeader )
  21. #pragma alloc_text( PAGE, UlParseHeaders )
  22. #pragma alloc_text( PAGE, UlParseHttp )
  23. #pragma alloc_text( PAGE, UlpFormatPort )
  24. #pragma alloc_text( PAGE, UlpCookUrl )
  25. #pragma alloc_text( PAGE, UlGenerateRoutingToken )
  26. #pragma alloc_text( PAGE, UlGenerateFixedHeaders )
  27. #pragma alloc_text( PAGE, UlpGenerateDateHeaderString )
  28. #pragma alloc_text( PAGE, UlGenerateDateHeader )
  29. #pragma alloc_text( INIT, UlInitializeDateCache )
  30. #pragma alloc_text( PAGE, UlTerminateDateCache )
  31. #pragma alloc_text( PAGE, UlComputeFixedHeaderSize )
  32. #pragma alloc_text( PAGE, UlComputeMaxVariableHeaderSize )
  33. #pragma alloc_text( PAGE, UlGenerateVariableHeaders )
  34. #pragma alloc_text( PAGE, UlAppendHeaderValue )
  35. #pragma alloc_text( PAGE, UlSingleHeaderHandler )
  36. #pragma alloc_text( PAGE, UlMultipleHeaderHandler )
  37. #pragma alloc_text( PAGE, UlAcceptHeaderHandler )
  38. #pragma alloc_text( PAGE, UlHostHeaderHandler )
  39. #pragma alloc_text( PAGE, UlCheckCacheControlHeaders )
  40. #pragma alloc_text( PAGE, UlIsAcceptHeaderOk )
  41. #pragma alloc_text( PAGE, UlGetTypeAndSubType )
  42. #if DBG
  43. #pragma alloc_text( PAGE, UlVerbToString )
  44. #pragma alloc_text( PAGE, UlParseStateToString )
  45. #endif
  46. #endif // ALLOC_PRAGMA
  47. #if 0 // Non-Pageable Functions
  48. NOT PAGEABLE -- UlIsContentEncodingOk
  49. #endif // Non-Pageable Functions
  50. //
  51. // Global initialization flag.
  52. //
  53. BOOLEAN g_DateCacheInitialized = FALSE;
  54. //
  55. // Keep track of UrlLength statistics
  56. //
  57. #define URL_LENGTH_STATS 1
  58. #ifdef URL_LENGTH_STATS
  59. struct {
  60. ULONGLONG SumUrlLengths;
  61. ULONG NumUrls;
  62. ULONG NumReallocs;
  63. } g_UrlLengthStats = {0, 0, 0};
  64. #define URL_LENGTH_STATS_UPDATE(UrlLength) \
  65. UlInterlockedAdd64((PLONGLONG) &g_UrlLengthStats.SumUrlLengths, UrlLength);\
  66. InterlockedIncrement((PLONG) &g_UrlLengthStats.NumUrls)
  67. #define URL_LENGTH_STATS_REALLOC() \
  68. InterlockedIncrement((PLONG) &g_UrlLengthStats.NumReallocs)
  69. #else // !URL_LENGTH_STATS
  70. #define URL_LENGTH_STATS_UPDATE(UrlLength) ((void) 0)
  71. #define URL_LENGTH_STATS_REALLOC() ((void) 0)
  72. #endif // !URL_LENGTH_STATS
  73. // Hack for the special case of an AbsUri without a '/' for the abspath
  74. const UCHAR g_SlashPath[3] = "/ ";
  75. //
  76. // The fast verb translation table. Ordered by frequency.
  77. //
  78. const DECLSPEC_ALIGN(UL_CACHE_LINE) FAST_VERB_ENTRY
  79. FastVerbTable[] =
  80. {
  81. CREATE_FAST_VERB_ENTRY(GET),
  82. CREATE_FAST_VERB_ENTRY(HEAD),
  83. CREATE_FAST_VERB_ENTRY(POST),
  84. CREATE_FAST_VERB_ENTRY(PUT),
  85. CREATE_FAST_VERB_ENTRY(DELETE),
  86. CREATE_FAST_VERB_ENTRY(TRACE),
  87. CREATE_FAST_VERB_ENTRY(TRACK),
  88. CREATE_FAST_VERB_ENTRY(OPTIONS),
  89. CREATE_FAST_VERB_ENTRY(CONNECT),
  90. CREATE_FAST_VERB_ENTRY(MOVE),
  91. CREATE_FAST_VERB_ENTRY(COPY),
  92. CREATE_FAST_VERB_ENTRY(MKCOL),
  93. CREATE_FAST_VERB_ENTRY(LOCK),
  94. CREATE_FAST_VERB_ENTRY(UNLOCK),
  95. CREATE_FAST_VERB_ENTRY(SEARCH)
  96. };
  97. //
  98. // The long verb translation table. All known verbs more than 7 characters
  99. // long belong in this table.
  100. //
  101. const LONG_VERB_ENTRY LongVerbTable[] =
  102. {
  103. CREATE_LONG_VERB_ENTRY(PROPFIND),
  104. CREATE_LONG_VERB_ENTRY(PROPPATCH)
  105. };
  106. #define NUMBER_FAST_VERB_ENTRIES DIMENSION(FastVerbTable)
  107. #define NUMBER_LONG_VERB_ENTRIES DIMENSION(LongVerbTable)
  108. //
  109. // The enum->verb translation table for error logging.
  110. //
  111. LONG_VERB_ENTRY NewVerbTable[HttpVerbMaximum] =
  112. {
  113. CREATE_LONG_VERB_ENTRY(Unparsed),
  114. CREATE_LONG_VERB_ENTRY(Unknown),
  115. CREATE_LONG_VERB_ENTRY(Invalid),
  116. CREATE_LONG_VERB_ENTRY(OPTIONS),
  117. CREATE_LONG_VERB_ENTRY(GET),
  118. CREATE_LONG_VERB_ENTRY(HEAD),
  119. CREATE_LONG_VERB_ENTRY(POST),
  120. CREATE_LONG_VERB_ENTRY(PUT),
  121. CREATE_LONG_VERB_ENTRY(DELETE),
  122. CREATE_LONG_VERB_ENTRY(TRACE),
  123. CREATE_LONG_VERB_ENTRY(CONNECT),
  124. CREATE_LONG_VERB_ENTRY(TRACK),
  125. CREATE_LONG_VERB_ENTRY(MOVE),
  126. CREATE_LONG_VERB_ENTRY(COPY),
  127. CREATE_LONG_VERB_ENTRY(PROPFIND),
  128. CREATE_LONG_VERB_ENTRY(PROPPATCH),
  129. CREATE_LONG_VERB_ENTRY(MKCOL),
  130. CREATE_LONG_VERB_ENTRY(LOCK),
  131. CREATE_LONG_VERB_ENTRY(UNLOCK),
  132. CREATE_LONG_VERB_ENTRY(SEARCH)
  133. };
  134. /*++
  135. Routine Description:
  136. A utility routine to find a token. We take an input pointer, skip any
  137. preceding LWS, then scan the token until we find either LWS or a CRLF
  138. pair.
  139. Arguments:
  140. pBuffer - Buffer to search for token.
  141. BufferLength - Length of data pointed to by pBuffer.
  142. ppTokenStart - Start of token or NULL
  143. pTokenLength - Where to return the length of the token.
  144. Return Values:
  145. STATUS_SUCCESS - Valid token, described by *ppTokenStart and *pTokenLength
  146. STATUS_MORE_PROCESSING_REQUIRED - No terminating WS_TOKEN found
  147. => retry later with more data
  148. STATUS_INVALID_DEVICE_REQUEST - Invalid token characters.
  149. --*/
  150. NTSTATUS
  151. UlpFindWSToken(
  152. IN PUCHAR pBuffer,
  153. IN ULONG BufferLength,
  154. OUT PUCHAR* ppTokenStart,
  155. OUT PULONG pTokenLength
  156. )
  157. {
  158. PUCHAR pTokenStart;
  159. #if DBG
  160. ULONG OriginalBufferLength = BufferLength;
  161. #endif
  162. PAGED_CODE();
  163. ASSERT(NULL != pBuffer);
  164. ASSERT(NULL != ppTokenStart);
  165. ASSERT(NULL != pTokenLength);
  166. *ppTokenStart = NULL;
  167. //
  168. // First, skip any preceding LWS (SP | HT).
  169. //
  170. while (BufferLength > 0 && IS_HTTP_LWS(*pBuffer))
  171. {
  172. pBuffer++;
  173. BufferLength--;
  174. }
  175. // If we stopped because we ran out of buffer, fail soft.
  176. if (BufferLength == 0)
  177. {
  178. return STATUS_MORE_PROCESSING_REQUIRED;
  179. }
  180. pTokenStart = pBuffer;
  181. // Now skip over the token, until we see (SP | HT | CR | LF)
  182. do
  183. {
  184. // token = 1*<any CHAR except CTLs or separators>
  185. // If a non-token, non-whitespace character is found, fail hard.
  186. if (!IS_HTTP_TOKEN(*pBuffer))
  187. {
  188. UlTraceError(PARSER, (
  189. "http!UlpFindWSToken(): non-token char %02x\n",
  190. *pBuffer
  191. ));
  192. return STATUS_INVALID_DEVICE_REQUEST;
  193. }
  194. pBuffer++;
  195. BufferLength--;
  196. } while ( ( BufferLength != 0 ) && ( !IS_HTTP_WS_TOKEN(*pBuffer) ));
  197. // See why we stopped.
  198. if (BufferLength == 0)
  199. {
  200. // Ran out of buffer before end of token. Fail soft.
  201. return STATUS_MORE_PROCESSING_REQUIRED;
  202. }
  203. // Success. Set the token length and return the start of the token.
  204. *ppTokenStart = pTokenStart;
  205. *pTokenLength = DIFF(pBuffer - pTokenStart);
  206. ASSERT(0 < *pTokenLength && *pTokenLength < OriginalBufferLength);
  207. return STATUS_SUCCESS;
  208. } // UlpFindWSToken
  209. /*++
  210. Routine Description:
  211. The slower way to look up a verb. We find the verb in the request and then
  212. look for it in the LongVerbTable. If it's not found, we'll return
  213. UnknownVerb. If it can't be parsed we return UnparsedVerb. Otherwise
  214. we return the verb type.
  215. Arguments:
  216. pRequest - HTTP request.
  217. pHttpRequest - Pointer to the incoming HTTP request data.
  218. HttpRequestLength - Length of data pointed to by pHttpRequest.
  219. pBytesTaken - The total length consumed, including the length of
  220. the verb plus preceding & 1 trailing whitespace.
  221. Return Value:
  222. STATUS_SUCCESS or STATUS_INVALID_DEVICE_REQUEST
  223. --*/
  224. NTSTATUS
  225. UlpLookupVerb(
  226. IN OUT PUL_INTERNAL_REQUEST pRequest,
  227. IN PUCHAR pHttpRequest,
  228. IN ULONG HttpRequestLength,
  229. OUT PULONG pBytesTaken
  230. )
  231. {
  232. NTSTATUS Status;
  233. ULONG TokenLength;
  234. PUCHAR pToken;
  235. PUCHAR pTempRequest;
  236. ULONG TempLength;
  237. ULONG i;
  238. //
  239. // Sanity check.
  240. //
  241. PAGED_CODE();
  242. // Since we may have gotten here due to a extraneous CRLF pair, skip
  243. // any of those now. Need to use a temporary variable since
  244. // the original input pointer and length are used below.
  245. pTempRequest = pHttpRequest;
  246. TempLength = HttpRequestLength;
  247. while ( TempLength != 0 &&
  248. ((*pTempRequest == CR) || (*pTempRequest == LF)) )
  249. {
  250. pTempRequest++;
  251. TempLength--;
  252. }
  253. // First find the verb.
  254. Status = UlpFindWSToken(pTempRequest, TempLength, &pToken, &TokenLength);
  255. if (!NT_SUCCESS(Status))
  256. {
  257. if (STATUS_MORE_PROCESSING_REQUIRED == Status)
  258. {
  259. // Didn't find it, let's get more buffer
  260. pRequest->Verb = HttpVerbUnparsed;
  261. *pBytesTaken = 0;
  262. return STATUS_SUCCESS;
  263. }
  264. ASSERT(STATUS_INVALID_DEVICE_REQUEST == Status);
  265. pRequest->Verb = HttpVerbInvalid;
  266. UlTraceError(PARSER, (
  267. "http!UlpLookupVerb(pRequest = %p): "
  268. "invalid token in verb\n",
  269. pRequest
  270. ));
  271. UlSetErrorCode(pRequest, UlErrorVerb, NULL);
  272. return Status;
  273. }
  274. ASSERT(STATUS_SUCCESS == Status);
  275. ASSERT(NULL != pToken);
  276. ASSERT(0 < TokenLength && TokenLength < TempLength);
  277. ASSERT(IS_HTTP_WS_TOKEN(pToken[TokenLength]));
  278. // Is the verb terminated by CR or LF (instead of SP or HT),
  279. // or is it ridiculously long? Reject, if so.
  280. if (!IS_HTTP_LWS(pToken[TokenLength]) || TokenLength > MAX_VERB_LENGTH)
  281. {
  282. pRequest->Verb = HttpVerbInvalid;
  283. if (!IS_HTTP_LWS(pToken[TokenLength]))
  284. {
  285. UlTraceError(PARSER, (
  286. "http!UlpLookupVerb(pRequest = %p) "
  287. "ERROR: no LWS after verb, %02x\n",
  288. pRequest, pToken[TokenLength]
  289. ));
  290. }
  291. else
  292. {
  293. UlTraceError(PARSER, (
  294. "http!UlpLookupVerb(pRequest = %p) "
  295. "ERROR: Verb too long\n",
  296. pRequest, TokenLength
  297. ));
  298. }
  299. UlSetErrorCode(pRequest, UlErrorVerb, NULL);
  300. return STATUS_INVALID_DEVICE_REQUEST;
  301. }
  302. // Otherwise, we found one, so update bytes taken and look up up in
  303. // the tables.
  304. *pBytesTaken = DIFF(pToken - pHttpRequest) + TokenLength + 1;
  305. //
  306. // If we ate some leading whitespace, or if the HttpRequestLength is less
  307. // than sizeof(ULONGLONG), we must look through the "fast" verb table
  308. // again, but do it the "slow" way. Note: verbs are case-sensitive.
  309. //
  310. for (i = 0; i < NUMBER_FAST_VERB_ENTRIES; i++)
  311. {
  312. ASSERT(FastVerbTable[i].RawVerbLength - STRLEN_LIT(" ")
  313. < sizeof(ULONGLONG));
  314. if ((FastVerbTable[i].RawVerbLength == (TokenLength + STRLEN_LIT(" ")))
  315. && RtlEqualMemory(pToken, FastVerbTable[i].RawVerb.Char,
  316. TokenLength))
  317. {
  318. // It matched. Save the translated verb from the
  319. // table, and bail out.
  320. //
  321. pRequest->Verb = FastVerbTable[i].TranslatedVerb;
  322. return STATUS_SUCCESS;
  323. }
  324. }
  325. //
  326. // Now look through the "long" verb table
  327. //
  328. for (i = 0; i < NUMBER_LONG_VERB_ENTRIES; i++)
  329. {
  330. ASSERT(LongVerbTable[i].RawVerbLength >= sizeof(ULONGLONG));
  331. if (LongVerbTable[i].RawVerbLength == TokenLength &&
  332. RtlEqualMemory(pToken, LongVerbTable[i].RawVerb, TokenLength))
  333. {
  334. // Found it.
  335. //
  336. pRequest->Verb = LongVerbTable[i].TranslatedVerb;
  337. return STATUS_SUCCESS;
  338. }
  339. }
  340. //
  341. // If we got here, we searched both tables and didn't find it.
  342. // It's a raw (unknown) verb
  343. //
  344. pRequest->Verb = HttpVerbUnknown;
  345. pRequest->pRawVerb = pToken;
  346. pRequest->RawVerbLength = (UCHAR) TokenLength;
  347. ASSERT(pRequest->RawVerbLength == TokenLength);
  348. UlTrace(PARSER, (
  349. "http!UlpLookupVerb(pRequest = %p) "
  350. "Unknown verb (%lu) '%.*s'\n",
  351. pRequest, TokenLength, TokenLength, pToken
  352. ));
  353. //
  354. // include room for the terminator
  355. //
  356. pRequest->TotalRequestSize += (TokenLength + 1) * sizeof(CHAR);
  357. ASSERT( !(pRequest->RawVerbLength==3
  358. && RtlEqualMemory(pRequest->pRawVerb,"GET",3)));
  359. return STATUS_SUCCESS;
  360. } // UlpLookupVerb
  361. /*++
  362. Routine Description:
  363. A utility routine to parse an absolute URL in a URL string. When this
  364. is called, we already have loaded the entire url into RawUrl.pUrl and
  365. know that it starts with "http".
  366. This function's job is to set RawUrl.pHost and RawUrl.pAbsPath.
  367. Arguments:
  368. pRequest - Pointer to the HTTP_REQUEST
  369. Return Value:
  370. NTSTATUS
  371. Author:
  372. Henry Sanders () 1998
  373. Paul McDaniel (paulmcd) 6-Mar-1999
  374. --*/
  375. NTSTATUS
  376. UlpParseFullUrl(
  377. IN PUL_INTERNAL_REQUEST pRequest
  378. )
  379. {
  380. PUCHAR pURL;
  381. ULONG UrlLength;
  382. PUCHAR pUrlStart;
  383. //
  384. // Sanity check.
  385. //
  386. PAGED_CODE();
  387. pURL = pRequest->RawUrl.pUrl;
  388. UrlLength = pRequest->RawUrl.Length;
  389. ASSERT(NULL != pURL);
  390. ASSERT(0 < UrlLength);
  391. // First four characters must be "http" (case-insensitive),
  392. // as guaranteed by the caller.
  393. ASSERT(UrlLength >= HTTP_PREFIX_SIZE &&
  394. (*(UNALIGNED64 ULONG *) pURL & HTTP_PREFIX_MASK) == HTTP_PREFIX);
  395. //
  396. // When we're called, we know that the start of the URL must point at
  397. // an absolute scheme prefix. Adjust for that now.
  398. //
  399. pUrlStart = pURL + HTTP_PREFIX_SIZE;
  400. UrlLength -= HTTP_PREFIX_SIZE;
  401. //
  402. // Now check the second half of the absolute URL prefix. We use the larger
  403. // of the two possible prefix lengths here to do the check, because even if
  404. // it's the smaller of the two, we'll need the extra bytes after the prefix
  405. // anyway for the host name.
  406. //
  407. if (UrlLength < HTTP_PREFIX2_SIZE)
  408. {
  409. C_ASSERT(HTTP_PREFIX2_SIZE >= HTTP_PREFIX1_SIZE);
  410. UlTraceError(PARSER, (
  411. "http!UlpParseFullUrl(pRequest = %p) "
  412. "ERROR: no room for URL scheme name\n",
  413. pRequest
  414. ));
  415. UlSetErrorCode(pRequest, UlErrorUrl, NULL);
  416. return STATUS_INVALID_DEVICE_REQUEST;
  417. }
  418. // Are the next three characters == "://", i.e, starts with "http://" ?
  419. if ( (*(UNALIGNED64 ULONG *)pUrlStart & HTTP_PREFIX1_MASK) == HTTP_PREFIX1)
  420. {
  421. // Valid absolute URL.
  422. pUrlStart += HTTP_PREFIX1_SIZE;
  423. UrlLength -= HTTP_PREFIX1_SIZE;
  424. ASSERT(0 == _strnicmp((const char*) pRequest->RawUrl.pUrl,
  425. "http://",
  426. STRLEN_LIT("http://")));
  427. if (pRequest->Secure)
  428. {
  429. UlTraceError(PARSER, (
  430. "http!UlpParseFullUrl(pRequest = %p) "
  431. "ERROR: URL scheme name does not match endpoint "
  432. "security: \"http://\" seen on secure endpoint\n",
  433. pRequest
  434. ));
  435. UlSetErrorCode(pRequest, UlErrorUrl, NULL);
  436. return STATUS_INVALID_DEVICE_REQUEST;
  437. }
  438. }
  439. // Or are the next four characters == "s://", i.e, starts with "https://" ?
  440. else if ( (*(UNALIGNED64 ULONG *)pUrlStart & HTTP_PREFIX2_MASK)
  441. == HTTP_PREFIX2)
  442. {
  443. // Valid absolute URL.
  444. pUrlStart += HTTP_PREFIX2_SIZE;
  445. UrlLength -= HTTP_PREFIX2_SIZE;
  446. ASSERT(0 == _strnicmp((const char*) pRequest->RawUrl.pUrl,
  447. "https://",
  448. STRLEN_LIT("https://")));
  449. if (!pRequest->Secure)
  450. {
  451. UlTraceError(PARSER, (
  452. "http!UlpParseFullUrl(pRequest = %p) "
  453. "ERROR: URL scheme name does not match endpoint "
  454. "security: \"https://\" seen on insecure endpoint\n",
  455. pRequest
  456. ));
  457. UlSetErrorCode(pRequest, UlErrorUrl, NULL);
  458. return STATUS_INVALID_DEVICE_REQUEST;
  459. }
  460. }
  461. else
  462. {
  463. UlTraceError(PARSER, (
  464. "http!UlpParseFullUrl(pRequest = %p) "
  465. "ERROR: invalid URL scheme name\n",
  466. pRequest
  467. ));
  468. UlSetErrorCode(pRequest, UlErrorUrl, NULL);
  469. return STATUS_INVALID_DEVICE_REQUEST;
  470. }
  471. //
  472. // OK, we've got a valid absolute URL, and we've skipped over
  473. // the prefix part of it. Save a pointer to the host, and
  474. // search the host string until we find the trailing slash,
  475. // which signifies the end of the host/start of the absolute
  476. // path.
  477. //
  478. pRequest->RawUrl.pHost = pUrlStart;
  479. //
  480. // scan the host looking for the terminator
  481. //
  482. while (UrlLength > 0 && pUrlStart[0] != '/')
  483. {
  484. pUrlStart++;
  485. UrlLength--;
  486. }
  487. if (UrlLength == 0)
  488. {
  489. // Special case: we've received something like
  490. // GET http://www.example.com HTTP/1.1
  491. // (perhaps as a result of a redirect to "http://www.example.com",
  492. // see bug #527947). We will create a special path of "/".
  493. //
  494. pUrlStart = (PUCHAR) &g_SlashPath[0];
  495. }
  496. //
  497. // pUrlStart points to the start of the absolute path portion.
  498. //
  499. pRequest->RawUrl.pAbsPath = pUrlStart;
  500. return STATUS_SUCCESS;
  501. } // UlpParseFullUrl
  502. /*++
  503. Routine Description:
  504. Look up a header that we don't have in our fast lookup table. This
  505. could be because it's a header we don't understand, or because we
  506. couldn't use the fast lookup table due to insufficient buffer length.
  507. The latter reason is uncommon, but we'll check the input table anyway
  508. if we're given one. If we find a header match in our mapping table,
  509. we'll call the header handler. Otherwise we'll try to allocate an
  510. unknown header element, fill it in, and chain it on the http connection.
  511. Arguments:
  512. pRequest - Pointer to the current request
  513. pHttpRequest - Pointer to the current raw request data.
  514. HttpRequestLength - Bytes left in the request data.
  515. pHeaderMap - Pointer to start of an array of header map entries
  516. (may be NULL).
  517. HeaderMapCount - Number of entries in array pointed to by pHeaderMap.
  518. bIgnore - We don't want to write to the buffer.
  519. (used for parsing trailers)
  520. pBytesTaken - Bytes consumed by this routine from pHttpRequest,
  521. including CRLF.
  522. Return Value:
  523. STATUS_SUCCESS or an error.
  524. --*/
  525. NTSTATUS
  526. UlLookupHeader(
  527. IN PUL_INTERNAL_REQUEST pRequest,
  528. IN PUCHAR pHttpRequest,
  529. IN ULONG HttpRequestLength,
  530. IN PHEADER_MAP_ENTRY pHeaderMap,
  531. IN ULONG HeaderMapCount,
  532. IN BOOLEAN bIgnore,
  533. OUT ULONG * pBytesTaken
  534. )
  535. {
  536. NTSTATUS Status = STATUS_SUCCESS;
  537. ULONG CurrentOffset;
  538. USHORT HeaderNameLength;
  539. USHORT HeaderNameAndTrailingWSLength;
  540. ULONG i;
  541. ULONG BytesTaken;
  542. USHORT HeaderValueLength;
  543. USHORT TrailingWhiteSpaceCount;
  544. UCHAR CurrentChar;
  545. PUL_HTTP_UNKNOWN_HEADER pUnknownHeader;
  546. PLIST_ENTRY pListStart;
  547. PLIST_ENTRY pCurrentListEntry;
  548. ULONG OldHeaderLength;
  549. PUCHAR pHeaderValue;
  550. BOOLEAN ExternalAllocated;
  551. //
  552. // Sanity check.
  553. //
  554. PAGED_CODE();
  555. //
  556. // First, let's find the terminating : of the header name, if there is one.
  557. // This will also give us the length of the header, which we can then
  558. // use to search the header map table if we have one.
  559. //
  560. TrailingWhiteSpaceCount = 0;
  561. for (CurrentOffset = 0; CurrentOffset < HttpRequestLength; CurrentOffset++)
  562. {
  563. CurrentChar = *(pHttpRequest + CurrentOffset);
  564. if (CurrentChar == ':')
  565. {
  566. // We've found the end of the header.
  567. break;
  568. }
  569. else
  570. {
  571. if (CurrentOffset <= ANSI_STRING_MAX_CHAR_LEN)
  572. {
  573. if (IS_HTTP_TOKEN(CurrentChar))
  574. {
  575. if (TrailingWhiteSpaceCount == 0)
  576. {
  577. // We haven't come across any LWS yet.
  578. continue;
  579. }
  580. // else:
  581. // We are in the midst of skipping trailing LWS,
  582. // and don't expect anything but ':' or more LWS.
  583. // Fall through to error handler.
  584. //
  585. }
  586. else
  587. {
  588. //
  589. // Allow for trailing white-space chars
  590. //
  591. if (IS_HTTP_LWS(CurrentChar))
  592. {
  593. TrailingWhiteSpaceCount++;
  594. continue;
  595. }
  596. // else:
  597. // Invalid header; fall through to error handler
  598. //
  599. }
  600. }
  601. // Uh-oh, this isn't a valid header. What do we do now?
  602. UlTraceError(PARSER, (
  603. "UlLookupHeader(pRequest = %p) "
  604. "CurrentChar = 0x%02x '%c' Offset=%lu\n"
  605. " ERROR: invalid header char\n",
  606. pRequest,
  607. CurrentChar,
  608. isprint(CurrentChar) ? CurrentChar : '.',
  609. CurrentOffset
  610. ));
  611. UlSetErrorCode(pRequest, UlErrorHeader, NULL);
  612. Status = STATUS_INVALID_DEVICE_REQUEST;
  613. goto end;
  614. }
  615. }
  616. // Find out why we got out. If the current offset is less than the
  617. // header length, we got out because we found the :.
  618. if (CurrentOffset < HttpRequestLength)
  619. {
  620. // Found the terminator.
  621. ASSERT( ':' == *(pHttpRequest + CurrentOffset) );
  622. CurrentOffset++; // Update to point beyond terminator.
  623. HeaderNameAndTrailingWSLength = (USHORT) CurrentOffset;
  624. HeaderNameLength = HeaderNameAndTrailingWSLength - TrailingWhiteSpaceCount;
  625. }
  626. else
  627. {
  628. // Didn't find the :, need more.
  629. //
  630. *pBytesTaken = 0;
  631. goto end;
  632. }
  633. // See if we have a header map array we need to search.
  634. //
  635. if (pHeaderMap != NULL)
  636. {
  637. // We do have an array to search.
  638. for (i = 0; i < HeaderMapCount; i++)
  639. {
  640. ASSERT(pHeaderMap->pServerHandler != NULL);
  641. if (HeaderNameLength == pHeaderMap->HeaderLength &&
  642. //
  643. // Ignore the last character when doing the strnicmp - the
  644. // last character in pHeaderMap->Header.HeaderChar is a ':'
  645. // and the last character in pHttpRequest is either a space
  646. // or a ':'.
  647. //
  648. // We want to treat header-name<b>: as header-name:, so it's
  649. // OK to ignore the last character.
  650. //
  651. // Also note that we do this ONLY if the length's match.
  652. //
  653. _strnicmp(
  654. (const char *)(pHttpRequest),
  655. (const char *)(pHeaderMap->Header.HeaderChar),
  656. (HeaderNameLength-1)
  657. ) == 0 &&
  658. pHeaderMap->pServerHandler != NULL)
  659. {
  660. ASSERT(
  661. (*(pHttpRequest + HeaderNameLength - 1) == ':')
  662. ||
  663. (*(pHttpRequest + HeaderNameLength - 1) == ' ')
  664. );
  665. if (HttpRequestLength - HeaderNameLength
  666. > ANSI_STRING_MAX_CHAR_LEN)
  667. {
  668. UlTraceError(PARSER, (
  669. "UlLookupHeader(pRequest = %p) "
  670. "Header too long: %lu\n",
  671. pRequest,
  672. HttpRequestLength - HeaderNameLength
  673. ));
  674. UlSetErrorCode(pRequest, UlErrorHeader, NULL);
  675. Status = STATUS_INVALID_DEVICE_REQUEST;
  676. goto end;
  677. }
  678. // This header matches. Call the handling function for it.
  679. Status = (*(pHeaderMap->pServerHandler))(
  680. pRequest,
  681. pHttpRequest + HeaderNameAndTrailingWSLength,
  682. (USHORT) (HttpRequestLength - HeaderNameAndTrailingWSLength),
  683. pHeaderMap->HeaderID,
  684. &BytesTaken
  685. );
  686. if (NT_SUCCESS(Status) == FALSE)
  687. goto end;
  688. // If the handler consumed a non-zero number of bytes, it
  689. // worked, so return that number plus whatever is consumed
  690. // already.
  691. //
  692. // BUGBUG - it might be possible for a header handler to
  693. // encounter an error, for example being unable to
  694. // allocate memory, or a bad syntax in some header. We
  695. // need a more sophisticated method to detect this than
  696. // just checking bytes taken.
  697. //
  698. if (BytesTaken != 0)
  699. {
  700. *pBytesTaken = HeaderNameAndTrailingWSLength + BytesTaken;
  701. goto end;
  702. }
  703. // Otherwise he didn't take anything, so return 0.
  704. // we need more buffer
  705. //
  706. *pBytesTaken = 0;
  707. goto end;
  708. }
  709. pHeaderMap++;
  710. }
  711. }
  712. // The value's length must fit in a USHORT
  713. if (HttpRequestLength - HeaderNameLength > ANSI_STRING_MAX_CHAR_LEN)
  714. {
  715. UlTraceError(PARSER, (
  716. "UlLookupHeader(pRequest = %p) "
  717. "Header too long: %lu\n",
  718. pRequest,
  719. HttpRequestLength - HeaderNameLength
  720. ));
  721. UlSetErrorCode(pRequest, UlErrorHeader, NULL);
  722. Status = STATUS_INVALID_DEVICE_REQUEST;
  723. goto end;
  724. }
  725. // OK, at this point either we had no header map array or none of them
  726. // matched. We have an unknown header. Just make sure this header is
  727. // terminated and save a pointer to it.
  728. //
  729. // Find the end of the header value
  730. //
  731. Status = FindRequestHeaderEnd(
  732. pRequest,
  733. pHttpRequest + HeaderNameAndTrailingWSLength,
  734. (USHORT) (HttpRequestLength - HeaderNameAndTrailingWSLength),
  735. &BytesTaken
  736. );
  737. if (!NT_SUCCESS(Status))
  738. goto end;
  739. if (BytesTaken == 0)
  740. {
  741. *pBytesTaken = 0;
  742. goto end;
  743. }
  744. ASSERT(BytesTaken - CRLF_SIZE <= ANSI_STRING_MAX_CHAR_LEN);
  745. //
  746. // Strip off the trailing CRLF from the header value length
  747. //
  748. HeaderValueLength = (USHORT) (BytesTaken - CRLF_SIZE);
  749. pHeaderValue = pHttpRequest + HeaderNameAndTrailingWSLength;
  750. //
  751. // skip any preceding LWS.
  752. //
  753. while ( HeaderValueLength > 0 && IS_HTTP_LWS(*pHeaderValue) )
  754. {
  755. pHeaderValue++;
  756. HeaderValueLength--;
  757. }
  758. if(!bIgnore)
  759. {
  760. //
  761. // Have an unknown header. Search our list of unknown headers,
  762. // and if we've already seen one instance of this header add this
  763. // on. Otherwise allocate an unknown header structure and set it
  764. // to point at this header.
  765. //
  766. // RFC 2616, Section 4.2 "Message Headers" says:
  767. // "Multiple message-header fields with the same field-name MAY be
  768. // present in a message if and only if the entire field-value for
  769. // that header field is defined as a comma-separated list
  770. // [i.e., #(values)]. It MUST be possible to combine the multiple
  771. // header fields into one "field-name: field-value" pair, without
  772. // changing the semantics of the message, by appending each subsequent
  773. // field-value to the first, each separated by a comma."
  774. //
  775. // Therefore we search the list of unknown headers and add the new
  776. // field-value to the end of the existing comma delimited list of
  777. // field-values
  778. //
  779. pListStart = &pRequest->UnknownHeaderList;
  780. for (pCurrentListEntry = pRequest->UnknownHeaderList.Flink;
  781. pCurrentListEntry != pListStart;
  782. pCurrentListEntry = pCurrentListEntry->Flink
  783. )
  784. {
  785. pUnknownHeader = CONTAINING_RECORD(
  786. pCurrentListEntry,
  787. UL_HTTP_UNKNOWN_HEADER,
  788. List
  789. );
  790. //
  791. // somehow HeaderNameLength includes the ':' character,
  792. // which is not the case of pUnknownHeader->HeaderNameLength.
  793. //
  794. // so we need to adjust for this here
  795. //
  796. if ((HeaderNameLength-1) == pUnknownHeader->HeaderNameLength &&
  797. _strnicmp(
  798. (const char *)(pHttpRequest),
  799. (const char *)(pUnknownHeader->pHeaderName),
  800. (HeaderNameLength-1)
  801. ) == 0)
  802. {
  803. // This header matches.
  804. OldHeaderLength = pUnknownHeader->HeaderValue.HeaderLength;
  805. Status = UlAppendHeaderValue(
  806. pRequest,
  807. &pUnknownHeader->HeaderValue,
  808. pHeaderValue,
  809. HeaderValueLength
  810. );
  811. if (NT_SUCCESS(Status) == FALSE)
  812. goto end;
  813. //
  814. // Successfully appended it. Update the total request
  815. // length for the length added. no need to add 1 for
  816. // the terminator, just add our new char count.
  817. //
  818. pRequest->TotalRequestSize +=
  819. (pUnknownHeader->HeaderValue.HeaderLength
  820. - OldHeaderLength) * sizeof(CHAR);
  821. //
  822. // don't subtract for the ':' character, as that character
  823. // was "taken"
  824. //
  825. *pBytesTaken = HeaderNameAndTrailingWSLength + BytesTaken;
  826. goto end;
  827. } // if (headermatch)
  828. } // for (walk list)
  829. //
  830. // Didn't find a match. Allocate a new unknown header structure, set
  831. // it up and add it to the list.
  832. //
  833. if (pRequest->NextUnknownHeaderIndex < DEFAULT_MAX_UNKNOWN_HEADERS)
  834. {
  835. ExternalAllocated = FALSE;
  836. pUnknownHeader = &pRequest->UnknownHeaders[pRequest->NextUnknownHeaderIndex];
  837. pRequest->NextUnknownHeaderIndex++;
  838. }
  839. else
  840. {
  841. ExternalAllocated = TRUE;
  842. pUnknownHeader = UL_ALLOCATE_STRUCT(
  843. NonPagedPool,
  844. UL_HTTP_UNKNOWN_HEADER,
  845. UL_HTTP_UNKNOWN_HEADER_POOL_TAG
  846. );
  847. //
  848. // Assume the memory allocation will succeed so just blindly set the
  849. // flag below to TRUE which forces us to take a slow path in
  850. // UlpFreeHttpRequest.
  851. //
  852. pRequest->HeadersAppended = TRUE;
  853. }
  854. if (pUnknownHeader == NULL)
  855. {
  856. Status = STATUS_NO_MEMORY;
  857. goto end;
  858. }
  859. //
  860. // subtract the : from the header name length
  861. //
  862. pUnknownHeader->HeaderNameLength = HeaderNameLength - 1;
  863. pUnknownHeader->pHeaderName = pHttpRequest;
  864. //
  865. // header value
  866. //
  867. pUnknownHeader->HeaderValue.HeaderLength = HeaderValueLength;
  868. pUnknownHeader->HeaderValue.pHeader = pHeaderValue;
  869. //
  870. // null terminate our copy, the terminating CRLF gives
  871. // us space for this
  872. //
  873. pHeaderValue[HeaderValueLength] = ANSI_NULL;
  874. //
  875. // flags
  876. //
  877. pUnknownHeader->HeaderValue.OurBuffer = 0;
  878. pUnknownHeader->HeaderValue.ExternalAllocated = ExternalAllocated;
  879. InsertTailList(&pRequest->UnknownHeaderList, &pUnknownHeader->List);
  880. pRequest->UnknownHeaderCount++;
  881. if(pRequest->UnknownHeaderCount == 0)
  882. {
  883. // Overflow!
  884. Status = STATUS_INVALID_DEVICE_REQUEST;
  885. goto end;
  886. }
  887. //
  888. // subtract 1 for the ':' and add space for the 2 terminators
  889. //
  890. pRequest->TotalRequestSize +=
  891. ((HeaderNameLength - 1 + 1) + HeaderValueLength + 1) * sizeof(CHAR);
  892. }
  893. *pBytesTaken = HeaderNameAndTrailingWSLength + BytesTaken;
  894. end:
  895. return Status;
  896. } // UlLookupHeader
  897. /*++
  898. Routine Description:
  899. The routine to parse an individual header based on a hint. We take in
  900. a pointer to the header and the bytes remaining in the request,
  901. and try to find the header based on the hint passed.
  902. On input, HttpRequestLength is at least CRLF_SIZE.
  903. Arguments:
  904. pRequest - Pointer to the current connection on which the
  905. request arrived.
  906. pHttpRequest - Pointer to the current request.
  907. HttpRequestLength - Bytes left in the request.
  908. pHeaderHintMap - Hint to the Map that may match the current request
  909. pBytesTaken - Bytes consumed by this routine from pHttpRequest,
  910. including CRLF.
  911. Return Value:
  912. STATUS_SUCCESS or an error.
  913. --*/
  914. __inline
  915. NTSTATUS
  916. UlParseHeaderWithHint(
  917. IN PUL_INTERNAL_REQUEST pRequest,
  918. IN PUCHAR pHttpRequest,
  919. IN ULONG HttpRequestLength,
  920. IN PHEADER_MAP_ENTRY pHeaderHintMap,
  921. OUT ULONG * pBytesTaken
  922. )
  923. {
  924. NTSTATUS Status = STATUS_SUCCESS;
  925. ULONG BytesTaken;
  926. ULONGLONG Temp;
  927. ULONG j;
  928. PAGED_CODE();
  929. ASSERT(pHeaderHintMap != NULL);
  930. if (HttpRequestLength >= pHeaderHintMap->MinBytesNeeded)
  931. {
  932. for (j = 0; j < pHeaderHintMap->ArrayCount; j++)
  933. {
  934. Temp = *(UNALIGNED64 ULONGLONG *)(pHttpRequest +
  935. (j * sizeof(ULONGLONG)));
  936. if ((Temp & pHeaderHintMap->HeaderMask[j]) !=
  937. pHeaderHintMap->Header.HeaderLong[j] )
  938. {
  939. break;
  940. }
  941. }
  942. // See why we exited out.
  943. if (j == pHeaderHintMap->ArrayCount &&
  944. pHeaderHintMap->pServerHandler != NULL)
  945. {
  946. if (HttpRequestLength - pHeaderHintMap->HeaderLength
  947. > ANSI_STRING_MAX_CHAR_LEN)
  948. {
  949. UlTraceError(PARSER, (
  950. "UlParseHeaderWithHint(pRequest = %p) "
  951. "Header too long: %lu\n",
  952. pRequest,
  953. HttpRequestLength - pHeaderHintMap->HeaderLength
  954. ));
  955. UlSetErrorCode(pRequest, UlErrorHeader, NULL);
  956. Status = STATUS_INVALID_DEVICE_REQUEST;
  957. goto end;
  958. }
  959. // Exited because we found a match. Call the
  960. // handler for this header to take cake of this.
  961. Status = (*(pHeaderHintMap->pServerHandler))(
  962. pRequest,
  963. pHttpRequest + pHeaderHintMap->HeaderLength,
  964. (USHORT) (HttpRequestLength - pHeaderHintMap->HeaderLength),
  965. pHeaderHintMap->HeaderID,
  966. &BytesTaken
  967. );
  968. if (NT_SUCCESS(Status) == FALSE)
  969. goto end;
  970. // If the handler consumed a non-zero number of
  971. // bytes, it worked, so return that number plus
  972. // the header length.
  973. if (BytesTaken != 0)
  974. {
  975. *pBytesTaken = pHeaderHintMap->HeaderLength + BytesTaken;
  976. goto end;
  977. }
  978. // Otherwise need more buffer
  979. *pBytesTaken = 0;
  980. }
  981. else
  982. {
  983. // No match
  984. *pBytesTaken = (ULONG) -1;
  985. }
  986. }
  987. else
  988. {
  989. // No match
  990. *pBytesTaken = (ULONG) -1;
  991. }
  992. end:
  993. return Status;
  994. } // UlParseHeaderWithHint
  995. /*++
  996. Routine Description:
  997. The routine to parse an individual header. We take in a pointer to the
  998. header and the bytes remaining in the request, and try to find
  999. the header in our lookup table. We try first the fast way, and then
  1000. try again the slow way in case there wasn't quite enough data the first
  1001. time.
  1002. On input, HttpRequestLength is at least CRLF_SIZE.
  1003. Arguments:
  1004. pRequest - Pointer to the current connection on which the
  1005. request arrived.
  1006. pHttpRequest - Pointer to the current request.
  1007. HttpRequestLength - Bytes left in the request.
  1008. pBytesTaken - Bytes consumed by this routine from pHttpRequest,
  1009. including CRLF.
  1010. Return Value:
  1011. STATUS_SUCCESS or an error.
  1012. --*/
  1013. NTSTATUS
  1014. UlParseHeader(
  1015. IN PUL_INTERNAL_REQUEST pRequest,
  1016. IN PUCHAR pHttpRequest,
  1017. IN ULONG HttpRequestLength,
  1018. OUT ULONG * pBytesTaken
  1019. )
  1020. {
  1021. NTSTATUS Status = STATUS_SUCCESS;
  1022. ULONG i;
  1023. ULONG j;
  1024. ULONG BytesTaken;
  1025. ULONGLONG Temp;
  1026. UCHAR c;
  1027. PHEADER_MAP_ENTRY pCurrentHeaderMap;
  1028. ULONG HeaderMapCount;
  1029. //
  1030. // Sanity check.
  1031. //
  1032. PAGED_CODE();
  1033. ASSERT(HttpRequestLength >= CRLF_SIZE);
  1034. c = *pHttpRequest;
  1035. // message-headers start with field-name [= token]
  1036. //
  1037. if (IS_HTTP_TOKEN(c) == FALSE)
  1038. {
  1039. UlTraceError(PARSER, (
  1040. "UlParseHeader (pRequest = %p) c = 0x%02x '%c'"
  1041. "ERROR: invalid header char \n",
  1042. pRequest,
  1043. c, isprint(c) ? c : '.'
  1044. ));
  1045. UlSetErrorCode(pRequest, UlErrorHeader, NULL);
  1046. return STATUS_INVALID_DEVICE_REQUEST;
  1047. }
  1048. // Does the header start with an alpha?
  1049. //
  1050. if (!IS_HTTP_ALPHA(c))
  1051. {
  1052. pCurrentHeaderMap = NULL;
  1053. HeaderMapCount = 0;
  1054. }
  1055. else
  1056. {
  1057. // Uppercase the character, and find the appropriate set of header map
  1058. // entries.
  1059. //
  1060. c = UPCASE_CHAR(c);
  1061. ASSERT('A' <= c && c <= 'Z');
  1062. c -= 'A';
  1063. pCurrentHeaderMap = g_RequestHeaderIndexTable[c].pHeaderMap;
  1064. HeaderMapCount = g_RequestHeaderIndexTable[c].Count;
  1065. // Loop through all the header map entries that might match
  1066. // this header, and check them. The count will be 0 if there
  1067. // are no entries that might match and we'll skip the loop.
  1068. for (i = 0; i < HeaderMapCount; i++)
  1069. {
  1070. ASSERT(pCurrentHeaderMap->pServerHandler != NULL);
  1071. // If we have enough bytes to do the fast check, do it.
  1072. // Otherwise skip this. We may skip a valid match, but if
  1073. // so we'll catch it later.
  1074. if (HttpRequestLength >= pCurrentHeaderMap->MinBytesNeeded)
  1075. {
  1076. for (j = 0; j < pCurrentHeaderMap->ArrayCount; j++)
  1077. {
  1078. Temp = *(UNALIGNED64 ULONGLONG *)(pHttpRequest +
  1079. (j * sizeof(ULONGLONG)));
  1080. if ((Temp & pCurrentHeaderMap->HeaderMask[j]) !=
  1081. pCurrentHeaderMap->Header.HeaderLong[j] )
  1082. {
  1083. break;
  1084. }
  1085. }
  1086. // See why we exited out.
  1087. if (j == pCurrentHeaderMap->ArrayCount &&
  1088. pCurrentHeaderMap->pServerHandler != NULL)
  1089. {
  1090. if (HttpRequestLength - pCurrentHeaderMap->HeaderLength
  1091. > ANSI_STRING_MAX_CHAR_LEN)
  1092. {
  1093. UlTraceError(PARSER, (
  1094. "UlParseHeader(pRequest = %p) "
  1095. "Header too long: %lu\n",
  1096. pRequest,
  1097. HttpRequestLength
  1098. - pCurrentHeaderMap->HeaderLength
  1099. ));
  1100. UlSetErrorCode(pRequest, UlErrorHeader, NULL);
  1101. Status = STATUS_INVALID_DEVICE_REQUEST;
  1102. goto end;
  1103. }
  1104. // Exited because we found a match. Call the
  1105. // handler for this header to take cake of this.
  1106. Status = (*(pCurrentHeaderMap->pServerHandler))(
  1107. pRequest,
  1108. pHttpRequest + pCurrentHeaderMap->HeaderLength,
  1109. (USHORT) (HttpRequestLength
  1110. - pCurrentHeaderMap->HeaderLength),
  1111. pCurrentHeaderMap->HeaderID,
  1112. &BytesTaken
  1113. );
  1114. if (NT_SUCCESS(Status) == FALSE)
  1115. goto end;
  1116. // If the handler consumed a non-zero number of
  1117. // bytes, it worked, so return that number plus
  1118. // the header length.
  1119. if (BytesTaken != 0)
  1120. {
  1121. *pBytesTaken = pCurrentHeaderMap->HeaderLength +
  1122. BytesTaken;
  1123. goto end;
  1124. }
  1125. // Otherwise need more buffer
  1126. //
  1127. *pBytesTaken = 0;
  1128. goto end;
  1129. }
  1130. // If we get here, we exited out early because a match
  1131. // failed, so keep going.
  1132. }
  1133. // Either didn't match or didn't have enough bytes for the
  1134. // check. In either case, check the next header map entry.
  1135. pCurrentHeaderMap++;
  1136. }
  1137. // Got all the way through the appropriate header map entries
  1138. // without a match. This could be because we're dealing with a
  1139. // header we don't know about or because it's a header we
  1140. // care about that was too small to do the fast check. The
  1141. // latter case should be very rare, but we still need to
  1142. // handle it.
  1143. // Update the current header map pointer to point back to the
  1144. // first of the possibles.
  1145. pCurrentHeaderMap = g_RequestHeaderIndexTable[c].pHeaderMap;
  1146. }
  1147. // At this point either the header starts with a non-alphabetic
  1148. // character or we don't have a set of header map entries for it.
  1149. Status = UlLookupHeader(
  1150. pRequest,
  1151. pHttpRequest,
  1152. HttpRequestLength,
  1153. pCurrentHeaderMap,
  1154. HeaderMapCount,
  1155. FALSE,
  1156. &BytesTaken
  1157. );
  1158. if (NT_SUCCESS(Status) == FALSE)
  1159. goto end;
  1160. // Lookup header returns the total bytes taken, including the header name
  1161. //
  1162. *pBytesTaken = BytesTaken;
  1163. end:
  1164. return Status;
  1165. } // UlParseHeader
  1166. /*++
  1167. Routine Description:
  1168. Parse all the remaining header data, breaking it into one or more
  1169. headers. Terminates when an empty line is seen (end of headers),
  1170. when buffer is exhausted (=> more data needed and UlParseHeaders()
  1171. will be called again), or upon error.
  1172. Arguments:
  1173. pRequest - Pointer to the current request.
  1174. pBuffer - where to start parsing
  1175. BufferLength - Length of pBuffer, in bytes
  1176. pBytesTaken - how much of pBuffer was consumed
  1177. Return Value:
  1178. STATUS_SUCCESS - Found the terminating CRLF that ends the headers
  1179. STATUS_MORE_PROCESSING_REQUIRED - haven't found the terminating CRLF,
  1180. so need more header data
  1181. various errors
  1182. --*/
  1183. NTSTATUS
  1184. UlParseHeaders(
  1185. IN OUT PUL_INTERNAL_REQUEST pRequest,
  1186. IN PUCHAR pBuffer,
  1187. IN ULONG BufferLength,
  1188. OUT PULONG pBytesTaken
  1189. )
  1190. {
  1191. NTSTATUS Status;
  1192. ULONG BytesTaken = 0;
  1193. LONG NextHintIndex = 0;
  1194. LONG HintIndex = 0;
  1195. PHEADER_MAP_ENTRY pHeaderHintMap;
  1196. UCHAR UpcaseChar;
  1197. PAGED_CODE();
  1198. *pBytesTaken = 0;
  1199. //
  1200. // loop over all headers
  1201. //
  1202. while (BufferLength >= CRLF_SIZE)
  1203. {
  1204. //
  1205. // If this is an empty header, we're done with this stage
  1206. //
  1207. if (*(UNALIGNED64 USHORT *)pBuffer == CRLF ||
  1208. *(UNALIGNED64 USHORT *)pBuffer == LFLF)
  1209. {
  1210. //
  1211. // consume it
  1212. //
  1213. pBuffer += CRLF_SIZE;
  1214. *pBytesTaken += CRLF_SIZE;
  1215. BufferLength -= CRLF_SIZE;
  1216. Status = STATUS_SUCCESS;
  1217. goto end;
  1218. }
  1219. // Otherwise call our header parse routine to deal with this.
  1220. //
  1221. // Try to find a header hint based on the first char and certain order
  1222. //
  1223. // We start from where we find a succeful hint + 1
  1224. //
  1225. pHeaderHintMap = NULL;
  1226. if (IS_HTTP_ALPHA(*pBuffer))
  1227. {
  1228. UpcaseChar = UPCASE_CHAR(*pBuffer);
  1229. for (
  1230. HintIndex = NextHintIndex;
  1231. HintIndex < NUMBER_HEADER_HINT_INDICES;
  1232. ++HintIndex
  1233. )
  1234. {
  1235. if (g_RequestHeaderHintIndexTable[HintIndex].c == UpcaseChar)
  1236. {
  1237. pHeaderHintMap
  1238. = g_RequestHeaderHintIndexTable[HintIndex].pHeaderMap;
  1239. break;
  1240. }
  1241. }
  1242. }
  1243. if (NULL != pHeaderHintMap)
  1244. {
  1245. Status = UlParseHeaderWithHint(
  1246. pRequest,
  1247. pBuffer,
  1248. BufferLength,
  1249. pHeaderHintMap,
  1250. &BytesTaken
  1251. );
  1252. if (-1 == BytesTaken)
  1253. {
  1254. // hint failed
  1255. Status = UlParseHeader(
  1256. pRequest,
  1257. pBuffer,
  1258. BufferLength,
  1259. &BytesTaken
  1260. );
  1261. } else
  1262. {
  1263. NextHintIndex = HintIndex + 1;
  1264. }
  1265. }
  1266. else
  1267. {
  1268. Status = UlParseHeader(
  1269. pRequest,
  1270. pBuffer,
  1271. BufferLength,
  1272. &BytesTaken
  1273. );
  1274. }
  1275. if (NT_SUCCESS(Status) == FALSE)
  1276. goto end;
  1277. //
  1278. // Check if the parsed headers are longer than maximum allowed.
  1279. //
  1280. if ( (*pBytesTaken+BytesTaken) > g_UlMaxFieldLength )
  1281. {
  1282. UlTraceError(PARSER, (
  1283. "UlParseHeaders(pRequest = %p) "
  1284. "ERROR: Header field is too big\n",
  1285. pRequest
  1286. ));
  1287. UlSetErrorCode(pRequest, UlErrorFieldLength, NULL);
  1288. Status = STATUS_SECTION_TOO_BIG;
  1289. goto end;
  1290. }
  1291. //
  1292. // If no bytes were consumed, the header must be incomplete, so
  1293. // bail out until we get more data on this connection.
  1294. //
  1295. if (BytesTaken == 0)
  1296. {
  1297. Status = STATUS_MORE_PROCESSING_REQUIRED;
  1298. goto end;
  1299. }
  1300. //
  1301. // Otherwise we parsed a header, so update and continue.
  1302. //
  1303. pBuffer += BytesTaken;
  1304. *pBytesTaken += BytesTaken;
  1305. BufferLength -= BytesTaken;
  1306. }
  1307. //
  1308. // we only get here if we didn't see the CRLF headers terminator
  1309. //
  1310. // we need more data
  1311. //
  1312. Status = STATUS_MORE_PROCESSING_REQUIRED;
  1313. end:
  1314. //
  1315. // Terminate the header index table when we are done with parsing headers.
  1316. //
  1317. pRequest->HeaderIndex[pRequest->HeaderCount] = HttpHeaderRequestMaximum;
  1318. return Status;
  1319. } // UlParseHeaders
  1320. /*++
  1321. Routine Description:
  1322. This is the core HTTP protocol request engine. It takes a stream of bytes
  1323. and parses them as an HTTP request.
  1324. Arguments:
  1325. pRequest - Pointer to the current request.
  1326. pHttpRequest - Pointer to the incoming raw HTTP request data.
  1327. HttpRequestLength - Length of data pointed to by pHttpRequest.
  1328. pBytesTaken - How much of pHttpRequest was consumed
  1329. Return Value:
  1330. Status of parse attempt.
  1331. --*/
  1332. NTSTATUS
  1333. UlParseHttp(
  1334. IN PUL_INTERNAL_REQUEST pRequest,
  1335. IN PUCHAR pHttpRequest,
  1336. IN ULONG HttpRequestLength,
  1337. OUT ULONG * pBytesTaken
  1338. )
  1339. {
  1340. ULONG OriginalBufferLength;
  1341. ULONG TokenLength;
  1342. ULONG CurrentBytesTaken = 0;
  1343. ULONG TotalBytesTaken;
  1344. ULONG i;
  1345. NTSTATUS ReturnStatus;
  1346. PUCHAR pStart;
  1347. USHORT Eol;
  1348. //
  1349. // Sanity check.
  1350. //
  1351. PAGED_CODE();
  1352. ASSERT( UL_IS_VALID_INTERNAL_REQUEST( pRequest ) );
  1353. ReturnStatus = STATUS_SUCCESS;
  1354. TotalBytesTaken = 0;
  1355. //
  1356. // remember the original buffer length
  1357. //
  1358. OriginalBufferLength = HttpRequestLength;
  1359. //
  1360. // Put this label here to allow for a manual re-pump of the
  1361. // parser. This is currently used for HTTP/0.9 requests.
  1362. //
  1363. parse_it:
  1364. //
  1365. // what state are we in ?
  1366. //
  1367. switch (pRequest->ParseState)
  1368. {
  1369. case ParseVerbState:
  1370. UlTraceVerbose(PARSER, (
  1371. "UlParseHttp(pRequest = %p): Entering ParseVerbState\n",
  1372. pRequest
  1373. ));
  1374. // Look through the fast verb table for the verb. We can only do
  1375. // this if the input data is big enough.
  1376. if (HttpRequestLength >= sizeof(ULONGLONG))
  1377. {
  1378. ULONGLONG RawInputVerb;
  1379. RawInputVerb = *(UNALIGNED64 ULONGLONG *) pHttpRequest;
  1380. // Loop through the fast verb table, looking for the verb.
  1381. for (i = 0; i < NUMBER_FAST_VERB_ENTRIES; i++)
  1382. {
  1383. // Mask out the raw input verb and compare against this
  1384. // entry. Note: verbs are case-sensitive.
  1385. ASSERT(FastVerbTable[i].RawVerbLength - STRLEN_LIT(" ")
  1386. < sizeof(ULONGLONG));
  1387. if ((RawInputVerb & FastVerbTable[i].RawVerbMask) ==
  1388. FastVerbTable[i].RawVerb.LongLong)
  1389. {
  1390. // It matched. Save the translated verb from the
  1391. // table, update the request pointer and length,
  1392. // switch states, and get out.
  1393. pRequest->Verb = FastVerbTable[i].TranslatedVerb;
  1394. CurrentBytesTaken = FastVerbTable[i].RawVerbLength;
  1395. pRequest->ParseState = ParseUrlState;
  1396. break;
  1397. }
  1398. }
  1399. }
  1400. if (pRequest->ParseState != ParseUrlState)
  1401. {
  1402. // Didn't switch states yet, because we haven't found the
  1403. // verb yet. This could be because a) the incoming request
  1404. // was too small to allow us to use our fast lookup (which
  1405. // might be OK in an HTTP/0.9 request), or b) the incoming
  1406. // verb is a PROPFIND or such that is too big to fit into
  1407. // our fast find table, or c) this is an unknown verb, or
  1408. // d) there was leading white-space before the verb.
  1409. // In any of these cases call our slower verb parser to try
  1410. // again.
  1411. ReturnStatus = UlpLookupVerb(
  1412. pRequest,
  1413. pHttpRequest,
  1414. HttpRequestLength,
  1415. &CurrentBytesTaken
  1416. );
  1417. if (NT_SUCCESS(ReturnStatus) == FALSE)
  1418. goto end;
  1419. if (CurrentBytesTaken == 0)
  1420. {
  1421. ReturnStatus = STATUS_MORE_PROCESSING_REQUIRED;
  1422. goto end;
  1423. }
  1424. //
  1425. // we finished parsing the custom verb
  1426. //
  1427. pRequest->ParseState = ParseUrlState;
  1428. }
  1429. //
  1430. // now fall through to ParseUrlState
  1431. //
  1432. ASSERT(CurrentBytesTaken <= HttpRequestLength);
  1433. pHttpRequest += CurrentBytesTaken;
  1434. HttpRequestLength -= CurrentBytesTaken;
  1435. TotalBytesTaken += CurrentBytesTaken;
  1436. case ParseUrlState:
  1437. ASSERT(ParseUrlState == pRequest->ParseState);
  1438. UlTraceVerbose(PARSER, (
  1439. "UlParseHttp(pRequest = %p): Verb='%s', "
  1440. "Entering ParseUrlState\n",
  1441. pRequest,
  1442. UlVerbToString(pRequest->Verb)
  1443. ));
  1444. //
  1445. // We're parsing the URL. pHttpRequest points to the incoming URL,
  1446. // HttpRequestLength is the length of this request that is left.
  1447. //
  1448. //
  1449. // Find the WS terminating the URL.
  1450. //
  1451. ReturnStatus = HttpFindUrlToken(
  1452. &g_UrlC14nConfig,
  1453. pHttpRequest,
  1454. HttpRequestLength,
  1455. &pRequest->RawUrl.pUrl,
  1456. &TokenLength,
  1457. &pRequest->RawUrlClean
  1458. );
  1459. if (NT_SUCCESS(ReturnStatus) == FALSE)
  1460. {
  1461. UlTraceError(PARSER, (
  1462. "UlParseHttp(pRequest = %p) ERROR %s: "
  1463. "could not parse URL\n",
  1464. pRequest,
  1465. HttpStatusToString(ReturnStatus)
  1466. ));
  1467. UlSetErrorCode(pRequest, UlErrorUrl, NULL);
  1468. goto end;
  1469. }
  1470. if (pRequest->RawUrl.pUrl == NULL)
  1471. {
  1472. ReturnStatus = STATUS_MORE_PROCESSING_REQUIRED;
  1473. goto end;
  1474. }
  1475. ASSERT(TokenLength > 0);
  1476. ASSERT(pHttpRequest <= pRequest->RawUrl.pUrl
  1477. && pRequest->RawUrl.pUrl < pHttpRequest + HttpRequestLength);
  1478. if (TokenLength > g_UlMaxFieldLength)
  1479. {
  1480. //
  1481. // The URL is longer than maximum allowed size
  1482. //
  1483. UlTraceError(PARSER, (
  1484. "UlParseHttp(pRequest = %p) ERROR: URL is too big\n",
  1485. pRequest
  1486. ));
  1487. UlSetErrorCode(pRequest, UlErrorUrlLength, NULL);
  1488. ReturnStatus = STATUS_SECTION_TOO_BIG;
  1489. goto end;
  1490. }
  1491. //
  1492. // Bytes taken includes leading WS in front of URL
  1493. //
  1494. CurrentBytesTaken = DIFF(pRequest->RawUrl.pUrl - pHttpRequest)
  1495. + TokenLength;
  1496. ASSERT(CurrentBytesTaken <= HttpRequestLength);
  1497. //
  1498. // set url length
  1499. //
  1500. pRequest->RawUrl.Length = TokenLength;
  1501. //
  1502. // Now, let's see if this is an absolute URL. Check to see
  1503. // if the first four characters are "http" (case-insensitive).
  1504. //
  1505. if (pRequest->RawUrl.Length >= HTTP_PREFIX_SIZE &&
  1506. (*(UNALIGNED64 ULONG *)pRequest->RawUrl.pUrl & HTTP_PREFIX_MASK)
  1507. == HTTP_PREFIX)
  1508. {
  1509. //
  1510. // It is. let's parse it and find the host.
  1511. //
  1512. ReturnStatus = UlpParseFullUrl(pRequest);
  1513. if (NT_SUCCESS(ReturnStatus) == FALSE)
  1514. goto end;
  1515. }
  1516. else
  1517. {
  1518. pRequest->RawUrl.pHost = NULL;
  1519. pRequest->RawUrl.pAbsPath = pRequest->RawUrl.pUrl;
  1520. }
  1521. //
  1522. // count the space it needs in the user's buffer, including terminator.
  1523. //
  1524. pRequest->TotalRequestSize +=
  1525. (pRequest->RawUrl.Length + 1) * sizeof(CHAR);
  1526. //
  1527. // adjust our book keeping vars
  1528. //
  1529. pHttpRequest += CurrentBytesTaken;
  1530. HttpRequestLength -= CurrentBytesTaken;
  1531. TotalBytesTaken += CurrentBytesTaken;
  1532. //
  1533. // fall through to parsing the version.
  1534. //
  1535. pRequest->ParseState = ParseVersionState;
  1536. case ParseVersionState:
  1537. ASSERT(ParseVersionState == pRequest->ParseState);
  1538. UlTraceVerbose(PARSER, (
  1539. "UlParseHttp(pRequest = %p): Entering ParseVersionState\n",
  1540. pRequest
  1541. ));
  1542. //
  1543. // skip lws
  1544. //
  1545. pStart = pHttpRequest;
  1546. while (HttpRequestLength > 0 && IS_HTTP_LWS(*pHttpRequest))
  1547. {
  1548. pHttpRequest++;
  1549. HttpRequestLength--;
  1550. }
  1551. //
  1552. // is this an HTTP/0.9 request (no version) ?
  1553. //
  1554. if (HttpRequestLength >= CRLF_SIZE)
  1555. {
  1556. Eol = *(UNALIGNED64 USHORT *)(pHttpRequest);
  1557. if (Eol == CRLF || Eol == LFLF)
  1558. {
  1559. // This IS a 0.9 request. No need to go any further,
  1560. // since by definition there are no more headers.
  1561. // Just update things and get out.
  1562. TotalBytesTaken += DIFF(pHttpRequest - pStart) + CRLF_SIZE;
  1563. HTTP_SET_VERSION(pRequest->Version, 0, 9);
  1564. UlTraceVerbose(PARSER, (
  1565. "UlParseHttp(pRequest = %p): HTTP/0.9 request\n",
  1566. pRequest
  1567. ));
  1568. //
  1569. // set the state to CookState so that we parse the url
  1570. //
  1571. pRequest->ParseState = ParseCookState;
  1572. //
  1573. // manually restart the parse switch, we changed the
  1574. // parse state
  1575. //
  1576. goto parse_it;
  1577. }
  1578. }
  1579. //
  1580. // do we have enough buffer to strcmp the version?
  1581. //
  1582. if (HttpRequestLength < (MIN_VERSION_SIZE + CRLF_SIZE))
  1583. {
  1584. ReturnStatus = STATUS_MORE_PROCESSING_REQUIRED;
  1585. goto end;
  1586. }
  1587. Eol = *(UNALIGNED64 USHORT *)(pHttpRequest + MIN_VERSION_SIZE);
  1588. //
  1589. // let's compare it
  1590. //
  1591. if ((*(UNALIGNED64 ULONGLONG *)pHttpRequest == HTTP_11_VERSION) &&
  1592. (Eol == CRLF || Eol == LFLF))
  1593. {
  1594. HTTP_SET_VERSION(pRequest->Version, 1, 1);
  1595. HttpRequestLength -= MIN_VERSION_SIZE;
  1596. pHttpRequest += MIN_VERSION_SIZE;
  1597. }
  1598. else if ((*(UNALIGNED64 ULONGLONG *)pHttpRequest == HTTP_10_VERSION) &&
  1599. (Eol == CRLF || Eol == LFLF))
  1600. {
  1601. HTTP_SET_VERSION(pRequest->Version, 1, 0);
  1602. HttpRequestLength -= MIN_VERSION_SIZE;
  1603. pHttpRequest += MIN_VERSION_SIZE;
  1604. }
  1605. else
  1606. {
  1607. ULONG VersionBytes = UlpParseHttpVersion(
  1608. pHttpRequest,
  1609. HttpRequestLength,
  1610. &pRequest->Version );
  1611. if (0 != VersionBytes)
  1612. {
  1613. pHttpRequest += VersionBytes;
  1614. HttpRequestLength -= VersionBytes;
  1615. }
  1616. else
  1617. {
  1618. // Could not parse version.
  1619. UlTraceError(PARSER, (
  1620. "UlParseHttp(pRequest = %p) "
  1621. "ERROR: could not parse HTTP version\n",
  1622. pRequest
  1623. ));
  1624. UlSetErrorCode(pRequest, UlError, NULL);
  1625. ReturnStatus = STATUS_INVALID_DEVICE_REQUEST;
  1626. goto end;
  1627. }
  1628. }
  1629. //
  1630. // skip trailing lws
  1631. //
  1632. while (HttpRequestLength > 0 && IS_HTTP_LWS(*pHttpRequest))
  1633. {
  1634. pHttpRequest++;
  1635. HttpRequestLength--;
  1636. }
  1637. //
  1638. // Make sure we're terminated on this line.
  1639. //
  1640. if (HttpRequestLength < CRLF_SIZE)
  1641. {
  1642. ReturnStatus = STATUS_MORE_PROCESSING_REQUIRED;
  1643. goto end;
  1644. }
  1645. Eol = *(UNALIGNED64 USHORT *)(pHttpRequest);
  1646. if (Eol != CRLF && Eol != LFLF)
  1647. {
  1648. // Bad line termination after successfully grabbing version.
  1649. UlTraceError(PARSER, (
  1650. "UlParseHttp(pRequest = %p) "
  1651. "ERROR: HTTP version not terminated correctly\n",
  1652. pRequest
  1653. ));
  1654. UlSetErrorCode(pRequest, UlError, NULL);
  1655. ReturnStatus = STATUS_INVALID_DEVICE_REQUEST;
  1656. goto end;
  1657. }
  1658. pHttpRequest += CRLF_SIZE;
  1659. HttpRequestLength -= CRLF_SIZE;
  1660. TotalBytesTaken += DIFF(pHttpRequest - pStart);
  1661. UlTraceVerbose(PARSER, (
  1662. "UlParseHttp(pRequest = %p): HTTP/%hu.%hu request\n",
  1663. pRequest,
  1664. pRequest->Version.MajorVersion,
  1665. pRequest->Version.MinorVersion
  1666. ));
  1667. //
  1668. // Fall through to parsing the headers
  1669. //
  1670. pRequest->ParseState = ParseHeadersState;
  1671. case ParseHeadersState:
  1672. ASSERT(ParseHeadersState == pRequest->ParseState);
  1673. UlTraceVerbose(PARSER, (
  1674. "UlParseHttp(pRequest = %p): Entering ParseHeadersState\n",
  1675. pRequest
  1676. ));
  1677. ReturnStatus = UlParseHeaders(
  1678. pRequest,
  1679. pHttpRequest,
  1680. HttpRequestLength,
  1681. &CurrentBytesTaken
  1682. );
  1683. pHttpRequest += CurrentBytesTaken;
  1684. HttpRequestLength -= CurrentBytesTaken;
  1685. TotalBytesTaken += CurrentBytesTaken;
  1686. if (NT_SUCCESS(ReturnStatus) == FALSE)
  1687. goto end;
  1688. //
  1689. // fall through, this is the only way to get here, we never return
  1690. // pending in this state
  1691. //
  1692. pRequest->ParseState = ParseCookState;
  1693. case ParseCookState:
  1694. ASSERT(ParseCookState == pRequest->ParseState);
  1695. UlTraceVerbose(PARSER, (
  1696. "UlParseHttp(pRequest = %p): Entering ParseCookState\n",
  1697. pRequest
  1698. ));
  1699. //
  1700. // time for post processing. cook it up!
  1701. //
  1702. {
  1703. //
  1704. // First cook up the url, unicode it + such.
  1705. //
  1706. ReturnStatus = UlpCookUrl(pRequest);
  1707. if (NT_SUCCESS(ReturnStatus) == FALSE)
  1708. goto end;
  1709. //
  1710. // mark if we are chunk encoded (only possible for 1.1)
  1711. //
  1712. if ((HTTP_GREATER_EQUAL_VERSION(pRequest->Version, 1, 1)) &&
  1713. (pRequest->HeaderValid[HttpHeaderTransferEncoding]))
  1714. {
  1715. ASSERT(pRequest->Headers[HttpHeaderTransferEncoding].pHeader != NULL);
  1716. //
  1717. // CODEWORK, there can be more than 1 encoding
  1718. //
  1719. if (_stricmp(
  1720. (const char *)(
  1721. pRequest->Headers[HttpHeaderTransferEncoding].pHeader
  1722. ),
  1723. "chunked"
  1724. ) == 0)
  1725. {
  1726. pRequest->Chunked = 1;
  1727. }
  1728. else
  1729. {
  1730. UlTraceError(PARSER, (
  1731. "UlParseHttp(pRequest = %p) "
  1732. "ERROR: unknown Transfer-Encoding!\n",
  1733. pRequest
  1734. ));
  1735. UlSetErrorCode(pRequest, UlErrorNotImplemented, NULL);
  1736. ReturnStatus = STATUS_INVALID_DEVICE_REQUEST;
  1737. goto end;
  1738. }
  1739. }
  1740. //
  1741. // Now let's decode the content length header
  1742. //
  1743. if (pRequest->HeaderValid[HttpHeaderContentLength])
  1744. {
  1745. ASSERT(pRequest->Headers[HttpHeaderContentLength].pHeader != NULL);
  1746. ReturnStatus =
  1747. UlAnsiToULongLong(
  1748. pRequest->Headers[HttpHeaderContentLength].pHeader,
  1749. pRequest->Headers[HttpHeaderContentLength].HeaderLength,
  1750. 10,
  1751. &pRequest->ContentLength
  1752. );
  1753. if (NT_SUCCESS(ReturnStatus) == FALSE)
  1754. {
  1755. UlTraceError(PARSER, (
  1756. "UlParseHttp(pRequest = %p) "
  1757. "ERROR: couldn't decode Content-Length\n",
  1758. pRequest
  1759. ));
  1760. if (ReturnStatus == STATUS_SECTION_TOO_BIG)
  1761. {
  1762. UlSetErrorCode(pRequest, UlErrorEntityTooLarge, NULL);
  1763. }
  1764. else
  1765. {
  1766. UlSetErrorCode(pRequest, UlErrorNum, NULL);
  1767. }
  1768. goto end;
  1769. }
  1770. if (pRequest->Chunked == 0)
  1771. {
  1772. //
  1773. // prime the first (and only) chunk size
  1774. //
  1775. pRequest->ChunkBytesToParse = pRequest->ContentLength;
  1776. pRequest->ChunkBytesToRead = pRequest->ContentLength;
  1777. }
  1778. }
  1779. }
  1780. pRequest->ParseState = ParseEntityBodyState;
  1781. //
  1782. // fall through
  1783. //
  1784. case ParseEntityBodyState:
  1785. UlTraceVerbose(PARSER, (
  1786. "UlParseHttp(pRequest = %p): Entering ParseEntityBodyState\n",
  1787. pRequest
  1788. ));
  1789. ASSERT(ParseEntityBodyState == pRequest->ParseState);
  1790. //
  1791. // the only parsing we do here is chunk length calculation,
  1792. // and that is not necessary if we have no more bytes to parse
  1793. //
  1794. if (pRequest->ChunkBytesToParse == 0)
  1795. {
  1796. //
  1797. // no more bytes left to parse, let's see if there are any
  1798. // more in the request
  1799. //
  1800. if (pRequest->Chunked == 1)
  1801. {
  1802. //
  1803. // the request is chunk encoded
  1804. //
  1805. //
  1806. // attempt to read the size of the next chunk
  1807. //
  1808. ReturnStatus = ParseChunkLength(
  1809. pRequest->ParsedFirstChunk,
  1810. pHttpRequest,
  1811. HttpRequestLength,
  1812. &CurrentBytesTaken,
  1813. &(pRequest->ChunkBytesToParse)
  1814. );
  1815. UlTraceVerbose(PARSER, (
  1816. "http!UlParseHttp(pRequest = %p): %s. "
  1817. "Chunk length: %lu bytes taken, "
  1818. "0x%I64x (%I64u) bytes to parse.\n",
  1819. pRequest,
  1820. HttpStatusToString(ReturnStatus),
  1821. CurrentBytesTaken,
  1822. pRequest->ChunkBytesToParse,
  1823. pRequest->ChunkBytesToParse
  1824. ));
  1825. if (NT_SUCCESS(ReturnStatus) == FALSE)
  1826. {
  1827. if (ReturnStatus == STATUS_MORE_PROCESSING_REQUIRED)
  1828. {
  1829. goto end;
  1830. }
  1831. UlTraceError(PARSER, (
  1832. "http!UlParseHttp (pRequest = %p) "
  1833. "ERROR: didn't grok chunk length\n",
  1834. pRequest
  1835. ));
  1836. if (ReturnStatus == STATUS_SECTION_TOO_BIG)
  1837. {
  1838. UlSetErrorCode(pRequest, UlErrorEntityTooLarge, NULL);
  1839. }
  1840. else
  1841. {
  1842. UlSetErrorCode(pRequest, UlErrorNum, NULL);
  1843. }
  1844. goto end;
  1845. }
  1846. //
  1847. // Otherwise we parsed it, so update and continue.
  1848. //
  1849. pHttpRequest += CurrentBytesTaken;
  1850. TotalBytesTaken += CurrentBytesTaken;
  1851. HttpRequestLength -= CurrentBytesTaken;
  1852. //
  1853. // was this the first chunk?
  1854. //
  1855. if (pRequest->ParsedFirstChunk == 0)
  1856. {
  1857. //
  1858. // Prime the reader, let it read the first chunk
  1859. // even though we haven't quite parsed it yet....
  1860. //
  1861. UlTraceVerbose(PARSER, (
  1862. "UlParseHttp (pRequest=%p) first-chunk seen\n",
  1863. pRequest
  1864. ));
  1865. pRequest->ChunkBytesToRead = pRequest->ChunkBytesToParse;
  1866. pRequest->ParsedFirstChunk = 1;
  1867. }
  1868. //
  1869. // is this the last chunk (denoted with a 0 byte chunk)?
  1870. //
  1871. if (pRequest->ChunkBytesToParse == 0)
  1872. {
  1873. //
  1874. // time to parse the trailer
  1875. //
  1876. UlTraceVerbose(PARSER, (
  1877. "UlParseHttp (pRequest=%p) last-chunk seen\n",
  1878. pRequest
  1879. ));
  1880. pRequest->ParseState = ParseTrailerState;
  1881. }
  1882. }
  1883. else // if (pRequest->Chunked == 1)
  1884. {
  1885. //
  1886. // not chunk-encoded , all done
  1887. //
  1888. UlTraceVerbose(PARSER, (
  1889. "UlParseHttp (pRequest=%p) State: EntityBody->Done\n",
  1890. pRequest
  1891. ));
  1892. pRequest->ParseState = ParseDoneState;
  1893. }
  1894. } // if (pRequest->ChunkBytesToParse == 0)
  1895. else
  1896. {
  1897. UlTraceVerbose(PARSER, (
  1898. "UlParseHttp (pRequest=%p) State: EntityBody, "
  1899. "ChunkBytesToParse=0x%I64x (%I64u).\n",
  1900. pRequest,
  1901. pRequest->ChunkBytesToParse, pRequest->ChunkBytesToParse
  1902. ));
  1903. }
  1904. //
  1905. // looks all good
  1906. //
  1907. if (pRequest->ParseState != ParseTrailerState)
  1908. {
  1909. break;
  1910. }
  1911. //
  1912. // fall through
  1913. //
  1914. case ParseTrailerState:
  1915. ASSERT(ParseTrailerState == pRequest->ParseState);
  1916. UlTraceVerbose(PARSER, (
  1917. "UlParseHttp(pRequest = %p): Entering ParseTrailerState\n",
  1918. pRequest
  1919. ));
  1920. //
  1921. // parse any existing trailer
  1922. //
  1923. // ParseHeaders will bail immediately if CRLF is
  1924. // next in the buffer (no trailer)
  1925. //
  1926. while(HttpRequestLength >= CRLF_SIZE)
  1927. {
  1928. if (*(UNALIGNED64 USHORT *)pHttpRequest == CRLF ||
  1929. *(UNALIGNED64 USHORT *)pHttpRequest == LFLF)
  1930. {
  1931. pHttpRequest += CRLF_SIZE;
  1932. HttpRequestLength -= CRLF_SIZE;
  1933. TotalBytesTaken += CRLF_SIZE;
  1934. //
  1935. // All done.
  1936. //
  1937. UlTrace(PARSER, (
  1938. "UlParseHttp (pRequest=%p) State: Trailer->Done\n",
  1939. pRequest
  1940. ));
  1941. pRequest->ParseState = ParseDoneState;
  1942. ReturnStatus = STATUS_SUCCESS;
  1943. goto end;
  1944. }
  1945. else
  1946. {
  1947. // Treat this as an unknown header, but don't write it.
  1948. ReturnStatus = UlLookupHeader(
  1949. pRequest,
  1950. pHttpRequest,
  1951. HttpRequestLength,
  1952. NULL,
  1953. 0,
  1954. TRUE,
  1955. &CurrentBytesTaken
  1956. );
  1957. if (NT_SUCCESS(ReturnStatus) == FALSE)
  1958. goto end;
  1959. //
  1960. // If no bytes were consumed, the header must be incomplete, so
  1961. // bail out until we get more data on this connection.
  1962. //
  1963. if (CurrentBytesTaken == 0)
  1964. {
  1965. ReturnStatus = STATUS_MORE_PROCESSING_REQUIRED;
  1966. goto end;
  1967. }
  1968. pHttpRequest += CurrentBytesTaken;
  1969. HttpRequestLength -= CurrentBytesTaken;
  1970. TotalBytesTaken += CurrentBytesTaken;
  1971. }
  1972. }
  1973. ReturnStatus = STATUS_MORE_PROCESSING_REQUIRED;
  1974. break;
  1975. default:
  1976. //
  1977. // this should never happen!
  1978. //
  1979. ASSERT(! "Unhandled ParseState");
  1980. break;
  1981. } // switch (pRequest->ParseState)
  1982. end:
  1983. *pBytesTaken = TotalBytesTaken;
  1984. if (ReturnStatus == STATUS_MORE_PROCESSING_REQUIRED &&
  1985. TotalBytesTaken == OriginalBufferLength)
  1986. {
  1987. //
  1988. // convert this to success, we consumed the entire buffer
  1989. //
  1990. ReturnStatus = STATUS_SUCCESS;
  1991. }
  1992. UlTrace(PARSER, (
  1993. "UlParseHttp returning %s, "
  1994. "(%p)->ParseState = %d (%s), TotalBytesTaken = %lu\n",
  1995. HttpStatusToString(ReturnStatus),
  1996. pRequest,
  1997. pRequest->ParseState,
  1998. UlParseStateToString(pRequest->ParseState),
  1999. TotalBytesTaken
  2000. ));
  2001. return ReturnStatus;
  2002. } // UlParseHttp
  2003. /*++
  2004. Routine Description:
  2005. Prints a TCP port number into a string buffer
  2006. Arguments:
  2007. pString - Output buffer
  2008. Port - Port number, in host order
  2009. Return Value:
  2010. Number of WCHARs
  2011. --*/
  2012. ULONG
  2013. UlpFormatPort(
  2014. OUT PWSTR pString,
  2015. IN ULONG Port
  2016. )
  2017. {
  2018. PWSTR p1;
  2019. PWSTR p2;
  2020. WCHAR ch;
  2021. ULONG digit;
  2022. ULONG length;
  2023. //
  2024. // Sanity check.
  2025. //
  2026. PAGED_CODE();
  2027. //
  2028. // Fast-path common ports. While we're at it, special case port 0,
  2029. // which is definitely not common, but handling it specially makes
  2030. // the general conversion code a bit simpler.
  2031. //
  2032. switch (Port)
  2033. {
  2034. case 0:
  2035. *pString++ = L'0';
  2036. *pString = UNICODE_NULL;
  2037. return 1;
  2038. case 80:
  2039. *pString++ = L'8';
  2040. *pString++ = L'0';
  2041. *pString = UNICODE_NULL;
  2042. return 2;
  2043. case 443:
  2044. *pString++ = L'4';
  2045. *pString++ = L'4';
  2046. *pString++ = L'3';
  2047. *pString = UNICODE_NULL;
  2048. return 3;
  2049. default:
  2050. break;
  2051. }
  2052. //
  2053. // Pull the least significant digits off the port value and store them
  2054. // into the pString. Note that this will store the digits in reverse
  2055. // order.
  2056. //
  2057. p1 = p2 = pString;
  2058. while (Port != 0)
  2059. {
  2060. digit = Port % 10;
  2061. Port = Port / 10;
  2062. *p1++ = L'0' + (WCHAR)digit;
  2063. }
  2064. length = DIFF(p1 - pString);
  2065. //
  2066. // Reverse the digits in the pString.
  2067. //
  2068. *p1-- = UNICODE_NULL;
  2069. while (p1 > p2)
  2070. {
  2071. ch = *p1;
  2072. *p1 = *p2;
  2073. *p2 = ch;
  2074. p2++;
  2075. p1--;
  2076. }
  2077. return length;
  2078. } // UlpFormatPort
  2079. /*
  2080. An origin server that does differentiate resources based on the host
  2081. requested (sometimes referred to as virtual hosts or vanity host
  2082. names) MUST use the following rules for determining the requested
  2083. resource on an HTTP/1.1 request:
  2084. 1. If Request-URI is an absoluteURI, the host is part of the
  2085. Request-URI. Any Host header field value in the request MUST be
  2086. ignored.
  2087. 2. If the Request-URI is not an absoluteURI, and the request includes
  2088. a Host header field, the host is determined by the Host header
  2089. field value.
  2090. 3. If the host as determined by rule 1 or 2 is not a valid host on
  2091. the server, the response MUST be a 400 (Bad Request) error message.
  2092. Recipients of an HTTP/1.0 request that lacks a Host header field MAY
  2093. attempt to use heuristics (e.g., examination of the URI path for
  2094. something unique to a particular host) in order to determine what
  2095. exact resource is being requested.
  2096. */
  2097. NTSTATUS
  2098. UlpCookUrl(
  2099. PUL_INTERNAL_REQUEST pRequest
  2100. )
  2101. {
  2102. NTSTATUS Status;
  2103. PUCHAR pHost;
  2104. ULONG HostLength;
  2105. PUCHAR pAbsPath;
  2106. ULONG AbsPathLength;
  2107. ULONG UrlLength;
  2108. ULONG LengthCopied;
  2109. PWSTR pUrl = NULL;
  2110. PWSTR pCurrent;
  2111. UCHAR IpAddressString[MAX_IP_ADDR_AND_PORT_STRING_LEN + 1];
  2112. USHORT IpPortNum;
  2113. HOSTNAME_TYPE HostnameType;
  2114. URL_ENCODING_TYPE HostnameEncodingType;
  2115. SHORT HostnameAddressType;
  2116. SHORT TransportAddressType
  2117. = pRequest->pHttpConn->pConnection->AddressType;
  2118. ULONG Index;
  2119. ULONG PortLength;
  2120. //
  2121. // Sanity check.
  2122. //
  2123. PAGED_CODE();
  2124. //
  2125. // We must have already parsed the entire headers + such
  2126. //
  2127. if (pRequest->ParseState != ParseCookState)
  2128. return STATUS_INVALID_DEVICE_STATE;
  2129. UlTraceVerbose(PARSER, ("http!UlpCookUrl(pRequest = %p)\n", pRequest));
  2130. //
  2131. // Better have an absolute url. We require a literal '/' at the beginning
  2132. // of pAbsPath; we will not accept %2F or UTF-8 encoding. See bug 467445.
  2133. //
  2134. if (pRequest->RawUrl.pAbsPath[0] != '/')
  2135. {
  2136. UCHAR FirstChar = pRequest->RawUrl.pAbsPath[0];
  2137. UCHAR SecondChar = pRequest->RawUrl.pAbsPath[1];
  2138. //
  2139. // allow * for Verb = OPTIONS
  2140. //
  2141. if (FirstChar == '*' &&
  2142. IS_HTTP_LWS(SecondChar) &&
  2143. pRequest->Verb == HttpVerbOPTIONS)
  2144. {
  2145. // ok
  2146. }
  2147. else
  2148. {
  2149. UlTraceError(PARSER, (
  2150. "http!UlpCookUrl(pRequest = %p): "
  2151. "Invalid lead chars for URL, verb='%s', "
  2152. "'%c' 0x%02x\n '%c' 0x%02x\n",
  2153. pRequest,
  2154. UlVerbToString(pRequest->Verb),
  2155. IS_HTTP_PRINT(FirstChar) ? FirstChar : '?',
  2156. FirstChar,
  2157. IS_HTTP_PRINT(SecondChar) ? SecondChar : '?',
  2158. SecondChar
  2159. ));
  2160. UlSetErrorCode(pRequest, UlErrorUrl, NULL);
  2161. Status = STATUS_INVALID_DEVICE_REQUEST;
  2162. goto end;
  2163. }
  2164. }
  2165. //
  2166. // get the IP port from the transport
  2167. //
  2168. if (TransportAddressType == TDI_ADDRESS_TYPE_IP)
  2169. {
  2170. IpPortNum =
  2171. pRequest->pHttpConn->pConnection->LocalAddrIn.sin_port;
  2172. }
  2173. else if (TransportAddressType == TDI_ADDRESS_TYPE_IP6)
  2174. {
  2175. IpPortNum =
  2176. pRequest->pHttpConn->pConnection->LocalAddrIn6.sin6_port;
  2177. }
  2178. else
  2179. {
  2180. ASSERT(! "Unexpected TdiAddressType");
  2181. IpPortNum = 0;
  2182. }
  2183. // Convert port from network order to host order
  2184. IpPortNum = SWAP_SHORT(IpPortNum);
  2185. //
  2186. // collect the host + abspath sections
  2187. //
  2188. if (pRequest->RawUrl.pHost != NULL)
  2189. {
  2190. // We have an absUri in the Request-line
  2191. PUCHAR pAbsUri;
  2192. pAbsUri = pRequest->RawUrl.pUrl;
  2193. pHost = pRequest->RawUrl.pHost;
  2194. pAbsPath = pRequest->RawUrl.pAbsPath;
  2195. HostnameType = Hostname_AbsUri;
  2196. ASSERT(pRequest->RawUrl.Length >= HTTP_PREFIX_SIZE &&
  2197. (*(UNALIGNED64 ULONG *) pAbsUri & HTTP_PREFIX_MASK)
  2198. == HTTP_PREFIX);
  2199. ASSERT('/' == *pAbsPath);
  2200. //
  2201. // Even though we have a hostname in the Request-line, we still
  2202. // MUST have a Host header for HTTP/1.1 requests. The Host header
  2203. // was syntax checked, if present, but will otherwise be ignored.
  2204. //
  2205. if (!pRequest->HeaderValid[HttpHeaderHost]
  2206. && HTTP_GREATER_EQUAL_VERSION(pRequest->Version, 1, 1))
  2207. {
  2208. UlTraceError(PARSER, (
  2209. "http!UlpCookUrl(pRequest = %p) "
  2210. "ERROR: 1.1 (or greater) request w/o host header\n",
  2211. pRequest
  2212. ));
  2213. UlSetErrorCode(pRequest, UlErrorHost, NULL);
  2214. Status = STATUS_INVALID_DEVICE_REQUEST;
  2215. goto end;
  2216. }
  2217. // Hack for the special case of an AbsUri without a '/' for the abspath
  2218. if (&g_SlashPath[0] == pAbsPath)
  2219. {
  2220. // We just have the scheme and the hostname, no real AbsPath
  2221. HostLength = pRequest->RawUrl.Length - DIFF(pHost - pAbsUri);
  2222. AbsPathLength = STRLEN_LIT("/");
  2223. }
  2224. else
  2225. {
  2226. HostLength = DIFF(pAbsPath - pHost);
  2227. AbsPathLength = pRequest->RawUrl.Length - DIFF(pAbsPath - pAbsUri);
  2228. }
  2229. ASSERT(HostLength > 0);
  2230. ASSERT(AbsPathLength > 0);
  2231. }
  2232. else
  2233. {
  2234. // We do not have a hostname in the Request-line
  2235. pHost = NULL;
  2236. HostLength = 0;
  2237. pAbsPath = pRequest->RawUrl.pAbsPath;
  2238. AbsPathLength = pRequest->RawUrl.Length;
  2239. //
  2240. // do we have a Host header?
  2241. //
  2242. if (pRequest->HeaderValid[HttpHeaderHost] &&
  2243. (pRequest->Headers[HttpHeaderHost].HeaderLength > 0) )
  2244. {
  2245. ASSERT(pRequest->Headers[HttpHeaderHost].pHeader != NULL);
  2246. pHost = pRequest->Headers[HttpHeaderHost].pHeader;
  2247. HostLength = pRequest->Headers[HttpHeaderHost].HeaderLength;
  2248. HostnameType = Hostname_HostHeader;
  2249. }
  2250. else
  2251. {
  2252. //
  2253. // If this was a 1.1 client, it's an invalid request
  2254. // if it does not have a Host header, so fail it.
  2255. // RFC 2616, 14.23 "Host" says the Host header can be empty,
  2256. // but it MUST be present.
  2257. //
  2258. if (!pRequest->HeaderValid[HttpHeaderHost]
  2259. && HTTP_GREATER_EQUAL_VERSION(pRequest->Version, 1, 1))
  2260. {
  2261. UlTraceError(PARSER, (
  2262. "http!UlpCookUrl(pRequest = %p) "
  2263. "ERROR: 1.1 (or greater) request w/o host header\n",
  2264. pRequest
  2265. ));
  2266. UlSetErrorCode(pRequest, UlErrorHost, NULL);
  2267. Status = STATUS_INVALID_DEVICE_REQUEST;
  2268. goto end;
  2269. }
  2270. //
  2271. // format the transport address into a string
  2272. //
  2273. pHost = IpAddressString;
  2274. // CODEWORK: we probably ought to be writing the scope ID
  2275. // for IPv6 literals
  2276. HostLength = HostAddressAndPortToString(
  2277. pHost,
  2278. pRequest->pHttpConn->pConnection->LocalAddress,
  2279. TransportAddressType);
  2280. ASSERT(HostLength < sizeof(IpAddressString));
  2281. HostnameType = Hostname_Transport;
  2282. }
  2283. }
  2284. //
  2285. // is there a port # already there ?
  2286. //
  2287. for (Index = HostLength; Index-- > 0; )
  2288. {
  2289. if (pHost[Index] == ':')
  2290. {
  2291. //
  2292. // Remove the port length from HostLength since we always
  2293. // generate the port number ourselves from the transport's port
  2294. //
  2295. HostLength = Index;
  2296. break;
  2297. }
  2298. else if (pHost[Index] == ']')
  2299. {
  2300. // ']' => end of a literal IPv6 address. Not going to find
  2301. // a valid port before this, so abort the rest of the loop
  2302. break;
  2303. }
  2304. }
  2305. //
  2306. // Validate that the hostname is syntactically correct
  2307. //
  2308. Status = HttpValidateHostname(
  2309. &g_UrlC14nConfig,
  2310. pHost,
  2311. HostLength,
  2312. HostnameType,
  2313. &HostnameAddressType
  2314. );
  2315. if (!NT_SUCCESS(Status))
  2316. {
  2317. UlSetErrorCode(pRequest, UlErrorHost, NULL);
  2318. goto end;
  2319. }
  2320. //
  2321. // If the hostname is a literal IPv4 or IPv6 address,
  2322. // it must match the transport that the request was received on.
  2323. //
  2324. if (0 != HostnameAddressType)
  2325. {
  2326. BOOLEAN Valid = (BOOLEAN) (TransportAddressType == HostnameAddressType);
  2327. // CODEWORK: should we check that the transport IP address
  2328. // matches the hostname address?
  2329. if (!Valid)
  2330. {
  2331. UlTraceError(PARSER, (
  2332. "http!UlpCookUrl(pRequest = %p): "
  2333. "Host is IPv%c, transport is IPv%c\n",
  2334. pRequest,
  2335. (TDI_ADDRESS_TYPE_IP == HostnameAddressType)
  2336. ? '4'
  2337. : (TDI_ADDRESS_TYPE_IP6 == HostnameAddressType)
  2338. ? '6' : '?',
  2339. (TDI_ADDRESS_TYPE_IP == TransportAddressType)
  2340. ? '4'
  2341. : (TDI_ADDRESS_TYPE_IP6 == TransportAddressType)
  2342. ? '6' : '?'
  2343. ));
  2344. UlSetErrorCode(pRequest, UlErrorHost, NULL);
  2345. Status = STATUS_INVALID_DEVICE_REQUEST;
  2346. goto end;
  2347. }
  2348. }
  2349. //
  2350. // Pessimistically calculate the largest buffer needed to hold the
  2351. // cooked URL. If DBCS, %-encoded, or UTF-8 characters are present
  2352. // in the raw URL, the cooked URL won't need this many WCHARs.
  2353. //
  2354. UrlLength = ((HTTP_PREFIX_SIZE+HTTP_PREFIX2_SIZE)
  2355. + HostLength
  2356. + STRLEN_LIT(":")
  2357. + MAX_PORT_LENGTH
  2358. + AbsPathLength
  2359. + 1) // terminating NUL
  2360. * sizeof(WCHAR);
  2361. //
  2362. // Too big? Too bad!
  2363. //
  2364. if (UrlLength > UNICODE_STRING_MAX_BYTE_LEN)
  2365. {
  2366. UlTraceError(PARSER, (
  2367. "http!UlpCookUrl(pRequest = %p): "
  2368. "Url is too long, %lu\n",
  2369. pRequest, UrlLength
  2370. ));
  2371. Status = STATUS_DATA_OVERRUN;
  2372. goto end;
  2373. }
  2374. //
  2375. // allocate a new buffer to hold this guy
  2376. //
  2377. URL_LENGTH_STATS_UPDATE(UrlLength);
  2378. if (UrlLength > g_UlMaxInternalUrlLength)
  2379. {
  2380. pUrl = UL_ALLOCATE_ARRAY(
  2381. NonPagedPool,
  2382. WCHAR,
  2383. UrlLength / sizeof(WCHAR),
  2384. URL_POOL_TAG
  2385. );
  2386. URL_LENGTH_STATS_REALLOC();
  2387. }
  2388. else
  2389. {
  2390. pUrl = pRequest->pUrlBuffer;
  2391. }
  2392. if (pUrl == NULL)
  2393. {
  2394. Status = STATUS_NO_MEMORY;
  2395. goto end;
  2396. }
  2397. pRequest->CookedUrl.pUrl = pCurrent = pUrl;
  2398. HTTP_FILL_BUFFER(pCurrent, UrlLength);
  2399. //
  2400. // compute the scheme
  2401. //
  2402. if (pRequest->Secure)
  2403. {
  2404. RtlCopyMemory(pCurrent, L"https://", sizeof(L"https://"));
  2405. pCurrent += WCSLEN_LIT(L"https://");
  2406. pRequest->CookedUrl.Length = WCSLEN_LIT(L"https://") * sizeof(WCHAR);
  2407. }
  2408. else
  2409. {
  2410. RtlCopyMemory(pCurrent, L"http://", sizeof(L"http://"));
  2411. pCurrent += WCSLEN_LIT(L"http://");
  2412. pRequest->CookedUrl.Length = WCSLEN_LIT(L"http://") * sizeof(WCHAR);
  2413. }
  2414. //
  2415. // assemble the rest of the url
  2416. //
  2417. Status = HttpCopyHost(
  2418. &g_UrlC14nConfig,
  2419. pCurrent,
  2420. pHost,
  2421. HostLength,
  2422. &LengthCopied,
  2423. &HostnameEncodingType
  2424. );
  2425. if (NT_SUCCESS(Status) == FALSE)
  2426. {
  2427. UlSetErrorCode(pRequest, UlErrorHost, NULL);
  2428. goto end;
  2429. }
  2430. if (pRequest->CookedUrl.Length + LengthCopied > UNICODE_STRING_MAX_BYTE_LEN)
  2431. {
  2432. Status = STATUS_DATA_OVERRUN;
  2433. goto end;
  2434. }
  2435. pRequest->CookedUrl.pHost = pCurrent;
  2436. pRequest->CookedUrl.Length += LengthCopied;
  2437. pCurrent += LengthCopied / sizeof(WCHAR);
  2438. //
  2439. // port
  2440. //
  2441. *pCurrent = L':';
  2442. ASSERT(0 != IpPortNum);
  2443. PortLength = UlpFormatPort( pCurrent+1, IpPortNum ) + 1;
  2444. ASSERT(PortLength <= (MAX_PORT_LENGTH+1));
  2445. pCurrent += PortLength;
  2446. // UlpFormatPort returns WCHAR not byte count
  2447. pRequest->CookedUrl.Length += PortLength * sizeof(WCHAR);
  2448. if (pRequest->CookedUrl.Length > UNICODE_STRING_MAX_BYTE_LEN)
  2449. {
  2450. Status = STATUS_DATA_OVERRUN;
  2451. goto end;
  2452. }
  2453. //
  2454. // abs_path
  2455. //
  2456. if (pRequest->RawUrlClean)
  2457. {
  2458. Status = HttpCopyUrl(
  2459. &g_UrlC14nConfig,
  2460. pCurrent,
  2461. pAbsPath,
  2462. AbsPathLength,
  2463. &LengthCopied,
  2464. &pRequest->CookedUrl.UrlEncoding
  2465. );
  2466. }
  2467. else
  2468. {
  2469. Status = HttpCleanAndCopyUrl(
  2470. &g_UrlC14nConfig,
  2471. UrlPart_AbsPath,
  2472. pCurrent,
  2473. pAbsPath,
  2474. AbsPathLength,
  2475. &LengthCopied,
  2476. &pRequest->CookedUrl.pQueryString,
  2477. &pRequest->CookedUrl.UrlEncoding
  2478. );
  2479. }
  2480. if (NT_SUCCESS(Status) == FALSE)
  2481. {
  2482. if (STATUS_OBJECT_PATH_INVALID == Status)
  2483. {
  2484. UlTraceError(PARSER, (
  2485. "http!UlpCookUrl(pRequest = %p) Invalid URL\n",
  2486. pRequest
  2487. ));
  2488. UlSetErrorCode(pRequest, UlErrorForbiddenUrl, NULL);
  2489. }
  2490. goto end;
  2491. }
  2492. if (pRequest->CookedUrl.Length + LengthCopied > UNICODE_STRING_MAX_BYTE_LEN)
  2493. {
  2494. Status = STATUS_DATA_OVERRUN;
  2495. goto end;
  2496. }
  2497. pRequest->CookedUrl.pAbsPath = pCurrent;
  2498. pRequest->CookedUrl.Length += LengthCopied;
  2499. ASSERT(pRequest->CookedUrl.Length <= UrlLength);
  2500. //
  2501. // Update pRequest, include space for the terminator
  2502. //
  2503. pRequest->TotalRequestSize += pRequest->CookedUrl.Length + sizeof(WCHAR);
  2504. //
  2505. // Let's create the hash for the whole CookedUrl
  2506. //
  2507. pRequest->CookedUrl.Hash = HashStringNoCaseW(pRequest->CookedUrl.pUrl, 0);
  2508. //
  2509. // Scramble it
  2510. //
  2511. pRequest->CookedUrl.Hash = HashRandomizeBits(pRequest->CookedUrl.Hash);
  2512. ASSERT(pRequest->CookedUrl.pHost != NULL);
  2513. ASSERT(pRequest->CookedUrl.pAbsPath != NULL);
  2514. //
  2515. // Setup the routing token Pointers to point to the default
  2516. // token buffer (allocated inline with request).
  2517. //
  2518. ASSERT(pRequest->CookedUrl.pRoutingToken == NULL);
  2519. ASSERT(pRequest->CookedUrl.RoutingTokenBufferSize == 0);
  2520. ASSERT(pRequest->pDefaultRoutingTokenBuffer);
  2521. pRequest->CookedUrl.pRoutingToken = pRequest->pDefaultRoutingTokenBuffer;
  2522. pRequest->CookedUrl.RoutingTokenBufferSize = DEFAULT_MAX_ROUTING_TOKEN_LENGTH;
  2523. pRequest->CookedUrl.RoutingTokenType = RoutingTokenNotExists;
  2524. Status = STATUS_SUCCESS;
  2525. end:
  2526. if (! NT_SUCCESS(Status))
  2527. {
  2528. if (pUrl != NULL)
  2529. {
  2530. if (pUrl != pRequest->pUrlBuffer)
  2531. {
  2532. UL_FREE_POOL(pUrl, URL_POOL_TAG);
  2533. }
  2534. RtlZeroMemory(&pRequest->CookedUrl, sizeof(pRequest->CookedUrl));
  2535. }
  2536. //
  2537. // has a specific error code been set?
  2538. //
  2539. UlTraceError(PARSER, (
  2540. "http!UlpCookUrl(pRequest = %p) "
  2541. "ERROR: unhappy. %s\n",
  2542. pRequest,
  2543. HttpStatusToString(Status)
  2544. ));
  2545. if (pRequest->ErrorCode == UlErrorNone)
  2546. {
  2547. UlSetErrorCode(pRequest, UlErrorUrl, NULL);
  2548. }
  2549. }
  2550. return Status;
  2551. } // UlpCookUrl
  2552. /*++
  2553. Routine Description:
  2554. A utility routine to generate the routing token. As well as
  2555. the corresponding token hash. The hash is used if we decide
  2556. to cache this url.
  2557. This function is used for the IP Bound site lookup in the
  2558. cgroup tree.
  2559. When token is generated TokenLength is in bytes, and does
  2560. not include the terminating NULL.
  2561. Arguments:
  2562. pRequest - The request's cooked url holds the routing token
  2563. IpBased - If True, rather than copying the Host from
  2564. cooked url, the ip address used in place.
  2565. This is for Ip-only site lookup.
  2566. Returns:
  2567. STATUS_SUCCESS - If the requested token is already there.
  2568. - If the token is successfully generated.
  2569. STATUS_NO_MEMORY- If the memory allocation failed for a large
  2570. possible token size.
  2571. --*/
  2572. NTSTATUS
  2573. UlGenerateRoutingToken(
  2574. IN OUT PUL_INTERNAL_REQUEST pRequest,
  2575. IN BOOLEAN IpBased
  2576. )
  2577. {
  2578. USHORT TokenLength = 0;
  2579. PUL_HTTP_CONNECTION pHttpConn = NULL;
  2580. PWCHAR pToken = NULL;
  2581. PAGED_CODE();
  2582. //
  2583. // The Routing Token ends with a terminating null.
  2584. // And once it is generated, it will look like as;
  2585. //
  2586. // "http(s)://host:port:Ip" or "http(s)://Ip:port:Ip"
  2587. // --------- --- ------- ---
  2588. // X Y X Y
  2589. //
  2590. // When IpBased is set to FALSE, X comes from cookedUrl
  2591. // which's the host sent by the client. (case1)
  2592. // When IpBased is set to TRUE, X comes from the Ip address
  2593. // of the connection on which the request is received. (case2)
  2594. //
  2595. // Y always comes from the connection. For Host+Ip bound sites
  2596. // cgroup needs the token in case1. For IP only bound sites
  2597. // cgroup needs the token in case2.
  2598. //
  2599. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  2600. pHttpConn = pRequest->pHttpConn;
  2601. ASSERT(IS_VALID_CONNECTION(pHttpConn->pConnection));
  2602. ASSERT(pRequest->pDefaultRoutingTokenBuffer);
  2603. ASSERT(pRequest->CookedUrl.pRoutingToken);
  2604. ASSERT(pRequest->CookedUrl.RoutingTokenBufferSize);
  2605. ASSERT(IS_VALID_ROUTING_TOKEN(pRequest->CookedUrl.RoutingTokenType));
  2606. //
  2607. // Short cut if the requested token is already there.
  2608. //
  2609. if (IpBased &&
  2610. pRequest->CookedUrl.RoutingTokenType == RoutingTokenIP)
  2611. {
  2612. return STATUS_SUCCESS;
  2613. }
  2614. else if (IpBased == FALSE &&
  2615. pRequest->CookedUrl.RoutingTokenType == RoutingTokenHostPlusIP)
  2616. {
  2617. return STATUS_SUCCESS;
  2618. }
  2619. //
  2620. // We should not be trying to generate a new token on the same
  2621. // buffer, if the previous token is already matched in the cgroup.
  2622. // or in the uri cache.
  2623. //
  2624. ASSERT(pRequest->ConfigInfo.SiteUrlType == HttpUrlSite_None);
  2625. if (IpBased)
  2626. {
  2627. PWSTR pUrl = pRequest->CookedUrl.pUrl;
  2628. PWSTR pTemp;
  2629. ASSERT(pUrl);
  2630. pToken = (PWCHAR) pRequest->CookedUrl.pRoutingToken;
  2631. //
  2632. // Default buffer should always be big enough to hold the max
  2633. // possible IP Based routing token.
  2634. //
  2635. ASSERT(MAX_IP_BASED_ROUTING_TOKEN_LENGTH
  2636. <= pRequest->CookedUrl.RoutingTokenBufferSize);
  2637. //
  2638. // Build the HTTP prefix first.
  2639. //
  2640. if (pUrl[HTTP_PREFIX_COLON_INDEX] == L':')
  2641. {
  2642. RtlCopyMemory(
  2643. pToken,
  2644. HTTP_IP_PREFIX,
  2645. HTTP_IP_PREFIX_LENGTH
  2646. );
  2647. TokenLength = HTTP_IP_PREFIX_LENGTH;
  2648. }
  2649. else
  2650. {
  2651. ASSERT(pUrl[HTTPS_PREFIX_COLON_INDEX] == L':');
  2652. RtlCopyMemory(
  2653. pToken,
  2654. HTTPS_IP_PREFIX,
  2655. HTTPS_IP_PREFIX_LENGTH
  2656. );
  2657. TokenLength = HTTPS_IP_PREFIX_LENGTH;
  2658. }
  2659. pTemp = pToken + (TokenLength / sizeof(WCHAR));
  2660. //
  2661. // Now Add the "Ip : Port : Ip"
  2662. //
  2663. ASSERT(IS_VALID_CONNECTION(pHttpConn->pConnection));
  2664. TokenLength = TokenLength +
  2665. HostAddressAndPortToRoutingTokenW(
  2666. pTemp,
  2667. pHttpConn->pConnection->LocalAddress,
  2668. pHttpConn->pConnection->AddressType
  2669. );
  2670. ASSERT(TokenLength <= MAX_IP_BASED_ROUTING_TOKEN_LENGTH);
  2671. pRequest->CookedUrl.RoutingTokenType = RoutingTokenIP;
  2672. }
  2673. else // Host + Ip based token (IpBased == FALSE)
  2674. {
  2675. USHORT MaxRoutingTokenSize;
  2676. USHORT CookedHostLength =
  2677. DIFF_USHORT(pRequest->CookedUrl.pAbsPath - pRequest->CookedUrl.pUrl);
  2678. ASSERT(CookedHostLength);
  2679. //
  2680. // Check if the default buffer is big enough to hold the token.
  2681. //
  2682. MaxRoutingTokenSize = (
  2683. CookedHostLength + // For http(s)://host:port
  2684. 1 + // For ':' after port
  2685. 1 + // For terminating Null
  2686. MAX_IP_ADDR_PLUS_BRACKETS_STRING_LEN // For IP Address at the end
  2687. ) * sizeof(WCHAR)
  2688. ;
  2689. if (MaxRoutingTokenSize > pRequest->CookedUrl.RoutingTokenBufferSize)
  2690. {
  2691. PWSTR pRoutingToken = UL_ALLOCATE_ARRAY(
  2692. NonPagedPool,
  2693. WCHAR,
  2694. MaxRoutingTokenSize / sizeof(WCHAR),
  2695. URL_POOL_TAG
  2696. );
  2697. if (pRoutingToken == NULL)
  2698. {
  2699. return STATUS_NO_MEMORY;
  2700. }
  2701. //
  2702. // There shouldn't be a previous large buffer, but let's check
  2703. // it out anyway.
  2704. //
  2705. if (pRequest->CookedUrl.pRoutingToken !=
  2706. pRequest->pDefaultRoutingTokenBuffer)
  2707. {
  2708. ASSERT(!"This should never happen !");
  2709. UL_FREE_POOL(pRequest->CookedUrl.pRoutingToken, URL_POOL_TAG);
  2710. }
  2711. pRequest->CookedUrl.pRoutingToken = pRoutingToken;
  2712. pRequest->CookedUrl.RoutingTokenBufferSize = MaxRoutingTokenSize;
  2713. }
  2714. //
  2715. // Copy over everything from the beginning of the cooked url
  2716. // to the AbsPath of the cooked url.
  2717. //
  2718. pToken = (PWCHAR) pRequest->CookedUrl.pRoutingToken;
  2719. RtlCopyMemory(pToken,
  2720. pRequest->CookedUrl.pUrl,
  2721. CookedHostLength * sizeof(WCHAR)
  2722. );
  2723. pToken += CookedHostLength;
  2724. ASSERT((pRequest->CookedUrl.pUrl)[CookedHostLength] == L'/');
  2725. *pToken++ = L':';
  2726. TokenLength = (CookedHostLength + 1) * sizeof(WCHAR);
  2727. //
  2728. // Now copy over the IP Address to the end.
  2729. //
  2730. TokenLength = TokenLength +
  2731. HostAddressToStringW(
  2732. pToken,
  2733. pHttpConn->pConnection->LocalAddress,
  2734. pHttpConn->pConnection->AddressType
  2735. );
  2736. pRequest->CookedUrl.RoutingTokenType = RoutingTokenHostPlusIP;
  2737. }
  2738. //
  2739. // Make sure that we did not overflow the allocated buffer.
  2740. // TokenLength does not include the terminating null.
  2741. //
  2742. ASSERT((TokenLength + sizeof(WCHAR))
  2743. <= pRequest->CookedUrl.RoutingTokenBufferSize);
  2744. //
  2745. // Set the tokenlength to show the actual amount we are using.
  2746. // Also recalculate the token hash INCLUDING the AbsPath from the
  2747. // original cooked url.
  2748. //
  2749. pRequest->CookedUrl.RoutingTokenLength = TokenLength;
  2750. //
  2751. // Create the hash for the whole RoutingToken plus AbsPath.
  2752. //
  2753. pRequest->CookedUrl.RoutingHash =
  2754. HashStringsNoCaseW(
  2755. pRequest->CookedUrl.pRoutingToken,
  2756. pRequest->CookedUrl.pAbsPath,
  2757. 0
  2758. );
  2759. pRequest->CookedUrl.RoutingHash =
  2760. HashRandomizeBits(pRequest->CookedUrl.RoutingHash);
  2761. UlTrace(CONFIG_GROUP_FNC,
  2762. ("Http!UlGenerateRoutingToken: "
  2763. "pRoutingToken:(%S) pAbsPath: (%S) Hash %lx\n",
  2764. pRequest->CookedUrl.pRoutingToken,
  2765. pRequest->CookedUrl.pAbsPath,
  2766. pRequest->CookedUrl.RoutingHash
  2767. ));
  2768. //
  2769. // Ready for a cgroup lookup !
  2770. //
  2771. return STATUS_SUCCESS;
  2772. } // UlGenerateRoutingToken
  2773. /***************************************************************************++
  2774. Routine Description:
  2775. Generates the fixed part of the header. Fixed headers include the
  2776. status line, and any headers that don't have to be generated for
  2777. every request (such as Date and Connection).
  2778. The final CRLF separating headers from body is considered part of
  2779. the variable headers.
  2780. Arguments:
  2781. Version - the http version for the status line
  2782. pUserResponse - the user specified response
  2783. BufferLength - length of pBuffer
  2784. AccessMode - UserMode (probe) or KernelMode (no probe)
  2785. pBuffer - generate the headers here
  2786. pBytesCopied - gets the number of bytes generated
  2787. --***************************************************************************/
  2788. NTSTATUS
  2789. UlGenerateFixedHeaders(
  2790. IN HTTP_VERSION Version,
  2791. IN PHTTP_RESPONSE pUserResponse,
  2792. IN USHORT HttpStatusCode,
  2793. IN ULONG BufferLength,
  2794. IN KPROCESSOR_MODE AccessMode,
  2795. OUT PUCHAR pBuffer,
  2796. OUT PULONG pBytesCopied
  2797. )
  2798. {
  2799. PUCHAR pStartBuffer;
  2800. PUCHAR pEndBuffer;
  2801. ULONG BytesToCopy;
  2802. ULONG i;
  2803. PHTTP_KNOWN_HEADER pKnownHeaders;
  2804. PHTTP_UNKNOWN_HEADER pUnknownHeaders;
  2805. NTSTATUS Status = STATUS_SUCCESS;
  2806. USHORT ReasonLength;
  2807. PCSTR pReason;
  2808. USHORT RawValueLength;
  2809. PCSTR pRawValue;
  2810. //
  2811. // Sanity check.
  2812. //
  2813. PAGED_CODE();
  2814. ASSERT(pUserResponse != NULL);
  2815. ASSERT(pBuffer != NULL && BufferLength > 0);
  2816. ASSERT(pBytesCopied != NULL);
  2817. //
  2818. // The pUserResponse is (probably) user-mode data and cannot be trusted.
  2819. // Hence the try/except.
  2820. //
  2821. __try
  2822. {
  2823. pStartBuffer = pBuffer;
  2824. pEndBuffer = pBuffer + BufferLength;
  2825. // Check for arithmetic overflow
  2826. if (pEndBuffer <= pStartBuffer)
  2827. return STATUS_INVALID_PARAMETER;
  2828. ReasonLength = pUserResponse->ReasonLength;
  2829. pReason = pUserResponse->pReason;
  2830. //
  2831. // Build the response headers.
  2832. //
  2833. if (HTTP_NOT_EQUAL_VERSION(Version, 0, 9))
  2834. {
  2835. BytesToCopy =
  2836. STRLEN_LIT("HTTP/1.1 ") +
  2837. 4 +
  2838. ReasonLength +
  2839. sizeof(USHORT);
  2840. if (DIFF(pEndBuffer - pBuffer) < BytesToCopy)
  2841. {
  2842. return STATUS_INSUFFICIENT_RESOURCES;
  2843. }
  2844. //
  2845. // Always send back 1.1 in the response.
  2846. //
  2847. RtlCopyMemory(pBuffer, "HTTP/1.1 ", STRLEN_LIT("HTTP/1.1 "));
  2848. pBuffer += STRLEN_LIT("HTTP/1.1 ");
  2849. //
  2850. // Build ASCII representation of 3-digit status code
  2851. // in reverse order: units, tens, hundreds.
  2852. // Section 6.1.1 of RFC 2616 says that Status-Code is 3DIGIT;
  2853. // reject anything that can't be represented in three digits.
  2854. //
  2855. if (HttpStatusCode > UL_MAX_HTTP_STATUS_CODE)
  2856. return STATUS_INVALID_PARAMETER;
  2857. pBuffer[2] = (UCHAR) ('0' + (HttpStatusCode % 10));
  2858. HttpStatusCode /= 10;
  2859. pBuffer[1] = (UCHAR) ('0' + (HttpStatusCode % 10));
  2860. HttpStatusCode /= 10;
  2861. pBuffer[0] = (UCHAR) ('0' + (HttpStatusCode % 10));
  2862. pBuffer[3] = SP;
  2863. pBuffer += 4;
  2864. //
  2865. // Copy the optional reason phrase.
  2866. //
  2867. if (0 != ReasonLength)
  2868. {
  2869. UlProbeAnsiString(pReason, ReasonLength, AccessMode);
  2870. for (i = 0; i < ReasonLength; ++i)
  2871. {
  2872. // Reason-Phrase must be printable ASCII characters or LWS
  2873. if (IS_HTTP_PRINT(pReason[i]))
  2874. {
  2875. *pBuffer++ = pReason[i];
  2876. }
  2877. else
  2878. {
  2879. return STATUS_INVALID_PARAMETER;
  2880. }
  2881. }
  2882. }
  2883. //
  2884. // Terminate with the CRLF.
  2885. //
  2886. ((UNALIGNED64 USHORT *)pBuffer)[0] = CRLF;
  2887. pBuffer += sizeof(USHORT);
  2888. //
  2889. // Loop through the known headers.
  2890. //
  2891. pKnownHeaders = pUserResponse->Headers.KnownHeaders;
  2892. for (i = 0; i < HttpHeaderResponseMaximum; ++i)
  2893. {
  2894. //
  2895. // Skip some headers we'll generate.
  2896. //
  2897. if ((i == HttpHeaderDate) ||
  2898. (i == HttpHeaderConnection) ||
  2899. (i == HttpHeaderServer))
  2900. {
  2901. continue;
  2902. }
  2903. RawValueLength = pKnownHeaders[i].RawValueLength;
  2904. // We have no provision for sending back a known header
  2905. // with an empty value, but the RFC specifies non-empty
  2906. // values for each of the known headers.
  2907. if (RawValueLength > 0)
  2908. {
  2909. PHEADER_MAP_ENTRY pEntry
  2910. = &(g_ResponseHeaderMapTable[g_ResponseHeaderMap[i]]);
  2911. pRawValue = pKnownHeaders[i].pRawValue;
  2912. UlProbeAnsiString(
  2913. pRawValue,
  2914. RawValueLength,
  2915. AccessMode
  2916. );
  2917. BytesToCopy =
  2918. pEntry->HeaderLength +
  2919. 1 + // space
  2920. RawValueLength +
  2921. sizeof(USHORT); // CRLF
  2922. if (DIFF(pEndBuffer - pBuffer) < BytesToCopy)
  2923. {
  2924. return STATUS_INSUFFICIENT_RESOURCES;
  2925. }
  2926. RtlCopyMemory(
  2927. pBuffer,
  2928. pEntry->MixedCaseHeader,
  2929. pEntry->HeaderLength
  2930. );
  2931. pBuffer += pEntry->HeaderLength;
  2932. *pBuffer++ = SP;
  2933. RtlCopyMemory(
  2934. pBuffer,
  2935. pRawValue,
  2936. RawValueLength
  2937. );
  2938. pBuffer += RawValueLength;
  2939. ((UNALIGNED64 USHORT *)pBuffer)[0] = CRLF;
  2940. pBuffer += sizeof(USHORT);
  2941. }
  2942. }
  2943. //
  2944. // Handle the Server: header
  2945. //
  2946. if ( UL_DISABLE_SERVER_HEADER_ALL != g_UlDisableServerHeader )
  2947. {
  2948. if ( g_UlDisableServerHeader == UL_DISABLE_SERVER_HEADER_DRIVER &&
  2949. (pUserResponse->Flags & HTTP_RESPONSE_FLAG_DRIVER) )
  2950. {
  2951. // skip generating server header on driver-created responses
  2952. }
  2953. else
  2954. {
  2955. BOOLEAN Suppress = FALSE;
  2956. pRawValue = pKnownHeaders[HttpHeaderServer].pRawValue;
  2957. RawValueLength = pKnownHeaders[HttpHeaderServer
  2958. ].RawValueLength;
  2959. // check to see if app wishes to suppress Server: header
  2960. if ( (0 == RawValueLength) && pRawValue )
  2961. {
  2962. // Probe pRawValue and see if it's a single null char
  2963. UlProbeAnsiString(
  2964. pRawValue,
  2965. sizeof(UCHAR),
  2966. AccessMode
  2967. );
  2968. if ( '\0' == *pRawValue )
  2969. {
  2970. Suppress = TRUE;
  2971. }
  2972. }
  2973. // If we're not supressing it, generate it!
  2974. if ( !Suppress )
  2975. {
  2976. PHEADER_MAP_ENTRY pEntry;
  2977. pEntry = &(g_ResponseHeaderMapTable[g_ResponseHeaderMap[
  2978. HttpHeaderServer]]);
  2979. BytesToCopy =
  2980. pEntry->HeaderLength +
  2981. 1 +
  2982. DEFAULT_SERVER_HDR_LENGTH +
  2983. sizeof(USHORT);
  2984. if (DIFF(pEndBuffer - pBuffer) < BytesToCopy)
  2985. {
  2986. return STATUS_INSUFFICIENT_RESOURCES;
  2987. }
  2988. RtlCopyMemory(
  2989. pBuffer,
  2990. pEntry->MixedCaseHeader,
  2991. pEntry->HeaderLength
  2992. );
  2993. pBuffer += pEntry->HeaderLength;
  2994. *pBuffer++ = SP;
  2995. //
  2996. // Prepend user's Server header data + SP
  2997. //
  2998. if ( RawValueLength )
  2999. {
  3000. BytesToCopy = RawValueLength + // User's Data
  3001. 1 + // SP
  3002. DEFAULT_SERVER_HDR_LENGTH +
  3003. sizeof(USHORT); // CRLF
  3004. if (DIFF(pEndBuffer - pBuffer) < BytesToCopy)
  3005. {
  3006. return STATUS_INSUFFICIENT_RESOURCES;
  3007. }
  3008. UlProbeAnsiString(
  3009. pRawValue,
  3010. RawValueLength,
  3011. AccessMode
  3012. );
  3013. RtlCopyMemory(
  3014. pBuffer,
  3015. pRawValue,
  3016. RawValueLength
  3017. );
  3018. pBuffer += RawValueLength;
  3019. *pBuffer++ = SP;
  3020. }
  3021. //
  3022. // Append default Server header vaule
  3023. //
  3024. RtlCopyMemory(
  3025. pBuffer,
  3026. DEFAULT_SERVER_HDR,
  3027. DEFAULT_SERVER_HDR_LENGTH
  3028. );
  3029. pBuffer += DEFAULT_SERVER_HDR_LENGTH;
  3030. // Terminate the header
  3031. ((UNALIGNED64 USHORT *)pBuffer)[0] = CRLF;
  3032. pBuffer += sizeof(USHORT);
  3033. }
  3034. }
  3035. }
  3036. //
  3037. // And now the unknown headers (this might throw an exception).
  3038. //
  3039. pUnknownHeaders = pUserResponse->Headers.pUnknownHeaders;
  3040. if (pUnknownHeaders != NULL)
  3041. {
  3042. USHORT UnknownHeaderCount
  3043. = pUserResponse->Headers.UnknownHeaderCount;
  3044. if (UnknownHeaderCount >= UL_MAX_CHUNKS)
  3045. {
  3046. return STATUS_INVALID_PARAMETER;
  3047. }
  3048. UlProbeForRead(
  3049. pUnknownHeaders,
  3050. sizeof(HTTP_UNKNOWN_HEADER) * UnknownHeaderCount,
  3051. TYPE_ALIGNMENT(PVOID),
  3052. AccessMode
  3053. );
  3054. for (i = 0 ; i < UnknownHeaderCount; ++i)
  3055. {
  3056. USHORT NameLength = pUnknownHeaders[i].NameLength;
  3057. PCSTR pName;
  3058. RawValueLength = pUnknownHeaders[i].RawValueLength;
  3059. if (NameLength > 0)
  3060. {
  3061. BytesToCopy =
  3062. NameLength +
  3063. STRLEN_LIT(": ") +
  3064. RawValueLength +
  3065. sizeof(USHORT); // CRLF
  3066. if (DIFF(pEndBuffer - pBuffer) < BytesToCopy)
  3067. {
  3068. return STATUS_INSUFFICIENT_RESOURCES;
  3069. }
  3070. pName = pUnknownHeaders[i].pName;
  3071. UlProbeAnsiString(
  3072. pName,
  3073. NameLength,
  3074. AccessMode
  3075. );
  3076. RtlCopyMemory(
  3077. pBuffer,
  3078. pName,
  3079. NameLength
  3080. );
  3081. pBuffer += NameLength;
  3082. *pBuffer++ = ':';
  3083. *pBuffer++ = SP;
  3084. //
  3085. // Empty values are legitimate
  3086. //
  3087. if (0 != RawValueLength)
  3088. {
  3089. pRawValue = pUnknownHeaders[i].pRawValue;
  3090. UlProbeAnsiString(
  3091. pRawValue,
  3092. RawValueLength,
  3093. AccessMode
  3094. );
  3095. RtlCopyMemory(
  3096. pBuffer,
  3097. pRawValue,
  3098. RawValueLength
  3099. );
  3100. pBuffer += RawValueLength;
  3101. }
  3102. ((UNALIGNED64 USHORT *)pBuffer)[0] = CRLF;
  3103. pBuffer += sizeof(USHORT);
  3104. } // if (NameLength > 0)
  3105. }
  3106. } // if (pUnknownHeaders != NULL)
  3107. *pBytesCopied = DIFF(pBuffer - pStartBuffer);
  3108. } // if (Version > UlHttpVersion09)
  3109. else
  3110. {
  3111. *pBytesCopied = 0;
  3112. }
  3113. //
  3114. // Ensure we didn't use too much.
  3115. //
  3116. ASSERT(DIFF(pBuffer - pStartBuffer) <= BufferLength);
  3117. }
  3118. __except( UL_EXCEPTION_FILTER() )
  3119. {
  3120. Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode());
  3121. }
  3122. return Status;
  3123. } // UlGenerateFixedHeaders
  3124. PCSTR Weekdays[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
  3125. PCSTR Months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  3126. "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
  3127. /***************************************************************************++
  3128. Routine Description:
  3129. Generates a date header string from a LARGE_INTEGER.
  3130. Arguments:
  3131. pBuffer: Buffer to store generated date string
  3132. systemTime: A 64-bit Time value to be converted
  3133. --***************************************************************************/
  3134. ULONG
  3135. UlpGenerateDateHeaderString(
  3136. OUT PUCHAR pBuffer,
  3137. IN LARGE_INTEGER systemTime
  3138. )
  3139. {
  3140. TIME_FIELDS timeFields;
  3141. int length;
  3142. PAGED_CODE();
  3143. RtlTimeToTimeFields( &systemTime, &timeFields );
  3144. length = _snprintf(
  3145. (char *) pBuffer,
  3146. DATE_HDR_LENGTH,
  3147. "%s, %02hd %s %04hd %02hd:%02hd:%02hd GMT",
  3148. Weekdays[timeFields.Weekday],
  3149. timeFields.Day,
  3150. Months[timeFields.Month - 1],
  3151. timeFields.Year,
  3152. timeFields.Hour,
  3153. timeFields.Minute,
  3154. timeFields.Second
  3155. );
  3156. return (ULONG)length;
  3157. } // UlpGenerateDateHeaderString
  3158. /***************************************************************************++
  3159. Routine Description:
  3160. Generates a date header and updates cached value if required.
  3161. Arguments:
  3162. pBuffer: Buffer to store generated date header.
  3163. pSystemTime: caller allocated buffer to receive the SystemTime equivalent
  3164. of the generated string time.
  3165. --***************************************************************************/
  3166. ULONG
  3167. UlGenerateDateHeader(
  3168. OUT PUCHAR pBuffer,
  3169. OUT PLARGE_INTEGER pSystemTime
  3170. )
  3171. {
  3172. LARGE_INTEGER CacheTime;
  3173. LONGLONG timediff;
  3174. PAGED_CODE();
  3175. //
  3176. // Get the current time.
  3177. //
  3178. KeQuerySystemTime( pSystemTime );
  3179. CacheTime.QuadPart = g_UlSystemTime.QuadPart;
  3180. //
  3181. // Check the difference between the current time, and
  3182. // the cached time. Note that timediff is signed.
  3183. //
  3184. timediff = pSystemTime->QuadPart - CacheTime.QuadPart;
  3185. if (timediff < ONE_SECOND)
  3186. {
  3187. //
  3188. // The entry hasn't gone stale yet. We can copy.
  3189. // Force a barrier around reading the string into memory.
  3190. //
  3191. UL_READMOSTLY_READ_BARRIER();
  3192. RtlCopyMemory(pBuffer, g_UlDateString, g_UlDateStringLength+1);
  3193. UL_READMOSTLY_READ_BARRIER();
  3194. //
  3195. // Inspect the global time value again in case it changed.
  3196. //
  3197. if (CacheTime.QuadPart == g_UlSystemTime.QuadPart) {
  3198. //
  3199. // Global value hasn't changed. We are all set.
  3200. //
  3201. pSystemTime->QuadPart = CacheTime.QuadPart;
  3202. return g_UlDateStringLength;
  3203. }
  3204. }
  3205. //
  3206. // The cached value is stale, or is/was being changed. We need to update
  3207. // or re-read it. Note that we could also spin trying to re-read and
  3208. // acquire the lock.
  3209. //
  3210. UlAcquirePushLockExclusive(&g_pUlNonpagedData->DateHeaderPushLock);
  3211. //
  3212. // Has someone else updated the time while we were blocked?
  3213. //
  3214. CacheTime.QuadPart = g_UlSystemTime.QuadPart;
  3215. timediff = pSystemTime->QuadPart - CacheTime.QuadPart;
  3216. if (timediff >= ONE_SECOND)
  3217. {
  3218. g_UlSystemTime.QuadPart = 0;
  3219. KeQuerySystemTime( pSystemTime );
  3220. UL_READMOSTLY_WRITE_BARRIER();
  3221. g_UlDateStringLength = UlpGenerateDateHeaderString(
  3222. g_UlDateString,
  3223. *pSystemTime
  3224. );
  3225. UL_READMOSTLY_WRITE_BARRIER();
  3226. g_UlSystemTime.QuadPart = pSystemTime->QuadPart;
  3227. }
  3228. else
  3229. {
  3230. // Capture the system time used to generate the buffer
  3231. pSystemTime->QuadPart = g_UlSystemTime.QuadPart;
  3232. }
  3233. //
  3234. // The time has been updated. Copy the new string into
  3235. // the caller's buffer.
  3236. //
  3237. RtlCopyMemory(
  3238. pBuffer,
  3239. g_UlDateString,
  3240. g_UlDateStringLength + 1
  3241. );
  3242. UlReleasePushLockExclusive(&g_pUlNonpagedData->DateHeaderPushLock);
  3243. return g_UlDateStringLength;
  3244. } // UlGenerateDateHeader
  3245. /***************************************************************************++
  3246. Routine Description:
  3247. Initializes the date cache.
  3248. --***************************************************************************/
  3249. NTSTATUS
  3250. UlInitializeDateCache( VOID )
  3251. {
  3252. LARGE_INTEGER now;
  3253. KeQuerySystemTime(&now);
  3254. g_UlDateStringLength = UlpGenerateDateHeaderString(g_UlDateString, now);
  3255. UlInitializePushLock(
  3256. &g_pUlNonpagedData->DateHeaderPushLock,
  3257. "DateHeaderPushLock",
  3258. 0,
  3259. UL_DATE_HEADER_PUSHLOCK_TAG
  3260. );
  3261. g_DateCacheInitialized = TRUE;
  3262. return STATUS_SUCCESS;
  3263. } // UlInitializeDateCache
  3264. /***************************************************************************++
  3265. Routine Description:
  3266. Terminates the date header cache.
  3267. --***************************************************************************/
  3268. VOID
  3269. UlTerminateDateCache( VOID )
  3270. {
  3271. if (g_DateCacheInitialized)
  3272. {
  3273. UlDeletePushLock(&g_pUlNonpagedData->DateHeaderPushLock);
  3274. }
  3275. }
  3276. /***************************************************************************++
  3277. Routine Description:
  3278. Figures out how big the fixed headers are. Fixed headers include the
  3279. status line, and any headers that don't have to be generated for
  3280. every request (such as Date and Connection).
  3281. The final CRLF separating headers from body is considered part of
  3282. the variable headers.
  3283. Arguments:
  3284. Version - HTTP Version of the request: 0.9, 1.0., 1.1
  3285. pUserResponse - the response containing the headers
  3286. pHeaderLength - result: the number of bytes in the fixed headers.
  3287. Return Value:
  3288. NTSTATUS - STATUS_SUCCESS or an error code (possibly from an exception)
  3289. --***************************************************************************/
  3290. NTSTATUS
  3291. UlComputeFixedHeaderSize(
  3292. IN HTTP_VERSION Version,
  3293. IN PHTTP_RESPONSE pUserResponse,
  3294. IN KPROCESSOR_MODE AccessMode,
  3295. OUT PULONG pHeaderLength
  3296. )
  3297. {
  3298. NTSTATUS Status = STATUS_SUCCESS;
  3299. ULONG HeaderLength = 0;
  3300. ULONG i;
  3301. PHTTP_UNKNOWN_HEADER pUnknownHeaders;
  3302. USHORT UnknownHeaderCount;
  3303. //
  3304. // Sanity check.
  3305. //
  3306. PAGED_CODE();
  3307. ASSERT(pHeaderLength != NULL);
  3308. if ((pUserResponse == NULL) || (HTTP_EQUAL_VERSION(Version, 0, 9)))
  3309. {
  3310. *pHeaderLength = 0;
  3311. return STATUS_SUCCESS;
  3312. }
  3313. //
  3314. // The pUserResponse is user-mode data and cannot be trusted.
  3315. // Hence the try/except.
  3316. //
  3317. __try
  3318. {
  3319. HeaderLength
  3320. += (VERSION_SIZE + // HTTP-Version
  3321. 1 + // SP
  3322. 3 + // Status-Code
  3323. 1 + // SP
  3324. pUserResponse->ReasonLength / sizeof(CHAR) +// Reason-Phrase
  3325. CRLF_SIZE // CRLF
  3326. );
  3327. //
  3328. // Loop through the known headers.
  3329. //
  3330. for (i = 0; i < HttpHeaderResponseMaximum; ++i)
  3331. {
  3332. USHORT RawValueLength
  3333. = pUserResponse->Headers.KnownHeaders[i].RawValueLength;
  3334. // skip some headers we'll generate
  3335. if ((i == HttpHeaderDate) ||
  3336. (i == HttpHeaderConnection) ||
  3337. (i == HttpHeaderServer)) {
  3338. continue;
  3339. }
  3340. if (RawValueLength > 0)
  3341. {
  3342. HeaderLength
  3343. += (g_ResponseHeaderMapTable[
  3344. g_ResponseHeaderMap[i]
  3345. ].HeaderLength + // Header-Name
  3346. 1 + // SP
  3347. RawValueLength / sizeof(CHAR) + // Header-Value
  3348. CRLF_SIZE // CRLF
  3349. );
  3350. }
  3351. }
  3352. //
  3353. // Handle the Server header
  3354. //
  3355. if ( UL_DISABLE_SERVER_HEADER_ALL != g_UlDisableServerHeader )
  3356. {
  3357. if ( g_UlDisableServerHeader == UL_DISABLE_SERVER_HEADER_DRIVER &&
  3358. (pUserResponse->Flags & HTTP_RESPONSE_FLAG_DRIVER) )
  3359. {
  3360. // skip generating server header on driver-created responses
  3361. }
  3362. else
  3363. {
  3364. BOOLEAN Suppress = FALSE;
  3365. USHORT RawValueLength =
  3366. pUserResponse->Headers.KnownHeaders
  3367. [HttpHeaderServer].RawValueLength;
  3368. PCSTR pRawValue =
  3369. pUserResponse->Headers.KnownHeaders
  3370. [HttpHeaderServer].pRawValue;
  3371. // check to see if app wishes to suppress Server: header
  3372. if ( (0 == RawValueLength) && pRawValue )
  3373. {
  3374. // Probe pRawValue and see if it's a single null char
  3375. UlProbeAnsiString(
  3376. pRawValue,
  3377. sizeof(UCHAR),
  3378. AccessMode
  3379. );
  3380. if ( '\0' == *pRawValue )
  3381. {
  3382. Suppress = TRUE;
  3383. }
  3384. }
  3385. // If user specifies a server header, append it to
  3386. // the default Server header
  3387. if ( !Suppress )
  3388. {
  3389. HeaderLength += (g_ResponseHeaderMapTable[
  3390. g_ResponseHeaderMap[HttpHeaderServer]
  3391. ].HeaderLength + // Header-Name
  3392. 1 + // SP
  3393. DEFAULT_SERVER_HDR_LENGTH + // Header-Value
  3394. CRLF_SIZE // CRLF
  3395. );
  3396. if (RawValueLength)
  3397. {
  3398. HeaderLength += (1 + // SP
  3399. RawValueLength // User's Data to append
  3400. );
  3401. }
  3402. }
  3403. }
  3404. }
  3405. //
  3406. // And the unknown headers (this might throw an exception).
  3407. //
  3408. pUnknownHeaders = pUserResponse->Headers.pUnknownHeaders;
  3409. if (pUnknownHeaders != NULL)
  3410. {
  3411. UnknownHeaderCount = pUserResponse->Headers.UnknownHeaderCount;
  3412. if (UnknownHeaderCount >= UL_MAX_CHUNKS)
  3413. {
  3414. ExRaiseStatus( STATUS_INVALID_PARAMETER );
  3415. }
  3416. UlProbeForRead(
  3417. pUnknownHeaders,
  3418. sizeof(HTTP_UNKNOWN_HEADER) * UnknownHeaderCount,
  3419. sizeof(PVOID),
  3420. AccessMode
  3421. );
  3422. for (i = 0 ; i < UnknownHeaderCount; ++i)
  3423. {
  3424. USHORT Length = pUnknownHeaders[i].NameLength;
  3425. if (Length > 0)
  3426. {
  3427. HeaderLength += (Length / sizeof(CHAR) + // Header-Name
  3428. 1 + // ':'
  3429. 1 + // SP
  3430. pUnknownHeaders[i].RawValueLength /
  3431. sizeof(CHAR) + // Header-Value
  3432. CRLF_SIZE // CRLF
  3433. );
  3434. }
  3435. }
  3436. }
  3437. }
  3438. __except( UL_EXCEPTION_FILTER() )
  3439. {
  3440. Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode());
  3441. HeaderLength = 0;
  3442. }
  3443. *pHeaderLength = HeaderLength;
  3444. return Status;
  3445. } // UlComputeFixedHeaderSize
  3446. /***************************************************************************++
  3447. Routine Description:
  3448. Figures out how big the maximum default variable headers are. Variable
  3449. headers include Date, Content and Connection.
  3450. The final CRLF separating headers from body is considered part of
  3451. the variable headers.
  3452. Arguments:
  3453. Return Values:
  3454. The maximum number of bytes in the variable headers.
  3455. --***************************************************************************/
  3456. ULONG
  3457. UlComputeMaxVariableHeaderSize( VOID )
  3458. {
  3459. ULONG Length = 0;
  3460. PHEADER_MAP_ENTRY pEntry;
  3461. PAGED_CODE();
  3462. //
  3463. // Date: header
  3464. //
  3465. pEntry = &(g_ResponseHeaderMapTable[g_ResponseHeaderMap[HttpHeaderDate]]);
  3466. Length += pEntry->HeaderLength; // header name
  3467. Length += 1; // SP
  3468. Length += DATE_HDR_LENGTH; // header value
  3469. Length += CRLF_SIZE; // CRLF
  3470. //
  3471. // Connection: header
  3472. //
  3473. pEntry = &(g_ResponseHeaderMapTable[g_ResponseHeaderMap[HttpHeaderConnection]]);
  3474. Length += pEntry->HeaderLength;
  3475. Length += 1;
  3476. Length += MAX(CONN_CLOSE_HDR_LENGTH, CONN_KEEPALIVE_HDR_LENGTH);
  3477. Length += CRLF_SIZE;
  3478. //
  3479. // Content-Length: header
  3480. //
  3481. pEntry = &(g_ResponseHeaderMapTable[g_ResponseHeaderMap[HttpHeaderContentLength]]);
  3482. Length += pEntry->HeaderLength;
  3483. Length += 1;
  3484. Length += MAX_ULONGLONG_STR;
  3485. Length += CRLF_SIZE;
  3486. //
  3487. // final CRLF
  3488. //
  3489. Length += CRLF_SIZE;
  3490. return Length;
  3491. } // UlComputeMaxVariableHeaderSize
  3492. /***************************************************************************++
  3493. Routine Description:
  3494. Generates the variable part of the header, namely, Date:, Connection:,
  3495. Content-Length:, and final CRLF.
  3496. Relies on the caller having correctly allocated enough memory to hold
  3497. these variable headers, which should have been done in
  3498. UlComputeMaxVariableHeaderSize().
  3499. Arguments:
  3500. ConnHeader - Supplies the type of Connection: header to generate.
  3501. pContentLengthString - Supplies a header value for an optional
  3502. Content-Length header. If this is the empty string "", then no
  3503. Content-Length header is generated.
  3504. ContentLengthStringLength - Supplies the length of the above string.
  3505. pBuffer - Supplies the target buffer for the generated headers.
  3506. pBytesCopied - Receives the number of header bytes generated.
  3507. pDateTime - Receives the system time equivalent of the Date: header
  3508. --***************************************************************************/
  3509. VOID
  3510. UlGenerateVariableHeaders(
  3511. IN UL_CONN_HDR ConnHeader,
  3512. IN BOOLEAN GenerateDate,
  3513. IN PUCHAR pContentLengthString,
  3514. IN ULONG ContentLengthStringLength,
  3515. OUT PUCHAR pBuffer,
  3516. OUT PULONG pBytesCopied,
  3517. OUT PLARGE_INTEGER pDateTime
  3518. )
  3519. {
  3520. PHEADER_MAP_ENTRY pEntry;
  3521. PUCHAR pStartBuffer;
  3522. PUCHAR pCloseHeaderValue;
  3523. ULONG CloseHeaderValueLength;
  3524. ULONG BytesCopied;
  3525. PAGED_CODE();
  3526. ASSERT( pContentLengthString != NULL );
  3527. ASSERT( pBuffer );
  3528. ASSERT( pBytesCopied );
  3529. ASSERT( pDateTime );
  3530. pStartBuffer = pBuffer;
  3531. //
  3532. // generate Date: header
  3533. //
  3534. if (GenerateDate)
  3535. {
  3536. pEntry = &(g_ResponseHeaderMapTable[g_ResponseHeaderMap[HttpHeaderDate]]);
  3537. RtlCopyMemory(
  3538. pBuffer,
  3539. pEntry->MixedCaseHeader,
  3540. pEntry->HeaderLength
  3541. );
  3542. pBuffer += pEntry->HeaderLength;
  3543. pBuffer[0] = SP;
  3544. pBuffer += 1;
  3545. BytesCopied = UlGenerateDateHeader( pBuffer, pDateTime );
  3546. pBuffer += BytesCopied;
  3547. ((UNALIGNED64 USHORT *)pBuffer)[0] = CRLF;
  3548. pBuffer += sizeof(USHORT);
  3549. }
  3550. else
  3551. {
  3552. pDateTime->QuadPart = (LONGLONG) 0L;
  3553. }
  3554. //
  3555. // generate Connection: header
  3556. //
  3557. switch (ConnHeader)
  3558. {
  3559. case ConnHdrNone:
  3560. pCloseHeaderValue = NULL;
  3561. CloseHeaderValueLength = 0;
  3562. break;
  3563. case ConnHdrClose:
  3564. pCloseHeaderValue = (PUCHAR) CONN_CLOSE_HDR;
  3565. CloseHeaderValueLength = CONN_CLOSE_HDR_LENGTH;
  3566. break;
  3567. case ConnHdrKeepAlive:
  3568. pCloseHeaderValue = (PUCHAR) CONN_KEEPALIVE_HDR;
  3569. CloseHeaderValueLength = CONN_KEEPALIVE_HDR_LENGTH;
  3570. break;
  3571. default:
  3572. ASSERT(ConnHeader < ConnHdrMax);
  3573. pCloseHeaderValue = NULL;
  3574. CloseHeaderValueLength = 0;
  3575. break;
  3576. }
  3577. if (pCloseHeaderValue != NULL)
  3578. {
  3579. pEntry = &(g_ResponseHeaderMapTable[g_ResponseHeaderMap[HttpHeaderConnection]]);
  3580. RtlCopyMemory(
  3581. pBuffer,
  3582. pEntry->MixedCaseHeader,
  3583. pEntry->HeaderLength
  3584. );
  3585. pBuffer += pEntry->HeaderLength;
  3586. pBuffer[0] = SP;
  3587. pBuffer += 1;
  3588. RtlCopyMemory(
  3589. pBuffer,
  3590. pCloseHeaderValue,
  3591. CloseHeaderValueLength
  3592. );
  3593. pBuffer += CloseHeaderValueLength;
  3594. ((UNALIGNED64 USHORT *)pBuffer)[0] = CRLF;
  3595. pBuffer += sizeof(USHORT);
  3596. }
  3597. if (pContentLengthString[0] != '\0')
  3598. {
  3599. ASSERT( ContentLengthStringLength > 0 );
  3600. pEntry = &(g_ResponseHeaderMapTable[g_ResponseHeaderMap[HttpHeaderContentLength]]);
  3601. RtlCopyMemory(
  3602. pBuffer,
  3603. pEntry->MixedCaseHeader,
  3604. pEntry->HeaderLength
  3605. );
  3606. pBuffer += pEntry->HeaderLength;
  3607. pBuffer[0] = SP;
  3608. pBuffer += 1;
  3609. RtlCopyMemory(
  3610. pBuffer,
  3611. pContentLengthString,
  3612. ContentLengthStringLength
  3613. );
  3614. pBuffer += ContentLengthStringLength;
  3615. ((UNALIGNED64 USHORT *)pBuffer)[0] = CRLF;
  3616. pBuffer += sizeof(USHORT);
  3617. }
  3618. else
  3619. {
  3620. ASSERT( ContentLengthStringLength == 0 );
  3621. }
  3622. //
  3623. // generate final CRLF
  3624. //
  3625. ((UNALIGNED64 USHORT *)pBuffer)[0] = CRLF;
  3626. pBuffer += sizeof(USHORT);
  3627. //
  3628. // make sure we didn't use too much
  3629. //
  3630. BytesCopied = DIFF(pBuffer - pStartBuffer);
  3631. *pBytesCopied = BytesCopied;
  3632. } // UlGenerateVariableHeaders
  3633. /*++
  3634. Routine Description:
  3635. Append a header value to an existing HTTP_HEADER entry, allocating
  3636. a buffer and copying the existing buffer.
  3637. Arguments:
  3638. pHttpHeader - Pointer to HTTP_HEADER structure to append to.
  3639. pHeader - Pointer header to be appended.
  3640. HeaderLength - Length of data pointed to by pHeader.
  3641. Return Value:
  3642. TRUE if we succeed, FALSE otherwise.
  3643. --*/
  3644. NTSTATUS
  3645. UlAppendHeaderValue(
  3646. PUL_INTERNAL_REQUEST pRequest,
  3647. PUL_HTTP_HEADER pHttpHeader,
  3648. PUCHAR pHeader,
  3649. USHORT HeaderLength
  3650. )
  3651. {
  3652. PUCHAR pNewHeader, pOldHeader;
  3653. USHORT OldHeaderLength;
  3654. PAGED_CODE();
  3655. OldHeaderLength = pHttpHeader->HeaderLength;
  3656. pNewHeader = UL_ALLOCATE_ARRAY(
  3657. NonPagedPool,
  3658. UCHAR,
  3659. OldHeaderLength + HeaderLength
  3660. + STRLEN_LIT(", ") + sizeof(CHAR),
  3661. HEADER_VALUE_POOL_TAG
  3662. );
  3663. if (pNewHeader == NULL)
  3664. {
  3665. // Had a failure.
  3666. return STATUS_NO_MEMORY;
  3667. }
  3668. //
  3669. // Copy the old data into the new header.
  3670. //
  3671. RtlCopyMemory(pNewHeader, pHttpHeader->pHeader, OldHeaderLength);
  3672. // And copy in the new data as well, seperated by a comma.
  3673. //
  3674. *(pNewHeader + OldHeaderLength) = ',';
  3675. *(pNewHeader + OldHeaderLength + 1) = ' ';
  3676. OldHeaderLength += STRLEN_LIT(", ");
  3677. RtlCopyMemory( pNewHeader + OldHeaderLength, pHeader, HeaderLength);
  3678. // Now replace the existing header.
  3679. //
  3680. pOldHeader = pHttpHeader->pHeader;
  3681. pHttpHeader->HeaderLength = OldHeaderLength + HeaderLength;
  3682. pHttpHeader->pHeader = pNewHeader;
  3683. // If the old header was our buffer, free it too.
  3684. //
  3685. if (pHttpHeader->OurBuffer)
  3686. {
  3687. UL_FREE_POOL( pOldHeader, HEADER_VALUE_POOL_TAG );
  3688. }
  3689. pHttpHeader->OurBuffer = 1;
  3690. //
  3691. // null terminate it
  3692. //
  3693. pHttpHeader->pHeader[pHttpHeader->HeaderLength] = ANSI_NULL;
  3694. pRequest->HeadersAppended = TRUE;
  3695. return STATUS_SUCCESS;
  3696. }
  3697. /*++
  3698. Routine Description:
  3699. The default routine for handling headers. Used when we don't want to
  3700. do anything with the header but find out if we have the whole thing
  3701. and save a pointer to it if we do. This does not allow multiple header
  3702. values to exist for this header. Use UlMultipleHeaderHandler for
  3703. handling that by appending the values together (CSV).
  3704. Arguments:
  3705. pHttpConn - HTTP connection on which this header was received.
  3706. pHeader - Pointer to the header value.
  3707. HeaderLength - Length of data pointed to by pHeader.
  3708. HeaderID - ID of the header.
  3709. Return Value:
  3710. The length of the header value, or 0 if it's not terminated.
  3711. --*/
  3712. NTSTATUS
  3713. UlSingleHeaderHandler(
  3714. IN PUL_INTERNAL_REQUEST pRequest,
  3715. IN PUCHAR pHeader,
  3716. IN USHORT HeaderLength,
  3717. IN HTTP_HEADER_ID HeaderID,
  3718. OUT PULONG pBytesTaken
  3719. )
  3720. {
  3721. NTSTATUS Status = STATUS_SUCCESS;
  3722. ULONG BytesTaken;
  3723. USHORT HeaderValueLength;
  3724. PAGED_CODE();
  3725. // Find the end of the header value
  3726. //
  3727. Status = FindRequestHeaderEnd(pRequest, pHeader, HeaderLength, &BytesTaken);
  3728. if (!NT_SUCCESS(Status))
  3729. goto end;
  3730. if (BytesTaken > 0)
  3731. {
  3732. ASSERT(BytesTaken <= ANSI_STRING_MAX_CHAR_LEN);
  3733. // Strip off the trailing CRLF from the header value length
  3734. HeaderValueLength = (USHORT) (BytesTaken - CRLF_SIZE);
  3735. // skip any preceding LWS.
  3736. while (HeaderValueLength > 0 && IS_HTTP_LWS(*pHeader))
  3737. {
  3738. pHeader++;
  3739. HeaderValueLength--;
  3740. }
  3741. // remove any trailing LWS.
  3742. while (HeaderValueLength > 0 && IS_HTTP_LWS(pHeader[HeaderValueLength-1]))
  3743. {
  3744. HeaderValueLength--;
  3745. }
  3746. // do we have an existing header?
  3747. //
  3748. if (pRequest->HeaderValid[HeaderID] == FALSE)
  3749. {
  3750. // No existing header, just save this pointer for now.
  3751. //
  3752. pRequest->HeaderIndex[pRequest->HeaderCount] = (UCHAR)HeaderID;
  3753. pRequest->HeaderCount++;
  3754. pRequest->HeaderValid[HeaderID] = TRUE;
  3755. pRequest->Headers[HeaderID].HeaderLength = HeaderValueLength;
  3756. pRequest->Headers[HeaderID].pHeader = pHeader;
  3757. pRequest->Headers[HeaderID].OurBuffer = 0;
  3758. //
  3759. // null terminate it. we have space as all headers end with CRLF.
  3760. // we are over-writing the CR
  3761. //
  3762. pHeader[HeaderValueLength] = ANSI_NULL;
  3763. //
  3764. // make space for a terminator
  3765. //
  3766. pRequest->TotalRequestSize += (HeaderValueLength + 1) * sizeof(CHAR);
  3767. }
  3768. else
  3769. {
  3770. //
  3771. // uh oh. Have an existing header; ignore duplicate only if this
  3772. // one exactly matches the first one. See RAID: 466626
  3773. //
  3774. UlTrace(PARSER, (
  3775. "http!UlSingleHeaderHandler(pRequest = %p, pHeader = %p)\n"
  3776. " WARNING: duplicate headers found.\n",
  3777. pRequest,
  3778. pHeader
  3779. ));
  3780. //
  3781. // null terminate it. we have space as all headers end with CRLF.
  3782. // we are over-writing the CR
  3783. //
  3784. pHeader[HeaderValueLength] = ANSI_NULL;
  3785. //
  3786. // If they aren't the same length or don't EXACTLY compare, fail
  3787. // the request.
  3788. //
  3789. if ( (pRequest->Headers[HeaderID].HeaderLength != HeaderValueLength)
  3790. || (HeaderValueLength != RtlCompareMemory(
  3791. pRequest->Headers[HeaderID].pHeader,
  3792. pHeader,
  3793. HeaderValueLength)) )
  3794. {
  3795. UlTraceError(PARSER, (
  3796. "http!UlSingleHeaderHandler(pRequest = %p, pHeader = %p)\n"
  3797. " ERROR: mismatching duplicate headers found.\n",
  3798. pRequest,
  3799. pHeader
  3800. ));
  3801. UlSetErrorCode(pRequest, UlErrorHeader, NULL);
  3802. Status = STATUS_INVALID_DEVICE_REQUEST;
  3803. goto end;
  3804. }
  3805. }
  3806. }
  3807. // Success!
  3808. //
  3809. *pBytesTaken = BytesTaken;
  3810. end:
  3811. return Status;
  3812. } // UlSingleHeaderHandler
  3813. /*++
  3814. Routine Description:
  3815. The default routine for handling headers. Used when we don't want to
  3816. do anything with the header but find out if we have the whole thing
  3817. and save a pointer to it if we do. This function handles multiple
  3818. headers with the same name, and appends the values together separated
  3819. by commas.
  3820. Arguments:
  3821. pHttpConn - HTTP connection on which this header was received.
  3822. pHeader - Pointer to the header value.
  3823. HeaderLength - Length of data pointed to by pHeader.
  3824. HeaderID - ID of the header.
  3825. Return Value:
  3826. The length of the header value, or 0 if it's not terminated.
  3827. --*/
  3828. NTSTATUS
  3829. UlMultipleHeaderHandler(
  3830. IN PUL_INTERNAL_REQUEST pRequest,
  3831. IN PUCHAR pHeader,
  3832. IN USHORT HeaderLength,
  3833. IN HTTP_HEADER_ID HeaderID,
  3834. OUT PULONG pBytesTaken
  3835. )
  3836. {
  3837. NTSTATUS Status = STATUS_SUCCESS;
  3838. ULONG BytesTaken;
  3839. USHORT HeaderValueLength;
  3840. PAGED_CODE();
  3841. // Find the end of the header value
  3842. //
  3843. Status = FindRequestHeaderEnd(pRequest, pHeader, HeaderLength, &BytesTaken);
  3844. if (!NT_SUCCESS(Status))
  3845. goto end;
  3846. if (BytesTaken > 0)
  3847. {
  3848. ASSERT(BytesTaken <= ANSI_STRING_MAX_CHAR_LEN);
  3849. // Strip off the trailing CRLF from the header value length
  3850. //
  3851. HeaderValueLength = (USHORT) (BytesTaken - CRLF_SIZE);
  3852. //
  3853. // skip any preceding LWS.
  3854. //
  3855. while (HeaderValueLength > 0 && IS_HTTP_LWS(*pHeader))
  3856. {
  3857. pHeader++;
  3858. HeaderValueLength--;
  3859. }
  3860. //
  3861. // remove any trailing LWS.
  3862. //
  3863. while (HeaderValueLength > 0 && IS_HTTP_LWS(pHeader[HeaderValueLength-1]))
  3864. {
  3865. HeaderValueLength--;
  3866. }
  3867. // do we have an existing header?
  3868. //
  3869. if (pRequest->HeaderValid[HeaderID] == FALSE)
  3870. {
  3871. // No existing header, just save this pointer for now.
  3872. //
  3873. pRequest->HeaderIndex[pRequest->HeaderCount] = (UCHAR)HeaderID;
  3874. pRequest->HeaderCount++;
  3875. pRequest->HeaderValid[HeaderID] = TRUE;
  3876. pRequest->Headers[HeaderID].HeaderLength = HeaderValueLength;
  3877. pRequest->Headers[HeaderID].pHeader = pHeader;
  3878. pRequest->Headers[HeaderID].OurBuffer = 0;
  3879. //
  3880. // null terminate it. we have space as all headers end with CRLF.
  3881. // we are over-writing the CR
  3882. //
  3883. pHeader[HeaderValueLength] = ANSI_NULL;
  3884. //
  3885. // make space for a terminator
  3886. //
  3887. pRequest->TotalRequestSize += (HeaderValueLength + 1) * sizeof(CHAR);
  3888. }
  3889. else
  3890. {
  3891. USHORT OldHeaderLength;
  3892. // Have an existing header, append this one.
  3893. OldHeaderLength = pRequest->Headers[HeaderID].HeaderLength;
  3894. Status = UlAppendHeaderValue(
  3895. pRequest,
  3896. &pRequest->Headers[HeaderID],
  3897. pHeader,
  3898. HeaderValueLength
  3899. );
  3900. if (NT_SUCCESS(Status) == FALSE)
  3901. goto end;
  3902. //
  3903. // Update total request length for the amount we just added.
  3904. // space for the terminator is already in there
  3905. //
  3906. pRequest->TotalRequestSize +=
  3907. (pRequest->Headers[HeaderID].HeaderLength - OldHeaderLength) *
  3908. sizeof(CHAR);
  3909. }
  3910. }
  3911. // Success!
  3912. //
  3913. *pBytesTaken = BytesTaken;
  3914. end:
  3915. return Status;
  3916. } // UlMultipleHeaderHandler
  3917. /*++
  3918. Routine Description:
  3919. The routine for handling Accept headers.
  3920. Arguments:
  3921. pHttpConn - HTTP connection on which this header was received.
  3922. pHeader - Pointer to the header value.
  3923. HeaderLength - Length of data pointed to by pHeader.
  3924. HeaderID - ID of the header.
  3925. Return Value:
  3926. The length of the header value, or 0 if it's not terminated.
  3927. Wildcard bit is set in the request if found
  3928. --*/
  3929. NTSTATUS
  3930. UlAcceptHeaderHandler(
  3931. IN PUL_INTERNAL_REQUEST pRequest,
  3932. IN PUCHAR pHeader,
  3933. IN USHORT HeaderLength,
  3934. IN HTTP_HEADER_ID HeaderID,
  3935. OUT PULONG pBytesTaken
  3936. )
  3937. {
  3938. NTSTATUS Status = STATUS_SUCCESS;
  3939. ULONG BytesTaken;
  3940. USHORT HeaderValueLength;
  3941. PAGED_CODE();
  3942. // Find the end of the header value
  3943. //
  3944. Status = FindRequestHeaderEnd(pRequest, pHeader, HeaderLength, &BytesTaken);
  3945. if (!NT_SUCCESS(Status))
  3946. goto end;
  3947. if (BytesTaken > 0)
  3948. {
  3949. ASSERT(BytesTaken <= ANSI_STRING_MAX_CHAR_LEN);
  3950. // Strip off the trailing CRLF from the header value length
  3951. //
  3952. HeaderValueLength = (USHORT) (BytesTaken - CRLF_SIZE);
  3953. //
  3954. // skip any preceding LWS.
  3955. //
  3956. while (HeaderValueLength > 0 && IS_HTTP_LWS(*pHeader))
  3957. {
  3958. pHeader++;
  3959. HeaderValueLength--;
  3960. }
  3961. //
  3962. // remove any trailing LWS.
  3963. //
  3964. while (HeaderValueLength > 0 && IS_HTTP_LWS(pHeader[HeaderValueLength-1]))
  3965. {
  3966. HeaderValueLength--;
  3967. }
  3968. // do we have an existing header?
  3969. //
  3970. if (pRequest->HeaderValid[HeaderID] == FALSE)
  3971. {
  3972. // No existing header, just save this pointer for now.
  3973. //
  3974. pRequest->HeaderIndex[pRequest->HeaderCount] = (UCHAR)HeaderID;
  3975. pRequest->HeaderCount++;
  3976. pRequest->HeaderValid[HeaderID] = TRUE;
  3977. pRequest->Headers[HeaderID].HeaderLength = HeaderValueLength;
  3978. pRequest->Headers[HeaderID].pHeader = pHeader;
  3979. pRequest->Headers[HeaderID].OurBuffer = 0;
  3980. //
  3981. // null terminate it. we have space as all headers end with CRLF.
  3982. // we are over-writing the CR
  3983. //
  3984. pHeader[HeaderValueLength] = ANSI_NULL;
  3985. //
  3986. // make space for a terminator
  3987. //
  3988. pRequest->TotalRequestSize += (HeaderValueLength + 1) * sizeof(CHAR);
  3989. if (HeaderValueLength > WILDCARD_SIZE)
  3990. {
  3991. //
  3992. // for the fast path, we'll check only */* at the end
  3993. //
  3994. if (
  3995. (*(UNALIGNED64 ULONG *) (&pHeader[HeaderValueLength - WILDCARD_SIZE]) == WILDCARD_SPACE) ||
  3996. (*(UNALIGNED64 ULONG *) (&pHeader[HeaderValueLength - WILDCARD_SIZE]) == WILDCARD_COMMA)
  3997. )
  3998. {
  3999. pRequest->AcceptWildcard = TRUE;
  4000. }
  4001. }
  4002. }
  4003. else
  4004. {
  4005. ULONG OldHeaderLength;
  4006. // Have an existing header, append this one.
  4007. OldHeaderLength = pRequest->Headers[HeaderID].HeaderLength;
  4008. Status = UlAppendHeaderValue(
  4009. pRequest,
  4010. &pRequest->Headers[HeaderID],
  4011. pHeader,
  4012. HeaderValueLength
  4013. );
  4014. if (NT_SUCCESS(Status) == FALSE)
  4015. goto end;
  4016. //
  4017. // Update total request length for the amount we just added.
  4018. // space for the terminator is already in there
  4019. //
  4020. pRequest->TotalRequestSize +=
  4021. (pRequest->Headers[HeaderID].HeaderLength - OldHeaderLength) *
  4022. sizeof(CHAR);
  4023. }
  4024. }
  4025. // Success!
  4026. //
  4027. *pBytesTaken = BytesTaken;
  4028. end:
  4029. return Status;
  4030. } // UlAcceptHeaderHandler
  4031. /*++
  4032. Routine Description:
  4033. The default routine for handling Host headers. Used when we don't want to
  4034. do anything with the Host header but find out if we have the whole thing
  4035. and save a pointer to it if we do. this does not allow multiple Host header
  4036. values to exist for this header. use UlMultipleHeaderHandler for
  4037. handling that by appending the values together (CSV) .
  4038. Arguments:
  4039. pHttpConn - HTTP connection on which this header was received.
  4040. pHeader - Pointer to the header value.
  4041. HeaderLength - Length of data pointed to by pHeader.
  4042. HeaderID - ID of the header.
  4043. Return Value:
  4044. The length of the header value, or 0 if it's not terminated.
  4045. --*/
  4046. NTSTATUS
  4047. UlHostHeaderHandler(
  4048. IN PUL_INTERNAL_REQUEST pRequest,
  4049. IN PUCHAR pHeader,
  4050. IN USHORT HeaderLength,
  4051. IN HTTP_HEADER_ID HeaderID,
  4052. OUT PULONG pBytesTaken
  4053. )
  4054. {
  4055. NTSTATUS Status = STATUS_SUCCESS;
  4056. ULONG BytesTaken;
  4057. USHORT HeaderValueLength;
  4058. SHORT AddressType;
  4059. PAGED_CODE();
  4060. // Find the end of the header value
  4061. Status = FindRequestHeaderEnd(pRequest, pHeader, HeaderLength, &BytesTaken);
  4062. if (!NT_SUCCESS(Status))
  4063. goto end;
  4064. // Non-zero => Found the CRLF that terminates the header
  4065. if (BytesTaken > 0)
  4066. {
  4067. ASSERT(BytesTaken <= ANSI_STRING_MAX_CHAR_LEN);
  4068. // Strip off the trailing CRLF from the header value length
  4069. HeaderValueLength = (USHORT) (BytesTaken - CRLF_SIZE);
  4070. // skip any preceding LWS.
  4071. while (HeaderValueLength > 0 && IS_HTTP_LWS(*pHeader))
  4072. {
  4073. pHeader++;
  4074. HeaderValueLength--;
  4075. }
  4076. // remove any trailing LWS.
  4077. while (HeaderValueLength > 0 && IS_HTTP_LWS(pHeader[HeaderValueLength-1]))
  4078. {
  4079. HeaderValueLength--;
  4080. }
  4081. // do we have an existing header?
  4082. if (pRequest->HeaderValid[HeaderID] == FALSE)
  4083. {
  4084. // No existing header, just save this pointer for now.
  4085. pRequest->HeaderIndex[pRequest->HeaderCount] = (UCHAR)HeaderID;
  4086. pRequest->HeaderCount++;
  4087. pRequest->HeaderValid[HeaderID] = TRUE;
  4088. pRequest->Headers[HeaderID].HeaderLength = HeaderValueLength;
  4089. pRequest->Headers[HeaderID].pHeader = pHeader;
  4090. pRequest->Headers[HeaderID].OurBuffer = 0;
  4091. //
  4092. // null terminate it. we have space as all headers end with CRLF.
  4093. // we are over-writing the CR
  4094. //
  4095. pHeader[HeaderValueLength] = ANSI_NULL;
  4096. //
  4097. // make space for a terminator
  4098. //
  4099. pRequest->TotalRequestSize += (HeaderValueLength + 1) * sizeof(CHAR);
  4100. //
  4101. // Now validate that the Host header has a well-formed value
  4102. //
  4103. Status = HttpValidateHostname(
  4104. &g_UrlC14nConfig,
  4105. pHeader,
  4106. HeaderValueLength,
  4107. Hostname_HostHeader,
  4108. &AddressType
  4109. );
  4110. if (!NT_SUCCESS(Status))
  4111. {
  4112. UlSetErrorCode(pRequest, UlErrorHost, NULL);
  4113. goto end;
  4114. }
  4115. }
  4116. else
  4117. {
  4118. //
  4119. // uh oh. Have an existing Host header, fail the request.
  4120. //
  4121. UlTraceError(PARSER, (
  4122. "ul!UlHostHeaderHandler(pRequest = %p, pHeader = %p)\n"
  4123. " ERROR: multiple headers not allowed.\n",
  4124. pRequest,
  4125. pHeader
  4126. ));
  4127. UlSetErrorCode(pRequest, UlErrorHeader, NULL);
  4128. Status = STATUS_INVALID_DEVICE_REQUEST;
  4129. goto end;
  4130. }
  4131. }
  4132. // Success!
  4133. //
  4134. *pBytesTaken = BytesTaken;
  4135. end:
  4136. return Status;
  4137. } // UlHostHeaderHandler
  4138. /***************************************************************************++
  4139. Routine Description:
  4140. Checks the request to see if it has any of the following headers:
  4141. If-Modified-Since:
  4142. If-Match:
  4143. If-None-Match:
  4144. If so, we see if we can skip the sending of the full item. If we can skip,
  4145. we send back the apropriate response of either 304 (not modified) or
  4146. set the parser state to send back a 412 (precondition not met).
  4147. Arguments:
  4148. pRequest - The request to check
  4149. pUriCacheEntry - The cache entry being requested
  4150. Returns:
  4151. 0 Send cannot be skipped; continue with sending the cache entry.
  4152. 304 Send can be skipped. 304 response sent. NOTE: pRequest may be
  4153. invalid on return.
  4154. 400 Send can be skipped. Caller must set ParseErrorState w/ErrorCode
  4155. set to UlError
  4156. 412 Send can be skipped. pRequest->ParseState set to ParseErrorState with
  4157. pRequest->ErrorCode set to UlErrorPreconditionFailed (412)
  4158. --***************************************************************************/
  4159. ULONG
  4160. UlCheckCacheControlHeaders(
  4161. IN PUL_INTERNAL_REQUEST pRequest,
  4162. IN PUL_URI_CACHE_ENTRY pUriCacheEntry,
  4163. IN BOOLEAN ResumeParsing
  4164. )
  4165. {
  4166. ULONG RetStatus = 0; // Assume can't skip.
  4167. BOOLEAN fIfNoneMatchPassed = TRUE;
  4168. BOOLEAN fSkipIfModifiedSince = FALSE;
  4169. LARGE_INTEGER liModifiedSince;
  4170. LARGE_INTEGER liUnmodifiedSince;
  4171. LARGE_INTEGER liNow;
  4172. ULONG BytesSent = 0;
  4173. FIND_ETAG_STATUS EtagStatus;
  4174. ASSERT( UL_IS_VALID_INTERNAL_REQUEST(pRequest) );
  4175. ASSERT( IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry) );
  4176. //
  4177. // 1. Check If-Match
  4178. //
  4179. if ( pRequest->HeaderValid[HttpHeaderIfMatch] )
  4180. {
  4181. EtagStatus = FindInETagList( pUriCacheEntry->pETag,
  4182. pRequest->Headers[HttpHeaderIfMatch].pHeader,
  4183. FALSE);
  4184. switch( EtagStatus )
  4185. {
  4186. case ETAG_NOT_FOUND:
  4187. // Match failed.
  4188. goto PreconditionFailed;
  4189. case ETAG_PARSE_ERROR:
  4190. goto ParseError;
  4191. default:
  4192. break;
  4193. }
  4194. }
  4195. //
  4196. // 2. Check If-None-Match
  4197. //
  4198. if ( pRequest->HeaderValid[HttpHeaderIfNoneMatch] )
  4199. {
  4200. EtagStatus = FindInETagList( pUriCacheEntry->pETag,
  4201. pRequest->Headers[HttpHeaderIfNoneMatch].pHeader,
  4202. TRUE);
  4203. switch( EtagStatus )
  4204. {
  4205. case ETAG_FOUND:
  4206. // ETag found on list.
  4207. fIfNoneMatchPassed = FALSE;
  4208. break;
  4209. case ETAG_NOT_FOUND:
  4210. //
  4211. // Header present and ETag not found on list. This modifies
  4212. // the semantic of the If-Modified-Since header; Namely,
  4213. // If-None-Match takes precidence over If-Modified-Since.
  4214. //
  4215. fSkipIfModifiedSince = TRUE;
  4216. break;
  4217. case ETAG_PARSE_ERROR:
  4218. goto ParseError;
  4219. }
  4220. }
  4221. //
  4222. // 3. Check If-Modified-Since
  4223. //
  4224. if ( !fSkipIfModifiedSince &&
  4225. pRequest->HeaderValid[HttpHeaderIfModifiedSince] )
  4226. {
  4227. if ( StringTimeToSystemTime(
  4228. (PCSTR) pRequest->Headers[HttpHeaderIfModifiedSince].pHeader,
  4229. pRequest->Headers[HttpHeaderIfModifiedSince].HeaderLength,
  4230. &liModifiedSince) )
  4231. {
  4232. //
  4233. // If the cache entry was created before the
  4234. // time specified in the If-Modified-Since header, we
  4235. // can return a 304 (Not Modified) status.
  4236. //
  4237. if ( pUriCacheEntry->CreationTime.QuadPart <= liModifiedSince.QuadPart )
  4238. {
  4239. //
  4240. // Check if the time specified in the request is
  4241. // greater than the current time (i.e., Invalid). If it is,
  4242. // ignore the If-Modified-Since header.
  4243. //
  4244. KeQuerySystemTime(&liNow);
  4245. if ( liModifiedSince.QuadPart < liNow.QuadPart )
  4246. {
  4247. // Valid time.
  4248. goto NotModified;
  4249. }
  4250. }
  4251. }
  4252. else
  4253. {
  4254. //
  4255. // if converting the If-Modified-Since header failed, we
  4256. // need to report the parse failure.
  4257. //
  4258. goto ParseError;
  4259. }
  4260. //
  4261. // If-Modified-Since overrides If-None-Match.
  4262. //
  4263. fIfNoneMatchPassed = TRUE;
  4264. }
  4265. if ( !fIfNoneMatchPassed )
  4266. {
  4267. //
  4268. // We could either skip the If-Modified-Since header, or it
  4269. // was not present, AND we did not pass the If-None-Match
  4270. // predicate. Since this is a "GET" or "HEAD" request (because
  4271. // that's all we cache, we should return 304. If this were
  4272. // any other verb, we should return 412.
  4273. //
  4274. ASSERT( (HttpVerbGET == pRequest->Verb) || (HttpVerbHEAD == pRequest->Verb) );
  4275. goto NotModified;
  4276. }
  4277. //
  4278. // 4. Check If-Unmodified-Since
  4279. //
  4280. if ( pRequest->HeaderValid[HttpHeaderIfUnmodifiedSince] )
  4281. {
  4282. if ( StringTimeToSystemTime(
  4283. (PCSTR) pRequest->Headers[HttpHeaderIfUnmodifiedSince].pHeader,
  4284. pRequest->Headers[HttpHeaderIfUnmodifiedSince].HeaderLength,
  4285. &liUnmodifiedSince) )
  4286. {
  4287. //
  4288. // If the cache entry was created after the time
  4289. // specified in the If-Unmodified-Since header, we
  4290. // MUST return a 412 (Precondition Failed) status.
  4291. //
  4292. if ( pUriCacheEntry->CreationTime.QuadPart > liUnmodifiedSince.QuadPart )
  4293. {
  4294. goto PreconditionFailed;
  4295. }
  4296. }
  4297. else
  4298. {
  4299. //
  4300. // if converting the If-Unmodified-Since header failed, we
  4301. // need to report the parse failure.
  4302. //
  4303. goto ParseError;
  4304. }
  4305. }
  4306. Cleanup:
  4307. return RetStatus;
  4308. NotModified:
  4309. RetStatus = 304;
  4310. //
  4311. // Send 304 (Not Modified) response
  4312. //
  4313. BytesSent = UlSendSimpleStatusEx(
  4314. pRequest,
  4315. UlStatusNotModified,
  4316. pUriCacheEntry,
  4317. ResumeParsing
  4318. );
  4319. //
  4320. // Update the server to client bytes sent.
  4321. // The logging & perf counters will use it.
  4322. //
  4323. pRequest->BytesSent += BytesSent;
  4324. goto Cleanup;
  4325. PreconditionFailed:
  4326. RetStatus = 412;
  4327. goto Cleanup;
  4328. ParseError:
  4329. // Parse Error encountered.
  4330. RetStatus = 400;
  4331. goto Cleanup;
  4332. } // UlCheckCacheControlHeaders
  4333. /***************************************************************************++
  4334. Routine Description:
  4335. Checks the cached response against the "Accept:" header in the request
  4336. to see if it can satisfy the requested Content-Type(s).
  4337. (Yes, I know this is really gross...I encourage anyone to find a better
  4338. way to parse this! --EricSten)
  4339. Arguments:
  4340. pRequest - The request to check.
  4341. pUriCacheEntry - The cache entry that might possibly match.
  4342. Returns:
  4343. TRUE At least one of the possible formats matched the Content-Type
  4344. of the cached entry.
  4345. FALSE None of the requested types matched the Content-Type of the
  4346. cached entry.
  4347. --***************************************************************************/
  4348. BOOLEAN
  4349. UlIsAcceptHeaderOk(
  4350. IN PUL_INTERNAL_REQUEST pRequest,
  4351. IN PUL_URI_CACHE_ENTRY pUriCacheEntry
  4352. )
  4353. {
  4354. BOOLEAN bRet = TRUE;
  4355. ULONG Len;
  4356. PUCHAR pHdr;
  4357. PUCHAR pSubType;
  4358. PUCHAR pTmp;
  4359. PUL_CONTENT_TYPE pContentType;
  4360. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  4361. ASSERT(IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry));
  4362. if ( pRequest->HeaderValid[HttpHeaderAccept] &&
  4363. (pRequest->Headers[HttpHeaderAccept].HeaderLength > 0) )
  4364. {
  4365. Len = pRequest->Headers[HttpHeaderAccept].HeaderLength;
  4366. pHdr = pRequest->Headers[HttpHeaderAccept].pHeader;
  4367. pContentType = &pUriCacheEntry->ContentType;
  4368. //
  4369. // First, do "fast-path" check; see if "*/*" is anywhere in the header.
  4370. //
  4371. pTmp = (PUCHAR) strstr( (const char*) pHdr, "*/*" );
  4372. //
  4373. // If we found "*/*" and its either at the beginning of the line,
  4374. // the end of the line, or surrounded by either ' ' or ',', then
  4375. // it's really a wildcard.
  4376. //
  4377. if ((pTmp != NULL) &&
  4378. ((pTmp == pHdr) ||
  4379. IS_HTTP_LWS(pTmp[-1]) ||
  4380. (pTmp[-1] == ',')) &&
  4381. ((pTmp[3] == '\0') ||
  4382. IS_HTTP_LWS(pTmp[3]) ||
  4383. (pTmp[3] == ',')))
  4384. {
  4385. goto end;
  4386. }
  4387. //
  4388. // Wildcard not found; continue with slow path
  4389. //
  4390. while (Len)
  4391. {
  4392. if (pContentType->TypeLen > Len)
  4393. {
  4394. // Bad! No more string left...Bail out.
  4395. bRet = FALSE;
  4396. goto end;
  4397. }
  4398. if ( (pContentType->TypeLen == RtlCompareMemory(
  4399. pHdr,
  4400. pContentType->Type,
  4401. pContentType->TypeLen
  4402. )) &&
  4403. ( '/' == pHdr[pContentType->TypeLen] ) )
  4404. {
  4405. //
  4406. // Found matching type; check subtype
  4407. //
  4408. pSubType = &pHdr[pContentType->TypeLen + 1];
  4409. if ( '*' == *pSubType )
  4410. {
  4411. // Subtype wildcard match!
  4412. goto end;
  4413. }
  4414. else
  4415. {
  4416. if ( pContentType->SubTypeLen >
  4417. (Len - ( pContentType->TypeLen + 1 )) )
  4418. {
  4419. // Bad! No more string left...Bail out.
  4420. bRet = FALSE;
  4421. goto end;
  4422. }
  4423. if ( pContentType->SubTypeLen == RtlCompareMemory(
  4424. pSubType,
  4425. pContentType->SubType,
  4426. pContentType->SubTypeLen
  4427. ) &&
  4428. !IS_HTTP_TOKEN(pSubType[pContentType->SubTypeLen]) )
  4429. {
  4430. // Subtype exact match!
  4431. goto end;
  4432. }
  4433. }
  4434. }
  4435. //
  4436. // Didn't match this one; advance to next Content-Type in the Accept field
  4437. //
  4438. pTmp = (PUCHAR) strchr( (const char *) pHdr, ',' );
  4439. if (pTmp)
  4440. {
  4441. // Found a comma; step over it and any whitespace.
  4442. ASSERT ( Len > DIFF(pTmp - pHdr));
  4443. Len -= (DIFF(pTmp - pHdr) +1);
  4444. pHdr = (pTmp+1);
  4445. while( Len && IS_HTTP_LWS(*pHdr) )
  4446. {
  4447. pHdr++;
  4448. Len--;
  4449. }
  4450. } else
  4451. {
  4452. // No more content-types; bail.
  4453. bRet = FALSE;
  4454. goto end;
  4455. }
  4456. } // walk list of things
  4457. //
  4458. // Walked all Accept items and didn't find a match.
  4459. //
  4460. bRet = FALSE;
  4461. }
  4462. end:
  4463. UlTrace(PARSER,
  4464. ("UlIsAcceptHeaderOk: returning %s\n",
  4465. bRet ? "TRUE" : "FALSE" ));
  4466. return bRet;
  4467. } // UlIsAcceptHeaderOk
  4468. /***************************************************************************++
  4469. Routine Description:
  4470. parses a content-type into its type and subtype components.
  4471. Arguments:
  4472. pStr String containing valid content type
  4473. StrLen Length of string (in bytes)
  4474. pContentType pointer to user provided UL_CONTENT_TYPE structure
  4475. --***************************************************************************/
  4476. VOID
  4477. UlGetTypeAndSubType(
  4478. IN PCSTR pStr,
  4479. IN ULONG StrLen,
  4480. IN PUL_CONTENT_TYPE pContentType
  4481. )
  4482. {
  4483. PCHAR pSlash;
  4484. ASSERT(pStr && StrLen);
  4485. ASSERT(pContentType);
  4486. pSlash = strnchr(pStr, '/', StrLen);
  4487. if ( NULL == pSlash ||
  4488. pStr == pSlash ||
  4489. (pSlash == (pStr + (StrLen-1))) )
  4490. {
  4491. //
  4492. // BAD!
  4493. // 1. content types should always have a slash!
  4494. // 2. content type can't have a null type
  4495. // 3. content type can't have a null sub-type
  4496. //
  4497. return;
  4498. }
  4499. //
  4500. // Minimal content type is "a/b"
  4501. //
  4502. ASSERT( StrLen >= 3 );
  4503. pContentType->TypeLen = (ULONG) MIN( (pSlash - pStr), MAX_TYPE_LENGTH );
  4504. RtlCopyMemory(
  4505. pContentType->Type,
  4506. pStr,
  4507. pContentType->TypeLen
  4508. );
  4509. ASSERT( StrLen > (pContentType->TypeLen + 1) );
  4510. pContentType->SubTypeLen = MIN( (StrLen - (pContentType->TypeLen + 1)), MAX_SUBTYPE_LENGTH );
  4511. RtlCopyMemory(
  4512. pContentType->SubType,
  4513. pSlash+1,
  4514. pContentType->SubTypeLen
  4515. );
  4516. } // UlGetTypeAndSubType
  4517. /*--
  4518. Routine Description:
  4519. This function converts the enum verb type to string to the
  4520. provided buffer. Used normally by the error logging.
  4521. Conversion happens as follows;
  4522. For HttpVerbUnknown, pRequest->pRawVerb is copied over
  4523. to the output buffer up to MAX_VERB_LENGTH characters.
  4524. For others NewVerbTable is used.
  4525. Arguments:
  4526. psz - Pointer to output buffer
  4527. pHttpRequest - Pointer to the incoming HTTP request.
  4528. chTerminator - Terminator will be written at the end.
  4529. Return Value:
  4530. Pointer to the end of the copied space.
  4531. --*/
  4532. PCHAR
  4533. UlCopyHttpVerb(
  4534. IN OUT PCHAR psz,
  4535. IN PUL_INTERNAL_REQUEST pRequest,
  4536. IN CHAR chTerminator
  4537. )
  4538. {
  4539. //
  4540. // Sanity check.
  4541. //
  4542. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  4543. ASSERT(pRequest->Verb < HttpVerbMaximum);
  4544. if (pRequest->Verb == HttpVerbUnknown)
  4545. {
  4546. ULONG RawVerbLength =
  4547. MIN(MAX_VERB_LENGTH, pRequest->RawVerbLength);
  4548. ASSERT(pRequest->pRawVerb && pRequest->RawVerbLength);
  4549. RtlCopyMemory(
  4550. psz,
  4551. pRequest->pRawVerb,
  4552. RawVerbLength
  4553. );
  4554. psz += RawVerbLength;
  4555. }
  4556. else
  4557. {
  4558. //
  4559. // Using the raw verb in the request should be fine.
  4560. //
  4561. RtlCopyMemory(
  4562. psz,
  4563. NewVerbTable[pRequest->Verb].RawVerb,
  4564. NewVerbTable[pRequest->Verb].RawVerbLength
  4565. );
  4566. psz += NewVerbTable[pRequest->Verb].RawVerbLength;
  4567. }
  4568. *psz = chTerminator;
  4569. // Move past the terminator character unless it's a nul
  4570. if (chTerminator != '\0')
  4571. psz++;
  4572. return psz;
  4573. }
  4574. #if DBG
  4575. PCSTR
  4576. UlVerbToString(
  4577. HTTP_VERB Verb
  4578. )
  4579. {
  4580. PAGED_CODE();
  4581. switch (Verb)
  4582. {
  4583. case HttpVerbUnparsed:
  4584. return "Unparsed";
  4585. case HttpVerbUnknown:
  4586. return "Unknown";
  4587. case HttpVerbInvalid:
  4588. return "Invalid";
  4589. case HttpVerbOPTIONS:
  4590. return "OPTIONS";
  4591. case HttpVerbGET:
  4592. return "GET";
  4593. case HttpVerbHEAD:
  4594. return "HEAD";
  4595. case HttpVerbPOST:
  4596. return "POST";
  4597. case HttpVerbPUT:
  4598. return "PUT";
  4599. case HttpVerbDELETE:
  4600. return "DELETE";
  4601. case HttpVerbTRACE:
  4602. return "TRACE";
  4603. case HttpVerbCONNECT:
  4604. return "CONNECT";
  4605. case HttpVerbTRACK:
  4606. return "TRACK";
  4607. case HttpVerbMOVE:
  4608. return "MOVE";
  4609. case HttpVerbCOPY:
  4610. return "COPY";
  4611. case HttpVerbPROPFIND:
  4612. return "PROPFIND";
  4613. case HttpVerbPROPPATCH:
  4614. return "PROPPATCH";
  4615. case HttpVerbMKCOL:
  4616. return "MKCOL";
  4617. case HttpVerbLOCK:
  4618. return "LOCK";
  4619. case HttpVerbUNLOCK:
  4620. return "UNLOCK";
  4621. case HttpVerbSEARCH:
  4622. return "SEARCH";
  4623. default:
  4624. ASSERT(! "Unrecognized HTTP_VERB");
  4625. return "???";
  4626. }
  4627. } // UlVerbToString
  4628. PCSTR
  4629. UlParseStateToString(
  4630. PARSE_STATE ParseState
  4631. )
  4632. {
  4633. PAGED_CODE();
  4634. switch (ParseState)
  4635. {
  4636. case ParseVerbState:
  4637. return "Verb";
  4638. case ParseUrlState:
  4639. return "Url";
  4640. case ParseVersionState:
  4641. return "Version";
  4642. case ParseHeadersState:
  4643. return "Headers";
  4644. case ParseCookState:
  4645. return "Cook";
  4646. case ParseEntityBodyState:
  4647. return "EntityBody";
  4648. case ParseTrailerState:
  4649. return "Trailer";
  4650. case ParseDoneState:
  4651. return "Done";
  4652. case ParseErrorState:
  4653. return "Error";
  4654. default:
  4655. ASSERT(! "Unknown PARSE_STATE");
  4656. return "?Unknown?";
  4657. };
  4658. } // UlParseStateToString
  4659. #endif // DBG