Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5372 lines
129 KiB

  1. /*++
  2. Copyright (c) 1998-2001 Microsoft Corporation
  3. Module Name:
  4. cgroup.cxx
  5. Abstract:
  6. Note that most of the routines in this module assume they are called
  7. at PASSIVE_LEVEL.
  8. Author:
  9. Paul McDaniel (paulmcd) 12-Jan-1999
  10. Revision History:
  11. --*/
  12. #include "precomp.h" // Project wide headers
  13. #include "cgroupp.h" // Private data structures
  14. #ifdef ALLOC_PRAGMA
  15. #pragma alloc_text( INIT, UlInitializeCG )
  16. #pragma alloc_text( PAGE, UlTerminateCG )
  17. #pragma alloc_text( PAGE, UlAddUrlToConfigGroup )
  18. #pragma alloc_text( PAGE, UlConfigGroupFromListEntry )
  19. #pragma alloc_text( PAGE, UlCreateConfigGroup )
  20. #pragma alloc_text( PAGE, UlDeleteConfigGroup )
  21. #pragma alloc_text( PAGE, UlGetConfigGroupInfoForUrl )
  22. #pragma alloc_text( PAGE, UlQueryConfigGroupInformation )
  23. #pragma alloc_text( PAGE, UlRemoveUrlFromConfigGroup )
  24. #pragma alloc_text( PAGE, UlRemoveAllUrlsFromConfigGroup )
  25. #pragma alloc_text( PAGE, UlAddTransientUrl )
  26. #pragma alloc_text( PAGE, UlRemoveTransientUrl )
  27. #pragma alloc_text( PAGE, UlSetConfigGroupInformation )
  28. #pragma alloc_text( PAGE, UlNotifyOrphanedConfigGroup )
  29. #pragma alloc_text( PAGE, UlpCreateConfigGroupObject )
  30. #pragma alloc_text( PAGE, UlpCleanAllUrls )
  31. #pragma alloc_text( PAGE, UlpDeferredRemoveSite )
  32. #pragma alloc_text( PAGE, UlpDeferredRemoveSiteWorker )
  33. #pragma alloc_text( PAGE, UlpSanitizeUrl )
  34. #pragma alloc_text( PAGE, UlpSetUrlInfo )
  35. #pragma alloc_text( PAGE, UlpConfigGroupInfoRelease )
  36. #pragma alloc_text( PAGE, UlpConfigGroupInfoDeepCopy )
  37. #pragma alloc_text( PAGE, UlpTreeFreeNode )
  38. #pragma alloc_text( PAGE, UlpTreeFindNode )
  39. #pragma alloc_text( PAGE, UlpTreeFindNodeWalker )
  40. #pragma alloc_text( PAGE, UlpTreeFindEntry )
  41. #pragma alloc_text( PAGE, UlpTreeFindSite )
  42. #pragma alloc_text( PAGE, UlpTreeFindWildcardMatch )
  43. #pragma alloc_text( PAGE, UlpTreeFindIpMatch )
  44. #pragma alloc_text( PAGE, UlpTreeInsert )
  45. #pragma alloc_text( PAGE, UlpTreeInsertEntry )
  46. #endif // ALLOC_PRAGMA
  47. #if 0
  48. #endif
  49. //
  50. // Globals
  51. //
  52. PUL_CG_URL_TREE_HEADER g_pSites = NULL;
  53. BOOLEAN g_InitCGCalled = FALSE;
  54. /***************************************************************************++
  55. Routine Description:
  56. Free's the node pEntry. This funciton walks up and tree of parent entries
  57. and deletes them if they are supposed to free'd (dummy nodes) .
  58. it will free the memory pEntry + unlink it from the config group LIST_ENTRY.
  59. Arguments:
  60. IN PUL_CG_URL_TREE_ENTRY pEntry - the entry to free
  61. Return Value:
  62. NTSTATUS - Completion status.
  63. --***************************************************************************/
  64. NTSTATUS
  65. UlpTreeFreeNode(
  66. IN PUL_CG_URL_TREE_ENTRY pEntry
  67. )
  68. {
  69. NTSTATUS Status;
  70. PUL_CG_URL_TREE_HEADER pHeader;
  71. ULONG Index;
  72. PUL_CG_URL_TREE_ENTRY pParent;
  73. //
  74. // Sanity check.
  75. //
  76. PAGED_CODE();
  77. ASSERT(IS_VALID_TREE_ENTRY(pEntry));
  78. Status = STATUS_SUCCESS;
  79. //
  80. // Loop! we are going to walk up the tree deleting as much
  81. // as we can of this branch.
  82. //
  83. while (pEntry != NULL)
  84. {
  85. ASSERT(IS_VALID_TREE_ENTRY(pEntry));
  86. //
  87. // Init
  88. //
  89. pParent = NULL;
  90. UlTrace(
  91. CONFIG_GROUP_TREE, (
  92. "http!UlpTreeFreeNode - pEntry('%S', %d, %S, %S%S)\n",
  93. pEntry->pToken,
  94. pEntry->FullUrl,
  95. (pEntry->pChildren == NULL || pEntry->pChildren->UsedCount == 0) ? L"no children" : L"children",
  96. pEntry->pParent == NULL ? L"no parent" : L"parent=",
  97. pEntry->pParent == NULL ? L"" : pEntry->pParent->pToken
  98. )
  99. );
  100. //
  101. // 1) might not be a "real" leaf - we are walking up the tree in this loop
  102. //
  103. // 2) also we clean this first because we might not be deleting
  104. // this node at all, if it has dependent children.
  105. //
  106. if (pEntry->FullUrl == 1)
  107. {
  108. //
  109. // Remove it from the cfg group list
  110. //
  111. RemoveEntryList(&(pEntry->ConfigGroupListEntry));
  112. pEntry->ConfigGroupListEntry.Flink = pEntry->ConfigGroupListEntry.Blink = NULL;
  113. pEntry->pConfigGroup = NULL;
  114. //
  115. // Mark it as a dummy node
  116. //
  117. pEntry->FullUrl = 0;
  118. }
  119. //
  120. // do we have children?
  121. //
  122. if (pEntry->pChildren != NULL && pEntry->pChildren->UsedCount > 0)
  123. {
  124. //
  125. // can't delete it. dependant children exist.
  126. // it's already be converted to a dummy node above.
  127. //
  128. // leave it. it will get cleaned by a subsequent child.
  129. //
  130. break;
  131. }
  132. //
  133. // we are really deleting this one, remove it from the sibling list.
  134. //
  135. //
  136. // find our location in the sibling list
  137. //
  138. if (pEntry->pParent == NULL)
  139. {
  140. pHeader = g_pSites;
  141. }
  142. else
  143. {
  144. pHeader = pEntry->pParent->pChildren;
  145. }
  146. Status = UlpTreeFindEntry(
  147. pHeader,
  148. pEntry->pToken,
  149. pEntry->TokenLength,
  150. pEntry->TokenHash,
  151. &Index,
  152. NULL
  153. );
  154. if (NT_SUCCESS(Status) == FALSE)
  155. {
  156. ASSERT(FALSE);
  157. goto end;
  158. }
  159. //
  160. // time to remove it
  161. //
  162. // if not the last one, shift left the array at Index
  163. //
  164. if (Index < (pHeader->UsedCount-1))
  165. {
  166. RtlMoveMemory(
  167. &(pHeader->pEntries[Index]),
  168. &(pHeader->pEntries[Index+1]),
  169. (pHeader->UsedCount - 1 - Index) * sizeof(UL_CG_HEADER_ENTRY)
  170. );
  171. }
  172. //
  173. // now we have 1 less
  174. //
  175. pHeader->UsedCount -= 1;
  176. //
  177. // need to clean parent entries that were here just for this leaf
  178. //
  179. if (pEntry->pParent != NULL)
  180. {
  181. //
  182. // Does this parent have any other children?
  183. //
  184. ASSERT(IS_VALID_TREE_HEADER(pEntry->pParent->pChildren));
  185. if (pEntry->pParent->pChildren->UsedCount == 0)
  186. {
  187. //
  188. // no more, time to clean the child list
  189. //
  190. UL_FREE_POOL_WITH_SIG(pEntry->pParent->pChildren, UL_CG_TREE_HEADER_POOL_TAG);
  191. //
  192. // is the parent a real url entry?
  193. //
  194. if (pEntry->pParent->FullUrl != 1)
  195. {
  196. //
  197. // nope . let's scrub it.
  198. //
  199. pParent = pEntry->pParent;
  200. }
  201. }
  202. else
  203. {
  204. //
  205. // ouch. siblings. can't nuke parent.
  206. //
  207. }
  208. }
  209. else
  210. {
  211. //
  212. // we are deleting a site node; stop listening.
  213. // We have to stop listening on another thread
  214. // because otherwise there will be a deadlock
  215. // between the config group lock and http connection
  216. // locks.
  217. //
  218. UlpDeferredRemoveSite(pEntry);
  219. }
  220. //
  221. // Free the entry
  222. //
  223. UL_FREE_POOL_WITH_SIG(pEntry, UL_CG_TREE_ENTRY_POOL_TAG);
  224. //
  225. // Move on to the next one
  226. //
  227. pEntry = pParent;
  228. }
  229. end:
  230. return Status;
  231. }
  232. /***************************************************************************++
  233. Routine Description:
  234. Allocates and initializes a config group object.
  235. Arguments:
  236. ppObject - gets a pointer to the object on success
  237. --***************************************************************************/
  238. NTSTATUS
  239. UlpCreateConfigGroupObject(
  240. PUL_CONFIG_GROUP_OBJECT * ppObject
  241. )
  242. {
  243. HTTP_CONFIG_GROUP_ID NewId = HTTP_NULL_ID;
  244. PUL_CONFIG_GROUP_OBJECT pNewObject = NULL;
  245. NTSTATUS Status;
  246. //
  247. // Sanity check.
  248. //
  249. PAGED_CODE();
  250. ASSERT(ppObject != NULL);
  251. UlTrace(CONFIG_GROUP_FNC, ("http!UlpCreateConfigGroupObject\n"));
  252. //
  253. // Create an empty config group object structure - PAGED
  254. //
  255. pNewObject = UL_ALLOCATE_STRUCT(
  256. NonPagedPool,
  257. UL_CONFIG_GROUP_OBJECT,
  258. UL_CG_OBJECT_POOL_TAG
  259. );
  260. if (pNewObject == NULL)
  261. {
  262. //
  263. // Oops. Couldn't allocate the memory for it.
  264. //
  265. Status = STATUS_NO_MEMORY;
  266. goto end;
  267. }
  268. RtlZeroMemory(pNewObject, sizeof(UL_CONFIG_GROUP_OBJECT));
  269. //
  270. // Create an opaque id for it
  271. //
  272. Status = UlAllocateOpaqueId(
  273. &NewId, // pOpaqueId
  274. UlOpaqueIdTypeConfigGroup, // OpaqueIdType
  275. pNewObject // pContext
  276. );
  277. if (NT_SUCCESS(Status) == FALSE)
  278. goto end;
  279. //
  280. // Fill in the structure
  281. //
  282. pNewObject->Signature = UL_CG_OBJECT_POOL_TAG;
  283. pNewObject->RefCount = 1;
  284. pNewObject->ConfigGroupId = NewId;
  285. pNewObject->AppPoolFlags.Present = 0;
  286. pNewObject->pAppPool = NULL;
  287. pNewObject->pAutoResponse = NULL;
  288. pNewObject->MaxBandwidth.Flags.Present = 0;
  289. pNewObject->MaxConnections.Flags.Present = 0;
  290. pNewObject->State.Flags.Present = 0;
  291. pNewObject->Security.Flags.Present = 0;
  292. pNewObject->LoggingConfig.Flags.Present = 0;
  293. pNewObject->pLogFileEntry = NULL;
  294. //
  295. // init the bandwidth throttling flow list
  296. //
  297. InitializeListHead(&pNewObject->FlowListHead);
  298. //
  299. // init notification entries & head
  300. //
  301. UlInitializeNotifyEntry(&pNewObject->HandleEntry, pNewObject);
  302. UlInitializeNotifyEntry(&pNewObject->ParentEntry, pNewObject);
  303. UlInitializeNotifyHead(
  304. &pNewObject->ChildHead,
  305. &g_pUlNonpagedData->ConfigGroupResource
  306. );
  307. //
  308. // init the url list
  309. //
  310. InitializeListHead(&pNewObject->UrlListHead);
  311. //
  312. // return the pointer
  313. //
  314. *ppObject = pNewObject;
  315. end:
  316. if (!NT_SUCCESS(Status))
  317. {
  318. //
  319. // Something failed. Let's clean up.
  320. //
  321. if (pNewObject != NULL)
  322. {
  323. UL_FREE_POOL_WITH_SIG(pNewObject, UL_CG_OBJECT_POOL_TAG);
  324. }
  325. if (!HTTP_IS_NULL_ID(&NewId))
  326. {
  327. UlFreeOpaqueId(NewId, UlOpaqueIdTypeConfigGroup);
  328. }
  329. }
  330. return Status;
  331. } // UlpCreateConfigGroupObject
  332. /***************************************************************************++
  333. Routine Description:
  334. This will clean all of the urls in the LIST_ENTRY for the config group
  335. Arguments:
  336. IN PUL_CONFIG_GROUP_OBJECT pObject the group to clean the urls for
  337. Return Value:
  338. NTSTATUS - Completion status.
  339. --***************************************************************************/
  340. NTSTATUS
  341. UlpCleanAllUrls(
  342. IN PUL_CONFIG_GROUP_OBJECT pObject
  343. )
  344. {
  345. NTSTATUS Status;
  346. //
  347. // Sanity check.
  348. //
  349. PAGED_CODE();
  350. ASSERT(pObject != NULL);
  351. Status = STATUS_SUCCESS;
  352. //
  353. // Remove all of the url's associated with this cfg group
  354. //
  355. //
  356. // walk the list
  357. //
  358. while (IsListEmpty(&pObject->UrlListHead) == FALSE)
  359. {
  360. PUL_CG_URL_TREE_ENTRY pTreeEntry;
  361. //
  362. // get the containing struct
  363. //
  364. pTreeEntry = CONTAINING_RECORD(
  365. pObject->UrlListHead.Flink,
  366. UL_CG_URL_TREE_ENTRY,
  367. ConfigGroupListEntry
  368. );
  369. ASSERT(IS_VALID_TREE_ENTRY(pTreeEntry) && pTreeEntry->FullUrl == 1);
  370. //
  371. // delete it - this unlinks it from the list
  372. //
  373. if (NT_SUCCESS(Status))
  374. {
  375. Status = UlpTreeFreeNode(pTreeEntry);
  376. }
  377. else
  378. {
  379. //
  380. // just record the first error, but still attempt to free all
  381. //
  382. UlpTreeFreeNode(pTreeEntry);
  383. }
  384. }
  385. // the list is empty now
  386. //
  387. return Status;
  388. } // UlpCleanAllUrls
  389. /***************************************************************************++
  390. Routine Description:
  391. Removes a site entry's url from the listening endpoint.
  392. Arguments:
  393. pEntry - the site entry
  394. --***************************************************************************/
  395. VOID
  396. UlpDeferredRemoveSite(
  397. IN PUL_CG_URL_TREE_ENTRY pEntry
  398. )
  399. {
  400. PUL_DEFERRED_REMOVE_ITEM pItem;
  401. //
  402. // Sanity check
  403. //
  404. PAGED_CODE();
  405. ASSERT( IS_VALID_TREE_ENTRY(pEntry) );
  406. ASSERT( pEntry->pParent == NULL );
  407. ASSERT( pEntry->TokenLength > 0 );
  408. pItem = UL_ALLOCATE_STRUCT_WITH_SPACE(
  409. NonPagedPool,
  410. UL_DEFERRED_REMOVE_ITEM,
  411. pEntry->TokenLength + sizeof(WCHAR),
  412. UL_DEFERRED_REMOVE_ITEM_POOL_TAG
  413. );
  414. if (pItem) {
  415. pItem->NameLength = pEntry->TokenLength;
  416. RtlCopyMemory(
  417. pItem->pName,
  418. pEntry->pToken,
  419. pEntry->TokenLength + sizeof(WCHAR)
  420. );
  421. //
  422. // REVIEW: Because UlRemoveSiteFromEndpointList can block
  423. // REVIEW: indefinitely while waiting for other work items
  424. // REVIEW: to complete, we must not queue it with UlQueueWorkItem.
  425. // REVIEW: (could lead to deadlock, esp. in a single-threded queue)
  426. //
  427. UL_QUEUE_BLOCKING_ITEM(
  428. &pItem->WorkItem,
  429. &UlpDeferredRemoveSiteWorker
  430. );
  431. }
  432. }
  433. /***************************************************************************++
  434. Routine Description:
  435. Removes a site entry's url from the listening endpoint.
  436. Arguments:
  437. pWorkItem - in a UL_DEFERRED_REMOVE_ITEM struct with the endpoint name
  438. --***************************************************************************/
  439. VOID
  440. UlpDeferredRemoveSiteWorker(
  441. IN PUL_WORK_ITEM pWorkItem
  442. )
  443. {
  444. NTSTATUS Status;
  445. PUL_DEFERRED_REMOVE_ITEM pItem;
  446. //
  447. // Sanity check
  448. //
  449. PAGED_CODE();
  450. ASSERT( pWorkItem );
  451. //
  452. // get the name of the site
  453. //
  454. pItem = CONTAINING_RECORD(
  455. pWorkItem,
  456. UL_DEFERRED_REMOVE_ITEM,
  457. WorkItem
  458. );
  459. ASSERT( pItem->pName[pItem->NameLength / 2] == UNICODE_NULL );
  460. //
  461. // remove it
  462. //
  463. Status = UlRemoveSiteFromEndpointList(pItem->pName);
  464. if (!NT_SUCCESS(Status)) {
  465. // c'est la vie
  466. UlTrace(CONFIG_GROUP_TREE, (
  467. "http!UlpDeferredRemoveSiteWorker(%ws) failed %08x\n",
  468. pItem->pName,
  469. Status
  470. ));
  471. }
  472. //
  473. // free the work item
  474. //
  475. UL_FREE_POOL(pItem, UL_DEFERRED_REMOVE_ITEM_POOL_TAG);
  476. }
  477. /***************************************************************************++
  478. Routine Description:
  479. This will return a fresh buffer containing the url sanitized. caller
  480. must free this from paged pool.
  481. Arguments:
  482. IN PUNICODE_STRING pUrl, the url to clean
  483. OUT PWSTR * ppUrl the cleaned url
  484. Return Value:
  485. NTSTATUS - Completion status.
  486. STATUS_NO_MEMORY; the memroy alloc failed
  487. --***************************************************************************/
  488. NTSTATUS
  489. UlpSanitizeUrl(
  490. IN PUNICODE_STRING pUrl,
  491. OUT PWSTR * ppUrl
  492. )
  493. {
  494. NTSTATUS Status;
  495. PWSTR pNewUrl = NULL;
  496. PWSTR pPort;
  497. PWSTR pEnd;
  498. PWSTR pStart;
  499. ULONGLONG PortNum;
  500. //
  501. // Sanity check.
  502. //
  503. PAGED_CODE();
  504. ASSERT(pUrl != NULL && pUrl->Length > 0 && pUrl->Buffer != NULL);
  505. ASSERT(ppUrl != NULL);
  506. //
  507. // Is the url prefix too long?
  508. //
  509. if ( (pUrl->Length / sizeof(WCHAR) ) > g_UlMaxUrlLength )
  510. {
  511. Status = STATUS_INVALID_PARAMETER;
  512. goto end;
  513. }
  514. //
  515. // Make a private copy of the url
  516. //
  517. pNewUrl = UL_ALLOCATE_ARRAY(
  518. PagedPool,
  519. WCHAR,
  520. (pUrl->Length / sizeof(WCHAR)) + 1,
  521. URL_POOL_TAG
  522. );
  523. if (pNewUrl == NULL)
  524. {
  525. Status = STATUS_NO_MEMORY;
  526. goto end;
  527. }
  528. RtlCopyMemory(pNewUrl, pUrl->Buffer, pUrl->Length);
  529. //
  530. // NULL terminate it
  531. //
  532. pNewUrl[pUrl->Length/sizeof(WCHAR)] = UNICODE_NULL;
  533. //
  534. // Validate it
  535. //
  536. if (pUrl->Length < (sizeof(L"https://")-sizeof(WCHAR)))
  537. {
  538. Status = STATUS_INVALID_PARAMETER;
  539. goto end;
  540. }
  541. if (_wcsnicmp(pNewUrl, L"http://", (sizeof(L"http://")-1)/sizeof(WCHAR)) != 0 &&
  542. _wcsnicmp(pNewUrl, L"https://", (sizeof(L"https://")-1)/sizeof(WCHAR)) != 0)
  543. {
  544. Status = STATUS_INVALID_PARAMETER;
  545. goto end;
  546. }
  547. pStart = wcschr(pNewUrl, ':');
  548. ASSERT(pStart != NULL); // already checked above
  549. //
  550. // skip the "://" to find the start
  551. //
  552. pStart += 3;
  553. //
  554. // find the end, right before the trailing slash
  555. //
  556. pEnd = wcschr(pStart, '/');
  557. if (pEnd == NULL)
  558. {
  559. Status = STATUS_INVALID_PARAMETER;
  560. goto end;
  561. }
  562. //
  563. // find the port (right after the colon) colon
  564. //
  565. pPort = wcschr(pStart, ':');
  566. if (pPort == NULL)
  567. {
  568. Status = STATUS_INVALID_PARAMETER;
  569. goto end;
  570. }
  571. pPort += 1;
  572. //
  573. // no hostname || no port (or colon past the end)
  574. //
  575. if (pPort == (pStart + 1) || pPort >= pEnd)
  576. {
  577. Status = STATUS_INVALID_PARAMETER;
  578. goto end;
  579. }
  580. //
  581. // NULL terminate it
  582. //
  583. pEnd[0] = UNICODE_NULL;
  584. //
  585. // Now convert the port to a number
  586. //
  587. Status = UlUnicodeToULongLong(pPort, 10, &PortNum);
  588. pEnd[0] = L'/';
  589. if (NT_SUCCESS(Status) == FALSE)
  590. goto end;
  591. if (PortNum > 0xffff || PortNum == 0)
  592. {
  593. Status = STATUS_INVALID_PARAMETER;
  594. goto end;
  595. }
  596. //
  597. // return the clean url
  598. //
  599. *ppUrl = pNewUrl;
  600. Status = STATUS_SUCCESS;
  601. end:
  602. if (NT_SUCCESS(Status) == FALSE)
  603. {
  604. if (pNewUrl != NULL)
  605. {
  606. UL_FREE_POOL(pNewUrl, URL_POOL_TAG);
  607. pNewUrl = NULL;
  608. }
  609. }
  610. RETURN(Status);
  611. }
  612. /***************************************************************************++
  613. Routine Description:
  614. walks the tree and find's a matching entry for pUrl. 2 output options,
  615. you can get the entry back, or a computed URL_INFO with inheritence applied.
  616. you must free the URL_INFO from NonPagedPool.
  617. Arguments:
  618. IN PUL_CG_URL_TREE_ENTRY pEntry, the top of the tree
  619. IN PWSTR pNextToken, where to start looking under
  620. the tree
  621. IN OUT PUL_URL_CONFIG_GROUP_INFO * ppInfo, [optional] the info to set,
  622. might have to grow it.
  623. OUT PUL_CG_URL_TREE_ENTRY * ppEntry [optional] returns the found
  624. entry
  625. Return Value:
  626. NTSTATUS - Completion status.
  627. STATUS_OBJECT_NAME_NOT_FOUND no entry found
  628. --***************************************************************************/
  629. NTSTATUS
  630. UlpTreeFindNodeWalker(
  631. IN PUL_CG_URL_TREE_ENTRY pEntry,
  632. IN PWSTR pNextToken,
  633. IN OUT PUL_URL_CONFIG_GROUP_INFO pInfo OPTIONAL,
  634. OUT PUL_CG_URL_TREE_ENTRY * ppEntry OPTIONAL
  635. )
  636. {
  637. NTSTATUS Status;
  638. PWSTR pToken = NULL;
  639. ULONG TokenLength;
  640. PUL_CG_URL_TREE_ENTRY pMatchEntry;
  641. ULONG Index;
  642. //
  643. // Sanity check.
  644. //
  645. PAGED_CODE();
  646. ASSERT(IS_VALID_TREE_ENTRY(pEntry));
  647. ASSERT(pNextToken != NULL);
  648. ASSERT(pInfo != NULL || ppEntry != NULL);
  649. //
  650. // walk the site tree looking for more matches
  651. //
  652. pMatchEntry = NULL;
  653. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  654. while (TRUE)
  655. {
  656. //
  657. // A bonafide match?
  658. //
  659. if (pEntry->FullUrl == 1)
  660. {
  661. pMatchEntry = pEntry;
  662. if (pInfo != NULL)
  663. {
  664. Status = UlpSetUrlInfo(pInfo, pMatchEntry);
  665. if (NT_SUCCESS(Status) == FALSE)
  666. goto end;
  667. }
  668. }
  669. //
  670. // Are we already at the end of the url?
  671. //
  672. if (pNextToken == NULL || *pNextToken == UNICODE_NULL)
  673. break;
  674. //
  675. // find the next token
  676. //
  677. pToken = pNextToken;
  678. pNextToken = wcschr(pNextToken, L'/');
  679. //
  680. // can be null if this is a leaf
  681. //
  682. if (pNextToken != NULL)
  683. {
  684. //
  685. // replace the '/' with a null, we'll fix it later
  686. //
  687. pNextToken[0] = UNICODE_NULL;
  688. TokenLength = DIFF(pNextToken - pToken) * sizeof(WCHAR);
  689. pNextToken += 1;
  690. }
  691. else
  692. {
  693. TokenLength = wcslen(pToken) * sizeof(WCHAR);
  694. }
  695. //
  696. // match?
  697. //
  698. Status = UlpTreeBinaryFindEntry(
  699. pEntry->pChildren,
  700. pToken,
  701. TokenLength,
  702. HASH_INVALID_SIGNATURE,
  703. &Index
  704. );
  705. if (pNextToken != NULL)
  706. {
  707. //
  708. // Fix the string, i replaced the '/' with a UNICODE_NULL
  709. //
  710. (pNextToken-1)[0] = L'/';
  711. }
  712. if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
  713. {
  714. //
  715. // it's a sorted tree, the first non-match means we're done
  716. //
  717. break;
  718. }
  719. //
  720. // other error?
  721. //
  722. if (NT_SUCCESS(Status) == FALSE)
  723. goto end;
  724. //
  725. // found a match, look for deeper matches.
  726. //
  727. pEntry = pEntry->pChildren->pEntries[Index].pEntry;
  728. ASSERT(IS_VALID_TREE_ENTRY(pEntry));
  729. }
  730. //
  731. // did we find a match?
  732. //
  733. if (pMatchEntry != NULL)
  734. {
  735. Status = STATUS_SUCCESS;
  736. if (ppEntry != NULL)
  737. {
  738. *ppEntry = pMatchEntry;
  739. }
  740. }
  741. else
  742. {
  743. ASSERT(Status == STATUS_OBJECT_NAME_NOT_FOUND || NT_SUCCESS(Status));
  744. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  745. }
  746. end:
  747. return Status;
  748. }
  749. /***************************************************************************++
  750. Routine Description:
  751. walks the tree and find's a matching entry for pUrl. 2 output options,
  752. you can get the entry back, or a computed URL_INFO with inheritence applied.
  753. you must free the URL_INFO from NonPagedPool.
  754. Arguments:
  755. IN PWSTR pUrl, the entry to find
  756. CheckWildcard Should we do a wildcard match?
  757. pHttpConn [optional] If non-NULL, use IP of
  758. server to find Node (if not found
  759. on first pass). This search is done
  760. prior to the Wildcard search.
  761. OUT PUL_URL_CONFIG_GROUP_INFO * ppInfo, [optional] will be alloced
  762. and generated.
  763. OUT PUL_CG_URL_TREE_ENTRY * ppEntry [optional] returns the found
  764. entry
  765. Return Value:
  766. NTSTATUS - Completion status.
  767. --***************************************************************************/
  768. NTSTATUS
  769. UlpTreeFindNode(
  770. IN PWSTR pUrl,
  771. IN BOOLEAN CheckWildcard,
  772. IN PUL_HTTP_CONNECTION pHttpConn OPTIONAL,
  773. OUT PUL_URL_CONFIG_GROUP_INFO pInfo OPTIONAL,
  774. OUT PUL_CG_URL_TREE_ENTRY * ppEntry OPTIONAL
  775. )
  776. {
  777. NTSTATUS Status;
  778. PWSTR pNextToken;
  779. PUL_CG_URL_TREE_ENTRY pEntry;
  780. //
  781. // Sanity check.
  782. //
  783. PAGED_CODE();
  784. ASSERT(pUrl != NULL);
  785. ASSERT(pInfo != NULL || ppEntry != NULL);
  786. //
  787. // get the site match
  788. //
  789. Status = UlpTreeFindSite(pUrl, &pNextToken, &pEntry, FALSE);
  790. if (NT_SUCCESS(Status))
  791. {
  792. //
  793. // does it exist in this tree?
  794. //
  795. Status = UlpTreeFindNodeWalker(pEntry, pNextToken, pInfo, ppEntry);
  796. }
  797. if ( (Status == STATUS_OBJECT_NAME_NOT_FOUND) && (NULL != pHttpConn) )
  798. {
  799. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pHttpConn));
  800. //
  801. // Didn't find it in first try. See if there is a binding for
  802. // the IP address & TCP Port on which the request was received.
  803. //
  804. Status = UlpTreeFindIpMatch(pUrl, pHttpConn, &pNextToken, &pEntry);
  805. if (NT_SUCCESS(Status))
  806. {
  807. //
  808. // and now check in the wildcard tree
  809. //
  810. Status = UlpTreeFindNodeWalker(pEntry, pNextToken, pInfo, ppEntry);
  811. }
  812. }
  813. if (Status == STATUS_OBJECT_NAME_NOT_FOUND && CheckWildcard)
  814. {
  815. //
  816. // shoot, didn't find a match. let's check wildcards
  817. //
  818. Status = UlpTreeFindWildcardMatch(pUrl, &pNextToken, &pEntry);
  819. if (NT_SUCCESS(Status))
  820. {
  821. //
  822. // and now check in the wildcard tree
  823. //
  824. Status = UlpTreeFindNodeWalker(pEntry, pNextToken, pInfo, ppEntry);
  825. }
  826. }
  827. //
  828. // all done.
  829. //
  830. return Status;
  831. } // UlpTreeFindNode
  832. /***************************************************************************++
  833. Routine Description:
  834. finds any matching wildcard site in g_pSites for pUrl.
  835. Arguments:
  836. IN PWSTR pUrl, the url to match
  837. OUT PWSTR * ppNextToken, output's the next token after
  838. matching the url
  839. OUT PUL_CG_URL_TREE_ENTRY * ppEntry, returns the entry
  840. Return Value:
  841. NTSTATUS - Completion status.
  842. --***************************************************************************/
  843. NTSTATUS
  844. UlpTreeFindWildcardMatch(
  845. IN PWSTR pUrl,
  846. OUT PWSTR * ppNextToken,
  847. OUT PUL_CG_URL_TREE_ENTRY * ppEntry
  848. )
  849. {
  850. NTSTATUS Status;
  851. PWSTR pNextToken;
  852. ULONG TokenLength;
  853. ULONG Index;
  854. PWSTR pPortNum;
  855. ULONG PortLength;
  856. WCHAR WildSiteUrl[HTTPS_WILD_PREFIX_LENGTH + MAX_PORT_LENGTH + 1];
  857. //
  858. // Sanity check.
  859. //
  860. PAGED_CODE();
  861. ASSERT(pUrl != NULL);
  862. ASSERT(ppNextToken != NULL);
  863. ASSERT(ppEntry != NULL);
  864. //
  865. // find the port # (colon location + 1 for https + 1 to skip the colon = + 2)
  866. //
  867. pPortNum = wcschr(pUrl + HTTP_PREFIX_COLON_INDEX + 2, L':');
  868. if (pPortNum == NULL)
  869. {
  870. //
  871. // ouch
  872. //
  873. Status = STATUS_INVALID_PARAMETER;
  874. goto end;
  875. }
  876. //
  877. // Skip the ':'
  878. //
  879. pPortNum += 1;
  880. //
  881. // find the trailing '/' after the port number
  882. //
  883. pNextToken = wcschr(pPortNum, '/');
  884. if (pNextToken == NULL)
  885. {
  886. Status = STATUS_INVALID_PARAMETER;
  887. goto end;
  888. }
  889. //
  890. // HTTPS or HTTP?
  891. //
  892. if (pUrl[HTTP_PREFIX_COLON_INDEX] == L':')
  893. {
  894. RtlCopyMemory(
  895. WildSiteUrl,
  896. HTTP_WILD_PREFIX,
  897. HTTP_WILD_PREFIX_LENGTH * sizeof(WCHAR)
  898. );
  899. PortLength = DIFF(pNextToken - pPortNum) * sizeof(WCHAR);
  900. TokenLength = HTTP_WILD_PREFIX_LENGTH + PortLength;
  901. ASSERT(TokenLength < (sizeof(WildSiteUrl)-sizeof(WCHAR)));
  902. RtlCopyMemory(
  903. &(WildSiteUrl[HTTP_WILD_PREFIX_LENGTH/sizeof(WCHAR)]),
  904. pPortNum,
  905. PortLength
  906. );
  907. WildSiteUrl[TokenLength/sizeof(WCHAR)] = UNICODE_NULL;
  908. }
  909. else
  910. {
  911. RtlCopyMemory(
  912. WildSiteUrl,
  913. HTTPS_WILD_PREFIX,
  914. HTTPS_WILD_PREFIX_LENGTH * sizeof(WCHAR)
  915. );
  916. PortLength = DIFF(pNextToken - pPortNum) * sizeof(WCHAR);
  917. TokenLength = HTTPS_WILD_PREFIX_LENGTH + PortLength;
  918. ASSERT(TokenLength < (sizeof(WildSiteUrl)-sizeof(WCHAR)));
  919. RtlCopyMemory(
  920. &(WildSiteUrl[HTTPS_WILD_PREFIX_LENGTH/sizeof(WCHAR)]),
  921. pPortNum,
  922. PortLength
  923. );
  924. WildSiteUrl[TokenLength/sizeof(WCHAR)] = UNICODE_NULL;
  925. }
  926. //
  927. // is there a wildcard entry?
  928. //
  929. Status = UlpTreeBinaryFindEntry(
  930. g_pSites,
  931. WildSiteUrl,
  932. TokenLength,
  933. HASH_INVALID_SIGNATURE,
  934. &Index
  935. );
  936. if (NT_SUCCESS(Status) == FALSE)
  937. goto end;
  938. //
  939. // return the spot right after the token we just ate
  940. //
  941. *ppNextToken = pNextToken + 1;
  942. //
  943. // and return the entry
  944. //
  945. *ppEntry = g_pSites->pEntries[Index].pEntry;
  946. end:
  947. if (NT_SUCCESS(Status) == FALSE)
  948. {
  949. *ppEntry = NULL;
  950. *ppNextToken = NULL;
  951. }
  952. return Status;
  953. } // UlpTreeFindWildcardMatch
  954. /***************************************************************************++
  955. Routine Description:
  956. finds any matching wildcard site in g_pSites for pUrl.
  957. ASSUMES that the pUrl ALWASY contains a port number and trailing slash,
  958. e.g.:
  959. http://<host>:<port>/
  960. https://<host>:<port>/
  961. http://<host>:<port>/foo/bar/banana.htm
  962. https://<host>:<port>/foo/bar/
  963. Arguments:
  964. IN PWSTR pUrl, the url to match
  965. pHttpConn HTTP Connection object to
  966. get the Server's IP & Port.
  967. CODEWORK: If TDI ever starts giving us back the LocalAddress by default,
  968. we should change this param to be that address (since that's the only
  969. reason we need the pHttpConn).
  970. OUT PWSTR * ppNextToken, output's the next token after
  971. matching the url
  972. OUT PUL_CG_URL_TREE_ENTRY * ppEntry, returns the entry
  973. Return Value:
  974. NTSTATUS - Completion status.
  975. --***************************************************************************/
  976. NTSTATUS
  977. UlpTreeFindIpMatch(
  978. IN PWSTR pUrl,
  979. IN PUL_HTTP_CONNECTION pHttpConn,
  980. OUT PWSTR * ppNextToken,
  981. OUT PUL_CG_URL_TREE_ENTRY * ppEntry
  982. )
  983. {
  984. NTSTATUS Status;
  985. PWSTR pNextToken;
  986. ULONG TokenLength; // Length, in bytes, of token, not counting the NULL
  987. ULONG Index;
  988. WCHAR IpSiteUrl[HTTPS_IP_PREFIX_LENGTH + MAX_ADDRESS_LENGTH + 1];
  989. PWSTR pPortNum;
  990. PWSTR pTmp;
  991. TA_IP_ADDRESS RawAddress;
  992. USHORT IpPortNum;
  993. ULONG IpAddress;
  994. //
  995. // Sanity check.
  996. //
  997. PAGED_CODE();
  998. ASSERT(pUrl != NULL);
  999. ASSERT(ppNextToken != NULL);
  1000. ASSERT(ppEntry != NULL);
  1001. //
  1002. // find the port # (colon location + 1 for https + 1 to skip the colon = + 2)
  1003. //
  1004. pPortNum = wcschr(pUrl + HTTP_PREFIX_COLON_INDEX + 2, L':');
  1005. if (pPortNum == NULL)
  1006. {
  1007. //
  1008. // ouch
  1009. //
  1010. Status = STATUS_INVALID_PARAMETER;
  1011. goto end;
  1012. }
  1013. //
  1014. // Skip the ':'
  1015. //
  1016. pPortNum += 1;
  1017. //
  1018. // find the trailing '/' after the port number
  1019. //
  1020. pNextToken = wcschr(pPortNum, '/');
  1021. if (pNextToken == NULL)
  1022. {
  1023. Status = STATUS_INVALID_PARAMETER;
  1024. goto end;
  1025. }
  1026. //
  1027. // Build IP & Port URL
  1028. //
  1029. //
  1030. // HTTPS or HTTP?
  1031. //
  1032. if (pUrl[HTTP_PREFIX_COLON_INDEX] == L':')
  1033. {
  1034. // HTTP
  1035. RtlCopyMemory(
  1036. IpSiteUrl,
  1037. HTTP_IP_PREFIX,
  1038. HTTP_IP_PREFIX_LENGTH /* * sizeof(WCHAR) */
  1039. );
  1040. TokenLength = HTTP_IP_PREFIX_LENGTH;
  1041. }
  1042. else
  1043. {
  1044. // HTTPS
  1045. RtlCopyMemory(
  1046. IpSiteUrl,
  1047. HTTPS_IP_PREFIX,
  1048. HTTPS_IP_PREFIX_LENGTH /* * sizeof(WCHAR) */
  1049. );
  1050. TokenLength = HTTPS_IP_PREFIX_LENGTH;
  1051. }
  1052. //
  1053. // Add IP & PORT to url
  1054. //
  1055. ASSERT( IS_VALID_CONNECTION(pHttpConn->pConnection) );
  1056. IpAddress = pHttpConn->pConnection->LocalAddress;
  1057. IpPortNum = pHttpConn->pConnection->LocalPort;
  1058. TokenLength += HostAddressAndPortToStringW(
  1059. &(IpSiteUrl[TokenLength/sizeof(WCHAR)]),
  1060. IpAddress,
  1061. IpPortNum
  1062. );
  1063. //
  1064. // Finally, is there an IP & PORT entry?
  1065. //
  1066. Status = UlpTreeFindEntry(
  1067. g_pSites,
  1068. IpSiteUrl,
  1069. TokenLength,
  1070. HASH_INVALID_SIGNATURE,
  1071. &Index,
  1072. NULL
  1073. );
  1074. if (NT_SUCCESS(Status) == FALSE)
  1075. goto end;
  1076. //
  1077. // return the spot right after the token we just ate
  1078. //
  1079. *ppNextToken = pNextToken + 1;
  1080. //
  1081. // and return the entry
  1082. //
  1083. *ppEntry = g_pSites->pEntries[Index].pEntry;
  1084. end:
  1085. if (NT_SUCCESS(Status) == FALSE)
  1086. {
  1087. *ppEntry = NULL;
  1088. *ppNextToken = NULL;
  1089. }
  1090. return Status;
  1091. } // UlpTreeFindIpMatch
  1092. /***************************************************************************++
  1093. Routine Description:
  1094. finds the matching site in g_pSites for pUrl.
  1095. Arguments:
  1096. IN PWSTR pUrl, the url to match
  1097. OUT PWSTR * ppNextToken, output's the next token after
  1098. matching the url
  1099. OUT PUL_CG_URL_TREE_ENTRY * ppEntry, returns the entry
  1100. BOOLEAN AutoCreate should the entry be created?
  1101. Return Value:
  1102. NTSTATUS - Completion status.
  1103. --***************************************************************************/
  1104. NTSTATUS
  1105. UlpTreeFindSite(
  1106. IN PWSTR pUrl,
  1107. OUT PWSTR * ppNextToken,
  1108. OUT PUL_CG_URL_TREE_ENTRY * ppEntry,
  1109. OUT BOOLEAN AutoCreate
  1110. )
  1111. {
  1112. NTSTATUS Status;
  1113. PWSTR pToken;
  1114. PWSTR pNextToken = NULL;
  1115. ULONG TokenLength;
  1116. ULONG TokenHash;
  1117. ULONG Index;
  1118. //
  1119. // Sanity check.
  1120. //
  1121. PAGED_CODE();
  1122. ASSERT(pUrl != NULL);
  1123. ASSERT(ppNextToken != NULL);
  1124. ASSERT(ppEntry != NULL);
  1125. //
  1126. // find the very first '/'
  1127. //
  1128. pToken = wcschr(pUrl, L'/');
  1129. if (pToken == NULL)
  1130. {
  1131. //
  1132. // ouch
  1133. //
  1134. Status = STATUS_INVALID_PARAMETER;
  1135. goto end;
  1136. }
  1137. //
  1138. // skip the '/' and the trailing '/'
  1139. //
  1140. if (pToken[1] != L'/')
  1141. {
  1142. //
  1143. // ouch
  1144. //
  1145. Status = STATUS_INVALID_PARAMETER;
  1146. goto end;
  1147. }
  1148. //
  1149. // skip the "//"
  1150. //
  1151. pToken += 2;
  1152. //
  1153. // Find the closing '/'
  1154. //
  1155. pNextToken = wcschr(pToken, L'/');
  1156. if (pNextToken == NULL)
  1157. {
  1158. //
  1159. // ouch
  1160. //
  1161. Status = STATUS_INVALID_PARAMETER;
  1162. goto end;
  1163. }
  1164. //
  1165. // replace the '/' with a null, we'll fix it later
  1166. //
  1167. pNextToken[0] = UNICODE_NULL;
  1168. //
  1169. // Use the entire prefix as the site token
  1170. //
  1171. pToken = pUrl;
  1172. //
  1173. // CODEWORK longer than 64k token?
  1174. //
  1175. TokenLength = DIFF(pNextToken - pToken) * sizeof(WCHAR);
  1176. pNextToken += 1;
  1177. //
  1178. // find the matching site
  1179. //
  1180. if (AutoCreate)
  1181. {
  1182. Status = UlpTreeFindEntry(
  1183. g_pSites,
  1184. pToken,
  1185. TokenLength,
  1186. HASH_INVALID_SIGNATURE,
  1187. &Index,
  1188. &TokenHash
  1189. );
  1190. //
  1191. // no match?
  1192. //
  1193. if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
  1194. {
  1195. //
  1196. // autocreate the new site
  1197. //
  1198. Status = UlpTreeInsertEntry(
  1199. &g_pSites,
  1200. NULL,
  1201. pToken,
  1202. TokenLength,
  1203. TokenHash,
  1204. Index
  1205. );
  1206. if (NT_SUCCESS(Status) == FALSE)
  1207. goto end;
  1208. //
  1209. // start listening for this site
  1210. //
  1211. Status = UlAddSiteToEndpointList(pToken);
  1212. if (!NT_SUCCESS(Status))
  1213. {
  1214. NTSTATUS TempStatus;
  1215. //
  1216. // free the site we just created
  1217. //
  1218. TempStatus = UlpTreeFreeNode(g_pSites->pEntries[Index].pEntry);
  1219. ASSERT(NT_SUCCESS(TempStatus));
  1220. goto end;
  1221. }
  1222. }
  1223. else if (!NT_SUCCESS(Status))
  1224. {
  1225. //
  1226. // UlpTreeFindEntry returned an error other than
  1227. // "not found". Bail out.
  1228. //
  1229. goto end;
  1230. }
  1231. }
  1232. else
  1233. {
  1234. Status = UlpTreeBinaryFindEntry(
  1235. g_pSites,
  1236. pToken,
  1237. TokenLength,
  1238. HASH_INVALID_SIGNATURE,
  1239. &Index
  1240. );
  1241. if (!NT_SUCCESS(Status))
  1242. {
  1243. goto end;
  1244. }
  1245. }
  1246. //
  1247. // set returns
  1248. //
  1249. *ppEntry = g_pSites->pEntries[Index].pEntry;
  1250. *ppNextToken = pNextToken;
  1251. end:
  1252. if (pNextToken != NULL)
  1253. {
  1254. //
  1255. // Fix the string, i replaced the '/' with a UNICODE_NULL
  1256. //
  1257. (pNextToken-1)[0] = L'/';
  1258. }
  1259. if (!NT_SUCCESS(Status))
  1260. {
  1261. *ppEntry = NULL;
  1262. *ppNextToken = NULL;
  1263. }
  1264. return Status;
  1265. }
  1266. /***************************************************************************++
  1267. Routine Description:
  1268. walks the sorted children array pHeader looking for a matching entry
  1269. for pToken.
  1270. Arguments:
  1271. IN PUL_CG_URL_TREE_HEADER pHeader, The children array to look in
  1272. IN PWSTR pToken, the token to look for
  1273. IN ULONG TokenLength, the length in char's of pToken
  1274. OUT ULONG * pIndex the found index. or if not found
  1275. the index of the place an entry
  1276. with pToken should be inserted.
  1277. Return Value:
  1278. NTSTATUS - Completion status.
  1279. STATUS_OBJECT_NAME_NOT_FOUND didn't find it
  1280. --***************************************************************************/
  1281. NTSTATUS
  1282. UlpTreeFindEntry(
  1283. IN PUL_CG_URL_TREE_HEADER pHeader OPTIONAL,
  1284. IN PWSTR pToken,
  1285. IN ULONG TokenLength,
  1286. IN ULONG TokenHash OPTIONAL,
  1287. OUT ULONG * pIndex,
  1288. OUT ULONG * pTokenHash OPTIONAL
  1289. )
  1290. {
  1291. NTSTATUS Status;
  1292. ULONG Index = 0;
  1293. //
  1294. // Sanity check.
  1295. //
  1296. PAGED_CODE();
  1297. ASSERT(pHeader == NULL || IS_VALID_TREE_HEADER(pHeader));
  1298. ASSERT(pIndex != NULL);
  1299. if (TokenLength == 0)
  1300. {
  1301. return STATUS_INVALID_PARAMETER;
  1302. }
  1303. //
  1304. // Assume we didn't find it
  1305. //
  1306. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  1307. ASSERT(TokenLength > 0 && pToken != NULL && pToken[0] != UNICODE_NULL);
  1308. //
  1309. // compute the hash
  1310. //
  1311. if (TokenHash == HASH_INVALID_SIGNATURE)
  1312. {
  1313. TokenHash = HashRandomizeBits(HashStringNoCaseW(pToken, 0));
  1314. ASSERT(TokenHash != HASH_INVALID_SIGNATURE);
  1315. }
  1316. //
  1317. // any siblings to search through?
  1318. //
  1319. if (pHeader != NULL)
  1320. {
  1321. //
  1322. // Walk the sorted array looking for a match (linear)
  1323. //
  1324. //
  1325. // CODEWORK: make this a binary search!
  1326. //
  1327. for (Index = 0; Index < pHeader->UsedCount; Index++)
  1328. {
  1329. ASSERT(IS_VALID_TREE_ENTRY(pHeader->pEntries[Index].pEntry));
  1330. //
  1331. // How does the hash compare?
  1332. //
  1333. if (TokenHash == pHeader->pEntries[Index].TokenHash)
  1334. {
  1335. //
  1336. // and the length?
  1337. //
  1338. if (TokenLength == pHeader->pEntries[Index].pEntry->TokenLength)
  1339. {
  1340. //
  1341. // double check with a strcmp just for fun
  1342. //
  1343. int Temp = _wcsnicmp(
  1344. pToken,
  1345. pHeader->pEntries[Index].pEntry->pToken,
  1346. TokenLength/sizeof(WCHAR)
  1347. );
  1348. if (Temp > 0)
  1349. {
  1350. //
  1351. // larger , time to stop
  1352. //
  1353. break;
  1354. }
  1355. else if (Temp == 0)
  1356. {
  1357. // Found it
  1358. //
  1359. Status = STATUS_SUCCESS;
  1360. break;
  1361. }
  1362. //
  1363. // else if (Temp < 0) keep scanning,
  1364. //
  1365. }
  1366. else if (TokenLength > pHeader->pEntries[Index].pEntry->TokenLength)
  1367. {
  1368. //
  1369. // larger, time to stop
  1370. //
  1371. break;
  1372. }
  1373. }
  1374. else if (TokenHash > pHeader->pEntries[Index].TokenHash)
  1375. {
  1376. //
  1377. // larger, time to stop
  1378. //
  1379. break;
  1380. }
  1381. }
  1382. }
  1383. //
  1384. // return vals
  1385. //
  1386. *pIndex = Index;
  1387. if (pTokenHash != NULL)
  1388. {
  1389. *pTokenHash = TokenHash;
  1390. }
  1391. return Status;
  1392. }
  1393. /***************************************************************************++
  1394. Routine Description:
  1395. walks the sorted children array pHeader looking for a matching entry
  1396. for pToken.
  1397. Arguments:
  1398. IN PUL_CG_URL_TREE_HEADER pHeader, The children array to look in
  1399. IN PWSTR pToken, the token to look for
  1400. IN ULONG TokenLength, the length in char's of pToken
  1401. OUT ULONG * pIndex the found index. or if not found
  1402. the index of the place an entry
  1403. with pToken should be inserted.
  1404. Return Value:
  1405. NTSTATUS - Completion status.
  1406. STATUS_OBJECT_NAME_NOT_FOUND didn't find it
  1407. --***************************************************************************/
  1408. NTSTATUS
  1409. UlpTreeBinaryFindEntry(
  1410. IN PUL_CG_URL_TREE_HEADER pHeader OPTIONAL,
  1411. IN PWSTR pToken,
  1412. IN ULONG TokenLength,
  1413. IN ULONG TokenHash OPTIONAL,
  1414. OUT PULONG pIndex
  1415. )
  1416. {
  1417. NTSTATUS Status;
  1418. LONG Index = 0;
  1419. LONG StartIndex;
  1420. LONG EndIndex;
  1421. //
  1422. // Sanity check.
  1423. //
  1424. PAGED_CODE();
  1425. ASSERT(pHeader == NULL || IS_VALID_TREE_HEADER(pHeader));
  1426. ASSERT(pIndex != NULL);
  1427. if (TokenLength == 0)
  1428. {
  1429. return STATUS_INVALID_PARAMETER;
  1430. }
  1431. //
  1432. // Assume we didn't find it
  1433. //
  1434. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  1435. ASSERT(TokenLength > 0 && pToken != NULL && pToken[0] != UNICODE_NULL);
  1436. //
  1437. // any siblings to search through?
  1438. //
  1439. if (pHeader != NULL)
  1440. {
  1441. //
  1442. // compute the hash
  1443. //
  1444. if (TokenHash == HASH_INVALID_SIGNATURE)
  1445. {
  1446. TokenHash = HashRandomizeBits(HashStringNoCaseW(pToken, 0));
  1447. ASSERT(TokenHash != HASH_INVALID_SIGNATURE);
  1448. }
  1449. //
  1450. // Walk the sorted array looking for a match (binary search)
  1451. //
  1452. StartIndex = 0;
  1453. EndIndex = pHeader->UsedCount - 1;
  1454. while (StartIndex <= EndIndex)
  1455. {
  1456. Index = (StartIndex + EndIndex) / 2;
  1457. ASSERT(IS_VALID_TREE_ENTRY(pHeader->pEntries[Index].pEntry));
  1458. //
  1459. // How does the hash compare?
  1460. //
  1461. if (TokenHash == pHeader->pEntries[Index].TokenHash)
  1462. {
  1463. //
  1464. // and the length?
  1465. //
  1466. if (TokenLength == pHeader->pEntries[Index].pEntry->TokenLength)
  1467. {
  1468. //
  1469. // double check with a strcmp just for fun
  1470. //
  1471. int Temp = _wcsnicmp(
  1472. pToken,
  1473. pHeader->pEntries[Index].pEntry->pToken,
  1474. TokenLength/sizeof(WCHAR)
  1475. );
  1476. if (Temp == 0)
  1477. {
  1478. // Found it
  1479. //
  1480. Status = STATUS_SUCCESS;
  1481. break;
  1482. }
  1483. else
  1484. {
  1485. if (Temp < 0)
  1486. {
  1487. //
  1488. // Adjust StartIndex forward.
  1489. //
  1490. StartIndex = Index + 1;
  1491. }
  1492. else
  1493. {
  1494. //
  1495. // Adjust EndIndex backward.
  1496. //
  1497. EndIndex = Index - 1;
  1498. }
  1499. }
  1500. }
  1501. else
  1502. {
  1503. if (TokenLength < pHeader->pEntries[Index].pEntry->TokenLength)
  1504. {
  1505. //
  1506. // Adjust StartIndex forward.
  1507. //
  1508. StartIndex = Index + 1;
  1509. }
  1510. else
  1511. {
  1512. //
  1513. // Adjust EndIndex backward.
  1514. //
  1515. EndIndex = Index - 1;
  1516. }
  1517. }
  1518. }
  1519. else
  1520. {
  1521. if (TokenHash < pHeader->pEntries[Index].TokenHash)
  1522. {
  1523. //
  1524. // Adjust StartIndex forward.
  1525. //
  1526. StartIndex = Index + 1;
  1527. }
  1528. else
  1529. {
  1530. //
  1531. // Adjust EndIndex backward.
  1532. //
  1533. EndIndex = Index - 1;
  1534. }
  1535. }
  1536. }
  1537. }
  1538. //
  1539. // return vals
  1540. //
  1541. *pIndex = Index;
  1542. return Status;
  1543. }
  1544. /***************************************************************************++
  1545. Routine Description:
  1546. inserts a new entry storing pToken as a child in the array ppHeader.
  1547. it will grow/allocate ppHeader as necessary.
  1548. Arguments:
  1549. IN OUT PUL_CG_URL_TREE_HEADER * ppHeader, the children array (might change)
  1550. IN PUL_CG_URL_TREE_ENTRY pParent, the parent to set this child to
  1551. IN PWSTR pToken, the token of the new entry
  1552. IN ULONG TokenLength, token length
  1553. IN ULONG Index the index to insert it at.
  1554. it will shuffle the array
  1555. if necessary.
  1556. Return Value:
  1557. NTSTATUS - Completion status.
  1558. STATUS_NO_MEMORY allocation failed
  1559. --***************************************************************************/
  1560. NTSTATUS
  1561. UlpTreeInsertEntry(
  1562. IN OUT PUL_CG_URL_TREE_HEADER * ppHeader,
  1563. IN PUL_CG_URL_TREE_ENTRY pParent OPTIONAL,
  1564. IN PWSTR pToken,
  1565. IN ULONG TokenLength,
  1566. IN ULONG TokenHash,
  1567. IN ULONG Index
  1568. )
  1569. {
  1570. NTSTATUS Status;
  1571. PUL_CG_URL_TREE_HEADER pHeader = NULL;
  1572. PUL_CG_URL_TREE_ENTRY pEntry = NULL;
  1573. //
  1574. // Sanity check.
  1575. //
  1576. PAGED_CODE();
  1577. ASSERT(ppHeader != NULL);
  1578. ASSERT(pParent == NULL || IS_VALID_TREE_ENTRY(pParent));
  1579. ASSERT(pToken != NULL);
  1580. ASSERT(TokenLength > 0);
  1581. ASSERT(TokenHash != HASH_INVALID_SIGNATURE);
  1582. ASSERT(
  1583. (*ppHeader == NULL) ?
  1584. Index == 0 :
  1585. IS_VALID_TREE_HEADER(*ppHeader) && (Index <= (*ppHeader)->UsedCount)
  1586. );
  1587. pHeader = *ppHeader;
  1588. //
  1589. // any exiting siblings?
  1590. //
  1591. if (pHeader == NULL)
  1592. {
  1593. //
  1594. // allocate a sibling array
  1595. //
  1596. pHeader = UL_ALLOCATE_STRUCT_WITH_SPACE(
  1597. PagedPool,
  1598. UL_CG_URL_TREE_HEADER,
  1599. sizeof(UL_CG_HEADER_ENTRY) * UL_CG_DEFAULT_TREE_WIDTH,
  1600. UL_CG_TREE_HEADER_POOL_TAG
  1601. );
  1602. if (pHeader == NULL)
  1603. {
  1604. Status = STATUS_NO_MEMORY;
  1605. goto end;
  1606. }
  1607. RtlZeroMemory(
  1608. pHeader,
  1609. sizeof(UL_CG_URL_TREE_HEADER) +
  1610. sizeof(UL_CG_HEADER_ENTRY) * UL_CG_DEFAULT_TREE_WIDTH
  1611. );
  1612. pHeader->Signature = UL_CG_TREE_HEADER_POOL_TAG;
  1613. pHeader->AllocCount = UL_CG_DEFAULT_TREE_WIDTH;
  1614. }
  1615. else if ((pHeader->UsedCount + 1) > pHeader->AllocCount)
  1616. {
  1617. PUL_CG_URL_TREE_HEADER pNewHeader;
  1618. //
  1619. // Grow a bigger array
  1620. //
  1621. pNewHeader = UL_ALLOCATE_STRUCT_WITH_SPACE(
  1622. PagedPool,
  1623. UL_CG_URL_TREE_HEADER,
  1624. sizeof(UL_CG_HEADER_ENTRY) * (pHeader->AllocCount * 2),
  1625. UL_CG_TREE_HEADER_POOL_TAG
  1626. );
  1627. if (pNewHeader == NULL)
  1628. {
  1629. Status = STATUS_NO_MEMORY;
  1630. goto end;
  1631. }
  1632. RtlCopyMemory(
  1633. pNewHeader,
  1634. pHeader,
  1635. sizeof(UL_CG_URL_TREE_HEADER) +
  1636. sizeof(UL_CG_HEADER_ENTRY) * pHeader->AllocCount
  1637. );
  1638. RtlZeroMemory(
  1639. ((PUCHAR)pNewHeader) + sizeof(UL_CG_URL_TREE_HEADER) +
  1640. sizeof(UL_CG_HEADER_ENTRY) * pHeader->AllocCount,
  1641. sizeof(UL_CG_HEADER_ENTRY) * pHeader->AllocCount
  1642. );
  1643. pNewHeader->AllocCount *= 2;
  1644. pHeader = pNewHeader;
  1645. }
  1646. //
  1647. // Allocate an entry
  1648. //
  1649. pEntry = UL_ALLOCATE_STRUCT_WITH_SPACE(
  1650. PagedPool,
  1651. UL_CG_URL_TREE_ENTRY,
  1652. TokenLength + sizeof(WCHAR),
  1653. UL_CG_TREE_ENTRY_POOL_TAG
  1654. );
  1655. if (pEntry == NULL)
  1656. {
  1657. Status = STATUS_NO_MEMORY;
  1658. goto end;
  1659. }
  1660. RtlZeroMemory(
  1661. pEntry,
  1662. sizeof(UL_CG_URL_TREE_ENTRY) +
  1663. TokenLength + sizeof(WCHAR)
  1664. );
  1665. pEntry->Signature = UL_CG_TREE_ENTRY_POOL_TAG;
  1666. pEntry->pParent = pParent;
  1667. pEntry->TokenHash = TokenHash;
  1668. pEntry->TokenLength = TokenLength;
  1669. RtlCopyMemory(pEntry->pToken, pToken, TokenLength + sizeof(WCHAR));
  1670. //
  1671. // need to shuffle things around?
  1672. //
  1673. if (Index < pHeader->UsedCount)
  1674. {
  1675. //
  1676. // shift right the chunk at Index
  1677. //
  1678. RtlMoveMemory(
  1679. &(pHeader->pEntries[Index+1]),
  1680. &(pHeader->pEntries[Index]),
  1681. (pHeader->UsedCount - Index) * sizeof(UL_CG_HEADER_ENTRY)
  1682. );
  1683. }
  1684. pHeader->pEntries[Index].TokenHash = TokenHash;
  1685. pHeader->pEntries[Index].pEntry = pEntry;
  1686. pHeader->UsedCount += 1;
  1687. Status = STATUS_SUCCESS;
  1688. UlTrace(
  1689. CONFIG_GROUP_TREE, (
  1690. "http!UlpTreeInsertEntry('%S', %d) %S%S\n",
  1691. pToken,
  1692. Index,
  1693. (Index < (pHeader->UsedCount - 1)) ? L"[shifted]" : L"",
  1694. (*ppHeader == NULL) ? L"[alloc'd siblings]" : L""
  1695. )
  1696. );
  1697. end:
  1698. if (NT_SUCCESS(Status) == FALSE)
  1699. {
  1700. if (*ppHeader != pHeader && pHeader != NULL)
  1701. {
  1702. UL_FREE_POOL_WITH_SIG(pHeader, UL_CG_TREE_HEADER_POOL_TAG);
  1703. }
  1704. if (pEntry != NULL)
  1705. {
  1706. UL_FREE_POOL_WITH_SIG(pEntry, UL_CG_TREE_ENTRY_POOL_TAG);
  1707. }
  1708. }
  1709. else
  1710. {
  1711. //
  1712. // return a new buffer to the caller ?
  1713. //
  1714. if (*ppHeader != pHeader)
  1715. {
  1716. if (*ppHeader != NULL)
  1717. {
  1718. //
  1719. // free the old one
  1720. //
  1721. UL_FREE_POOL_WITH_SIG(*ppHeader, UL_CG_TREE_HEADER_POOL_TAG);
  1722. }
  1723. *ppHeader = pHeader;
  1724. }
  1725. }
  1726. return Status;
  1727. }
  1728. /***************************************************************************++
  1729. Routine Description:
  1730. Inserts pUrl into the url tree. returns the inserted entry.
  1731. Arguments:
  1732. IN PWSTR pUrl, the url to insert
  1733. OUT PUL_CG_URL_TREE_ENTRY * ppEntry the new entry
  1734. Return Value:
  1735. NTSTATUS - Completion status.
  1736. STATUS_ADDRESS_ALREADY_EXISTS this url is already in the tree
  1737. --***************************************************************************/
  1738. NTSTATUS
  1739. UlpTreeInsert(
  1740. IN PWSTR pUrl,
  1741. OUT PUL_CG_URL_TREE_ENTRY * ppEntry
  1742. )
  1743. {
  1744. NTSTATUS Status;
  1745. PWSTR pToken;
  1746. PWSTR pNextToken;
  1747. ULONG TokenLength;
  1748. ULONG TokenHash;
  1749. PUL_CG_URL_TREE_ENTRY pEntry = NULL;
  1750. ULONG Index;
  1751. //
  1752. // Sanity check.
  1753. //
  1754. PAGED_CODE();
  1755. ASSERT(g_pSites != NULL);
  1756. ASSERT(ppEntry != NULL);
  1757. ASSERT(pUrl != NULL && pUrl[0] != UNICODE_NULL);
  1758. UlTrace(CONFIG_GROUP_TREE, ("http!UlpTreeInsert('%S')\n", pUrl));
  1759. //
  1760. // get the site match
  1761. //
  1762. Status = UlpTreeFindSite(pUrl, &pNextToken, &pEntry, TRUE);
  1763. if (NT_SUCCESS(Status) == FALSE)
  1764. goto end;
  1765. ASSERT(IS_VALID_TREE_ENTRY(pEntry));
  1766. //
  1767. // any abs_path to add also?
  1768. //
  1769. while (pNextToken != NULL && pNextToken[0] != UNICODE_NULL)
  1770. {
  1771. pToken = pNextToken;
  1772. pNextToken = wcschr(pNextToken, L'/');
  1773. //
  1774. // can be null if this is a leaf
  1775. //
  1776. if (pNextToken != NULL)
  1777. {
  1778. pNextToken[0] = UNICODE_NULL;
  1779. // CODEWORK, longer than 64k token?
  1780. TokenLength = DIFF(pNextToken - pToken) * sizeof(WCHAR);
  1781. pNextToken += 1;
  1782. }
  1783. else
  1784. {
  1785. TokenLength = wcslen(pToken) * sizeof(WCHAR);
  1786. }
  1787. //
  1788. // insert this token as a child
  1789. //
  1790. Status = UlpTreeFindEntry(
  1791. pEntry->pChildren,
  1792. pToken,
  1793. TokenLength,
  1794. HASH_INVALID_SIGNATURE,
  1795. &Index,
  1796. &TokenHash
  1797. );
  1798. if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
  1799. {
  1800. //
  1801. // no match, let's add this new one
  1802. //
  1803. Status = UlpTreeInsertEntry(
  1804. &pEntry->pChildren,
  1805. pEntry,
  1806. pToken,
  1807. TokenLength,
  1808. TokenHash,
  1809. Index
  1810. );
  1811. }
  1812. if (pNextToken != NULL)
  1813. {
  1814. //
  1815. // fixup the UNICODE_NULL from above
  1816. //
  1817. (pNextToken-1)[0] = L'/';
  1818. }
  1819. if (NT_SUCCESS(Status) == FALSE)
  1820. goto end;
  1821. //
  1822. // dive down deeper !
  1823. //
  1824. pEntry = pEntry->pChildren->pEntries[Index].pEntry;
  1825. ASSERT(IS_VALID_TREE_ENTRY(pEntry));
  1826. //
  1827. // loop!
  1828. //
  1829. }
  1830. //
  1831. // Are we actually creating anything?
  1832. //
  1833. if (pEntry->FullUrl == 1)
  1834. {
  1835. //
  1836. // nope, a dup. bail.
  1837. //
  1838. Status = STATUS_ADDRESS_ALREADY_EXISTS;
  1839. goto end;
  1840. }
  1841. //
  1842. // at the leaf node, set our leaf marks
  1843. //
  1844. pEntry->FullUrl = 1;
  1845. //
  1846. // return the entry
  1847. //
  1848. *ppEntry = pEntry;
  1849. //
  1850. // all done
  1851. //
  1852. Status = STATUS_SUCCESS;
  1853. end:
  1854. if (NT_SUCCESS(Status) == FALSE)
  1855. {
  1856. //
  1857. // Something failed. Need to clean up the partial branch
  1858. //
  1859. if (pEntry != NULL && pEntry->FullUrl == 0)
  1860. {
  1861. NTSTATUS TempStatus;
  1862. TempStatus = UlpTreeFreeNode(pEntry);
  1863. ASSERT(NT_SUCCESS(TempStatus));
  1864. }
  1865. }
  1866. return Status;
  1867. }
  1868. /***************************************************************************++
  1869. Routine Description:
  1870. init code. not re-entrant.
  1871. Arguments:
  1872. none.
  1873. Return Value:
  1874. NTSTATUS - Completion status.
  1875. STATUS_NO_MEMORY allocation failed
  1876. --***************************************************************************/
  1877. NTSTATUS
  1878. UlInitializeCG(
  1879. VOID
  1880. )
  1881. {
  1882. NTSTATUS Status;
  1883. //
  1884. // Sanity check.
  1885. //
  1886. PAGED_CODE();
  1887. ASSERT( g_InitCGCalled == FALSE );
  1888. if (g_InitCGCalled == FALSE)
  1889. {
  1890. //
  1891. // init our globals
  1892. //
  1893. //
  1894. // Alloc our site array
  1895. //
  1896. g_pSites = UL_ALLOCATE_STRUCT_WITH_SPACE(
  1897. PagedPool,
  1898. UL_CG_URL_TREE_HEADER,
  1899. sizeof(UL_CG_HEADER_ENTRY) * UL_CG_DEFAULT_TREE_WIDTH,
  1900. UL_CG_TREE_HEADER_POOL_TAG
  1901. );
  1902. if (g_pSites == NULL)
  1903. return STATUS_NO_MEMORY;
  1904. RtlZeroMemory(
  1905. g_pSites,
  1906. sizeof(UL_CG_URL_TREE_HEADER) +
  1907. sizeof(UL_CG_HEADER_ENTRY) * UL_CG_DEFAULT_TREE_WIDTH
  1908. );
  1909. g_pSites->Signature = UL_CG_TREE_HEADER_POOL_TAG;
  1910. g_pSites->AllocCount = UL_CG_DEFAULT_TREE_WIDTH;
  1911. //
  1912. // init our non-paged entries
  1913. //
  1914. Status = UlInitializeResource(
  1915. &(g_pUlNonpagedData->ConfigGroupResource),
  1916. "ConfigGroupResource",
  1917. 0,
  1918. UL_CG_RESOURCE_TAG
  1919. );
  1920. if (NT_SUCCESS(Status) == FALSE)
  1921. {
  1922. UL_FREE_POOL_WITH_SIG(g_pSites, UL_CG_TREE_HEADER_POOL_TAG);
  1923. return Status;
  1924. }
  1925. g_InitCGCalled = TRUE;
  1926. }
  1927. return STATUS_SUCCESS;
  1928. }
  1929. /***************************************************************************++
  1930. Routine Description:
  1931. termination code
  1932. Arguments:
  1933. none.
  1934. Return Value:
  1935. none.
  1936. --***************************************************************************/
  1937. VOID
  1938. UlTerminateCG(
  1939. VOID
  1940. )
  1941. {
  1942. NTSTATUS Status;
  1943. //
  1944. // Sanity check.
  1945. //
  1946. PAGED_CODE();
  1947. if (g_InitCGCalled)
  1948. {
  1949. Status = UlDeleteResource(&(g_pUlNonpagedData->ConfigGroupResource));
  1950. ASSERT(NT_SUCCESS(Status));
  1951. if (g_pSites != NULL)
  1952. {
  1953. ASSERT( g_pSites->UsedCount == 0 );
  1954. //
  1955. // Nuke the header.
  1956. //
  1957. UL_FREE_POOL_WITH_SIG(
  1958. g_pSites,
  1959. UL_CG_TREE_HEADER_POOL_TAG
  1960. );
  1961. }
  1962. //
  1963. // The tree should be gone, all handles have been closed
  1964. //
  1965. ASSERT(g_pSites == NULL || g_pSites->UsedCount == 0);
  1966. g_InitCGCalled = FALSE;
  1967. }
  1968. }
  1969. /***************************************************************************++
  1970. Routine Description:
  1971. creates a new config group and returns the id
  1972. Arguments:
  1973. OUT PUL_CONFIG_GROUP_ID pConfigGroupId returns the new id
  1974. Return Value:
  1975. NTSTATUS - Completion status.
  1976. STATUS_NO_MEMORY allocation failed
  1977. --***************************************************************************/
  1978. NTSTATUS
  1979. UlCreateConfigGroup(
  1980. IN PUL_CONTROL_CHANNEL pControlChannel,
  1981. OUT PHTTP_CONFIG_GROUP_ID pConfigGroupId
  1982. )
  1983. {
  1984. PUL_CONFIG_GROUP_OBJECT pNewObject = NULL;
  1985. NTSTATUS Status;
  1986. //
  1987. // Sanity check.
  1988. //
  1989. PAGED_CODE();
  1990. ASSERT(pControlChannel != NULL);
  1991. ASSERT(pConfigGroupId != NULL);
  1992. UlTrace(CONFIG_GROUP_FNC, ("http!UlCreateConfigGroup\n"));
  1993. //
  1994. // Create an empty config group object structure - PAGED
  1995. //
  1996. Status = UlpCreateConfigGroupObject(&pNewObject);
  1997. if (!NT_SUCCESS(Status)) {
  1998. goto end;
  1999. }
  2000. //
  2001. // Link it into the control channel
  2002. //
  2003. UlAddNotifyEntry(
  2004. &pControlChannel->ConfigGroupHead,
  2005. &pNewObject->HandleEntry
  2006. );
  2007. //
  2008. // remember the control channel
  2009. //
  2010. pNewObject->pControlChannel = pControlChannel;
  2011. //
  2012. // Return the new id.
  2013. //
  2014. *pConfigGroupId = pNewObject->ConfigGroupId;
  2015. end:
  2016. if (!NT_SUCCESS(Status))
  2017. {
  2018. //
  2019. // Something failed. Let's clean up.
  2020. //
  2021. HTTP_SET_NULL_ID(pConfigGroupId);
  2022. if (pNewObject != NULL)
  2023. {
  2024. UlDeleteConfigGroup(pNewObject->ConfigGroupId);
  2025. }
  2026. }
  2027. return Status;
  2028. }
  2029. /***************************************************************************++
  2030. Routine Description:
  2031. returns the config group id that matches the config group object linked
  2032. in list_entry
  2033. Arguments:
  2034. IN PLIST_ENTRY pControlChannelEntry - the listentry for this config group
  2035. Return Value:
  2036. NTSTATUS - Completion status.
  2037. --***************************************************************************/
  2038. HTTP_CONFIG_GROUP_ID
  2039. UlConfigGroupFromListEntry(
  2040. IN PLIST_ENTRY pControlChannelEntry
  2041. )
  2042. {
  2043. PUL_CONFIG_GROUP_OBJECT pObject;
  2044. //
  2045. // Sanity check.
  2046. //
  2047. PAGED_CODE();
  2048. ASSERT(pControlChannelEntry != NULL);
  2049. pObject = CONTAINING_RECORD(
  2050. pControlChannelEntry,
  2051. UL_CONFIG_GROUP_OBJECT,
  2052. ControlChannelEntry
  2053. );
  2054. ASSERT(IS_VALID_CONFIG_GROUP(pObject));
  2055. return pObject->ConfigGroupId;
  2056. }
  2057. /***************************************************************************++
  2058. Routine Description:
  2059. deletes the config group ConfigGroupId cleaning all of it's urls.
  2060. Arguments:
  2061. IN HTTP_CONFIG_GROUP_ID ConfigGroupId the group to delete
  2062. Return Value:
  2063. NTSTATUS - Completion status.
  2064. STATUS_INVALID_PARAMETER bad config group id
  2065. --***************************************************************************/
  2066. NTSTATUS
  2067. UlDeleteConfigGroup(
  2068. IN HTTP_CONFIG_GROUP_ID ConfigGroupId
  2069. )
  2070. {
  2071. NTSTATUS Status;
  2072. PUL_CONFIG_GROUP_OBJECT pObject;
  2073. //
  2074. // Sanity check.
  2075. //
  2076. PAGED_CODE();
  2077. UlTrace(CONFIG_GROUP_FNC, ("http!UlDeleteConfigGroup\n"));
  2078. CG_LOCK_WRITE();
  2079. //
  2080. // Get ConfigGroup from opaque id
  2081. //
  2082. pObject = (PUL_CONFIG_GROUP_OBJECT)
  2083. UlGetObjectFromOpaqueId(
  2084. ConfigGroupId,
  2085. UlOpaqueIdTypeConfigGroup,
  2086. UlReferenceConfigGroup
  2087. );
  2088. if (pObject == NULL)
  2089. {
  2090. Status = STATUS_INVALID_PARAMETER;
  2091. goto end;
  2092. }
  2093. ASSERT(IS_VALID_CONFIG_GROUP(pObject));
  2094. HTTP_SET_NULL_ID(&(pObject->ConfigGroupId));
  2095. //
  2096. // Drop the extra reference as a result of the successful get
  2097. //
  2098. DEREFERENCE_CONFIG_GROUP(pObject);
  2099. //
  2100. // Unlink it from the control channel and parent
  2101. //
  2102. UlRemoveNotifyEntry(&pObject->HandleEntry);
  2103. UlRemoveNotifyEntry(&pObject->ParentEntry);
  2104. //
  2105. // let the control channel go
  2106. //
  2107. pObject->pControlChannel = NULL;
  2108. //
  2109. // flush the URI cache.
  2110. // CODEWORK: if we were smarter we could make this more granular
  2111. //
  2112. UlFlushCache();
  2113. //
  2114. // unlink any transient urls below us
  2115. //
  2116. UlNotifyAllEntries(
  2117. UlNotifyOrphanedConfigGroup,
  2118. &pObject->ChildHead,
  2119. NULL
  2120. );
  2121. //
  2122. // Unlink all of the url's in the config group
  2123. //
  2124. Status = UlpCleanAllUrls(pObject);
  2125. //
  2126. // let the error fall through ....
  2127. //
  2128. //
  2129. // remove the opaque id and its reference
  2130. //
  2131. UlFreeOpaqueId(ConfigGroupId, UlOpaqueIdTypeConfigGroup);
  2132. DEREFERENCE_CONFIG_GROUP(pObject);
  2133. //
  2134. // all done
  2135. //
  2136. end:
  2137. CG_UNLOCK_WRITE();
  2138. return Status;
  2139. } // UlDeleteConfigGroup
  2140. /***************************************************************************++
  2141. Routine Description:
  2142. Addref's the config group object
  2143. Arguments:
  2144. pConfigGroup - the object to add ref
  2145. Return Value:
  2146. NTSTATUS - Completion status.
  2147. --***************************************************************************/
  2148. VOID
  2149. UlReferenceConfigGroup(
  2150. IN PVOID pObject
  2151. REFERENCE_DEBUG_FORMAL_PARAMS
  2152. )
  2153. {
  2154. LONG refCount;
  2155. PUL_CONFIG_GROUP_OBJECT pConfigGroup = (PUL_CONFIG_GROUP_OBJECT) pObject;
  2156. //
  2157. // Sanity check.
  2158. //
  2159. ASSERT(IS_VALID_CONFIG_GROUP(pConfigGroup));
  2160. refCount = InterlockedIncrement(&pConfigGroup->RefCount);
  2161. WRITE_REF_TRACE_LOG(
  2162. g_pConfigGroupTraceLog,
  2163. REF_ACTION_REFERENCE_CONFIG_GROUP,
  2164. refCount,
  2165. pConfigGroup,
  2166. pFileName,
  2167. LineNumber
  2168. );
  2169. UlTrace(
  2170. REFCOUNT, (
  2171. "http!UlReferenceConfigGroup cgroup=%p refcount=%d\n",
  2172. pConfigGroup,
  2173. refCount)
  2174. );
  2175. } // UlReferenceConfigGroup
  2176. /***************************************************************************++
  2177. Routine Description:
  2178. Releases the config group object
  2179. Arguments:
  2180. pConfigGroup - the object to deref
  2181. Return Value:
  2182. NTSTATUS - Completion status.
  2183. --***************************************************************************/
  2184. VOID
  2185. UlDereferenceConfigGroup(
  2186. PUL_CONFIG_GROUP_OBJECT pConfigGroup
  2187. REFERENCE_DEBUG_FORMAL_PARAMS
  2188. )
  2189. {
  2190. LONG refCount;
  2191. //
  2192. // Sanity check.
  2193. //
  2194. ASSERT(IS_VALID_CONFIG_GROUP(pConfigGroup));
  2195. refCount = InterlockedDecrement( &pConfigGroup->RefCount );
  2196. WRITE_REF_TRACE_LOG(
  2197. g_pConfigGroupTraceLog,
  2198. REF_ACTION_DEREFERENCE_CONFIG_GROUP,
  2199. refCount,
  2200. pConfigGroup,
  2201. pFileName,
  2202. LineNumber
  2203. );
  2204. UlTrace(
  2205. REFCOUNT, (
  2206. "http!UlDereferenceConfigGroup cgroup=%p refcount=%d\n",
  2207. pConfigGroup,
  2208. refCount)
  2209. );
  2210. if (refCount == 0)
  2211. {
  2212. //
  2213. // now it's time to free the object
  2214. //
  2215. // If OpaqueId is non-zero, then refCount should not be zero
  2216. ASSERT(HTTP_IS_NULL_ID(&pConfigGroup->ConfigGroupId));
  2217. #if INVESTIGATE_LATER
  2218. //
  2219. // Release the opaque id
  2220. //
  2221. UlFreeOpaqueId(pConfigGroup->ConfigGroupId, UlOpaqueIdTypeConfigGroup);
  2222. #endif
  2223. //
  2224. // Release the app pool
  2225. //
  2226. if (pConfigGroup->AppPoolFlags.Present == 1)
  2227. {
  2228. if (pConfigGroup->pAppPool != NULL)
  2229. {
  2230. DEREFERENCE_APP_POOL(pConfigGroup->pAppPool);
  2231. pConfigGroup->pAppPool = NULL;
  2232. }
  2233. pConfigGroup->AppPoolFlags.Present = 0;
  2234. }
  2235. //
  2236. // Release the autoresponse buffer
  2237. //
  2238. if (pConfigGroup->pAutoResponse != NULL)
  2239. {
  2240. UL_DEREFERENCE_INTERNAL_RESPONSE(pConfigGroup->pAutoResponse);
  2241. pConfigGroup->pAutoResponse = NULL;
  2242. }
  2243. //
  2244. // Release the entire object
  2245. //
  2246. if (pConfigGroup->LoggingConfig.Flags.Present &&
  2247. pConfigGroup->LoggingConfig.LogFileDir.Buffer != NULL )
  2248. {
  2249. UL_FREE_POOL(
  2250. pConfigGroup->LoggingConfig.LogFileDir.Buffer,
  2251. UL_CG_LOGDIR_POOL_TAG );
  2252. if ( pConfigGroup->pLogFileEntry )
  2253. UlRemoveLogFileEntry( pConfigGroup->pLogFileEntry );
  2254. }
  2255. else
  2256. {
  2257. ASSERT( NULL == pConfigGroup->pLogFileEntry );
  2258. }
  2259. //
  2260. // Remove any qos flows for this site. This settings should
  2261. // only exists for the root app's cgroup.
  2262. //
  2263. if (pConfigGroup->MaxBandwidth.Flags.Present &&
  2264. pConfigGroup->MaxBandwidth.MaxBandwidth != HTTP_LIMIT_INFINITE )
  2265. {
  2266. UlTcRemoveFlowsForSite( pConfigGroup );
  2267. }
  2268. ASSERT(IsListEmpty(&pConfigGroup->FlowListHead));
  2269. // Deref the connection limit stuff
  2270. if (pConfigGroup->pConnectionCountEntry)
  2271. {
  2272. DEREFERENCE_CONNECTION_COUNT_ENTRY(pConfigGroup->pConnectionCountEntry);
  2273. }
  2274. // deref Site Counters object
  2275. if (pConfigGroup->pSiteCounters)
  2276. {
  2277. DEREFERENCE_SITE_COUNTER_ENTRY(pConfigGroup->pSiteCounters);
  2278. }
  2279. UL_FREE_POOL_WITH_SIG(pConfigGroup, UL_CG_OBJECT_POOL_TAG);
  2280. }
  2281. } // UlDereferenceConfigGroup
  2282. /***************************************************************************++
  2283. Routine Description:
  2284. adds pUrl to the config group ConfigGroupId.
  2285. Arguments:
  2286. IN HTTP_CONFIG_GROUP_ID ConfigGroupId, the cgroup id
  2287. IN PUNICODE_STRING pUrl, the url. must be null terminated.
  2288. IN HTTP_URL_CONTEXT UrlContext the context to associate
  2289. Return Value:
  2290. NTSTATUS - Completion status.
  2291. STATUS_INVALID_PARAMETER bad config group id
  2292. --***************************************************************************/
  2293. NTSTATUS
  2294. UlAddUrlToConfigGroup(
  2295. IN HTTP_CONFIG_GROUP_ID ConfigGroupId,
  2296. IN PUNICODE_STRING pUrl,
  2297. IN HTTP_URL_CONTEXT UrlContext
  2298. )
  2299. {
  2300. NTSTATUS Status;
  2301. PUL_CONFIG_GROUP_OBJECT pObject = NULL;
  2302. PUL_CG_URL_TREE_ENTRY pEntry;
  2303. PUL_CG_URL_TREE_ENTRY pParent;
  2304. PWSTR pNewUrl = NULL;
  2305. //
  2306. // Sanity check.
  2307. //
  2308. PAGED_CODE();
  2309. ASSERT(pUrl != NULL && pUrl->Length > 0 && pUrl->Buffer != NULL);
  2310. ASSERT(pUrl->Buffer[pUrl->Length / sizeof(WCHAR)] == UNICODE_NULL);
  2311. UlTrace(CONFIG_GROUP_FNC,
  2312. ("http!UlAddUrlToConfigGroup('%S' -> %d)\n", pUrl->Buffer, ConfigGroupId));
  2313. //
  2314. // Clean up the url
  2315. //
  2316. Status = UlpSanitizeUrl(pUrl, &pNewUrl);
  2317. if (NT_SUCCESS(Status) == FALSE)
  2318. {
  2319. //
  2320. // no goto end, resource not grabbed
  2321. //
  2322. RETURN(Status);
  2323. }
  2324. CG_LOCK_WRITE();
  2325. //
  2326. // Get the object ptr from id
  2327. //
  2328. pObject = (PUL_CONFIG_GROUP_OBJECT)(
  2329. UlGetObjectFromOpaqueId(
  2330. ConfigGroupId,
  2331. UlOpaqueIdTypeConfigGroup,
  2332. UlReferenceConfigGroup
  2333. )
  2334. );
  2335. if (IS_VALID_CONFIG_GROUP(pObject) == FALSE)
  2336. {
  2337. Status = STATUS_INVALID_PARAMETER;
  2338. goto end;
  2339. }
  2340. //
  2341. // insert it into the tree
  2342. //
  2343. Status = UlpTreeInsert(pNewUrl, &pEntry);
  2344. if (NT_SUCCESS(Status) == FALSE)
  2345. goto end;
  2346. ASSERT(IS_VALID_TREE_ENTRY(pEntry));
  2347. //
  2348. // context associated with this url
  2349. //
  2350. pEntry->UrlContext = UrlContext;
  2351. //
  2352. // link the cfg group + the url
  2353. //
  2354. ASSERT(pEntry->pConfigGroup == NULL);
  2355. ASSERT(pEntry->ConfigGroupListEntry.Flink == NULL);
  2356. pEntry->pConfigGroup = pObject;
  2357. InsertTailList(&pObject->UrlListHead, &pEntry->ConfigGroupListEntry);
  2358. //
  2359. // flush the URI cache.
  2360. // CODEWORK: if we were smarter we could make this more granular
  2361. //
  2362. UlFlushCache();
  2363. //
  2364. // Make sure to also mark a parent dirty .
  2365. // need to search for a non-dummy parent
  2366. //
  2367. // pParent can be NULL for site entries
  2368. //
  2369. //
  2370. pParent = pEntry->pParent;
  2371. while (pParent != NULL)
  2372. {
  2373. ASSERT(IS_VALID_TREE_ENTRY(pParent));
  2374. //
  2375. // a non-dummy?
  2376. //
  2377. if (pParent->FullUrl == 1)
  2378. {
  2379. ASSERT(pParent->pConfigGroup != NULL);
  2380. ASSERT(IS_VALID_CONFIG_GROUP(pParent->pConfigGroup));
  2381. //
  2382. // 1 is enough
  2383. //
  2384. break;
  2385. }
  2386. //
  2387. // walk higher, keep looking for a non-dummy
  2388. //
  2389. pParent = pParent->pParent;
  2390. }
  2391. end:
  2392. if (pObject != NULL)
  2393. {
  2394. DEREFERENCE_CONFIG_GROUP(pObject);
  2395. pObject = NULL;
  2396. }
  2397. CG_UNLOCK_WRITE();
  2398. if (pNewUrl != NULL)
  2399. {
  2400. UL_FREE_POOL(pNewUrl, URL_POOL_TAG);
  2401. pNewUrl = NULL;
  2402. }
  2403. RETURN(Status);
  2404. } // UlAddUrlToConfigGroup
  2405. /***************************************************************************++
  2406. Routine Description:
  2407. removes pUrl from the url tree (and thus the config group) .
  2408. Arguments:
  2409. IN HTTP_CONFIG_GROUP_ID ConfigGroupId, the cgroup id. ignored.
  2410. IN PUNICODE_STRING pUrl, the url. must be null terminated.
  2411. Return Value:
  2412. NTSTATUS - Completion status.
  2413. --***************************************************************************/
  2414. NTSTATUS
  2415. UlRemoveUrlFromConfigGroup(
  2416. IN HTTP_CONFIG_GROUP_ID ConfigGroupId,
  2417. IN PUNICODE_STRING pUrl
  2418. )
  2419. {
  2420. NTSTATUS Status;
  2421. PUL_CG_URL_TREE_ENTRY pEntry;
  2422. PWSTR pNewUrl = NULL;
  2423. PUL_CONFIG_GROUP_OBJECT pObject = NULL;
  2424. //
  2425. // Sanity check.
  2426. //
  2427. PAGED_CODE();
  2428. ASSERT(pUrl != NULL && pUrl->Buffer != NULL && pUrl->Length > 0);
  2429. UlTrace(CONFIG_GROUP_FNC, ("http!UlRemoveUrlFromConfigGroup\n"));
  2430. //
  2431. // Cleanup the passed in url
  2432. //
  2433. Status = UlpSanitizeUrl(pUrl, &pNewUrl);
  2434. if (!NT_SUCCESS(Status))
  2435. {
  2436. //
  2437. // no goto end, resource not grabbed
  2438. //
  2439. return Status;
  2440. }
  2441. //
  2442. // grab the lock
  2443. //
  2444. CG_LOCK_WRITE();
  2445. //
  2446. // Get the object ptr from id
  2447. //
  2448. pObject = (PUL_CONFIG_GROUP_OBJECT)(
  2449. UlGetObjectFromOpaqueId(
  2450. ConfigGroupId,
  2451. UlOpaqueIdTypeConfigGroup,
  2452. UlReferenceConfigGroup
  2453. )
  2454. );
  2455. if (!IS_VALID_CONFIG_GROUP(pObject))
  2456. {
  2457. Status = STATUS_INVALID_PARAMETER;
  2458. goto end;
  2459. }
  2460. //
  2461. // Lookup the entry in the tree
  2462. //
  2463. Status = UlpTreeFindNode(pNewUrl, FALSE, NULL, NULL, &pEntry);
  2464. if (!NT_SUCCESS(Status)) {
  2465. goto end;
  2466. }
  2467. ASSERT(IS_VALID_TREE_ENTRY(pEntry));
  2468. //
  2469. // Does this tree entry match this config group?
  2470. //
  2471. if (pEntry->pConfigGroup != pObject)
  2472. {
  2473. Status = STATUS_INVALID_OWNER;
  2474. goto end;
  2475. }
  2476. //
  2477. // flush the URI cache.
  2478. // CODEWORK: if we were smarter we could make this more granular
  2479. //
  2480. UlFlushCache();
  2481. //
  2482. // Everything looks good, free the node!
  2483. //
  2484. Status = UlpTreeFreeNode(pEntry);
  2485. if (!NT_SUCCESS(Status)) {
  2486. goto end;
  2487. }
  2488. //
  2489. // NOTE: don't do any more cleanup here... put it in freenode.
  2490. // otherwise it won't get cleaned on handle closes.
  2491. //
  2492. end:
  2493. if (pObject != NULL)
  2494. {
  2495. DEREFERENCE_CONFIG_GROUP(pObject);
  2496. pObject = NULL;
  2497. }
  2498. CG_UNLOCK_WRITE();
  2499. if (pNewUrl != NULL)
  2500. {
  2501. UL_FREE_POOL(pNewUrl, URL_POOL_TAG);
  2502. pNewUrl = NULL;
  2503. }
  2504. return Status;
  2505. } // UlRemoveUrlFromConfigGroup
  2506. /***************************************************************************++
  2507. Routine Description:
  2508. Removes all URLS from the config group.
  2509. Arguments:
  2510. ConfigGroupId - Supplies the config group ID.
  2511. Return Value:
  2512. NTSTATUS - Completion status.
  2513. --***************************************************************************/
  2514. NTSTATUS
  2515. UlRemoveAllUrlsFromConfigGroup(
  2516. IN HTTP_CONFIG_GROUP_ID ConfigGroupId
  2517. )
  2518. {
  2519. NTSTATUS Status;
  2520. PUL_CONFIG_GROUP_OBJECT pObject = NULL;
  2521. //
  2522. // Sanity check.
  2523. //
  2524. PAGED_CODE();
  2525. UlTrace(CONFIG_GROUP_FNC, ("http!UlRemoveAllUrlsFromConfigGroup\n"));
  2526. //
  2527. // grab the lock
  2528. //
  2529. CG_LOCK_WRITE();
  2530. //
  2531. // Get the object ptr from id
  2532. //
  2533. pObject = (PUL_CONFIG_GROUP_OBJECT)(
  2534. UlGetObjectFromOpaqueId(
  2535. ConfigGroupId,
  2536. UlOpaqueIdTypeConfigGroup,
  2537. UlReferenceConfigGroup
  2538. )
  2539. );
  2540. if (IS_VALID_CONFIG_GROUP(pObject) == FALSE)
  2541. {
  2542. Status = STATUS_INVALID_PARAMETER;
  2543. goto end;
  2544. }
  2545. //
  2546. // flush the URI cache.
  2547. // CODEWORK: if we were smarter we could make this more granular
  2548. //
  2549. UlFlushCache();
  2550. //
  2551. // Clean it.
  2552. //
  2553. Status = UlpCleanAllUrls( pObject );
  2554. end:
  2555. if (pObject != NULL)
  2556. {
  2557. DEREFERENCE_CONFIG_GROUP(pObject);
  2558. pObject = NULL;
  2559. }
  2560. CG_UNLOCK_WRITE();
  2561. return Status;
  2562. } // UlRemoveAllUrlsFromConfigGroup
  2563. /***************************************************************************++
  2564. Routine Description:
  2565. Adds a transiently registered URL to the processes special transient
  2566. registration config group. Requests matching this URL will be routed
  2567. to this pool.
  2568. Arguments:
  2569. pProcess - the apool process that will receive the requests
  2570. pUrl - the URL prefix to be mapped in
  2571. --***************************************************************************/
  2572. NTSTATUS
  2573. UlAddTransientUrl(
  2574. PUL_APP_POOL_OBJECT pAppPool,
  2575. PUNICODE_STRING pUrl
  2576. )
  2577. {
  2578. NTSTATUS Status;
  2579. PUL_CONFIG_GROUP_OBJECT pObject = NULL;
  2580. PWSTR pNewUrl = NULL;
  2581. PUL_CG_URL_TREE_ENTRY pEntry = NULL;
  2582. PUL_CG_URL_TREE_ENTRY pParent;
  2583. PUL_CONFIG_GROUP_OBJECT pParentObject;
  2584. //
  2585. // Sanity check
  2586. //
  2587. PAGED_CODE();
  2588. ASSERT(pAppPool);
  2589. ASSERT(pUrl);
  2590. UlTrace(CONFIG_GROUP_FNC, (
  2591. "http!UlAddTransientUrl(pAppPool = %p, Url '%S')\n",
  2592. pAppPool,
  2593. pUrl->Buffer
  2594. ));
  2595. //
  2596. // Clean up the url
  2597. //
  2598. Status = UlpSanitizeUrl(pUrl, &pNewUrl);
  2599. if (!NT_SUCCESS(Status)) {
  2600. RETURN(Status);
  2601. }
  2602. //
  2603. // create a new config group
  2604. //
  2605. Status = UlpCreateConfigGroupObject(&pObject);
  2606. if (!NT_SUCCESS(Status)) {
  2607. UL_FREE_POOL(pNewUrl, URL_POOL_TAG);
  2608. RETURN(Status);
  2609. }
  2610. ASSERT(IS_VALID_CONFIG_GROUP(pObject));
  2611. UlTrace(CONFIG_GROUP_FNC, (
  2612. "http!UlAddTransientUrl(pAppPool = %p, Url '%S')\n"
  2613. " Created pObject = %p\n",
  2614. pAppPool,
  2615. pUrl->Buffer,
  2616. pObject
  2617. ));
  2618. //
  2619. // Set the app pool in the object
  2620. //
  2621. REFERENCE_APP_POOL(pAppPool);
  2622. pObject->AppPoolFlags.Present = 1;
  2623. pObject->pAppPool = pAppPool;
  2624. //
  2625. // lock the tree
  2626. //
  2627. CG_LOCK_WRITE();
  2628. //
  2629. // Add to the tree
  2630. //
  2631. Status = UlpTreeInsert(pNewUrl, &pEntry);
  2632. if (NT_SUCCESS(Status)) {
  2633. ASSERT(IS_VALID_TREE_ENTRY(pEntry));
  2634. UlTrace(CONFIG_GROUP_FNC, (
  2635. "http!UlAddTransientUrl(pAppPool = %p, Url '%S')\n"
  2636. " Created pEntry = %p\n",
  2637. pAppPool,
  2638. pUrl->Buffer,
  2639. pEntry
  2640. ));
  2641. //
  2642. // remember that this is a transient registration
  2643. //
  2644. pEntry->TransientUrl = 1;
  2645. //
  2646. // link the cfg group + the url
  2647. //
  2648. ASSERT(pEntry->pConfigGroup == NULL);
  2649. ASSERT(pEntry->ConfigGroupListEntry.Flink == NULL);
  2650. pEntry->pConfigGroup = pObject;
  2651. InsertTailList(&pObject->UrlListHead, &pEntry->ConfigGroupListEntry);
  2652. //
  2653. // Find parent config group
  2654. //
  2655. pParent = pEntry->pParent;
  2656. while (pParent && (pParent->TransientUrl || !pParent->FullUrl)) {
  2657. pParent = pParent->pParent;
  2658. }
  2659. if (pParent) {
  2660. pParentObject = pParent->pConfigGroup;
  2661. //
  2662. // check security
  2663. //
  2664. if (pParentObject->Security.Flags.Present) {
  2665. SECURITY_SUBJECT_CONTEXT SubjectContext;
  2666. BOOLEAN AccessOk;
  2667. PPRIVILEGE_SET pPrivileges = NULL;
  2668. ACCESS_MASK grantedAccess;
  2669. SeCaptureSubjectContext(&SubjectContext);
  2670. AccessOk = SeAccessCheck(
  2671. pParentObject->Security.pSecurityDescriptor,
  2672. &SubjectContext,
  2673. FALSE, // SubjectContextLocked
  2674. FILE_GENERIC_WRITE,
  2675. 0, // PrevioslyGrantedAccess
  2676. &pPrivileges,
  2677. IoGetFileObjectGenericMapping(),
  2678. UserMode,
  2679. &grantedAccess,
  2680. &Status
  2681. );
  2682. SeReleaseSubjectContext(&SubjectContext);
  2683. } else {
  2684. //
  2685. // if there is no security we do not allow
  2686. // transient registration
  2687. //
  2688. Status = STATUS_ACCESS_DENIED;
  2689. }
  2690. if (NT_SUCCESS(Status)) {
  2691. //
  2692. // link the group to the app pool
  2693. //
  2694. UlAddNotifyEntry(
  2695. &pParentObject->ChildHead,
  2696. &pObject->ParentEntry
  2697. );
  2698. }
  2699. } else {
  2700. //
  2701. // couldn't find a valid parent. We don't allow
  2702. // transients without parents.
  2703. //
  2704. Status = STATUS_ACCESS_DENIED;
  2705. }
  2706. }
  2707. //
  2708. // flush the URI cache.
  2709. // CODEWORK: if we were smarter we could make this more granular
  2710. //
  2711. UlFlushCache();
  2712. //
  2713. // Let go of the tree
  2714. //
  2715. CG_UNLOCK_WRITE();
  2716. if (NT_SUCCESS(Status)) {
  2717. //
  2718. // Link into app pool
  2719. //
  2720. UlLinkConfigGroupToAppPool(pObject, pAppPool);
  2721. } else {
  2722. //
  2723. // No static parent was found or we failed the
  2724. // access check. Clean up.
  2725. //
  2726. UlDeleteConfigGroup(pObject->ConfigGroupId);
  2727. }
  2728. UL_FREE_POOL(pNewUrl, URL_POOL_TAG);
  2729. RETURN(Status);
  2730. }
  2731. /***************************************************************************++
  2732. Routine Description:
  2733. Removes a transiently registered URL from the processes special transient
  2734. registration config group.
  2735. Arguments:
  2736. pProcess - the apool process that had the registration
  2737. pUrl - the URL prefix to be removed
  2738. --***************************************************************************/
  2739. NTSTATUS
  2740. UlRemoveTransientUrl(
  2741. PUL_APP_POOL_OBJECT pAppPool,
  2742. PUNICODE_STRING pUrl
  2743. )
  2744. {
  2745. NTSTATUS Status;
  2746. PUL_CG_URL_TREE_ENTRY pEntry;
  2747. PWSTR pNewUrl = NULL;
  2748. HTTP_CONFIG_GROUP_ID ConfigId = HTTP_NULL_ID;
  2749. //
  2750. // Sanity check.
  2751. //
  2752. PAGED_CODE();
  2753. ASSERT(pAppPool);
  2754. ASSERT(pUrl != NULL && pUrl->Buffer != NULL && pUrl->Length > 0);
  2755. UlTrace(CONFIG_GROUP_FNC, (
  2756. "http!UlRemoveTransientUrl(pAppPool = %p, Url '%S')\n",
  2757. pAppPool,
  2758. pUrl->Buffer
  2759. ));
  2760. //
  2761. // Cleanup the passed in url
  2762. //
  2763. Status = UlpSanitizeUrl(pUrl, &pNewUrl);
  2764. if (!NT_SUCCESS(Status))
  2765. {
  2766. RETURN(Status);
  2767. }
  2768. //
  2769. // grab the lock
  2770. //
  2771. CG_LOCK_WRITE();
  2772. //
  2773. // Lookup the entry in the tree
  2774. //
  2775. Status = UlpTreeFindNode(pNewUrl, FALSE, NULL, NULL, &pEntry);
  2776. if (NT_SUCCESS(Status)) {
  2777. ASSERT(IS_VALID_TREE_ENTRY(pEntry));
  2778. //
  2779. // Is it a transient URL for this app pool?
  2780. //
  2781. if (pEntry->TransientUrl &&
  2782. pEntry->pConfigGroup->AppPoolFlags.Present &&
  2783. pEntry->pConfigGroup->pAppPool == pAppPool)
  2784. {
  2785. ConfigId = pEntry->pConfigGroup->ConfigGroupId;
  2786. } else {
  2787. Status = STATUS_INVALID_OWNER;
  2788. }
  2789. }
  2790. CG_UNLOCK_WRITE();
  2791. //
  2792. // Everything looks good, free the group!
  2793. //
  2794. if (!HTTP_IS_NULL_ID(&ConfigId)) {
  2795. ASSERT(NT_SUCCESS(Status));
  2796. Status = UlDeleteConfigGroup(ConfigId);
  2797. }
  2798. if (pNewUrl != NULL)
  2799. {
  2800. UL_FREE_POOL(pNewUrl, URL_POOL_TAG);
  2801. pNewUrl = NULL;
  2802. }
  2803. RETURN(Status);
  2804. } // UlRemoveTransientUrl
  2805. /***************************************************************************++
  2806. Routine Description:
  2807. allows query information for cgroups. see uldef.h
  2808. Arguments:
  2809. IN HTTP_CONFIG_GROUP_ID ConfigGroupId, cgroup id
  2810. IN HTTP_CONFIG_GROUP_INFORMATION_CLASS InformationClass, what to fetch
  2811. IN PVOID pConfigGroupInformation, output buffer
  2812. IN ULONG Length, length of pConfigGroupInformation
  2813. OUT PULONG pReturnLength OPTIONAL how much was copied into the
  2814. output buffer
  2815. Return Value:
  2816. NTSTATUS - Completion status.
  2817. STATUS_INVALID_PARAMETER bad cgroup id
  2818. STATUS_BUFFER_TOO_SMALL output buffer too small
  2819. STATUS_INVALID_PARAMETER invalid infoclass
  2820. --***************************************************************************/
  2821. NTSTATUS
  2822. UlQueryConfigGroupInformation(
  2823. IN HTTP_CONFIG_GROUP_ID ConfigGroupId,
  2824. IN HTTP_CONFIG_GROUP_INFORMATION_CLASS InformationClass,
  2825. IN PVOID pConfigGroupInformation,
  2826. IN ULONG Length,
  2827. OUT PULONG pReturnLength OPTIONAL
  2828. )
  2829. {
  2830. NTSTATUS Status;
  2831. PUL_CONFIG_GROUP_OBJECT pObject = NULL;
  2832. //
  2833. // Sanity check.
  2834. //
  2835. PAGED_CODE();
  2836. ASSERT(pConfigGroupInformation != NULL || pReturnLength != NULL);
  2837. ASSERT(pConfigGroupInformation == NULL || Length > 0);
  2838. //
  2839. // If no buffer is supplied, we are being asked to return the length needed
  2840. //
  2841. if (pConfigGroupInformation == NULL && pReturnLength == NULL)
  2842. return STATUS_INVALID_PARAMETER;
  2843. CG_LOCK_READ();
  2844. //
  2845. // Get the object ptr from id
  2846. //
  2847. pObject = (PUL_CONFIG_GROUP_OBJECT)(
  2848. UlGetObjectFromOpaqueId(
  2849. ConfigGroupId,
  2850. UlOpaqueIdTypeConfigGroup,
  2851. UlReferenceConfigGroup
  2852. )
  2853. );
  2854. if (IS_VALID_CONFIG_GROUP(pObject) == FALSE)
  2855. {
  2856. Status = STATUS_INVALID_PARAMETER;
  2857. goto end;
  2858. }
  2859. //
  2860. // What are we being asked to do?
  2861. //
  2862. switch (InformationClass)
  2863. {
  2864. case HttpConfigGroupBandwidthInformation:
  2865. if (pConfigGroupInformation == NULL)
  2866. {
  2867. //
  2868. // return the size needed
  2869. //
  2870. *pReturnLength = sizeof(HTTP_CONFIG_GROUP_MAX_BANDWIDTH);
  2871. }
  2872. else
  2873. {
  2874. if (Length < sizeof(HTTP_CONFIG_GROUP_MAX_BANDWIDTH))
  2875. {
  2876. Status = STATUS_BUFFER_TOO_SMALL;
  2877. goto end;
  2878. }
  2879. *((PHTTP_CONFIG_GROUP_MAX_BANDWIDTH)pConfigGroupInformation) = pObject->MaxBandwidth;
  2880. }
  2881. break;
  2882. case HttpConfigGroupConnectionInformation:
  2883. if (pConfigGroupInformation == NULL)
  2884. {
  2885. //
  2886. // return the size needed
  2887. //
  2888. *pReturnLength = sizeof(HTTP_CONFIG_GROUP_MAX_CONNECTIONS);
  2889. }
  2890. else
  2891. {
  2892. if (Length < sizeof(HTTP_CONFIG_GROUP_MAX_CONNECTIONS))
  2893. {
  2894. Status = STATUS_BUFFER_TOO_SMALL;
  2895. goto end;
  2896. }
  2897. *((PHTTP_CONFIG_GROUP_MAX_CONNECTIONS)pConfigGroupInformation) = pObject->MaxConnections;
  2898. }
  2899. break;
  2900. case HttpConfigGroupStateInformation:
  2901. if (pConfigGroupInformation == NULL)
  2902. {
  2903. //
  2904. // return the size needed
  2905. //
  2906. *pReturnLength = sizeof(HTTP_CONFIG_GROUP_STATE);
  2907. }
  2908. else
  2909. {
  2910. if (Length < sizeof(HTTP_CONFIG_GROUP_STATE))
  2911. {
  2912. Status = STATUS_BUFFER_TOO_SMALL;
  2913. goto end;
  2914. }
  2915. *((PHTTP_CONFIG_GROUP_STATE)pConfigGroupInformation) = pObject->State;
  2916. }
  2917. break;
  2918. case HttpConfigGroupConnectionTimeoutInformation:
  2919. if (pConfigGroupInformation == NULL)
  2920. {
  2921. //
  2922. // return the size needed
  2923. //
  2924. *pReturnLength = sizeof(ULONG);
  2925. }
  2926. else
  2927. {
  2928. *((ULONG *)pConfigGroupInformation) =
  2929. (ULONG)(pObject->ConnectionTimeout / C_NS_TICKS_PER_SEC);
  2930. }
  2931. break;
  2932. case HttpConfigGroupAppPoolInformation:
  2933. case HttpConfigGroupAutoResponseInformation:
  2934. case HttpConfigGroupSecurityInformation:
  2935. //
  2936. // this is illegal
  2937. //
  2938. default:
  2939. Status = STATUS_INVALID_PARAMETER;
  2940. goto end;
  2941. }
  2942. Status = STATUS_SUCCESS;
  2943. end:
  2944. if (pObject != NULL)
  2945. {
  2946. DEREFERENCE_CONFIG_GROUP(pObject);
  2947. pObject = NULL;
  2948. }
  2949. CG_UNLOCK_READ();
  2950. return Status;
  2951. } // UlQueryConfigGroupInformation
  2952. /***************************************************************************++
  2953. Routine Description:
  2954. allows you to set info for the cgroup. see uldef.h
  2955. Arguments:
  2956. IN HTTP_CONFIG_GROUP_ID ConfigGroupId, cgroup id
  2957. IN HTTP_CONFIG_GROUP_INFORMATION_CLASS InformationClass, what to fetch
  2958. IN PVOID pConfigGroupInformation, input buffer
  2959. IN ULONG Length, length of pConfigGroupInformation
  2960. Return Value:
  2961. NTSTATUS - Completion status.
  2962. STATUS_INVALID_PARAMETER bad cgroup id
  2963. STATUS_BUFFER_TOO_SMALL input buffer too small
  2964. STATUS_INVALID_PARAMETER invalid infoclass
  2965. --***************************************************************************/
  2966. NTSTATUS
  2967. UlSetConfigGroupInformation(
  2968. IN HTTP_CONFIG_GROUP_ID ConfigGroupId,
  2969. IN HTTP_CONFIG_GROUP_INFORMATION_CLASS InformationClass,
  2970. IN PVOID pConfigGroupInformation,
  2971. IN ULONG Length
  2972. )
  2973. {
  2974. NTSTATUS Status = STATUS_SUCCESS;
  2975. PUL_CONFIG_GROUP_OBJECT pObject;
  2976. PHTTP_CONFIG_GROUP_LOGGING pLoggingInfo;
  2977. PHTTP_CONFIG_GROUP_MAX_BANDWIDTH pMaxBandwidth;
  2978. //
  2979. // Sanity check.
  2980. //
  2981. PAGED_CODE();
  2982. //
  2983. // no buffer?
  2984. //
  2985. if (pConfigGroupInformation == NULL)
  2986. return STATUS_INVALID_PARAMETER;
  2987. CG_LOCK_WRITE();
  2988. __try
  2989. {
  2990. //
  2991. // Get the object ptr from id
  2992. //
  2993. pObject = (PUL_CONFIG_GROUP_OBJECT)(
  2994. UlGetObjectFromOpaqueId(
  2995. ConfigGroupId,
  2996. UlOpaqueIdTypeConfigGroup,
  2997. UlReferenceConfigGroup
  2998. )
  2999. );
  3000. if (IS_VALID_CONFIG_GROUP(pObject) == FALSE)
  3001. {
  3002. Status = STATUS_INVALID_PARAMETER;
  3003. __leave;
  3004. }
  3005. //
  3006. // What are we being asked to do?
  3007. //
  3008. switch (InformationClass)
  3009. {
  3010. case HttpConfigGroupAutoResponseInformation:
  3011. {
  3012. PHTTP_AUTO_RESPONSE pAutoResponse;
  3013. pAutoResponse = (PHTTP_AUTO_RESPONSE)pConfigGroupInformation;
  3014. //
  3015. // double check the buffer is big enough
  3016. //
  3017. if (Length < sizeof(HTTP_AUTO_RESPONSE))
  3018. {
  3019. Status = STATUS_BUFFER_TOO_SMALL;
  3020. __leave;
  3021. }
  3022. //
  3023. // Is there an auto-response?
  3024. //
  3025. // BUGBUG: temp optional
  3026. // if (pAutoResponse->Flags.Present == 1 && pAutoResponse->pResponse != NULL)
  3027. if (pAutoResponse->Flags.Present == 1)
  3028. {
  3029. HTTP_VERSION HttpVersion11 = {1,1};
  3030. //
  3031. // Are we replacing one already there?
  3032. //
  3033. if (pObject->pAutoResponse != NULL)
  3034. {
  3035. UL_DEREFERENCE_INTERNAL_RESPONSE(pObject->pAutoResponse);
  3036. pObject->pAutoResponse = NULL;
  3037. }
  3038. //
  3039. // Convert it to kernel mode
  3040. //
  3041. Status = UlCaptureHttpResponse(
  3042. pAutoResponse->pResponse,
  3043. NULL,
  3044. HttpVersion11,
  3045. HttpVerbUnknown,
  3046. pAutoResponse->EntityChunkCount,
  3047. pAutoResponse->pEntityChunks,
  3048. UlCaptureCopyData,
  3049. FALSE,
  3050. NULL,
  3051. &pObject->pAutoResponse
  3052. );
  3053. if (NT_SUCCESS(Status) == FALSE)
  3054. {
  3055. pObject->pAutoResponse = NULL;
  3056. __leave;
  3057. }
  3058. Status = UlPrepareHttpResponse(
  3059. HttpVersion11,
  3060. pAutoResponse->pResponse,
  3061. pObject->pAutoResponse
  3062. );
  3063. if (NT_SUCCESS(Status) == FALSE)
  3064. {
  3065. UL_DEREFERENCE_INTERNAL_RESPONSE(pObject->pAutoResponse);
  3066. pObject->pAutoResponse = NULL;
  3067. __leave;
  3068. }
  3069. }
  3070. else
  3071. {
  3072. //
  3073. // Remove ours?
  3074. //
  3075. if (pObject->pAutoResponse != NULL)
  3076. {
  3077. UL_DEREFERENCE_INTERNAL_RESPONSE(pObject->pAutoResponse);
  3078. }
  3079. pObject->pAutoResponse = NULL;
  3080. }
  3081. }
  3082. break;
  3083. case HttpConfigGroupAppPoolInformation:
  3084. {
  3085. PHTTP_CONFIG_GROUP_APP_POOL pAppPoolInfo;
  3086. PUL_APP_POOL_OBJECT pOldAppPool;
  3087. pAppPoolInfo = (PHTTP_CONFIG_GROUP_APP_POOL)pConfigGroupInformation;
  3088. //
  3089. // double check the buffer is big enough
  3090. //
  3091. if (Length < sizeof(HTTP_CONFIG_GROUP_APP_POOL))
  3092. {
  3093. Status = STATUS_BUFFER_TOO_SMALL;
  3094. __leave;
  3095. }
  3096. //
  3097. // remember the old app pool if there is one, so we can deref it
  3098. // if we need to
  3099. //
  3100. if (pObject->AppPoolFlags.Present == 1 && pObject->pAppPool != NULL)
  3101. {
  3102. pOldAppPool = pObject->pAppPool;
  3103. } else {
  3104. pOldAppPool = NULL;
  3105. }
  3106. if (pAppPoolInfo->Flags.Present == 1)
  3107. {
  3108. //
  3109. // ok, were expecting a handle to the file object for the app pool
  3110. //
  3111. // let's open it
  3112. //
  3113. Status = UlGetPoolFromHandle(
  3114. pAppPoolInfo->AppPoolHandle,
  3115. &pObject->pAppPool
  3116. );
  3117. if (NT_SUCCESS(Status) == FALSE)
  3118. __leave;
  3119. pObject->AppPoolFlags.Present = 1;
  3120. }
  3121. else
  3122. {
  3123. pObject->AppPoolFlags.Present = 0;
  3124. pObject->pAppPool = NULL;
  3125. }
  3126. //
  3127. // deref the old app pool
  3128. //
  3129. if (pOldAppPool) {
  3130. DEREFERENCE_APP_POOL(pOldAppPool);
  3131. }
  3132. }
  3133. break;
  3134. case HttpConfigGroupLogInformation:
  3135. {
  3136. //
  3137. // Sanity Check first
  3138. //
  3139. if (Length < sizeof(HTTP_CONFIG_GROUP_LOGGING))
  3140. {
  3141. Status = STATUS_BUFFER_TOO_SMALL;
  3142. __leave;
  3143. }
  3144. pLoggingInfo = (PHTTP_CONFIG_GROUP_LOGGING)pConfigGroupInformation;
  3145. if ( !IS_VALID_DIR_NAME(&pLoggingInfo->LogFileDir) )
  3146. {
  3147. //
  3148. // If the passed down Directory_Name is corrupted or
  3149. // abnormally big. Then reject it.
  3150. //
  3151. Status = STATUS_INVALID_PARAMETER;
  3152. __leave;
  3153. }
  3154. __try
  3155. {
  3156. if (pObject->LoggingConfig.Flags.Present)
  3157. {
  3158. //
  3159. // Check to see if log dir is correct
  3160. //
  3161. Status = UlCheckLogDirectory( &pLoggingInfo->LogFileDir );
  3162. if ( NT_SUCCESS(Status) )
  3163. {
  3164. //
  3165. // Log settings are being reconfigured
  3166. //
  3167. Status = UlReConfigureLogEntry(
  3168. pObject, // For the file entry
  3169. &pObject->LoggingConfig, // The old config
  3170. pLoggingInfo // The new config
  3171. );
  3172. }
  3173. //
  3174. // Otherwise keep the old settings
  3175. //
  3176. }
  3177. else
  3178. {
  3179. // Save to config group
  3180. pObject->LoggingConfig = *pLoggingInfo;
  3181. pObject->LoggingConfig.LogFileDir.Buffer =
  3182. (PWSTR) UL_ALLOCATE_ARRAY(
  3183. PagedPool,
  3184. UCHAR,
  3185. pLoggingInfo->LogFileDir.MaximumLength,
  3186. UL_CG_LOGDIR_POOL_TAG
  3187. );
  3188. if (pObject->LoggingConfig.LogFileDir.Buffer == NULL)
  3189. {
  3190. Status = STATUS_NO_MEMORY;
  3191. __leave;
  3192. }
  3193. RtlCopyMemory(
  3194. pObject->LoggingConfig.LogFileDir.Buffer,
  3195. pLoggingInfo->LogFileDir.Buffer,
  3196. pObject->LoggingConfig.LogFileDir.MaximumLength
  3197. );
  3198. pObject->LoggingConfig.Flags.Present = 1;
  3199. Status = UlCreateLog( pObject );
  3200. if ( !NT_SUCCESS(Status) )
  3201. {
  3202. //
  3203. // Clean up and return to logging not present state
  3204. //
  3205. UlTrace( LOGGING,
  3206. ("UlSetConfigGroupInformation: directory %S failure %08lx\n",
  3207. pObject->LoggingConfig.LogFileDir.Buffer,
  3208. Status
  3209. ));
  3210. UL_FREE_POOL(
  3211. pObject->LoggingConfig.LogFileDir.Buffer,
  3212. UL_CG_LOGDIR_POOL_TAG
  3213. );
  3214. pObject->LoggingConfig.LogFileDir.Buffer = NULL;
  3215. if ( pObject->pLogFileEntry )
  3216. UlRemoveLogFileEntry( pObject->pLogFileEntry );
  3217. pObject->pLogFileEntry = NULL;
  3218. pObject->LoggingConfig.Flags.Present = 0;
  3219. pObject->LoggingConfig.LoggingEnabled = FALSE;
  3220. }
  3221. }
  3222. }
  3223. __except( UL_EXCEPTION_FILTER() )
  3224. {
  3225. Status = GetExceptionCode();
  3226. }
  3227. if (!NT_SUCCESS(Status))
  3228. __leave;
  3229. // both cleared or both set
  3230. ASSERT(!pObject->LoggingConfig.LoggingEnabled
  3231. == !pObject->pLogFileEntry);
  3232. }
  3233. break;
  3234. case HttpConfigGroupBandwidthInformation:
  3235. {
  3236. //
  3237. // Sanity Check first
  3238. //
  3239. if (Length < sizeof(HTTP_CONFIG_GROUP_MAX_BANDWIDTH))
  3240. {
  3241. Status = STATUS_BUFFER_TOO_SMALL;
  3242. __leave;
  3243. }
  3244. pMaxBandwidth = (PHTTP_CONFIG_GROUP_MAX_BANDWIDTH) pConfigGroupInformation;
  3245. //
  3246. // Interpret the ZERO as HTTP_LIMIT_INFINITE
  3247. //
  3248. if (pMaxBandwidth->MaxBandwidth == 0)
  3249. {
  3250. pMaxBandwidth->MaxBandwidth = HTTP_LIMIT_INFINITE;
  3251. }
  3252. //
  3253. // But check to see if PSched is installed or not before proceeding.
  3254. // By returning an error here, WAS will raise an event warning but
  3255. // proceed w/o terminating the web server
  3256. //
  3257. if (!UlTcPSchedInstalled())
  3258. {
  3259. Status = STATUS_SUCCESS;
  3260. __leave;
  3261. #if 0
  3262. // Enable when WAS get updated to expect this error.
  3263. if (pMaxBandwidth->MaxBandwidth != HTTP_LIMIT_INFINITE)
  3264. {
  3265. // There's a BWT limit coming down but PSched is not installed
  3266. Status = STATUS_INVALID_DEVICE_REQUEST;
  3267. __leave;
  3268. }
  3269. else
  3270. {
  3271. // By default Config Store has HTTP_LIMIT_INFINITE. Therefore
  3272. // return success for non-actions to prevent unnecessary event
  3273. // warnings.
  3274. Status = STATUS_SUCCESS;
  3275. __leave;
  3276. }
  3277. #endif
  3278. }
  3279. //
  3280. // Create the flow if this is the first time we see Bandwidth set
  3281. // otherwise call reconfiguration for the existing flow. The case
  3282. // that the limit is infinite can be interpreted as BTW disabled
  3283. //
  3284. if (pObject->MaxBandwidth.Flags.Present &&
  3285. pObject->MaxBandwidth.MaxBandwidth != HTTP_LIMIT_INFINITE)
  3286. {
  3287. // To see if there is a real change
  3288. if (pMaxBandwidth->MaxBandwidth != pObject->MaxBandwidth.MaxBandwidth)
  3289. {
  3290. if (pMaxBandwidth->MaxBandwidth != HTTP_LIMIT_INFINITE)
  3291. {
  3292. // This will modify all the flows on all interfaces for this site
  3293. Status = UlTcModifyFlowsForSite(
  3294. pObject, // for this site
  3295. pMaxBandwidth->MaxBandwidth // the new bandwidth
  3296. );
  3297. if (!NT_SUCCESS(Status))
  3298. __leave;
  3299. }
  3300. else
  3301. {
  3302. // Handle BTW disabling by removing the existing flows
  3303. Status = UlTcRemoveFlowsForSite(pObject);
  3304. if (!NT_SUCCESS(Status))
  3305. __leave;
  3306. }
  3307. // Don't forget to update the cgroup object if it was a success
  3308. pObject->MaxBandwidth.MaxBandwidth = pMaxBandwidth->MaxBandwidth;
  3309. }
  3310. }
  3311. else
  3312. {
  3313. // Its about time to add a flow for a site entry.
  3314. if (pMaxBandwidth->MaxBandwidth != HTTP_LIMIT_INFINITE)
  3315. {
  3316. Status = UlTcAddFlowsForSite(
  3317. pObject,
  3318. pMaxBandwidth->MaxBandwidth
  3319. );
  3320. if (!NT_SUCCESS(Status))
  3321. __leave;
  3322. }
  3323. //
  3324. // Success! Remember the bandwidth limit inside the cgroup
  3325. //
  3326. pObject->MaxBandwidth = *pMaxBandwidth;
  3327. pObject->MaxBandwidth.Flags.Present = 1;
  3328. //
  3329. // When the last reference to this cgroup released, corresponding
  3330. // flows are going to be removed.Alternatively flows might be removed
  3331. // by explicitly setting the bandwidth throttling limit to infinite
  3332. // or reseting the flags.present.The latter case is handled above
  3333. // Look at the deref config group for the former.
  3334. //
  3335. }
  3336. }
  3337. break;
  3338. case HttpConfigGroupConnectionInformation:
  3339. if (Length < sizeof(HTTP_CONFIG_GROUP_MAX_CONNECTIONS))
  3340. {
  3341. Status = STATUS_BUFFER_TOO_SMALL;
  3342. __leave;
  3343. }
  3344. pObject->MaxConnections =
  3345. *((PHTTP_CONFIG_GROUP_MAX_CONNECTIONS)pConfigGroupInformation);
  3346. if (pObject->pConnectionCountEntry)
  3347. {
  3348. // Update
  3349. UlSetMaxConnections(
  3350. &pObject->pConnectionCountEntry->MaxConnections,
  3351. pObject->MaxConnections.MaxConnections
  3352. );
  3353. }
  3354. else
  3355. {
  3356. // Create
  3357. Status = UlCreateConnectionCountEntry(
  3358. pObject,
  3359. pObject->MaxConnections.MaxConnections
  3360. );
  3361. }
  3362. break;
  3363. case HttpConfigGroupStateInformation:
  3364. if (Length < sizeof(HTTP_CONFIG_GROUP_STATE))
  3365. {
  3366. Status = STATUS_BUFFER_TOO_SMALL;
  3367. __leave;
  3368. }
  3369. else
  3370. {
  3371. PHTTP_CONFIG_GROUP_STATE pCGState =
  3372. ((PHTTP_CONFIG_GROUP_STATE) pConfigGroupInformation);
  3373. HTTP_ENABLED_STATE NewState = pCGState->State;
  3374. if ((NewState != HttpEnabledStateActive)
  3375. && (NewState != HttpEnabledStateInactive))
  3376. {
  3377. Status = STATUS_INVALID_PARAMETER;
  3378. }
  3379. else
  3380. {
  3381. pObject->State = *pCGState;
  3382. UlTrace(ROUTING,
  3383. ("UlSetConfigGroupInfo(StateInfo): obj=%p, "
  3384. "Flags.Present=%lu, State=%sactive.\n",
  3385. pObject,
  3386. (ULONG) pObject->State.Flags.Present,
  3387. (NewState == HttpEnabledStateActive) ? "" : "in"
  3388. ));
  3389. }
  3390. }
  3391. break;
  3392. case HttpConfigGroupSecurityInformation:
  3393. {
  3394. PHTTP_CONFIG_GROUP_SECURITY pSecurity;
  3395. PSECURITY_DESCRIPTOR pSecurityDescriptor;
  3396. if (Length < sizeof(HTTP_CONFIG_GROUP_SECURITY))
  3397. {
  3398. Status = STATUS_BUFFER_TOO_SMALL;
  3399. __leave;
  3400. }
  3401. //
  3402. // get a copy of the new descriptor (do we need this step?)
  3403. //
  3404. pSecurity = (PHTTP_CONFIG_GROUP_SECURITY) pConfigGroupInformation;
  3405. if (pSecurity->Flags.Present && pSecurity->pSecurityDescriptor) {
  3406. Status = SeCaptureSecurityDescriptor(
  3407. pSecurity->pSecurityDescriptor,
  3408. UserMode,
  3409. PagedPool,
  3410. FALSE,
  3411. &pSecurityDescriptor
  3412. );
  3413. if (NT_SUCCESS(Status)) {
  3414. SECURITY_SUBJECT_CONTEXT SubjectContext;
  3415. SeCaptureSubjectContext(&SubjectContext);
  3416. SeLockSubjectContext(&SubjectContext);
  3417. //
  3418. // stuff it in the structure
  3419. //
  3420. Status = SeAssignSecurity(
  3421. NULL, // ParentDescriptor
  3422. pSecurityDescriptor,
  3423. &pSecurity->pSecurityDescriptor,
  3424. FALSE, // IsDirectoryObject
  3425. &SubjectContext,
  3426. IoGetFileObjectGenericMapping(),
  3427. PagedPool
  3428. );
  3429. SeUnlockSubjectContext(&SubjectContext);
  3430. SeReleaseSubjectContext(&SubjectContext);
  3431. ExFreePool(pSecurityDescriptor);
  3432. }
  3433. } else {
  3434. pSecurity->Flags.Present = 0;
  3435. pSecurityDescriptor = NULL;
  3436. }
  3437. //
  3438. // replace the old information
  3439. //
  3440. if (NT_SUCCESS(Status)) {
  3441. if (pObject->Security.Flags.Present) {
  3442. ASSERT(pObject->Security.pSecurityDescriptor);
  3443. ExFreePool(pObject->Security.pSecurityDescriptor);
  3444. }
  3445. pObject->Security = *pSecurity;
  3446. } else {
  3447. // return the error
  3448. __leave;
  3449. }
  3450. }
  3451. break;
  3452. case HttpConfigGroupSiteInformation:
  3453. {
  3454. PHTTP_CONFIG_GROUP_SITE pSite;
  3455. if (Length < sizeof(HTTP_CONFIG_GROUP_SITE))
  3456. {
  3457. Status = STATUS_BUFFER_TOO_SMALL;
  3458. __leave;
  3459. }
  3460. pSite = (PHTTP_CONFIG_GROUP_SITE)pConfigGroupInformation;
  3461. Status = UlCreateSiteCounterEntry(
  3462. pObject,
  3463. pSite->SiteId
  3464. );
  3465. }
  3466. break;
  3467. case HttpConfigGroupConnectionTimeoutInformation:
  3468. if (Length < sizeof(ULONG))
  3469. {
  3470. Status = STATUS_BUFFER_TOO_SMALL;
  3471. __leave;
  3472. }
  3473. else
  3474. {
  3475. LONGLONG Timeout;
  3476. Timeout = *((ULONG *)pConfigGroupInformation);
  3477. //
  3478. // Set the per site Connection Timeout limit override
  3479. //
  3480. pObject->ConnectionTimeout = Timeout * C_NS_TICKS_PER_SEC;
  3481. }
  3482. break;
  3483. default:
  3484. Status = STATUS_INVALID_PARAMETER;
  3485. __leave;
  3486. }
  3487. //
  3488. // flush the URI cache.
  3489. // CODEWORK: if we were smarter we could make this more granular
  3490. //
  3491. UlFlushCache();
  3492. Status = STATUS_SUCCESS;
  3493. }
  3494. __except( UL_EXCEPTION_FILTER() )
  3495. {
  3496. Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode());
  3497. }
  3498. if (pObject != NULL)
  3499. {
  3500. DEREFERENCE_CONFIG_GROUP(pObject);
  3501. pObject = NULL;
  3502. }
  3503. CG_UNLOCK_WRITE();
  3504. return Status;
  3505. } // UlSetConfigGroupInformation
  3506. /***************************************************************************++
  3507. Routine Description:
  3508. applies the inheritence, gradually setting the information from pMatchEntry
  3509. into pInfo. it only copies present info from pMatchEntry.
  3510. also updates the timestamp info in pInfo. there MUST be enough space for
  3511. 1 more index prior to calling this function.
  3512. Arguments:
  3513. IN PUL_URL_CONFIG_GROUP_INFO pInfo, the place to set the info
  3514. IN PUL_CG_URL_TREE_ENTRY pMatchEntry the entry to use to set it
  3515. Return Value:
  3516. NTSTATUS - Completion status.
  3517. --***************************************************************************/
  3518. NTSTATUS
  3519. UlpSetUrlInfo(
  3520. IN OUT PUL_URL_CONFIG_GROUP_INFO pInfo,
  3521. IN PUL_CG_URL_TREE_ENTRY pMatchEntry
  3522. )
  3523. {
  3524. //
  3525. // Sanity check.
  3526. //
  3527. PAGED_CODE();
  3528. ASSERT(pInfo != NULL && IS_VALID_URL_CONFIG_GROUP_INFO(pInfo));
  3529. ASSERT(IS_VALID_TREE_ENTRY(pMatchEntry));
  3530. ASSERT(pMatchEntry->FullUrl == 1);
  3531. ASSERT(IS_VALID_CONFIG_GROUP(pMatchEntry->pConfigGroup));
  3532. //
  3533. // set the control channel. The current level might
  3534. // not have one (if it's transient), but in that
  3535. // case a parent should have had one.
  3536. //
  3537. if (pMatchEntry->pConfigGroup->pControlChannel) {
  3538. pInfo->pControlChannel = pMatchEntry->pConfigGroup->pControlChannel;
  3539. }
  3540. ASSERT(pInfo->pControlChannel);
  3541. if (pMatchEntry->pConfigGroup->AppPoolFlags.Present == 1)
  3542. {
  3543. if (pInfo->pAppPool != NULL)
  3544. {
  3545. //
  3546. // let go the one already there
  3547. //
  3548. DEREFERENCE_APP_POOL(pInfo->pAppPool);
  3549. }
  3550. pInfo->pAppPool = pMatchEntry->pConfigGroup->pAppPool;
  3551. REFERENCE_APP_POOL(pInfo->pAppPool);
  3552. }
  3553. //
  3554. // and the auto response
  3555. //
  3556. if (pMatchEntry->pConfigGroup->pAutoResponse != NULL)
  3557. {
  3558. if (pInfo->pAutoResponse != NULL)
  3559. {
  3560. //
  3561. // let go the one already there
  3562. //
  3563. UL_DEREFERENCE_INTERNAL_RESPONSE(pInfo->pAutoResponse);
  3564. }
  3565. pInfo->pAutoResponse = pMatchEntry->pConfigGroup->pAutoResponse;
  3566. UL_REFERENCE_INTERNAL_RESPONSE(pInfo->pAutoResponse);
  3567. }
  3568. else if (pInfo->pAutoResponse == NULL &&
  3569. pInfo->pControlChannel->pAutoResponse != NULL)
  3570. {
  3571. //
  3572. // set the one from the control channel
  3573. //
  3574. pInfo->pAutoResponse = pInfo->pControlChannel->pAutoResponse;
  3575. UL_REFERENCE_INTERNAL_RESPONSE(pInfo->pAutoResponse);
  3576. }
  3577. //
  3578. // url context
  3579. //
  3580. pInfo->UrlContext = pMatchEntry->UrlContext;
  3581. //
  3582. if (pMatchEntry->pConfigGroup->MaxBandwidth.Flags.Present == 1)
  3583. {
  3584. if (pInfo->pMaxBandwidth == NULL)
  3585. {
  3586. pInfo->pMaxBandwidth = pMatchEntry->pConfigGroup;
  3587. REFERENCE_CONFIG_GROUP(pInfo->pMaxBandwidth);
  3588. }
  3589. }
  3590. if (pMatchEntry->pConfigGroup->MaxConnections.Flags.Present == 1)
  3591. {
  3592. if (pInfo->pMaxConnections == NULL)
  3593. {
  3594. pInfo->pMaxConnections = pMatchEntry->pConfigGroup;
  3595. REFERENCE_CONFIG_GROUP(pInfo->pMaxConnections);
  3596. pInfo->pConnectionCountEntry = pMatchEntry->pConfigGroup->pConnectionCountEntry;
  3597. REFERENCE_CONNECTION_COUNT_ENTRY(pInfo->pConnectionCountEntry);
  3598. }
  3599. }
  3600. //
  3601. // Logging Info config can only be set from the Root App of
  3602. // the site. We do not need to keep updating it down the tree
  3603. // Therefore its update is slightly different.
  3604. //
  3605. if (pMatchEntry->pConfigGroup->LoggingConfig.Flags.Present == 1)
  3606. {
  3607. if (pInfo->pLoggingConfig == NULL)
  3608. {
  3609. pInfo->pLoggingConfig = pMatchEntry->pConfigGroup;
  3610. REFERENCE_CONFIG_GROUP(pInfo->pLoggingConfig);
  3611. }
  3612. }
  3613. //
  3614. // Site Counter Entry
  3615. //
  3616. if (pMatchEntry->pConfigGroup->pSiteCounters)
  3617. {
  3618. // the pSiteCounters entry will only be set on
  3619. // the "Site" ConfigGroup object.
  3620. if (NULL == pInfo->pSiteCounters)
  3621. {
  3622. UlTrace(PERF_COUNTERS,
  3623. ("http!UlpSetUrlInfo: pSiteCounters %p set on pInfo %p for SiteId %d\n",
  3624. pMatchEntry->pConfigGroup->pSiteCounters,
  3625. pInfo,
  3626. pMatchEntry->pConfigGroup->pSiteCounters->Counters.SiteId
  3627. ));
  3628. pInfo->pSiteCounters = pMatchEntry->pConfigGroup->pSiteCounters;
  3629. pInfo->SiteId = pInfo->pSiteCounters->Counters.SiteId;
  3630. REFERENCE_SITE_COUNTER_ENTRY(pInfo->pSiteCounters);
  3631. }
  3632. }
  3633. //
  3634. // Connection Timeout (100ns Ticks)
  3635. //
  3636. if ( pMatchEntry->pConfigGroup->ConnectionTimeout )
  3637. {
  3638. pInfo->ConnectionTimeout = pMatchEntry->pConfigGroup->ConnectionTimeout;
  3639. }
  3640. if (pMatchEntry->pConfigGroup->State.Flags.Present == 1)
  3641. {
  3642. if (pInfo->pCurrentState != NULL)
  3643. {
  3644. DEREFERENCE_CONFIG_GROUP(pInfo->pCurrentState);
  3645. }
  3646. pInfo->pCurrentState = pMatchEntry->pConfigGroup;
  3647. REFERENCE_CONFIG_GROUP(pInfo->pCurrentState);
  3648. //
  3649. // and a copy
  3650. //
  3651. pInfo->CurrentState = pInfo->pCurrentState->State.State;
  3652. }
  3653. UlTrace(
  3654. CONFIG_GROUP_TREE, (
  3655. "http!UlpSetUrlInfo: Matching entry(%S) points to cfg group(%p)\n",
  3656. pMatchEntry->pToken,
  3657. pMatchEntry->pConfigGroup
  3658. )
  3659. );
  3660. return STATUS_SUCCESS;
  3661. } // UlpSetUrlInfo
  3662. /***************************************************************************++
  3663. Routine Description:
  3664. walks the url tree and builds the URL_INFO.
  3665. Arguments:
  3666. IN PUNICODE_STRING pUrl, the url to fetch info for
  3667. OUT PUL_URL_CONFIG_GROUP_INFO * ppInfo the info. caller must free it.
  3668. Return Value:
  3669. NTSTATUS - Completion status.
  3670. --***************************************************************************/
  3671. NTSTATUS
  3672. UlGetConfigGroupInfoForUrl(
  3673. IN PWSTR pUrl,
  3674. IN PUL_HTTP_CONNECTION pHttpConn,
  3675. OUT PUL_URL_CONFIG_GROUP_INFO pInfo
  3676. )
  3677. {
  3678. NTSTATUS Status;
  3679. //
  3680. // Sanity check.
  3681. //
  3682. PAGED_CODE();
  3683. ASSERT(pUrl != NULL);
  3684. ASSERT(pInfo != NULL);
  3685. UlTrace(CONFIG_GROUP_FNC, ("http!UlGetConfigGroupInfoForUrl(%S)\n", pUrl));
  3686. //
  3687. // grab the lock
  3688. //
  3689. CG_LOCK_READ();
  3690. Status = UlpTreeFindNode(pUrl, TRUE, pHttpConn, pInfo, NULL);
  3691. CG_UNLOCK_READ();
  3692. return Status;
  3693. }
  3694. /***************************************************************************++
  3695. Routine Description:
  3696. must be called to free the info buffer.
  3697. Arguments:
  3698. IN PUL_URL_CONFIG_GROUP_INFO pInfo the info to free
  3699. Return Value:
  3700. NTSTATUS - Completion status.
  3701. --***************************************************************************/
  3702. NTSTATUS
  3703. UlpConfigGroupInfoRelease(
  3704. IN PUL_URL_CONFIG_GROUP_INFO pInfo
  3705. )
  3706. {
  3707. //
  3708. // Sanity check.
  3709. //
  3710. PAGED_CODE();
  3711. ASSERT(pInfo != NULL);
  3712. UlTrace(CONFIG_GROUP_FNC, ("http!UlpConfigGroupInfoRelease(%p)\n", pInfo));
  3713. if (pInfo->pAppPool != NULL)
  3714. {
  3715. DEREFERENCE_APP_POOL(pInfo->pAppPool);
  3716. }
  3717. if (pInfo->pAutoResponse != NULL)
  3718. {
  3719. UL_DEREFERENCE_INTERNAL_RESPONSE(pInfo->pAutoResponse);
  3720. }
  3721. if (pInfo->pMaxBandwidth != NULL)
  3722. {
  3723. DEREFERENCE_CONFIG_GROUP(pInfo->pMaxBandwidth);
  3724. }
  3725. if (pInfo->pMaxConnections != NULL)
  3726. {
  3727. DEREFERENCE_CONFIG_GROUP(pInfo->pMaxConnections);
  3728. }
  3729. if (pInfo->pCurrentState != NULL)
  3730. {
  3731. DEREFERENCE_CONFIG_GROUP(pInfo->pCurrentState);
  3732. }
  3733. if (pInfo->pLoggingConfig != NULL)
  3734. {
  3735. DEREFERENCE_CONFIG_GROUP(pInfo->pLoggingConfig);
  3736. }
  3737. // UL_SITE_COUNTER_ENTRY
  3738. if (pInfo->pSiteCounters != NULL)
  3739. {
  3740. DEREFERENCE_SITE_COUNTER_ENTRY(pInfo->pSiteCounters);
  3741. }
  3742. if (pInfo->pConnectionCountEntry != NULL)
  3743. {
  3744. DEREFERENCE_CONNECTION_COUNT_ENTRY(pInfo->pConnectionCountEntry);
  3745. }
  3746. return STATUS_SUCCESS;
  3747. } // UlpConfigGroupInfoRelease
  3748. /***************************************************************************++
  3749. Routine Description:
  3750. Rough equivalent of the asignment operator for safely copying the
  3751. UL_URL_CONFIG_GROUP_INFO object and all of its contained pointers.
  3752. Arguments:
  3753. IN pOrigInfo the info to copy from
  3754. IN OUT pNewInfo the destination object
  3755. Return Value:
  3756. NTSTATUS - Completion status.
  3757. --***************************************************************************/
  3758. NTSTATUS
  3759. UlpConfigGroupInfoDeepCopy(
  3760. IN const PUL_URL_CONFIG_GROUP_INFO pOrigInfo,
  3761. IN OUT PUL_URL_CONFIG_GROUP_INFO pNewInfo
  3762. )
  3763. {
  3764. //
  3765. // Sanity check.
  3766. //
  3767. PAGED_CODE();
  3768. UlTrace(CONFIG_GROUP_FNC,
  3769. ("http!UlpConfigGroupInfoDeepCopy(Orig: %p, New: %p)\n",
  3770. pOrigInfo,
  3771. pNewInfo
  3772. ));
  3773. ASSERT( pOrigInfo != NULL && pNewInfo != NULL );
  3774. if (pOrigInfo->pAppPool != NULL)
  3775. {
  3776. REFERENCE_APP_POOL(pOrigInfo->pAppPool);
  3777. }
  3778. if (pOrigInfo->pAutoResponse != NULL)
  3779. {
  3780. UL_REFERENCE_INTERNAL_RESPONSE(pOrigInfo->pAutoResponse);
  3781. }
  3782. if (pOrigInfo->pMaxBandwidth != NULL)
  3783. {
  3784. REFERENCE_CONFIG_GROUP(pOrigInfo->pMaxBandwidth);
  3785. }
  3786. if (pOrigInfo->pMaxConnections != NULL)
  3787. {
  3788. REFERENCE_CONFIG_GROUP(pOrigInfo->pMaxConnections);
  3789. }
  3790. if (pOrigInfo->pCurrentState != NULL)
  3791. {
  3792. REFERENCE_CONFIG_GROUP(pOrigInfo->pCurrentState);
  3793. }
  3794. if (pOrigInfo->pLoggingConfig != NULL)
  3795. {
  3796. REFERENCE_CONFIG_GROUP(pOrigInfo->pLoggingConfig);
  3797. }
  3798. // UL_SITE_COUNTER_ENTRY
  3799. if (pOrigInfo->pSiteCounters != NULL)
  3800. {
  3801. REFERENCE_SITE_COUNTER_ENTRY(pOrigInfo->pSiteCounters);
  3802. }
  3803. if (pOrigInfo->pConnectionCountEntry != NULL)
  3804. {
  3805. REFERENCE_CONNECTION_COUNT_ENTRY(pOrigInfo->pConnectionCountEntry);
  3806. }
  3807. //
  3808. // Copy the old stuff over
  3809. //
  3810. RtlCopyMemory(
  3811. pNewInfo,
  3812. pOrigInfo,
  3813. sizeof(UL_URL_CONFIG_GROUP_INFO)
  3814. );
  3815. return STATUS_SUCCESS;
  3816. } // UlpConfigGroupInfoDeepCopy
  3817. /***************************************************************************++
  3818. Routine Description:
  3819. This function gets called when a static config group's control channel
  3820. goes away, or when a transient config group's app pool or static parent
  3821. goes away.
  3822. Deletes the config group.
  3823. Arguments:
  3824. pEntry - A pointer to HandleEntry or ParentEntry.
  3825. pHost - Pointer to the config group
  3826. pv - unused
  3827. --***************************************************************************/
  3828. BOOLEAN
  3829. UlNotifyOrphanedConfigGroup(
  3830. IN PUL_NOTIFY_ENTRY pEntry,
  3831. IN PVOID pHost,
  3832. IN PVOID pv
  3833. )
  3834. {
  3835. PUL_CONFIG_GROUP_OBJECT pObject = (PUL_CONFIG_GROUP_OBJECT) pHost;
  3836. //
  3837. // Sanity check
  3838. //
  3839. PAGED_CODE();
  3840. ASSERT(pEntry);
  3841. ASSERT(IS_VALID_CONFIG_GROUP(pObject));
  3842. UlDeleteConfigGroup(pObject->ConfigGroupId);
  3843. return TRUE;
  3844. } // UlNotifyOrphanedConfigGroup
  3845. /***************************************************************************++
  3846. [design notes]
  3847. [url format]
  3848. url format = http[s]://[ ip-address|hostname|* :port-number/ [abs_path] ]
  3849. no escaping is allowed.
  3850. [caching cfg group info]
  3851. the tree was designed for quick lookup, but it is still costly to do the
  3852. traversal. so the cfg info for an url was designed to be cached and stored
  3853. in the actual response cache, which will be able to be directly hashed into.
  3854. this is the fast fast path.
  3855. in order to do this, the buffer for is allocated in non-paged pool. the
  3856. timestamp for each cfg group used to build the info (see building the url
  3857. info) is remembered in the returned struct. actually just indexes into the
  3858. global timestamp array are kept. the latest timestamp is then stored in the
  3859. struct itself.
  3860. later, if a cfg group is updated, the timestamp for that cfg group (in the
  3861. global array) is updated to current time.
  3862. if a request comes in, and we have a resposne cache hit, the driver will
  3863. check to see if it's cfg group info is stale. this simple requires scanning
  3864. the global timestamp array to see if any stamps are greater than the stamp
  3865. in the struct. this is not expensive . 1 memory lookup per level of url
  3866. depth.
  3867. this means that the passive mode code + dispatch mode code contend for the
  3868. timestamp spinlock. care is made to not hold this lock long. memory
  3869. allocs + frees are carefully moved outside the spinlocks.
  3870. care is also made as to the nature of tree updates + invalidating cached
  3871. data. to do this, the parent cfg group (non-dummy) is marked dirty. this
  3872. is to prevent the case that a new child cfg group is added that suddenly
  3873. affects an url it didn't effect before. image http://paul.com:80/ being
  3874. the only registered cfg group. a request comes in for /images/1.jpg.
  3875. the matching cfg group is that root one. later, a /images cfg group
  3876. is created. it now is a matching cfg group for that url sitting in the
  3877. response cache. thus needs to be invalidated.
  3878. [paging + irq]
  3879. the entire module assumes that it is called at IRQ==PASSIVE except for the
  3880. stale detection code.
  3881. this code accesses the timestamp array (see caching cfg group info) which
  3882. is stored in non-paged memory and synch'd with passive level access
  3883. using a spinlock.
  3884. [the tree]
  3885. the tree is made up of headers + entries. headers represent a group of entries
  3886. that share a parent. it's basically a length prefixed array of entries.
  3887. the parent pointer is in the entry not the header, as the entry does not really
  3888. now it's an element in an array.
  3889. entries have a pointer to a header that represents it's children. a header is
  3890. all of an entries children. headers don't link horizontally. they dyna-grow.
  3891. if you need to add a child to an entry, and his children header is full, boom.
  3892. gotta realloc to grow .
  3893. each node in the tree represents a token in a url, the things between the '/'
  3894. characters.
  3895. the entries in a header array are sorted. today they are sorted by their hash
  3896. value. this might change if the tokens are small enough, it's probably more
  3897. expensive to compute the hash value then just strcmp. the hash is 2bytes. the
  3898. token length is also 2bytes so no tokens greater than 32k.
  3899. i chose an array at first to attempt to get rid of right-left pointers. it
  3900. turned out to be futile as my array is an array of pointers. it has to be
  3901. an array of pointers as i grow + shrink it to keep it sorted. so i'm not
  3902. saving memory. however, as an array of pointers, it enables a binary search
  3903. as the array is sorted. this yields log(n) perf on a width search.
  3904. there are 2 types of entries in the tree. dummy entries and full url entries.
  3905. a full url is the leaf of an actual entry to UlAddUrl... dummy nodes are ones
  3906. that are there only to be parents to full url nodes.
  3907. dummy nodes have 2 ptrs + 2 ulongs.
  3908. full urls nodes have an extra 4 ptrs.
  3909. both store the actual token along with the entry. (unicode baby. 2 bytes per char).
  3910. at the top of the tree are sites. these are stored in a global header as siblings
  3911. in g_pSites. this can grow quite wide.
  3912. adding a full url entry creates the branch down to the entry.
  3913. deleting a full entry removes as far up the branch as possible without removing other
  3914. entries parents.
  3915. delete is also careful to not actually delete if other children exist. in this case
  3916. the full url entry is converted to a dummy node entry.
  3917. it was attempted to have big string url stored in the leaf node and the dummy nodes
  3918. simply point into this string for it's pToken. this doesn't work as the dummy nodes
  3919. pointers become invalid if the leaf node is later deleted. individual ownership of
  3920. tokens is needed to allow shared parents in the tree, with arbitrary node deletion.
  3921. an assumption throughout this code is that the tree is relatively static. changes
  3922. don't happen that often. basically inserts only on boot. and deletes only on
  3923. shutdown. this is why single clusters of siblings were chosen, as opposed to linked
  3924. clusters. children group width will remain fairly static meaning array growth is
  3925. rare.
  3926. [is it a graph or a tree?]
  3927. notice that the cfg groups are stored as a simple list with no relationships. its
  3928. the urls that are indexed with their relations.
  3929. so the urls build a tree, however do to the links from url to cfg group, it kind
  3930. of builds a graph also. 2 urls can be in the same cfg group. 1 url's child
  3931. can be in the same cfg group as the original url's parent.
  3932. when you focus on the actual url tree, it becomes less confusing. it really is a
  3933. tree. there can even be duplicates in the inheritence model, but the tree cleans
  3934. everything.
  3935. example of duplicates:
  3936. cgroup A = a/b + a/b/c/d
  3937. cgroup B = a/b/c
  3938. this walking the lineage branch for a/b/c/d you get A, then B, and A again. nodes
  3939. lower in the tree override parent values, so in this case A's values override B's .
  3940. [recursion]
  3941. [a sample tree]
  3942. server runs sites msw and has EVERY directory mapped to a cfg groups (really obscene)
  3943. <coming later>
  3944. [memory assumptions with the url tree]
  3945. [Paged]
  3946. a node per token in the url. 2 types. dummy nodes + leaf nodes.
  3947. (note: when 2 sizes are given, the second size is the sundown size)
  3948. dummy node = 4/6 longs
  3949. leaf node = 8/14 longs
  3950. + each node holds 2 * TokenLength+1 for the token.
  3951. + each cluster of siblings holds 2 longs + 1/2 longs per node in the cluster.
  3952. [NonPaged]
  3953. 2 longs per config group
  3954. [assume]
  3955. sites
  3956. max 100k
  3957. average <10
  3958. assume 32 char hostname
  3959. max apps per site
  3960. in the max case : 2 (main + admin)
  3961. in the avg case : 10
  3962. max apps : 1000s (in 1 site)
  3963. assume 32 char app name
  3964. (assume just hostheader access for now)
  3965. [max]
  3966. hostname strings = 100000*((32+1)*2) = 6.6 MB
  3967. cluster entries = 100000*8*4 = 3.2 MB
  3968. cluster header = 1*(2+(1*100000))*4 = .4 MB
  3969. total = 10.2 MB
  3970. per site:
  3971. app strings = 2*((32+1)*2) = 132 B
  3972. cluster entries = 2*8*4 = 64 B
  3973. cluster header = 1*(2+(1*2))*4 = 16 B
  3974. = 132 + 64 + 16 = 212 B
  3975. total = 100000*(212) = 21.2 MB
  3976. Paged Total = 10.2mb + 21.2mb = 31.4 MB
  3977. NonPaged Total = 200k*2*4 = 1.6 MB
  3978. [avg]
  3979. hostname strings = 10*((32+1)*2) = 660 B
  3980. cluster entries = 10*8*4 = 320 B
  3981. cluster header = 1*(2+(1*10))*4 = 48 B
  3982. total = 1028 B
  3983. per site:
  3984. app strings = 10*((32+1)*2) = 660 B
  3985. cluster entries = 10*8*4 = 320 B
  3986. cluster header = 1*(2+(1*10))*4 = 48 B
  3987. total = 10*(1028) = 10.2 KB
  3988. Paged Total = 1028b + 10.2lKB = 11.3 KB
  3989. NonPaged Total = 110*2*4 = 880 B
  3990. note: can we save space by refcounting strings. if all of these
  3991. 100k have apps with the same name, we save massive string space.
  3992. ~13MB .
  3993. [efficiency of the tree]
  3994. [lookup]
  3995. [insert]
  3996. [delete]
  3997. [data locality]
  3998. [alterates investigated to the tree]
  3999. hashing - was pretty much scrapped due to the longest prefix match issue.
  4000. basically to hash, we would have to compute a hash for each level in
  4001. the url, to see if there is a match. then we have the complete list
  4002. of matches. assuming an additive hash, the hash expense could be
  4003. minimized, but the lookup still requires cycles.
  4004. alpha trie - was expense for memory. however the tree we have though
  4005. is very very similar to a trie. each level of the tree is a token,
  4006. however, not a letter.
  4007. [building the url info]
  4008. the url info is actually built walking down the tree. for each match
  4009. node, we set the info. this allows for the top-down inheritence.
  4010. we also snapshot the timestamp offsets for each matching node in the
  4011. tree as we dive down it (see caching) .
  4012. this dynamic building of an urls' match was chosen over precomputing
  4013. every possible config through the inheritence tree. each leaf node
  4014. could have stored a cooked version of this, but it would have made
  4015. updates a bear. it's not that expensive to build it while we walk down
  4016. as we already visit each node in our lineage branch.
  4017. autoresponses are never copied, but refcounted.
  4018. [locking]
  4019. 2 locks are used in the module. a spinlock to protect the global
  4020. timestamp array, and a resource to protect both the cgroup objects
  4021. and the url tree (which links to the cgroup objects) .
  4022. the spinlock is sometimes acquired while the resource is locked,
  4023. but never vice-versa. also, while the spinlock is held, very
  4024. little is done, and definetly no other locks are contended.
  4025. 1 issue here is granularity of contention. currently the entire
  4026. tree + object list is protected with 1 resource, which is reader
  4027. writer. if this is a perf issue, we can look into locking single
  4028. lineage branches (sites) .
  4029. [legal]
  4030. > 1 cfg groups in an app pool
  4031. children cfg groups with no app pool (they inherit)
  4032. children cfg groups with no auto-response (they inherit)
  4033. children cfg groups with no max bandwitdth (they inherit)
  4034. children cfg groups with no max connections (they inherit)
  4035. * for a host name
  4036. fully qualified url of for http[s]://[host|*]:post/[abs_path]/
  4037. must have trailing slash if not pting to a file
  4038. only 1 cfg group for the site AND the root url. e.g. http://foo.com:80/
  4039. allow you to set config on http:// and https:// for "root" config info
  4040. [not legal]
  4041. an url in > 1 cfg group
  4042. > 1 app pools for 1 cfg group
  4043. > 1 root cfg group
  4044. * embedded anywhere in the url other than replacing a hostname
  4045. query strings in url's.
  4046. url's not ending in slash.
  4047. --***************************************************************************/