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

5946 lines
151 KiB

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