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

1506 lines
52 KiB

  1. /****************************************************************************/
  2. // sessdir.cpp
  3. //
  4. // TS Session Directory code used by TermSrv.exe.
  5. //
  6. // Copyright (C) 2000 Microsot Corporation
  7. /****************************************************************************/
  8. // precomp.h includes COM base headers.
  9. #define INITGUID
  10. #include "precomp.h"
  11. #pragma hdrstop
  12. #include "icaevent.h"
  13. #include "sessdir.h"
  14. #include <tssd.h>
  15. #pragma warning (push, 4)
  16. #define CLSIDLENGTH 39
  17. #define STORESERVERNAMELENGTH 64
  18. #define CLUSTERNAMELENGTH 64
  19. #define OPAQUESETTINGSLENGTH 256
  20. #define TOTAL_STRINGS_LENGTH 640
  21. #define USERNAME_OFFSET 0
  22. #define DOMAIN_OFFSET 256
  23. #define APPLICATIONTYPE_OFFSET 384
  24. #define SINGLE_SESSION_FLAG 0x1
  25. // Extern defined in icasrv.c.
  26. extern "C" WCHAR gpszServiceName[];
  27. // Extern defined in winsta.c
  28. extern "C" LIST_ENTRY WinStationListHead; // protected by WinStationListLock
  29. extern "C" void PostErrorValueEvent(unsigned EventCode, DWORD ErrVal);
  30. WCHAR g_LocalServerAddress[64];
  31. BOOL g_SessDirUseServerAddr = TRUE;
  32. // Do not access directly. Use *TSSD functions.
  33. //
  34. // These variables are used to manage synchronization with retrieving the
  35. // pointer to the COM object. See *TSSD, below, for details on how they are
  36. // used.
  37. ITSSessionDirectory *g_pTSSDPriv = NULL;
  38. CRITICAL_SECTION g_CritSecComObj;
  39. CRITICAL_SECTION g_CritSecInitialize;
  40. int g_nComObjRefCount = 0;
  41. BOOL g_bCritSecsInitialized = FALSE;
  42. // Do not access directly. Use *TSSDEx functions.
  43. //
  44. // These variables are used to manage synchronization with retrieving the
  45. // pointer to the COM object. See *TSSDEx, below, for details on how they are
  46. // used.
  47. ITSSessionDirectoryEx *g_pTSSDExPriv = NULL;
  48. int g_nTSSDExObjRefCount = 0;
  49. /****************************************************************************/
  50. // SessDirGetLocalIPAddr
  51. //
  52. // Gets the local IP address of this machine. On success, returns 0. On
  53. // failure, returns a failure code from the function that failed.
  54. /****************************************************************************/
  55. DWORD SessDirGetLocalIPAddr(WCHAR *LocalIP)
  56. {
  57. DWORD NameSize;
  58. unsigned char *tempaddr;
  59. WCHAR psServerName[64];
  60. char psServerNameA[64];
  61. NameSize = sizeof(psServerName) / sizeof(WCHAR);
  62. if (GetComputerNameEx(ComputerNamePhysicalDnsHostname,
  63. psServerName, &NameSize)) {
  64. // Temporary code to get an IP address. This should be replaced in the
  65. // fix to bug #323867.
  66. struct hostent *hptr;
  67. // change the wide character string to non-wide
  68. sprintf(psServerNameA, "%S", psServerName);
  69. if ((hptr = gethostbyname(psServerNameA)) == 0) {
  70. DWORD Err = WSAGetLastError();
  71. return Err;
  72. }
  73. tempaddr = (unsigned char *)*(hptr->h_addr_list);
  74. wsprintf(LocalIP, L"%d.%d.%d.%d", tempaddr[0], tempaddr[1],
  75. tempaddr[2], tempaddr[3]);
  76. }
  77. else {
  78. DWORD Err = GetLastError();
  79. return Err;
  80. }
  81. return 0;
  82. }
  83. /****************************************************************************/
  84. // InitSessionDirectoryEx
  85. //
  86. // Reads values from the registry, and either initializes the session
  87. // directory or updates it, depending on the value of the Update parameter.
  88. /****************************************************************************/
  89. DWORD InitSessionDirectoryEx(bool Update)
  90. {
  91. DWORD Len;
  92. DWORD Type;
  93. DWORD DataSize;
  94. BOOL hKeyTermSrvSucceeded = FALSE;
  95. HRESULT hr;
  96. DWORD NameSize;
  97. DWORD ErrVal;
  98. CLSID TSSDCLSID;
  99. CLSID TSSDEXCLSID;
  100. LONG RegRetVal;
  101. HKEY hKey = NULL;
  102. HKEY hKeyTermSrv = NULL;
  103. ITSSessionDirectory *pTSSD = NULL;
  104. ITSSessionDirectoryEx *pTSSDEx = NULL;
  105. BOOL bClusteringActive = FALSE;
  106. BOOL bThisServerIsInSingleSessionMode;
  107. WCHAR CLSIDStr[CLSIDLENGTH];
  108. WCHAR CLSIDEXStr[CLSIDLENGTH];
  109. WCHAR StoreServerName[STORESERVERNAMELENGTH];
  110. WCHAR ClusterName[CLUSTERNAMELENGTH];
  111. WCHAR OpaqueSettings[OPAQUESETTINGSLENGTH];
  112. if (g_bCritSecsInitialized == FALSE) {
  113. ASSERT(FALSE);
  114. PostErrorValueEvent(EVENT_TS_SESSDIR_FAIL_INIT_TSSD,
  115. (DWORD) E_OUTOFMEMORY);
  116. return (DWORD) E_OUTOFMEMORY;
  117. }
  118. // trevorfo: Load only if any 1 loaded protocol needs it? Requires running
  119. // off of StartAllWinStations.
  120. // No more than one thread should be doing initialization.
  121. EnterCriticalSection(&g_CritSecInitialize);
  122. // Load registry keys.
  123. RegRetVal = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_CONTROL_TSERVER, 0,
  124. KEY_READ, &hKeyTermSrv);
  125. if (RegRetVal != ERROR_SUCCESS) {
  126. PostErrorValueEvent(EVENT_TS_SESSDIR_FAIL_INIT_TSSD,
  127. RegRetVal);
  128. goto RegFailExit;
  129. }
  130. else {
  131. hKeyTermSrvSucceeded = TRUE;
  132. }
  133. RegRetVal = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_TS_CLUSTERSETTINGS, 0,
  134. KEY_READ, &hKey);
  135. if (RegRetVal != ERROR_SUCCESS) {
  136. DBGPRINT(("TERMSRV: RegOpenKeyEx for ClusterSettings err %u\n",
  137. RegRetVal));
  138. goto RegFailExit;
  139. }
  140. //
  141. // First, we get the serious settings--active, SD location, and cluster
  142. // name.
  143. //
  144. // If group policy exists for all three, use that. Otherwise, use what
  145. // is in the registry.
  146. //
  147. StoreServerName[0] = L'\0';
  148. ClusterName[0] = L'\0';
  149. OpaqueSettings[0] = L'\0';
  150. if (g_MachinePolicy.fPolicySessionDirectoryActive &&
  151. g_MachinePolicy.fPolicySessionDirectoryLocation &&
  152. g_MachinePolicy.fPolicySessionDirectoryClusterName) {
  153. // Copy over parameters
  154. bClusteringActive = g_MachinePolicy.SessionDirectoryActive;
  155. wcsncpy(StoreServerName, g_MachinePolicy.SessionDirectoryLocation,
  156. STORESERVERNAMELENGTH);
  157. StoreServerName[STORESERVERNAMELENGTH - 1] = '\0';
  158. wcsncpy(ClusterName, g_MachinePolicy.SessionDirectoryClusterName,
  159. CLUSTERNAMELENGTH);
  160. ClusterName[CLUSTERNAMELENGTH - 1] = '\0';
  161. if (g_MachinePolicy.fPolicySessionDirectoryAdditionalParams) {
  162. wcsncpy(OpaqueSettings,
  163. g_MachinePolicy.SessionDirectoryAdditionalParams,
  164. OPAQUESETTINGSLENGTH);
  165. OpaqueSettings[OPAQUESETTINGSLENGTH - 1] = '\0';
  166. }
  167. }
  168. else {
  169. Len = sizeof(bClusteringActive);
  170. RegQueryValueEx(hKeyTermSrv, REG_TS_SESSDIRACTIVE, NULL, &Type,
  171. (BYTE *)&bClusteringActive, &Len);
  172. // Not an error for the name to be absent or empty.
  173. DataSize = sizeof(StoreServerName);
  174. RegRetVal = RegQueryValueExW(hKey, REG_TS_CLUSTER_STORESERVERNAME,
  175. NULL, &Type, (BYTE *)StoreServerName, &DataSize);
  176. if (RegRetVal != ERROR_SUCCESS) {
  177. DBGPRINT(("TERMSRV: Failed RegQuery for StoreSvrName - "
  178. "err=%u, DataSize=%u, type=%u\n",
  179. RegRetVal, DataSize, Type));
  180. }
  181. // Not an error for the name to be absent or empty.
  182. DataSize = sizeof(ClusterName);
  183. RegRetVal = RegQueryValueExW(hKey, REG_TS_CLUSTER_CLUSTERNAME,
  184. NULL, &Type, (BYTE *)ClusterName, &DataSize);
  185. if (RegRetVal != ERROR_SUCCESS) {
  186. DBGPRINT(("TERMSRV: Failed RegQuery for ClusterName - "
  187. "err=%u, DataSize=%u, type=%u\n",
  188. RegRetVal, DataSize, Type));
  189. }
  190. // Not an error for the string to be absent or empty.
  191. DataSize = sizeof(OpaqueSettings);
  192. RegRetVal = RegQueryValueExW(hKey, REG_TS_CLUSTER_OPAQUESETTINGS,
  193. NULL, &Type, (BYTE *)OpaqueSettings, &DataSize);
  194. if (RegRetVal != ERROR_SUCCESS) {
  195. DBGPRINT(("TERMSRV: Failed RegQuery for OpaqueSettings - "
  196. "err=%u, DataSize=%u, type=%u\n",
  197. RegRetVal, DataSize, Type));
  198. }
  199. }
  200. //
  201. // Now for the less crucial settings.
  202. //
  203. // Get the setting that determines whether the server's local address is
  204. // visible to the client. Group Policy takes precedence over registry.
  205. //
  206. if (g_MachinePolicy.fPolicySessionDirectoryExposeServerIP) {
  207. g_SessDirUseServerAddr = g_MachinePolicy.SessionDirectoryExposeServerIP;
  208. }
  209. else {
  210. Len = sizeof(g_SessDirUseServerAddr);
  211. RegRetVal = RegQueryValueEx(hKeyTermSrv, REG_TS_SESSDIR_EXPOSE_SERVER_ADDR,
  212. NULL, &Type, (BYTE *)&g_SessDirUseServerAddr, &Len);
  213. if (RegRetVal == ERROR_SUCCESS) {
  214. //DBGPRINT(("TERMSRV: RegOpenKeyEx for allow server addr to client %d"
  215. // "\n", g_SessDirUseServerAddr));
  216. }
  217. else {
  218. DBGPRINT(("TERMSRV: RegQueryValueEx for allow server addr to client"
  219. " %d, err %u\n", g_SessDirUseServerAddr, RegRetVal));
  220. }
  221. }
  222. // Get the single session per user setting from GP if it's active, otherwise
  223. // from the registry.
  224. if (g_MachinePolicy.fPolicySingleSessionPerUser) {
  225. bThisServerIsInSingleSessionMode =
  226. g_MachinePolicy.fSingleSessionPerUser;
  227. }
  228. else {
  229. Len = sizeof(bThisServerIsInSingleSessionMode);
  230. RegRetVal = RegQueryValueEx(hKeyTermSrv,
  231. POLICY_TS_SINGLE_SESSION_PER_USER, NULL, &Type,
  232. (BYTE *)&bThisServerIsInSingleSessionMode, &Len);
  233. if (RegRetVal != ERROR_SUCCESS) {
  234. DBGPRINT(("TERMSRV: RegQueryValueEx for single session mode"
  235. ", Error %u\n", RegRetVal));
  236. }
  237. }
  238. // Get the CLSID of the session directory object to instantiate.
  239. CLSIDStr[0] = L'\0';
  240. Len = sizeof(CLSIDStr);
  241. RegQueryValueEx(hKeyTermSrv, REG_TS_SESSDIRCLSID, NULL, &Type,
  242. (BYTE *)CLSIDStr, &Len);
  243. // Get the CLSID of the session directory object to instantiate.
  244. CLSIDEXStr[0] = L'\0';
  245. Len = sizeof(CLSIDEXStr);
  246. RegQueryValueEx(hKeyTermSrv, REG_TS_SESSDIR_EX_CLSID, NULL, &Type,
  247. (BYTE *)CLSIDEXStr, &Len);
  248. RegCloseKey(hKey);
  249. RegCloseKey(hKeyTermSrv);
  250. //
  251. // Configuration loading complete.
  252. //
  253. // See what to do about activation/deactivation.
  254. //
  255. pTSSD = GetTSSD();
  256. if (pTSSD == NULL) {
  257. // This is the normal initialization path. If Update is true here, it
  258. // should be treated as a normal initialize because the COM object was
  259. // unloaded.
  260. Update = false;
  261. }
  262. else {
  263. // Clustering is already active. See whether we should deactivate it.
  264. if (bClusteringActive == FALSE) {
  265. ReleaseTSSD(); // Once here, once again at the end of the function.
  266. ReleaseTSSDEx();
  267. }
  268. }
  269. if (bClusteringActive) {
  270. // We need to get the local machine's address to pass in to
  271. // the directory.
  272. NameSize = 64;
  273. if ((ErrVal = SessDirGetLocalIPAddr(g_LocalServerAddress)) == 0) {
  274. if (wcslen(CLSIDStr) > 0 &&
  275. SUCCEEDED(CLSIDFromString(CLSIDStr, &TSSDCLSID))) {
  276. // If it's not an update, create the TSSD object.
  277. if (Update == false) {
  278. hr = CoCreateInstance(TSSDCLSID, NULL,
  279. CLSCTX_INPROC_SERVER, IID_ITSSessionDirectory,
  280. (void **)&pTSSD);
  281. if (SUCCEEDED(hr)) {
  282. if (SetTSSD(pTSSD) != 0) {
  283. DBGPRINT(("TERMSRV: InitSessDirEx: Could not set "
  284. "TSSD", E_FAIL));
  285. pTSSD->Release();
  286. pTSSD = NULL;
  287. hr = E_FAIL;
  288. }
  289. else {
  290. // Add 1 to the ref count because we're gonna use
  291. // it.
  292. pTSSD = GetTSSD();
  293. }
  294. }
  295. }
  296. else {
  297. hr = S_OK;
  298. }
  299. if (SUCCEEDED (hr)) {
  300. // Right now the only flag we pass in to session directory
  301. // says whether we are in single-session mode.
  302. DWORD Flags = 0;
  303. Flags |= (bThisServerIsInSingleSessionMode ?
  304. SINGLE_SESSION_FLAG : 0x0);
  305. if (Update == false)
  306. hr = pTSSD->Initialize(g_LocalServerAddress,
  307. StoreServerName, ClusterName, OpaqueSettings,
  308. Flags, RepopulateSessionDirectory);
  309. else
  310. hr = pTSSD->Update(g_LocalServerAddress,
  311. StoreServerName, ClusterName, OpaqueSettings,
  312. Flags);
  313. if (FAILED(hr)) {
  314. DBGPRINT(("TERMSRV: InitSessDirEx: Failed %s TSSD, "
  315. "hr=0x%X\n", Update ? "update" : "init", hr));
  316. ReleaseTSSD();
  317. PostErrorValueEvent(
  318. EVENT_TS_SESSDIR_FAIL_INIT_TSSD, hr);
  319. }
  320. }
  321. else {
  322. DBGPRINT(("TERMSRV: InitSessDirEx: Failed create TSSD, "
  323. "hr=0x%X\n", hr));
  324. PostErrorValueEvent(
  325. EVENT_TS_SESSDIR_FAIL_CREATE_TSSD, hr);
  326. }
  327. }
  328. else {
  329. DBGPRINT(("TERMSRV: InitSessDirEx: Failed get or parse "
  330. "CLSID\n"));
  331. PostErrorValueEvent(
  332. EVENT_TS_SESSDIR_FAIL_GET_TSSD_CLSID, 0);
  333. hr = E_INVALIDARG;
  334. }
  335. }
  336. else {
  337. DBGPRINT(("TERMSRV: InitSessDirEx: Failed to get local DNS name, "
  338. "lasterr=0x%X\n", ErrVal));
  339. PostErrorValueEvent(EVENT_TS_SESSDIR_NO_COMPUTER_DNS_NAME,
  340. ErrVal);
  341. hr = E_FAIL;
  342. }
  343. // Initialize the other COM object, but only if the above succeeded.
  344. if (SUCCEEDED(hr)) {
  345. if (wcslen(CLSIDEXStr) > 0 &&
  346. SUCCEEDED(CLSIDFromString(CLSIDEXStr, &TSSDEXCLSID))) {
  347. // If it's not an update, create the TSSDEX object.
  348. if (Update == false) {
  349. hr = CoCreateInstance(TSSDEXCLSID, NULL,
  350. CLSCTX_INPROC_SERVER, IID_ITSSessionDirectoryEx,
  351. (void **)&pTSSDEx);
  352. if (SUCCEEDED(hr)) {
  353. if (SetTSSDEx(pTSSDEx) != 0) {
  354. DBGPRINT(("TERMSRV: InitSessDirEx: Could not set "
  355. "TSSDEx\n", E_FAIL));
  356. pTSSDEx->Release();
  357. pTSSDEx = NULL;
  358. hr = E_FAIL;
  359. }
  360. }
  361. }
  362. else
  363. hr = S_OK;
  364. if (FAILED(hr)) {
  365. DBGPRINT(("TERMSRV: InitSessDirEx: Failed create TSSDEx, "
  366. "hr=0x%X\n", hr));
  367. PostErrorValueEvent(
  368. EVENT_TS_SESSDIR_FAIL_CREATE_TSSDEX, hr);
  369. }
  370. }
  371. else {
  372. DBGPRINT(("TERMSRV: InitSessDirEx: Failed get or parse "
  373. "CLSIDSDEx\n"));
  374. PostErrorValueEvent(
  375. EVENT_TS_SESSDIR_FAIL_GET_TSSDEX_CLSID, 0);
  376. }
  377. }
  378. }
  379. else {
  380. DBGPRINT(("TERMSRV: InitSessDirEx: SessDir not activated\n"));
  381. }
  382. if (pTSSD != NULL)
  383. ReleaseTSSD();
  384. // Initialization complete--someone else is allowed to enter now.
  385. LeaveCriticalSection(&g_CritSecInitialize);
  386. return S_OK;
  387. RegFailExit:
  388. // Initialization complete--someone else is allowed to enter now.
  389. LeaveCriticalSection(&g_CritSecInitialize);
  390. if (hKeyTermSrvSucceeded)
  391. RegCloseKey(hKeyTermSrv);
  392. return (DWORD) E_FAIL;
  393. }
  394. /****************************************************************************/
  395. // InitSessionDirectory
  396. //
  397. // Initializes the directory by loading and initializing the session directory
  398. // object, if load balancing is enabled. We assume COM has been initialized
  399. // on the service main thread as COINIT_MULTITHREADED.
  400. //
  401. // This function should only be called once ever. It is the location of the
  402. // initialization of the critical sections used by this module.
  403. /****************************************************************************/
  404. void InitSessionDirectory()
  405. {
  406. BOOL br1 = FALSE;
  407. BOOL br2 = FALSE;
  408. ASSERT(g_bCritSecsInitialized == FALSE);
  409. // Initialize critical sections.
  410. __try {
  411. // Initialize the provider critical section to preallocate the event
  412. // and spin 4096 times on each try (since we don't spend very
  413. // long in our critical section).
  414. br1 = InitializeCriticalSectionAndSpinCount(&g_CritSecComObj,
  415. 0x80001000);
  416. br2 = InitializeCriticalSectionAndSpinCount(&g_CritSecInitialize,
  417. 0x80001000);
  418. // Since this happens at startup time, we should not fail.
  419. ASSERT(br1 && br2);
  420. if (br1 && br2) {
  421. g_bCritSecsInitialized = TRUE;
  422. }
  423. else {
  424. DBGPRINT(("TERMSRV: InitSessDir: critsec init failed\n"));
  425. if (br1)
  426. DeleteCriticalSection(&g_CritSecComObj);
  427. if (br2)
  428. DeleteCriticalSection(&g_CritSecInitialize);
  429. PostErrorValueEvent(EVENT_TS_SESSDIR_FAIL_INIT_TSSD,
  430. GetLastError());
  431. }
  432. }
  433. __except (EXCEPTION_EXECUTE_HANDLER) {
  434. // Since this happens at startup time, we should not fail.
  435. ASSERT(FALSE);
  436. DBGPRINT(("TERMSRV: InitSessDir: critsec init failed\n"));
  437. if (br1)
  438. DeleteCriticalSection(&g_CritSecComObj);
  439. if (br2)
  440. DeleteCriticalSection(&g_CritSecInitialize);
  441. PostErrorValueEvent(EVENT_TS_SESSDIR_FAIL_INIT_TSSD,
  442. GetExceptionCode());
  443. }
  444. // Now do the common initialization.
  445. if (g_bCritSecsInitialized)
  446. InitSessionDirectoryEx(false);
  447. }
  448. /****************************************************************************/
  449. // UpdateSessionDirectory
  450. //
  451. // Updates the session directory with new settings. Assumes COM has been
  452. // initialized.
  453. /****************************************************************************/
  454. DWORD UpdateSessionDirectory()
  455. {
  456. return InitSessionDirectoryEx(true);
  457. }
  458. #define REPOP_FAIL 1
  459. #define REPOP_SUCCESS 0
  460. /****************************************************************************/
  461. // RepopulateSessionDirectory
  462. //
  463. // Repopulates the session directory. Returns REPOP_FAIL (1) on failure,
  464. // REPOP_SUCCESS(0) otherwise.
  465. /****************************************************************************/
  466. DWORD RepopulateSessionDirectory()
  467. {
  468. DWORD WinStationCount = 0;
  469. PLIST_ENTRY Head, Next;
  470. DWORD i = 0;
  471. HRESULT hr;
  472. PWINSTATION pWinStation = NULL;
  473. ITSSessionDirectory *pTSSD;
  474. WCHAR *wBuffer = NULL;
  475. // If we got here, it should be because of the session directory.
  476. pTSSD = GetTSSD();
  477. if (pTSSD != NULL) {
  478. // Grab WinStationListLock
  479. ENTERCRIT( &WinStationListLock );
  480. Head = &WinStationListHead;
  481. // Count the WinStations I care about.
  482. for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
  483. pWinStation = CONTAINING_RECORD( Next, WINSTATION, Links );
  484. switch (pWinStation->State) {
  485. case State_Active:
  486. case State_Disconnected:
  487. case State_Shadow:
  488. WinStationCount += 1;
  489. break;
  490. }
  491. }
  492. // Allocate the memory for the structure to pass to the session
  493. // directory.
  494. TSSD_RepopulateSessionInfo *rsi = new TSSD_RepopulateSessionInfo[
  495. WinStationCount];
  496. if (rsi == NULL) {
  497. DBGPRINT(("TERMSRV: RepopulateSessDir: mem alloc failed\n"));
  498. // Release WinStationListLock
  499. LEAVECRIT( &WinStationListLock );
  500. goto CleanUp;
  501. }
  502. // Allocate string arrays (for now)
  503. wBuffer = new WCHAR[WinStationCount * TOTAL_STRINGS_LENGTH];
  504. if (wBuffer == NULL) {
  505. DBGPRINT(("TERMSRV: RepopulateSessDir: mem alloc failed\n"));
  506. // Release WinStationListLock
  507. LEAVECRIT( &WinStationListLock );
  508. delete [] rsi;
  509. goto CleanUp;
  510. }
  511. // Set the pointers in the rsi
  512. for ( i = 0; i < WinStationCount; i += 1) {
  513. rsi[i].UserName = &(wBuffer[i * TOTAL_STRINGS_LENGTH +
  514. USERNAME_OFFSET]);
  515. rsi[i].Domain = &(wBuffer[i * TOTAL_STRINGS_LENGTH +
  516. DOMAIN_OFFSET]);
  517. rsi[i].ApplicationType = &(wBuffer[i * TOTAL_STRINGS_LENGTH +
  518. APPLICATIONTYPE_OFFSET]);
  519. }
  520. // Now populate the structure to pass in.
  521. // Reset index to 0
  522. i = 0;
  523. for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
  524. pWinStation = CONTAINING_RECORD( Next, WINSTATION, Links );
  525. ASSERT(i <= WinStationCount);
  526. // There are two sets of information here: First, if the session
  527. // is Active, we can do stuff, then we have an intentional
  528. // fallthrough to the common code used by Disconnected and Active
  529. // sessions for the common stuff. For now, if it's disconnected
  530. // we then call the update function in our COM object.
  531. switch (pWinStation->State) {
  532. case State_Active:
  533. case State_Shadow:
  534. rsi[i].State = 0;
  535. // NOTE INTENTIONAL FALLTHROUGH
  536. case State_Disconnected:
  537. rsi[i].TSProtocol = pWinStation->Client.ProtocolType;
  538. rsi[i].ResolutionWidth = pWinStation->Client.HRes;
  539. rsi[i].ResolutionHeight = pWinStation->Client.VRes;
  540. rsi[i].ColorDepth = pWinStation->Client.ColorDepth;
  541. // TODO: I don't get it--USERNAME_LENGTH is 20, yet in the csi,
  542. // TODO: it's 256. Likewise, DOMAIN_LENGTH is 17.
  543. wcsncpy(rsi[i].UserName, pWinStation->UserName,
  544. USERNAME_LENGTH);
  545. rsi[i].UserName[USERNAME_LENGTH] = '\0';
  546. wcsncpy(rsi[i].Domain, pWinStation->Domain, DOMAIN_LENGTH);
  547. rsi[i].Domain[DOMAIN_LENGTH] = '\0';
  548. // TODO: Here there is a problem in that the INITIALPROGRAM
  549. // TODO: length is 256 + 1, but the buffer we copy into is
  550. // TODO: 256, hence we lose a character.
  551. wcsncpy(rsi[i].ApplicationType, pWinStation->
  552. Client.InitialProgram, INITIALPROGRAM_LENGTH - 1);
  553. rsi[i].ApplicationType[INITIALPROGRAM_LENGTH - 2] = '\0';
  554. rsi[i].SessionID = pWinStation->LogonId;
  555. rsi[i].CreateTimeLow = pWinStation->LogonTime.LowPart;
  556. rsi[i].CreateTimeHigh = pWinStation->LogonTime.HighPart;
  557. if (pWinStation->State == State_Disconnected) {
  558. rsi[i].DisconnectionTimeLow = pWinStation->DisconnectTime.
  559. LowPart;
  560. rsi[i].DisconnectionTimeHigh = pWinStation->DisconnectTime.
  561. HighPart;
  562. rsi[i].State = 1;
  563. }
  564. i += 1;
  565. break;
  566. }
  567. }
  568. // Release WinStationListLock
  569. LEAVECRIT( &WinStationListLock );
  570. // Call the session directory provider with our big struct.
  571. hr = pTSSD->Repopulate(WinStationCount, rsi);
  572. delete [] rsi;
  573. delete [] wBuffer;
  574. if (hr == S_OK) {
  575. ReleaseTSSD();
  576. return REPOP_SUCCESS;
  577. }
  578. else {
  579. goto CleanUp;
  580. }
  581. CleanUp:
  582. ReleaseTSSD();
  583. }
  584. return REPOP_FAIL;
  585. }
  586. /****************************************************************************/
  587. // DestroySessionDirectory
  588. //
  589. // Destroys the directory, releasing any COM objects held and other memory
  590. // used. Assumes COM has been initialized.
  591. /****************************************************************************/
  592. void DestroySessionDirectory()
  593. {
  594. ITSSessionDirectory *pTSSD = NULL;
  595. ITSSessionDirectoryEx *pTSSDEx = NULL;
  596. pTSSD = GetTSSD();
  597. pTSSDEx = GetTSSDEx();
  598. if (pTSSD != NULL) {
  599. ReleaseTSSD();
  600. ReleaseTSSD();
  601. }
  602. if (pTSSDEx != NULL) {
  603. ReleaseTSSDEx();
  604. ReleaseTSSDEx();
  605. }
  606. }
  607. /****************************************************************************/
  608. // SessDirNotifyLogon
  609. //
  610. // Called to inform the session directory of session creation.
  611. /****************************************************************************/
  612. void SessDirNotifyLogon(TSSD_CreateSessionInfo *pCreateInfo)
  613. {
  614. HRESULT hr;
  615. ITSSessionDirectory *pTSSD;
  616. pTSSD = GetTSSD();
  617. // We can get called even when the directory is inactive.
  618. if (pTSSD != NULL) {
  619. hr = pTSSD->NotifyCreateLocalSession(pCreateInfo);
  620. if (FAILED(hr)) {
  621. DBGPRINT(("TERMSRV: SessDirNotifyLogon: Call failed, "
  622. "hr=0x%X\n", hr));
  623. PostErrorValueEvent(EVENT_TS_SESSDIR_FAIL_UPDATE, hr);
  624. }
  625. ReleaseTSSD();
  626. }
  627. }
  628. /****************************************************************************/
  629. // SessDirNotifyDisconnection
  630. //
  631. // Called on session disconnection to inform the session directory.
  632. /****************************************************************************/
  633. void SessDirNotifyDisconnection(DWORD SessionID, FILETIME DiscTime)
  634. {
  635. HRESULT hr;
  636. ITSSessionDirectory *pTSSD;
  637. pTSSD = GetTSSD();
  638. // We can get called even when the directory is inactive.
  639. if (pTSSD != NULL) {
  640. hr = pTSSD->NotifyDisconnectLocalSession(SessionID, DiscTime);
  641. if (FAILED(hr)) {
  642. DBGPRINT(("TERMSRV: SessDirNotifyDisc: Call failed, "
  643. "hr=0x%X\n", hr));
  644. PostErrorValueEvent(EVENT_TS_SESSDIR_FAIL_UPDATE, hr);
  645. }
  646. ReleaseTSSD();
  647. }
  648. }
  649. /****************************************************************************/
  650. // SessDirNotifyReconnection
  651. //
  652. // Called on session reconnection to inform the session directory.
  653. /****************************************************************************/
  654. void SessDirNotifyReconnection(TSSD_ReconnectSessionInfo *pReconnInfo)
  655. {
  656. HRESULT hr;
  657. ITSSessionDirectory *pTSSD;
  658. pTSSD = GetTSSD();
  659. // We can get called even when the directory is inactive.
  660. if (pTSSD != NULL) {
  661. hr = pTSSD->NotifyReconnectLocalSession(pReconnInfo);
  662. if (FAILED(hr)) {
  663. DBGPRINT(("TERMSRV: SessDirNotifyReconn: Call failed, "
  664. "hr=0x%X\n", hr));
  665. PostErrorValueEvent(EVENT_TS_SESSDIR_FAIL_UPDATE, hr);
  666. }
  667. ReleaseTSSD();
  668. }
  669. }
  670. /****************************************************************************/
  671. // SessDirNotifyLogoff
  672. //
  673. // Called on logoff to inform the session directory.
  674. /****************************************************************************/
  675. void SessDirNotifyLogoff(DWORD SessionID)
  676. {
  677. HRESULT hr;
  678. ITSSessionDirectory *pTSSD;
  679. pTSSD = GetTSSD();
  680. // We can get called even when the directory is inactive.
  681. if (pTSSD != NULL) {
  682. hr = pTSSD->NotifyDestroyLocalSession(SessionID);
  683. if (FAILED(hr)) {
  684. DBGPRINT(("TERMSRV: SessDirNotifyLogoff: Call failed, "
  685. "hr=0x%X\n", hr));
  686. PostErrorValueEvent(EVENT_TS_SESSDIR_FAIL_UPDATE, hr);
  687. }
  688. ReleaseTSSD();
  689. }
  690. }
  691. /****************************************************************************/
  692. // SessDirNotifyReconnectPending
  693. //
  694. // Called to inform the session directory a session should start soon on
  695. // another machine in the cluster (for Directory Integrity Service).
  696. /****************************************************************************/
  697. void SessDirNotifyReconnectPending(WCHAR *ServerName)
  698. {
  699. HRESULT hr;
  700. ITSSessionDirectory *pTSSD;
  701. pTSSD = GetTSSD();
  702. // We can get called even when the directory is inactive.
  703. if (pTSSD != NULL) {
  704. hr = pTSSD->NotifyReconnectPending(ServerName);
  705. if (FAILED(hr)) {
  706. DBGPRINT(("TERMSRV: SessDirNotifyReconnectPending: Call failed, "
  707. "hr=0x%X\n", hr));
  708. PostErrorValueEvent(EVENT_TS_SESSDIR_FAIL_UPDATE, hr);
  709. }
  710. ReleaseTSSD();
  711. }
  712. }
  713. /****************************************************************************/
  714. // SessDirGetDisconnectedSessions
  715. //
  716. // Returns in the provided TSSD_DisconnectedSessionInfo buffer space
  717. // up to TSSD_MaxDisconnectedSessions' worth of disconnected sessions
  718. // from the session directory. Returns the number of sessions returned, which
  719. // can be zero.
  720. /****************************************************************************/
  721. unsigned SessDirGetDisconnectedSessions(
  722. WCHAR *UserName,
  723. WCHAR *Domain,
  724. TSSD_DisconnectedSessionInfo Info[TSSD_MaxDisconnectedSessions])
  725. {
  726. DWORD NumSessions = 0;
  727. HRESULT hr;
  728. ITSSessionDirectory *pTSSD;
  729. pTSSD = GetTSSD();
  730. // We can get called even when the directory is inactive.
  731. if (pTSSD != NULL) {
  732. hr = pTSSD->GetUserDisconnectedSessions(UserName, Domain,
  733. &NumSessions, Info);
  734. if (FAILED(hr)) {
  735. DBGPRINT(("TERMSRV: SessDirGetDiscSessns: Call failed, "
  736. "hr=0x%X\n", hr));
  737. PostErrorValueEvent(EVENT_TS_SESSDIR_FAIL_QUERY, hr);
  738. }
  739. ReleaseTSSD();
  740. }
  741. return NumSessions;
  742. }
  743. /****************************************************************************/
  744. // SessDirGetLBInfo
  745. //
  746. // Call the SessDirEx COM object interface, using the server address, get
  747. // the opaque load balance info back, will send the info to the client.
  748. /****************************************************************************/
  749. BOOL SessDirGetLBInfo(
  750. WCHAR *ServerAddress,
  751. DWORD* pLBInfoSize,
  752. PBYTE* pLBInfo)
  753. {
  754. ITSSessionDirectoryEx *pTSSDEx;
  755. HRESULT hr;
  756. static BOOL EventLogged = FALSE;
  757. *pLBInfoSize = 0;
  758. *pLBInfo = NULL;
  759. pTSSDEx = GetTSSDEx();
  760. if (pTSSDEx != NULL) {
  761. hr = pTSSDEx->GetLoadBalanceInfo(ServerAddress, (BSTR *)pLBInfo);
  762. if(SUCCEEDED(hr))
  763. {
  764. *pLBInfoSize = SysStringByteLen((BSTR)(*pLBInfo));
  765. }
  766. else
  767. {
  768. DBGPRINT(("TERMSRV: SessDirGetLBInfo: Call failed, "
  769. "hr=0x%X\n", hr));
  770. if (EventLogged == FALSE) {
  771. PostErrorValueEvent(EVENT_TS_SESSDIR_FAIL_LBQUERY, hr);
  772. EventLogged = TRUE;
  773. }
  774. }
  775. ReleaseTSSDEx();
  776. }
  777. else {
  778. DBGPRINT(("TERMSRV: SessDirGetLBInfo: Call failed, pTSSDEx is NULL "));
  779. hr = E_FAIL;
  780. }
  781. return SUCCEEDED(hr);
  782. }
  783. #define SERVER_ADDRESS_LENGTH 64
  784. /****************************************************************************/
  785. // IsSameAsCurrentIP
  786. //
  787. // Determines whether the IP address given is the same as the current machine.
  788. // Returning FALSE in case of error is not a problem--the client will
  789. // just get redirected right back here.
  790. /****************************************************************************/
  791. BOOL IsSameAsCurrentIP(WCHAR *SessionIPAddress)
  792. {
  793. // Get the server adresses.
  794. int RetVal;
  795. unsigned long NumericalSessionIPAddr = 0;
  796. char achComputerName[256];
  797. DWORD dwComputerNameSize;
  798. PBYTE pServerAddrByte;
  799. PBYTE pSessionAddrByte;
  800. ADDRINFO *AddrInfo, *AI;
  801. struct sockaddr_in *pIPV4addr;
  802. char AnsiSessionIPAddress[SERVER_ADDRESS_LENGTH];
  803. // Compute integer for the server address.
  804. // First, get ServerAddress as an ANSI string.
  805. RetVal = WideCharToMultiByte(CP_ACP, 0, SessionIPAddress, -1,
  806. AnsiSessionIPAddress, SERVER_ADDRESS_LENGTH, NULL, NULL);
  807. if (RetVal == 0) {
  808. DBGPRINT(("IsSameServerIP: WideCharToMB failed %d\n", GetLastError()));
  809. return FALSE;
  810. }
  811. // Now, get the numerical server address.
  812. // Now, use inet_addr to turn into an unsigned long.
  813. NumericalSessionIPAddr = inet_addr(AnsiSessionIPAddress);
  814. if (NumericalSessionIPAddr == INADDR_NONE) {
  815. DBGPRINT(("IsSameServerIP: inet_addr failed\n"));
  816. return FALSE;
  817. }
  818. pSessionAddrByte = (PBYTE) &NumericalSessionIPAddr;
  819. dwComputerNameSize = sizeof(achComputerName);
  820. if (!GetComputerNameA(achComputerName,&dwComputerNameSize)) {
  821. return FALSE;
  822. }
  823. RetVal = getaddrinfo(achComputerName, NULL, NULL, &AddrInfo);
  824. if (RetVal != 0) {
  825. DBGPRINT (("Cannot resolve address, error: %d\n", RetVal));
  826. return FALSE;
  827. }
  828. else {
  829. // Compare all server adresses with client till a match is found.
  830. // Currently only works for IPv4.
  831. for (AI = AddrInfo; AI != NULL; AI = AI->ai_next) {
  832. if (AI->ai_family == AF_INET) {
  833. if (AI->ai_addrlen >= sizeof(struct sockaddr_in)) {
  834. pIPV4addr = (struct sockaddr_in *) AI->ai_addr;
  835. pServerAddrByte = (PBYTE)&pIPV4addr->sin_addr;
  836. if (RtlEqualMemory(pSessionAddrByte, pServerAddrByte, 4)) {
  837. return TRUE;
  838. }
  839. }
  840. }
  841. }
  842. }
  843. return FALSE;
  844. }
  845. /****************************************************************************/
  846. // SessDirCheckRedirectClient
  847. //
  848. // Performs the set of steps needed to get the client's clustering
  849. // capabilities, get the disconnected session list, and apply client
  850. // redirection policy. Returns TRUE if the client was redirected, FALSE if
  851. // the current WinStation transfer should be continued.
  852. /****************************************************************************/
  853. BOOL SessDirCheckRedirectClient(
  854. PWINSTATION pTargetWinStation,
  855. TS_LOAD_BALANCE_INFO *pLBInfo)
  856. {
  857. BOOL rc = FALSE;
  858. ULONG ReturnLength;
  859. unsigned i, NumSessions;
  860. NTSTATUS Status;
  861. ITSSessionDirectory *pTSSD;
  862. pTSSD = GetTSSD();
  863. pTargetWinStation->NumClusterDiscSessions = 0;
  864. if (pTSSD != NULL) {
  865. if (pLBInfo->bClientSupportsRedirection &&
  866. !pLBInfo->bRequestedSessionIDFieldValid) {
  867. // The client has not been redirected to this machine. See if we
  868. // have disconnected sessions in the database for redirecting.
  869. NumSessions = pTargetWinStation->NumClusterDiscSessions =
  870. SessDirGetDisconnectedSessions(
  871. pLBInfo->UserName,
  872. pLBInfo->Domain,
  873. pTargetWinStation->ClusterDiscSessions);
  874. if (pTargetWinStation->NumClusterDiscSessions > 0) {
  875. // trevorfo: Applying policy here for reconnection to only one session
  876. // (whichever is first). More general policy requires a selection UI at the
  877. // client or in WinLogon.
  878. // Find the first session in the list that matches the
  879. // client's session requirements. Namely, we filter based
  880. // on the client's TS protocol, wire protocol, and application
  881. // type.
  882. for (i = 0; i < NumSessions; i++) {
  883. if ((pLBInfo->ProtocolType ==
  884. pTargetWinStation->ClusterDiscSessions[i].
  885. TSProtocol) &&
  886. (!_wcsicmp(pLBInfo->InitialProgram,
  887. pTargetWinStation->ClusterDiscSessions[i].
  888. ApplicationType))) {
  889. break;
  890. }
  891. }
  892. if (i == NumSessions) {
  893. TRACE((hTrace,TC_ICASRV,TT_API1,
  894. "TERMSRV: SessDirCheckRedir: No matching sessions "
  895. "found\n"));
  896. }
  897. else {
  898. // If the session is not on this server, redirect the
  899. // client. See notes above about use of
  900. // _IcaStackIoControl().
  901. if (!IsSameAsCurrentIP(pTargetWinStation->
  902. ClusterDiscSessions[i].ServerAddress)) {
  903. BYTE *pRedirInfo, *pRedirInfoStart;
  904. BYTE *LBInfo = NULL;
  905. DWORD LBInfoSize = 0;
  906. DWORD RedirInfoSize = 0;
  907. DWORD ServerAddrLen = 0;
  908. DWORD DomainSize = 0;
  909. DWORD UserNameSize = 0;
  910. DWORD PasswordSize = 0;
  911. RedirInfoSize = sizeof(TS_CLIENT_REDIRECTION_INFO);
  912. // Setup the server addr
  913. if (g_SessDirUseServerAddr ||
  914. pLBInfo->bClientRequireServerAddr) {
  915. ServerAddrLen = (DWORD)((wcslen(pTargetWinStation->
  916. ClusterDiscSessions[i].ServerAddress) + 1) *
  917. sizeof(WCHAR));
  918. RedirInfoSize += (ServerAddrLen + sizeof(ULONG));
  919. DBGPRINT(("TERMSRV: SessDirCheckRedir: size=%d, "
  920. "addr=%S\n", ServerAddrLen,
  921. (WCHAR *)pTargetWinStation->
  922. ClusterDiscSessions[i].ServerAddress));
  923. }
  924. else {
  925. DBGPRINT(("TERMSRV: SessDirCheckRedir no server "
  926. "address: g_SessDirUseServerAddr = %d, "
  927. "bClientRequireServerAddr = %d\n",
  928. g_SessDirUseServerAddr,
  929. pLBInfo->bClientRequireServerAddr));
  930. }
  931. // Setup the load balance info
  932. if ((pLBInfo->bClientRequireServerAddr == 0) &&
  933. SessDirGetLBInfo(
  934. pTargetWinStation->ClusterDiscSessions[i].
  935. ServerAddress, &LBInfoSize, &LBInfo)) {
  936. if (LBInfo) {
  937. DBGPRINT(("TERMSRV: SessDirCheckRedir: size=%d, "
  938. "info=%S\n", LBInfoSize,
  939. (WCHAR *)LBInfo));
  940. RedirInfoSize += (LBInfoSize + sizeof(ULONG));
  941. }
  942. }
  943. else {
  944. DBGPRINT(("TERMSRV: SessDirCheckRedir failed: "
  945. "size=%d, info=%S\n", LBInfoSize,
  946. (WCHAR *)LBInfo));
  947. }
  948. // Only send domain, username and password info if client
  949. // redirection version is 3 and above
  950. if (pLBInfo->ClientRedirectionVersion >= TS_CLUSTER_REDIRECTION_VERSION3) {
  951. //domain
  952. if (pLBInfo->Domain) {
  953. DomainSize = (DWORD)(wcslen(pLBInfo->Domain) + 1) * sizeof(WCHAR);
  954. RedirInfoSize += DomainSize + sizeof(ULONG);
  955. }
  956. //username
  957. if (pLBInfo->UserName) {
  958. UserNameSize = (DWORD)(wcslen(pLBInfo->UserName) + 1) * sizeof(WCHAR);
  959. RedirInfoSize += UserNameSize + sizeof(ULONG);
  960. }
  961. //password
  962. if (pLBInfo->Password) {
  963. PasswordSize = (DWORD)(wcslen(pLBInfo->Password) + 1) * sizeof(WCHAR);
  964. RedirInfoSize += PasswordSize + sizeof(ULONG);
  965. }
  966. }
  967. // Setup the load balance IOCTL
  968. pRedirInfoStart = pRedirInfo = new BYTE[RedirInfoSize];
  969. TS_CLIENT_REDIRECTION_INFO *pClientRedirInfo =
  970. (TS_CLIENT_REDIRECTION_INFO *)pRedirInfo;
  971. if (pRedirInfo != NULL) {
  972. pClientRedirInfo->SessionID =
  973. pTargetWinStation->ClusterDiscSessions[i].
  974. SessionID;
  975. pClientRedirInfo->Flags = 0;
  976. pRedirInfo += sizeof(TS_CLIENT_REDIRECTION_INFO);
  977. if (ServerAddrLen) {
  978. *((ULONG UNALIGNED*)(pRedirInfo)) =
  979. ServerAddrLen;
  980. wcscpy((WCHAR*)(pRedirInfo + sizeof(ULONG)),
  981. pTargetWinStation->ClusterDiscSessions[i].
  982. ServerAddress);
  983. pRedirInfo += ServerAddrLen + sizeof(ULONG);
  984. pClientRedirInfo->Flags |= TARGET_NET_ADDRESS;
  985. }
  986. if (LBInfoSize) {
  987. *((ULONG UNALIGNED*)(pRedirInfo)) = LBInfoSize;
  988. memcpy((BYTE*)(pRedirInfo + sizeof(ULONG)),
  989. LBInfo, LBInfoSize);
  990. pRedirInfo += LBInfoSize + sizeof(ULONG);
  991. pClientRedirInfo->Flags |= LOAD_BALANCE_INFO;
  992. }
  993. if (UserNameSize) {
  994. *((ULONG UNALIGNED*)(pRedirInfo)) = UserNameSize;
  995. wcscpy((WCHAR*)(pRedirInfo + sizeof(ULONG)),
  996. pLBInfo->UserName);
  997. pRedirInfo += UserNameSize + sizeof(ULONG);
  998. pClientRedirInfo->Flags |= LB_USERNAME;
  999. }
  1000. if (DomainSize) {
  1001. *((ULONG UNALIGNED*)(pRedirInfo)) = DomainSize;
  1002. wcscpy((WCHAR*)(pRedirInfo + sizeof(ULONG)),
  1003. pLBInfo->Domain);
  1004. pRedirInfo += DomainSize + sizeof(ULONG);
  1005. pClientRedirInfo->Flags |= LB_DOMAIN;
  1006. }
  1007. if (PasswordSize) {
  1008. *((ULONG UNALIGNED*)(pRedirInfo)) = PasswordSize;
  1009. wcscpy((WCHAR*)(pRedirInfo + sizeof(ULONG)),
  1010. pLBInfo->Password);
  1011. pRedirInfo += PasswordSize + sizeof(ULONG);
  1012. pClientRedirInfo->Flags |= LB_PASSWORD;
  1013. }
  1014. }
  1015. else {
  1016. Status = STATUS_NO_MEMORY;
  1017. // The stack returned failure. Continue
  1018. // the current connection.
  1019. TRACE((hTrace,TC_ICASRV,TT_API1,
  1020. "TERMSRV: Failed STACK_CLIENT_REDIR, "
  1021. "SessionID=%u, Status=0x%X\n",
  1022. pTargetWinStation->LogonId, Status));
  1023. PostErrorValueEvent(
  1024. EVENT_TS_SESSDIR_FAIL_CLIENT_REDIRECT,
  1025. Status);
  1026. goto Cleanup;
  1027. }
  1028. Status = IcaStackIoControl(pTargetWinStation->hStack,
  1029. IOCTL_TS_STACK_SEND_CLIENT_REDIRECTION,
  1030. pClientRedirInfo, RedirInfoSize,
  1031. NULL, 0,
  1032. &ReturnLength);
  1033. if (NT_SUCCESS(Status)) {
  1034. // Notify session directory
  1035. //
  1036. // There is a relatively benign race condition here.
  1037. // If the second server logs in the user completely,
  1038. // it may end up hitting the session directory
  1039. // before this statement executes. In that case,
  1040. // the directory integrity service may end up
  1041. // pinging the machine once.
  1042. SessDirNotifyReconnectPending(pTargetWinStation->
  1043. ClusterDiscSessions[i].ServerAddress);
  1044. // Drop the current connection.
  1045. rc = TRUE;
  1046. }
  1047. else {
  1048. // The stack returned failure. Continue
  1049. // the current connection.
  1050. TRACE((hTrace,TC_ICASRV,TT_API1,
  1051. "TERMSRV: Failed STACK_CLIENT_REDIR, "
  1052. "SessionID=%u, Status=0x%X\n",
  1053. pTargetWinStation->LogonId, Status));
  1054. PostErrorValueEvent(
  1055. EVENT_TS_SESSDIR_FAIL_CLIENT_REDIRECT,
  1056. Status);
  1057. }
  1058. Cleanup:
  1059. // Cleanup the buffers
  1060. if (LBInfo != NULL) {
  1061. SysFreeString((BSTR)LBInfo);
  1062. LBInfo = NULL;
  1063. }
  1064. if (pRedirInfo != NULL) {
  1065. delete [] pRedirInfoStart;
  1066. pRedirInfoStart = NULL;
  1067. }
  1068. }
  1069. }
  1070. }
  1071. }
  1072. ReleaseTSSD();
  1073. }
  1074. return rc;
  1075. }
  1076. /****************************************************************************/
  1077. // SetTSSD
  1078. //
  1079. // These three functions ensure protected access to the session directory
  1080. // provider at all times. SetTSSD sets the pointer and increments the
  1081. // reference count to 1.
  1082. //
  1083. // SetTSSD returns:
  1084. // 0 on success
  1085. // -1 if failed because there was still a reference count on the COM object.
  1086. // This could happen if set was called too quickly after the final release
  1087. // to attempt to delete the object, as there still may be pending calls using
  1088. // the COM object.
  1089. // -2 if failed because the critical section is not initialized. This
  1090. // shouldn't be reached in normal operation, because Init is the only
  1091. // function that can call Set, and it bails if it fails the creation of the
  1092. // critical section.
  1093. /****************************************************************************/
  1094. int SetTSSD(ITSSessionDirectory *pTSSD)
  1095. {
  1096. int retval = 0;
  1097. if (g_bCritSecsInitialized != FALSE) {
  1098. EnterCriticalSection(&g_CritSecComObj);
  1099. if (g_nComObjRefCount == 0) {
  1100. ASSERT(g_pTSSDPriv == NULL);
  1101. g_pTSSDPriv = pTSSD;
  1102. g_nComObjRefCount = 1;
  1103. }
  1104. else {
  1105. DBGPRINT(("TERMSRV: SetTSSD: obj ref count not 0!\n"));
  1106. retval = -1;
  1107. }
  1108. LeaveCriticalSection(&g_CritSecComObj);
  1109. }
  1110. else {
  1111. ASSERT(g_bCritSecsInitialized == TRUE);
  1112. retval = -2;
  1113. }
  1114. return retval;
  1115. }
  1116. /****************************************************************************/
  1117. // GetTSSD
  1118. //
  1119. // GetTSSD returns a pointer to the session directory provider, if any, and
  1120. // increments the reference count if there is one.
  1121. /****************************************************************************/
  1122. ITSSessionDirectory *GetTSSD()
  1123. {
  1124. ITSSessionDirectory *pTSSD = NULL;
  1125. if (g_bCritSecsInitialized != FALSE) {
  1126. EnterCriticalSection(&g_CritSecComObj);
  1127. if (g_pTSSDPriv != NULL) {
  1128. g_nComObjRefCount += 1;
  1129. }
  1130. else {
  1131. ASSERT(g_nComObjRefCount == 0);
  1132. }
  1133. pTSSD = g_pTSSDPriv;
  1134. LeaveCriticalSection(&g_CritSecComObj);
  1135. }
  1136. return pTSSD;
  1137. }
  1138. /****************************************************************************/
  1139. // ReleaseTSSD
  1140. //
  1141. // ReleaseTSSD decrements the reference count of the session directory provider
  1142. // after a thread has finished using it, or when it is going to be deleted.
  1143. //
  1144. // If the reference count goes to zero, the pointer to the session directory
  1145. // provider is set to NULL.
  1146. /****************************************************************************/
  1147. void ReleaseTSSD()
  1148. {
  1149. ITSSessionDirectory *killthispTSSD = NULL;
  1150. if (g_bCritSecsInitialized != FALSE) {
  1151. EnterCriticalSection(&g_CritSecComObj);
  1152. ASSERT(g_nComObjRefCount != 0);
  1153. if (g_nComObjRefCount != 0) {
  1154. g_nComObjRefCount -= 1;
  1155. if (g_nComObjRefCount == 0) {
  1156. killthispTSSD = g_pTSSDPriv;
  1157. g_pTSSDPriv = NULL;
  1158. }
  1159. }
  1160. LeaveCriticalSection(&g_CritSecComObj);
  1161. }
  1162. // Now, release the session directory provider if our temppTSSD is NULL.
  1163. // We didn't want to release it while holding the critical section because
  1164. // that might create a deadlock in the recovery thread. Well, it did once.
  1165. if (killthispTSSD != NULL)
  1166. killthispTSSD->Release();
  1167. }
  1168. /****************************************************************************/
  1169. // SetTSSDEx
  1170. //
  1171. // These three functions ensure protected access to the session directory
  1172. // provider at all times. SetTSSDEx sets the pointer and increments the
  1173. // reference count to 1.
  1174. //
  1175. // SetTSSDEx returns:
  1176. // 0 on success
  1177. // -1 if failed because there was still a reference count on the COM object.
  1178. // This could happen if set was called too quickly after the final release
  1179. // to attempt to delete the object, as there still may be pending calls using
  1180. // the COM object.
  1181. /****************************************************************************/
  1182. int SetTSSDEx(ITSSessionDirectoryEx *pTSSDEx)
  1183. {
  1184. int retval = 0;
  1185. EnterCriticalSection(&g_CritSecComObj);
  1186. if (g_nTSSDExObjRefCount == 0) {
  1187. ASSERT(g_pTSSDExPriv == NULL);
  1188. g_pTSSDExPriv = pTSSDEx;
  1189. g_nTSSDExObjRefCount = 1;
  1190. }
  1191. else {
  1192. DBGPRINT(("TERMSRV: SetTSSDEx: obj ref count not 0!\n"));
  1193. retval = -1;
  1194. }
  1195. LeaveCriticalSection(&g_CritSecComObj);
  1196. return retval;
  1197. }
  1198. /****************************************************************************/
  1199. // GetTSSDEx
  1200. //
  1201. // GetTSSDEx returns a pointer to the session directory provider, if any, and
  1202. // increments the reference count if there is one.
  1203. /****************************************************************************/
  1204. ITSSessionDirectoryEx *GetTSSDEx()
  1205. {
  1206. ITSSessionDirectoryEx *pTSSDEx = NULL;
  1207. if (g_bCritSecsInitialized != FALSE) {
  1208. EnterCriticalSection(&g_CritSecComObj);
  1209. if (g_pTSSDExPriv != NULL) {
  1210. g_nTSSDExObjRefCount += 1;
  1211. }
  1212. else {
  1213. ASSERT(g_nTSSDExObjRefCount == 0);
  1214. }
  1215. pTSSDEx = g_pTSSDExPriv;
  1216. LeaveCriticalSection(&g_CritSecComObj);
  1217. }
  1218. return pTSSDEx;
  1219. }
  1220. /****************************************************************************/
  1221. // ReleaseTSSDEx
  1222. //
  1223. // ReleaseTSSDEx decrements the reference count of the session directory
  1224. // provider after a thread has finished using it, or when it is going to be
  1225. // deleted.
  1226. //
  1227. // If the reference count goes to zero, the pointer to the session directory
  1228. // provider is set to NULL.
  1229. /****************************************************************************/
  1230. void ReleaseTSSDEx()
  1231. {
  1232. ITSSessionDirectoryEx *killthispTSSDEx = NULL;
  1233. EnterCriticalSection(&g_CritSecComObj);
  1234. ASSERT(g_nTSSDExObjRefCount != 0);
  1235. if (g_nTSSDExObjRefCount != 0) {
  1236. g_nTSSDExObjRefCount -= 1;
  1237. if (g_nTSSDExObjRefCount == 0) {
  1238. killthispTSSDEx = g_pTSSDExPriv;
  1239. g_pTSSDExPriv = NULL;
  1240. }
  1241. }
  1242. LeaveCriticalSection(&g_CritSecComObj);
  1243. // Now, release the session directory provider if our temppTSSD is NULL.
  1244. // We didn't want to release it while holding the critical section because
  1245. // that might create a deadlock in the recovery thread. Well, it did once.
  1246. if (killthispTSSDEx != NULL)
  1247. killthispTSSDEx->Release();
  1248. }
  1249. #pragma warning (pop)