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.

2091 lines
74 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. #pragma warning (push, 4)
  15. #define CLSIDLENGTH 39
  16. #define STORESERVERNAMELENGTH 64
  17. #define CLUSTERNAMELENGTH 64
  18. #define OPAQUESETTINGSLENGTH 256
  19. #define IPADDRESSLENGTH 64
  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. extern "C" BOOL IsCallerSystem( VOID );
  31. extern "C" BOOL IsCallerAdmin( VOID );
  32. WCHAR g_LocalServerAddress[64];
  33. ULONG g_LocalIPAddress = 0;
  34. BOOL g_SessDirUseServerAddr = TRUE;
  35. DWORD g_WaitForRepopulate = TS_WAITFORREPOPULATE_TIMEOUT * 1000;
  36. // Do not access directly. Use *TSSD functions.
  37. //
  38. // These variables are used to manage synchronization with retrieving the
  39. // pointer to the COM object. See *TSSD, below, for details on how they are
  40. // used.
  41. ITSSessionDirectory *g_pTSSDPriv = NULL;
  42. CRITICAL_SECTION g_CritSecComObj;
  43. CRITICAL_SECTION g_CritSecInitialize;
  44. int g_nComObjRefCount = 0;
  45. BOOL g_bCritSecsInitialized = FALSE;
  46. // Do not access directly. Use *TSSDEx functions.
  47. //
  48. // These variables are used to manage synchronization with retrieving the
  49. // pointer to the COM object. See *TSSDEx, below, for details on how they are
  50. // used.
  51. ITSSessionDirectoryEx *g_pTSSDExPriv = NULL;
  52. int g_nTSSDExObjRefCount = 0;
  53. /****************************************************************************/
  54. // SessDirGetLocalIPAddr
  55. //
  56. // Gets the local IP address of this machine. On success, returns 0. On
  57. // failure, returns a failure code from the function that failed.
  58. /****************************************************************************/
  59. DWORD SessDirGetLocalIPAddr(WCHAR *LocalIP)
  60. {
  61. DWORD NameSize;
  62. unsigned char *tempaddr;
  63. WCHAR psServerName[64];
  64. char psServerNameA[64];
  65. NameSize = sizeof(psServerName) / sizeof(WCHAR);
  66. if (GetComputerNameEx(ComputerNamePhysicalDnsHostname,
  67. psServerName, &NameSize)) {
  68. // Temporary code to get an IP address. This should be replaced in the
  69. // fix to bug #323867.
  70. struct hostent *hptr;
  71. // change the wide character string to non-wide
  72. sprintf(psServerNameA, "%S", psServerName);
  73. if ((hptr = gethostbyname(psServerNameA)) == 0) {
  74. DWORD Err = WSAGetLastError();
  75. return Err;
  76. }
  77. tempaddr = (unsigned char *)*(hptr->h_addr_list);
  78. wsprintf(LocalIP, L"%d.%d.%d.%d", tempaddr[0], tempaddr[1],
  79. tempaddr[2], tempaddr[3]);
  80. }
  81. else {
  82. DWORD Err = GetLastError();
  83. return Err;
  84. }
  85. return 0;
  86. }
  87. /****************************************************************************/
  88. // Remove preceding and succeding space in str
  89. //
  90. // Assume the str is NULL terminated
  91. /****************************************************************************/
  92. void RemoveSpaceInStr(WCHAR *str)
  93. {
  94. WCHAR *strEnd, *strTemp;
  95. size_t len, i;
  96. if ((str == NULL) || (wcslen(str) == 0)) {
  97. return;
  98. }
  99. len = wcslen(str);
  100. // strEnd point to the last char in str
  101. strEnd = str + len -1;
  102. // Remove the succeding blank space in the str
  103. for (strTemp=strEnd; strTemp>=str; strTemp--) {
  104. if (strTemp[0] == L' ') {
  105. strTemp[0] = L'\0';
  106. }
  107. else {
  108. break;
  109. }
  110. }
  111. // Get the length of the new string
  112. len = wcslen(str);
  113. if (len == 0) {
  114. return;
  115. }
  116. // Find the 1st non-space char in str
  117. for (i=0; i<len; i++) {
  118. if (str[i] != L' ') {
  119. break;
  120. }
  121. }
  122. if (i != 0) {
  123. // New str length
  124. len -= i;
  125. wcsncpy(str, str + i, len);
  126. }
  127. str[len] = '\0';
  128. return;
  129. }
  130. /****************************************************************************/
  131. // InitSessionDirectoryEx
  132. //
  133. // Reads values from the registry, and either initializes the session
  134. // directory or updates it, depending on the value of the Update parameter.
  135. /****************************************************************************/
  136. DWORD InitSessionDirectoryEx(DWORD UpdatePara)
  137. {
  138. DWORD Len;
  139. DWORD Type;
  140. DWORD DataSize;
  141. BOOL hKeyTermSrvSucceeded = FALSE;
  142. HRESULT hr;
  143. DWORD ErrVal = 0;
  144. CLSID TSSDCLSID;
  145. CLSID TSSDEXCLSID;
  146. LONG RegRetVal;
  147. HKEY hKey = NULL;
  148. HKEY hKeyTermSrv = NULL;
  149. ITSSessionDirectory *pTSSD = NULL;
  150. ITSSessionDirectoryEx *pTSSDEx = NULL;
  151. BOOL bClusteringActive = FALSE;
  152. BOOL bThisServerIsInSingleSessionMode;
  153. WCHAR CLSIDStr[CLSIDLENGTH];
  154. WCHAR CLSIDEXStr[CLSIDLENGTH];
  155. WCHAR StoreServerName[STORESERVERNAMELENGTH];
  156. WCHAR ClusterName[CLUSTERNAMELENGTH];
  157. WCHAR OpaqueSettings[OPAQUESETTINGSLENGTH];
  158. WCHAR SDRedirectionIP[IPADDRESSLENGTH];
  159. unsigned char *tempaddr;
  160. BOOL Update = FALSE;
  161. BOOL ForceRejoin = FALSE;
  162. LONG RepopulateWaitTimeout = TS_WAITFORREPOPULATE_TIMEOUT;
  163. if (UpdatePara & TSSD_UPDATE)
  164. Update = TRUE;
  165. if (UpdatePara & TSSD_FORCEREJOIN)
  166. ForceRejoin = TRUE;
  167. if (g_bCritSecsInitialized == FALSE) {
  168. ASSERT(FALSE);
  169. PostErrorValueEvent(EVENT_TS_SESSDIR_FAIL_INIT_TSSD,
  170. (DWORD) E_OUTOFMEMORY);
  171. return (DWORD) E_OUTOFMEMORY;
  172. }
  173. // trevorfo: Load only if any 1 loaded protocol needs it? Requires running
  174. // off of StartAllWinStations.
  175. // No more than one thread should be doing initialization.
  176. EnterCriticalSection(&g_CritSecInitialize);
  177. // Load registry keys.
  178. RegRetVal = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_CONTROL_TSERVER, 0,
  179. KEY_READ, &hKeyTermSrv);
  180. if (RegRetVal != ERROR_SUCCESS) {
  181. PostErrorValueEvent(EVENT_TS_SESSDIR_FAIL_INIT_TSSD,
  182. RegRetVal);
  183. goto RegFailExit;
  184. }
  185. else {
  186. hKeyTermSrvSucceeded = TRUE;
  187. }
  188. RegRetVal = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_TS_CLUSTERSETTINGS, 0,
  189. KEY_READ, &hKey);
  190. if (RegRetVal != ERROR_SUCCESS) {
  191. DBGPRINT(("TERMSRV: RegOpenKeyEx for ClusterSettings err %u\n",
  192. RegRetVal));
  193. goto RegFailExit;
  194. }
  195. //
  196. // First, we get the serious settings--active, SD location, and cluster
  197. // name.
  198. //
  199. // If group policy exists for all three, use that. Otherwise, use what
  200. // is in the registry.
  201. //
  202. StoreServerName[0] = L'\0';
  203. ClusterName[0] = L'\0';
  204. OpaqueSettings[0] = L'\0';
  205. SDRedirectionIP[0] = L'\0';
  206. if (g_MachinePolicy.fPolicySessionDirectoryActive) {
  207. bClusteringActive = g_MachinePolicy.SessionDirectoryActive;
  208. }
  209. else { //Read from registry
  210. Len = sizeof(bClusteringActive);
  211. RegQueryValueEx(hKeyTermSrv, REG_TS_SESSDIRACTIVE, NULL, &Type,
  212. (BYTE *)&bClusteringActive, &Len);
  213. }
  214. // Get SD server name
  215. if (g_MachinePolicy.fPolicySessionDirectoryLocation) {
  216. wcsncpy(StoreServerName, g_MachinePolicy.SessionDirectoryLocation,
  217. STORESERVERNAMELENGTH);
  218. StoreServerName[STORESERVERNAMELENGTH - 1] = '\0';
  219. }
  220. else { //Read from registry
  221. // Not an error for the name to be absent or empty.
  222. DataSize = sizeof(StoreServerName);
  223. RegRetVal = RegQueryValueExW(hKey, REG_TS_CLUSTER_STORESERVERNAME,
  224. NULL, &Type, (BYTE *)StoreServerName, &DataSize);
  225. if (RegRetVal != ERROR_SUCCESS) {
  226. DBGPRINT(("TERMSRV: Failed RegQuery for StoreSvrName - "
  227. "err=%u, DataSize=%u, type=%u\n",
  228. RegRetVal, DataSize, Type));
  229. }
  230. }
  231. // Get SD cluster name
  232. if (g_MachinePolicy.fPolicySessionDirectoryClusterName) {
  233. wcsncpy(ClusterName, g_MachinePolicy.SessionDirectoryClusterName,
  234. CLUSTERNAMELENGTH);
  235. ClusterName[CLUSTERNAMELENGTH - 1] = '\0';
  236. }
  237. else { //Read from registry
  238. // Not an error for the name to be absent or empty.
  239. DataSize = sizeof(ClusterName);
  240. RegRetVal = RegQueryValueExW(hKey, REG_TS_CLUSTER_CLUSTERNAME,
  241. NULL, &Type, (BYTE *)ClusterName, &DataSize);
  242. if (RegRetVal != ERROR_SUCCESS) {
  243. DBGPRINT(("TERMSRV: Failed RegQuery for ClusterName - "
  244. "err=%u, DataSize=%u, type=%u\n",
  245. RegRetVal, DataSize, Type));
  246. }
  247. }
  248. if (g_MachinePolicy.fPolicySessionDirectoryAdditionalParams) {
  249. wcsncpy(OpaqueSettings,
  250. g_MachinePolicy.SessionDirectoryAdditionalParams,
  251. OPAQUESETTINGSLENGTH);
  252. OpaqueSettings[OPAQUESETTINGSLENGTH - 1] = '\0';
  253. }
  254. else { //Read from registry
  255. // Not an error for the string to be absent or empty.
  256. DataSize = sizeof(OpaqueSettings);
  257. RegRetVal = RegQueryValueExW(hKey, REG_TS_CLUSTER_OPAQUESETTINGS,
  258. NULL, &Type, (BYTE *)OpaqueSettings, &DataSize);
  259. if (RegRetVal != ERROR_SUCCESS) {
  260. DBGPRINT(("TERMSRV: Failed RegQuery for OpaqueSettings - "
  261. "err=%u, DataSize=%u, type=%u\n",
  262. RegRetVal, DataSize, Type));
  263. }
  264. }
  265. // Query for the IP address used for SD redirection
  266. DataSize = sizeof(SDRedirectionIP);
  267. RegRetVal = RegQueryValueExW(hKey, REG_TS_CLUSTER_REDIRECTIONIP,
  268. NULL, &Type, (BYTE *)SDRedirectionIP, &DataSize);
  269. if (RegRetVal != ERROR_SUCCESS) {
  270. SDRedirectionIP[0] = L'\0';
  271. DBGPRINT(("TERMSRV: Failed RegQuery for RedirectionIP for SD - "
  272. "err=%u, DataSize=%u, type=%u\n",
  273. RegRetVal, DataSize, Type));
  274. }
  275. //
  276. // Now for the less crucial settings.
  277. //
  278. // Get the setting that determines whether the server's local address is
  279. // visible to the client. Group Policy takes precedence over registry.
  280. //
  281. if (g_MachinePolicy.fPolicySessionDirectoryExposeServerIP) {
  282. g_SessDirUseServerAddr = g_MachinePolicy.SessionDirectoryExposeServerIP;
  283. }
  284. else {
  285. Len = sizeof(g_SessDirUseServerAddr);
  286. RegRetVal = RegQueryValueEx(hKeyTermSrv, REG_TS_SESSDIR_EXPOSE_SERVER_ADDR,
  287. NULL, &Type, (BYTE *)&g_SessDirUseServerAddr, &Len);
  288. if (RegRetVal == ERROR_SUCCESS) {
  289. //DBGPRINT(("TERMSRV: RegOpenKeyEx for allow server addr to client %d"
  290. // "\n", g_SessDirUseServerAddr));
  291. }
  292. else {
  293. DBGPRINT(("TERMSRV: RegQueryValueEx for allow server addr to client"
  294. " %d, err %u\n", g_SessDirUseServerAddr, RegRetVal));
  295. }
  296. }
  297. // Get the single session per user setting from GP if it's active, otherwise
  298. // from the registry.
  299. if (g_MachinePolicy.fPolicySingleSessionPerUser) {
  300. bThisServerIsInSingleSessionMode =
  301. g_MachinePolicy.fSingleSessionPerUser;
  302. }
  303. else {
  304. Len = sizeof(bThisServerIsInSingleSessionMode);
  305. RegRetVal = RegQueryValueEx(hKeyTermSrv,
  306. POLICY_TS_SINGLE_SESSION_PER_USER, NULL, &Type,
  307. (BYTE *)&bThisServerIsInSingleSessionMode, &Len);
  308. if (RegRetVal != ERROR_SUCCESS) {
  309. DBGPRINT(("TERMSRV: RegQueryValueEx for single session mode"
  310. ", Error %u\n", RegRetVal));
  311. }
  312. }
  313. //
  314. // Get the default wait timeout for repopulate thread to complete
  315. //
  316. Len = sizeof(RepopulateWaitTimeout);
  317. RegRetVal = RegQueryValueEx( hKeyTermSrv,
  318. L"RepopulateWaitTimeout",
  319. NULL,
  320. &Type,
  321. (LPBYTE)&RepopulateWaitTimeout,
  322. &Len);
  323. if( RegRetVal == ERROR_SUCCESS && REG_DWORD == Type ) {
  324. if( RepopulateWaitTimeout < 0 ) {
  325. g_WaitForRepopulate = INFINITE;
  326. }
  327. else {
  328. g_WaitForRepopulate = RepopulateWaitTimeout * 1000;
  329. }
  330. }
  331. DBGPRINT(("TERMSRV: WaitForRepopulateTimeout set to %d\n", g_WaitForRepopulate));
  332. // Get the CLSID of the session directory object to instantiate.
  333. CLSIDStr[0] = L'\0';
  334. Len = sizeof(CLSIDStr);
  335. RegQueryValueEx(hKeyTermSrv, REG_TS_SESSDIRCLSID, NULL, &Type,
  336. (BYTE *)CLSIDStr, &Len);
  337. // Get the CLSID of the session directory object to instantiate.
  338. CLSIDEXStr[0] = L'\0';
  339. Len = sizeof(CLSIDEXStr);
  340. RegQueryValueEx(hKeyTermSrv, REG_TS_SESSDIR_EX_CLSID, NULL, &Type,
  341. (BYTE *)CLSIDEXStr, &Len);
  342. RegCloseKey(hKey);
  343. RegCloseKey(hKeyTermSrv);
  344. //
  345. // Configuration loading complete.
  346. //
  347. // See what to do about activation/deactivation.
  348. //
  349. pTSSD = GetTSSD();
  350. if (pTSSD == NULL) {
  351. // This is the normal initialization path. If Update is true here, it
  352. // should be treated as a normal initialize because the COM object was
  353. // unloaded.
  354. Update = false;
  355. }
  356. else {
  357. // Clustering is already active. See whether we should deactivate it.
  358. if (bClusteringActive == FALSE) {
  359. ReleaseTSSD(); // Once here, once again at the end of the function.
  360. pTSSDEx = GetTSSDEx();
  361. if (pTSSDEx) {
  362. ReleaseTSSDEx();
  363. ReleaseTSSDEx();
  364. pTSSDEx = NULL;
  365. }
  366. }
  367. }
  368. if (bClusteringActive) {
  369. // We need to get the local machine's address to pass in to
  370. // the directory.
  371. // If SDRedirectionIP is not empty, i.e. RedirectionIP is selected in tscc or though WMI, use it,
  372. // otherwise, use the IP we get from TermSrv or from winsock API
  373. if (SDRedirectionIP[0] == L'\0') {
  374. if (g_LocalIPAddress != 0) {
  375. tempaddr = (unsigned char *)&g_LocalIPAddress;
  376. wsprintf(g_LocalServerAddress, L"%d.%d.%d.%d", tempaddr[0], tempaddr[1],
  377. tempaddr[2], tempaddr[3]);
  378. }
  379. else {
  380. //RPD-Enabled NIC is not specified in TSCC, need to get through winsock API
  381. ErrVal = SessDirGetLocalIPAddr(g_LocalServerAddress);
  382. }
  383. }
  384. else {
  385. wcsncpy(g_LocalServerAddress, SDRedirectionIP, IPADDRESSLENGTH);
  386. }
  387. if (ErrVal == 0) {
  388. if (wcslen(CLSIDStr) > 0 &&
  389. SUCCEEDED(CLSIDFromString(CLSIDStr, &TSSDCLSID))) {
  390. // If it's not an update, create the TSSD object.
  391. if (Update == false) {
  392. hr = CoCreateInstance(TSSDCLSID, NULL,
  393. CLSCTX_INPROC_SERVER, IID_ITSSessionDirectory,
  394. (void **)&pTSSD);
  395. if (SUCCEEDED(hr)) {
  396. if (SetTSSD(pTSSD) != 0) {
  397. DBGPRINT(("TERMSRV: InitSessDirEx: Could not set "
  398. "TSSD", E_FAIL));
  399. pTSSD->Release();
  400. pTSSD = NULL;
  401. hr = E_FAIL;
  402. }
  403. else {
  404. // Add 1 to the ref count because we're gonna use
  405. // it.
  406. pTSSD = GetTSSD();
  407. }
  408. }
  409. }
  410. else {
  411. hr = S_OK;
  412. }
  413. if (SUCCEEDED (hr)) {
  414. // Right now the only flag we pass in to session directory
  415. // says whether we are in single-session mode.
  416. DWORD Flags = 0;
  417. Flags |= (bThisServerIsInSingleSessionMode ?
  418. SINGLE_SESSION_FLAG : 0x0);
  419. if (UpdatePara & TSSD_NOREPOPULATE) {
  420. Flags |= NO_REPOPULATE_SESSION;
  421. }
  422. // Remove preceding and succeeding space in ClusterName
  423. RemoveSpaceInStr(ClusterName);
  424. if (Update == false)
  425. hr = pTSSD->Initialize(g_LocalServerAddress,
  426. StoreServerName, ClusterName, OpaqueSettings,
  427. Flags, RepopulateSessionDirectory, UpdateSessionDirectory);
  428. else
  429. hr = pTSSD->Update(g_LocalServerAddress,
  430. StoreServerName, ClusterName, OpaqueSettings,
  431. Flags, ForceRejoin);
  432. if (FAILED(hr)) {
  433. DBGPRINT(("TERMSRV: InitSessDirEx: Failed %s TSSD, "
  434. "hr=0x%X\n", Update ? "update" : "init", hr));
  435. ReleaseTSSD();
  436. PostErrorValueEvent(
  437. EVENT_TS_SESSDIR_FAIL_INIT_TSSD, hr);
  438. }
  439. }
  440. else {
  441. DBGPRINT(("TERMSRV: InitSessDirEx: Failed create TSSD, "
  442. "hr=0x%X\n", hr));
  443. PostErrorValueEvent(
  444. EVENT_TS_SESSDIR_FAIL_CREATE_TSSD, hr);
  445. }
  446. }
  447. else {
  448. DBGPRINT(("TERMSRV: InitSessDirEx: Failed get or parse "
  449. "CLSID\n"));
  450. PostErrorValueEvent(
  451. EVENT_TS_SESSDIR_FAIL_GET_TSSD_CLSID, 0);
  452. hr = E_INVALIDARG;
  453. }
  454. }
  455. else {
  456. DBGPRINT(("TERMSRV: InitSessDirEx: Failed to get local DNS name, "
  457. "lasterr=0x%X\n", ErrVal));
  458. PostErrorValueEvent(EVENT_TS_SESSDIR_NO_COMPUTER_DNS_NAME,
  459. ErrVal);
  460. hr = E_FAIL;
  461. }
  462. // Initialize the other COM object, but only if the above succeeded.
  463. if (SUCCEEDED(hr)) {
  464. if (wcslen(CLSIDEXStr) > 0 &&
  465. SUCCEEDED(CLSIDFromString(CLSIDEXStr, &TSSDEXCLSID))) {
  466. // If it's not an update, create the TSSDEX object.
  467. if (Update == false) {
  468. hr = CoCreateInstance(TSSDEXCLSID, NULL,
  469. CLSCTX_INPROC_SERVER, IID_ITSSessionDirectoryEx,
  470. (void **)&pTSSDEx);
  471. if (SUCCEEDED(hr)) {
  472. if (SetTSSDEx(pTSSDEx) != 0) {
  473. DBGPRINT(("TERMSRV: InitSessDirEx: Could not set "
  474. "TSSDEx\n", E_FAIL));
  475. pTSSDEx->Release();
  476. pTSSDEx = NULL;
  477. hr = E_FAIL;
  478. }
  479. }
  480. }
  481. else
  482. hr = S_OK;
  483. if (FAILED(hr)) {
  484. DBGPRINT(("TERMSRV: InitSessDirEx: Failed create TSSDEx, "
  485. "hr=0x%X\n", hr));
  486. PostErrorValueEvent(
  487. EVENT_TS_SESSDIR_FAIL_CREATE_TSSDEX, hr);
  488. }
  489. }
  490. else {
  491. DBGPRINT(("TERMSRV: InitSessDirEx: Failed get or parse "
  492. "CLSIDSDEx\n"));
  493. PostErrorValueEvent(
  494. EVENT_TS_SESSDIR_FAIL_GET_TSSDEX_CLSID, 0);
  495. }
  496. }
  497. }
  498. else {
  499. DBGPRINT(("TERMSRV: InitSessDirEx: SessDir not activated\n"));
  500. }
  501. if (pTSSD != NULL)
  502. ReleaseTSSD();
  503. // Initialization complete--someone else is allowed to enter now.
  504. LeaveCriticalSection(&g_CritSecInitialize);
  505. return S_OK;
  506. RegFailExit:
  507. // Initialization complete--someone else is allowed to enter now.
  508. LeaveCriticalSection(&g_CritSecInitialize);
  509. if (hKeyTermSrvSucceeded)
  510. RegCloseKey(hKeyTermSrv);
  511. return (DWORD) E_FAIL;
  512. }
  513. /****************************************************************************/
  514. // InitSessionDirectory
  515. //
  516. // Initializes the directory by loading and initializing the session directory
  517. // object, if load balancing is enabled. We assume COM has been initialized
  518. // on the service main thread as COINIT_MULTITHREADED.
  519. //
  520. // This function should only be called once ever. It is the location of the
  521. // initialization of the critical sections used by this module.
  522. /****************************************************************************/
  523. void InitSessionDirectory()
  524. {
  525. BOOL br1 = FALSE;
  526. BOOL br2 = FALSE;
  527. ASSERT(g_bCritSecsInitialized == FALSE);
  528. // Initialize critical sections.
  529. __try {
  530. // Initialize the provider critical section to preallocate the event
  531. // and spin 4096 times on each try (since we don't spend very
  532. // long in our critical section).
  533. br1 = InitializeCriticalSectionAndSpinCount(&g_CritSecComObj,
  534. 0x80001000);
  535. br2 = InitializeCriticalSectionAndSpinCount(&g_CritSecInitialize,
  536. 0x80001000);
  537. // Since this happens at startup time, we should not fail.
  538. ASSERT(br1 && br2);
  539. if (br1 && br2) {
  540. g_bCritSecsInitialized = TRUE;
  541. }
  542. else {
  543. DBGPRINT(("TERMSRV: InitSessDir: critsec init failed\n"));
  544. if (br1)
  545. DeleteCriticalSection(&g_CritSecComObj);
  546. if (br2)
  547. DeleteCriticalSection(&g_CritSecInitialize);
  548. PostErrorValueEvent(EVENT_TS_SESSDIR_FAIL_INIT_TSSD,
  549. GetLastError());
  550. }
  551. }
  552. __except (EXCEPTION_EXECUTE_HANDLER) {
  553. // Since this happens at startup time, we should not fail.
  554. ASSERT(FALSE);
  555. DBGPRINT(("TERMSRV: InitSessDir: critsec init failed\n"));
  556. if (br1)
  557. DeleteCriticalSection(&g_CritSecComObj);
  558. if (br2)
  559. DeleteCriticalSection(&g_CritSecInitialize);
  560. PostErrorValueEvent(EVENT_TS_SESSDIR_FAIL_INIT_TSSD,
  561. GetExceptionCode());
  562. }
  563. // Now do the common initialization.
  564. if (g_bCritSecsInitialized)
  565. InitSessionDirectoryEx(0);
  566. }
  567. /****************************************************************************/
  568. // UpdateSessionDirectory
  569. //
  570. // Updates the session directory with new settings. Assumes COM has been
  571. // initialized.
  572. /****************************************************************************/
  573. DWORD UpdateSessionDirectory(DWORD UpdatePara)
  574. {
  575. UpdatePara |= TSSD_UPDATE;
  576. return InitSessionDirectoryEx(UpdatePara);
  577. }
  578. #define REPOP_FAIL 1
  579. #define REPOP_SUCCESS 0
  580. /****************************************************************************/
  581. // RepopulateSessionDirectory
  582. //
  583. // Repopulates the session directory. Returns REPOP_FAIL (1) on failure,
  584. // REPOP_SUCCESS(0) otherwise.
  585. /****************************************************************************/
  586. DWORD RepopulateSessionDirectory()
  587. {
  588. DWORD WinStationCount = 0;
  589. PLIST_ENTRY Head, Next;
  590. DWORD i = 0;
  591. HRESULT hr = S_OK;
  592. PWINSTATION pWinStation = NULL;
  593. ITSSessionDirectory *pTSSD;
  594. WCHAR *wBuffer = NULL;
  595. #if DBG
  596. DWORD dwStartTime;
  597. DWORD dwEndTime;
  598. #endif
  599. // If we got here, it should be because of the session directory.
  600. pTSSD = GetTSSD();
  601. if (pTSSD != NULL) {
  602. // Grab WinStationListLock
  603. ENTERCRIT( &WinStationListLock );
  604. Head = &WinStationListHead;
  605. // Count the WinStations I care about.
  606. for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
  607. pWinStation = CONTAINING_RECORD( Next, WINSTATION, Links );
  608. //
  609. // In winstation reset, we only mark flag not the state but
  610. // we can't be sure progress of this logoff so we depends on
  611. // SessDirWaitForRepopulate() and let logoff thread
  612. // to notify session directory itself.
  613. //
  614. //
  615. //if ( (pWinStation->Flags & (WSF_RESET | WSF_DELETE)) ) {
  616. // continue;
  617. //}
  618. //
  619. // WinStation was disconnected and no user was logged on.
  620. //
  621. if( RtlLargeIntegerEqualToZero( pWinStation->LogonTime ) ) {
  622. // NotifyLogonWorker set winstation state and logontime after
  623. // getting user SID and user/domain name, it is possible on
  624. // next loop, this logon thread might finish setting logontime and
  625. // causing we pick it up from next loop and that will
  626. // cause buffer overwrite, so we increment counter here
  627. WinStationCount += 1;
  628. continue;
  629. }
  630. //
  631. // We need to handle console session differently.
  632. //
  633. // Action Physical Console State | Remote Console State UserName
  634. // ------- ---------------------- | -------------------- ---------
  635. // After boot Conn |
  636. // Logon Active | Active Logon User
  637. // Logoff Conn | Disc <Blank>
  638. // Disconnect | Disc Logon User
  639. //
  640. // We should not report to Session Directory when state is
  641. // Disconnect and user name is blank.
  642. //
  643. switch (pWinStation->State) {
  644. case State_Disconnected:
  645. if( (pWinStation->LogonId == 0) && (pWinStation->UserName[0] == 0) ) {
  646. break;
  647. }
  648. #if DBG
  649. if( (pWinStation->LogonId == 0) ) {
  650. DBGPRINT( ("TERMSRV: RepopulateSessDir: Include console session to Session Directory\n") );
  651. }
  652. #endif
  653. case State_Active:
  654. case State_Shadow:
  655. WinStationCount += 1;
  656. break;
  657. }
  658. }
  659. // Allocate the memory for the structure to pass to the session
  660. // directory.
  661. TSSD_RepopulateSessionInfo *rsi = new TSSD_RepopulateSessionInfo[
  662. WinStationCount];
  663. if (rsi == NULL) {
  664. DBGPRINT(("TERMSRV: RepopulateSessDir: mem alloc failed\n"));
  665. // Release WinStationListLock
  666. LEAVECRIT( &WinStationListLock );
  667. goto CleanUp;
  668. }
  669. // Allocate string arrays (for now)
  670. wBuffer = new WCHAR[WinStationCount * TOTAL_STRINGS_LENGTH];
  671. if (wBuffer == NULL) {
  672. DBGPRINT(("TERMSRV: RepopulateSessDir: mem alloc failed\n"));
  673. // Release WinStationListLock
  674. LEAVECRIT( &WinStationListLock );
  675. delete [] rsi;
  676. goto CleanUp;
  677. }
  678. // Set the pointers in the rsi
  679. for ( i = 0; i < WinStationCount; i += 1) {
  680. rsi[i].UserName = &(wBuffer[i * TOTAL_STRINGS_LENGTH +
  681. USERNAME_OFFSET]);
  682. rsi[i].Domain = &(wBuffer[i * TOTAL_STRINGS_LENGTH +
  683. DOMAIN_OFFSET]);
  684. rsi[i].ApplicationType = &(wBuffer[i * TOTAL_STRINGS_LENGTH +
  685. APPLICATIONTYPE_OFFSET]);
  686. }
  687. // Now populate the structure to pass in.
  688. // Reset index to 0
  689. i = 0;
  690. for ( Next = Head->Flink; Next != Head && i < WinStationCount; Next = Next->Flink ) {
  691. pWinStation = CONTAINING_RECORD( Next, WINSTATION, Links );
  692. //
  693. // In winstation reset, we only mark flag not the state but
  694. // we can't be sure progress of this logoff so we depends on
  695. // SessDirWaitForRepopulate() and let logoff thread
  696. // to notify session directory itself.
  697. //
  698. //if ( (pWinStation->Flags & (WSF_RESET | WSF_DELETE)) ) {
  699. // continue;
  700. //}
  701. // refer to comment above regarding console session
  702. if( (pWinStation->LogonId == 0) &&
  703. (pWinStation->State == State_Disconnected) &&
  704. (pWinStation->UserName[0] == 0) ) {
  705. continue;
  706. }
  707. //
  708. // WinStation was disconnected or in the middle of connect.
  709. // we will let notify logon do its job to report to SD
  710. //
  711. if( RtlLargeIntegerEqualToZero( pWinStation->LogonTime ) ) {
  712. continue;
  713. }
  714. // There are two sets of information here: First, if the session
  715. // is Active, we can do stuff, then we have an intentional
  716. // fallthrough to the common code used by Disconnected and Active
  717. // sessions for the common stuff. For now, if it's disconnected
  718. // we then call the update function in our COM object.
  719. switch (pWinStation->State) {
  720. case State_Active:
  721. case State_Shadow:
  722. rsi[i].State = 0;
  723. // NOTE INTENTIONAL FALLTHROUGH
  724. case State_Disconnected:
  725. rsi[i].TSProtocol = pWinStation->Client.ProtocolType;
  726. rsi[i].ResolutionWidth = pWinStation->Client.HRes;
  727. rsi[i].ResolutionHeight = pWinStation->Client.VRes;
  728. rsi[i].ColorDepth = pWinStation->Client.ColorDepth;
  729. // TODO: I don't get it--USERNAME_LENGTH is 20, yet in the csi,
  730. // TODO: it's 256. Likewise, DOMAIN_LENGTH is 17.
  731. wcsncpy(rsi[i].UserName, pWinStation->UserName,
  732. USERNAME_LENGTH);
  733. rsi[i].UserName[USERNAME_LENGTH] = '\0';
  734. wcsncpy(rsi[i].Domain, pWinStation->Domain, DOMAIN_LENGTH);
  735. rsi[i].Domain[DOMAIN_LENGTH] = '\0';
  736. // TODO: Here there is a problem in that the INITIALPROGRAM
  737. // TODO: length is 256 + 1, but the buffer we copy into is
  738. // TODO: 256, hence we lose a character.
  739. wcsncpy(rsi[i].ApplicationType, pWinStation->
  740. Client.InitialProgram, INITIALPROGRAM_LENGTH - 1);
  741. rsi[i].ApplicationType[INITIALPROGRAM_LENGTH - 2] = '\0';
  742. rsi[i].SessionID = pWinStation->LogonId;
  743. rsi[i].CreateTimeLow = pWinStation->LogonTime.LowPart;
  744. rsi[i].CreateTimeHigh = pWinStation->LogonTime.HighPart;
  745. if (pWinStation->State == State_Disconnected) {
  746. rsi[i].DisconnectionTimeLow = pWinStation->DisconnectTime.
  747. LowPart;
  748. rsi[i].DisconnectionTimeHigh = pWinStation->DisconnectTime.
  749. HighPart;
  750. rsi[i].State = 1;
  751. }
  752. if( (pWinStation->LogonId == 0) &&
  753. (pWinStation->State == State_Disconnected) &&
  754. (pWinStation->UserName[0] == 0) ) {
  755. break;
  756. }
  757. // make sure after we copy data over, winstation still valid.
  758. ASSERT( rsi[i].UserName[0] != 0 );
  759. ASSERT( rsi[i].Domain[0] != 0 );
  760. i += 1;
  761. break;
  762. }
  763. }
  764. // Release WinStationListLock
  765. LEAVECRIT( &WinStationListLock );
  766. #if DBG
  767. dwStartTime = GetTickCount();
  768. DBGPRINT( ("RepopulateSessionDirectory: Start repopulating session\n") );
  769. #endif
  770. if( i > 0 ) {
  771. // Call the session directory provider with our big struct.
  772. hr = pTSSD->Repopulate(i, rsi);
  773. }
  774. #if DBG
  775. dwEndTime = GetTickCount();
  776. DBGPRINT( ("RepopulateSessionDirectory: End repopulating %d sessions takes %d ms\n", i, dwEndTime - dwStartTime) );
  777. #endif
  778. delete [] rsi;
  779. delete [] wBuffer;
  780. if (hr == S_OK) {
  781. ReleaseTSSD();
  782. return REPOP_SUCCESS;
  783. }
  784. else {
  785. goto CleanUp;
  786. }
  787. CleanUp:
  788. ReleaseTSSD();
  789. }
  790. return REPOP_FAIL;
  791. }
  792. /****************************************************************************/
  793. // DestroySessionDirectory
  794. //
  795. // Destroys the directory, releasing any COM objects held and other memory
  796. // used. Assumes COM has been initialized.
  797. /****************************************************************************/
  798. void DestroySessionDirectory()
  799. {
  800. ITSSessionDirectory *pTSSD = NULL;
  801. ITSSessionDirectoryEx *pTSSDEx = NULL;
  802. pTSSD = GetTSSD();
  803. pTSSDEx = GetTSSDEx();
  804. if (pTSSD != NULL) {
  805. ReleaseTSSD();
  806. ReleaseTSSD();
  807. }
  808. if (pTSSDEx != NULL) {
  809. ReleaseTSSDEx();
  810. ReleaseTSSDEx();
  811. }
  812. }
  813. /****************************************************************************/
  814. // SessDirNotifyLogon
  815. //
  816. // Called to inform the session directory of session creation.
  817. /****************************************************************************/
  818. void SessDirNotifyLogon(TSSD_CreateSessionInfo *pCreateInfo)
  819. {
  820. HRESULT hr;
  821. ITSSessionDirectory *pTSSD;
  822. pTSSD = GetTSSD();
  823. // We can get called even when the directory is inactive.
  824. if (pTSSD != NULL) {
  825. hr = pTSSD->NotifyCreateLocalSession(pCreateInfo);
  826. if (FAILED(hr)) {
  827. DBGPRINT(("TERMSRV: SessDirNotifyLogon: Call failed, "
  828. "hr=0x%X\n", hr));
  829. PostErrorValueEvent(EVENT_TS_SESSDIR_FAIL_UPDATE, hr);
  830. }
  831. ReleaseTSSD();
  832. }
  833. }
  834. /****************************************************************************/
  835. // SessDirNotifyDisconnection
  836. //
  837. // Called on session disconnection to inform the session directory.
  838. /****************************************************************************/
  839. void SessDirNotifyDisconnection(DWORD SessionID, FILETIME DiscTime)
  840. {
  841. HRESULT hr;
  842. ITSSessionDirectory *pTSSD;
  843. pTSSD = GetTSSD();
  844. // We can get called even when the directory is inactive.
  845. if (pTSSD != NULL) {
  846. hr = pTSSD->NotifyDisconnectLocalSession(SessionID, DiscTime);
  847. if (FAILED(hr)) {
  848. DBGPRINT(("TERMSRV: SessDirNotifyDisc: Call failed, "
  849. "hr=0x%X\n", hr));
  850. PostErrorValueEvent(EVENT_TS_SESSDIR_FAIL_UPDATE, hr);
  851. }
  852. ReleaseTSSD();
  853. }
  854. }
  855. /****************************************************************************/
  856. // SessDirNotifyReconnection
  857. //
  858. // Called on session reconnection to inform the session directory.
  859. /****************************************************************************/
  860. void SessDirNotifyReconnection(PWINSTATION pTargetWinStation, TSSD_ReconnectSessionInfo *pReconnInfo)
  861. {
  862. HRESULT hr;
  863. ITSSessionDirectory *pTSSD;
  864. pTSSD = GetTSSD();
  865. // We can get called even when the directory is inactive.
  866. if (pTSSD != NULL) {
  867. PTS_LOAD_BALANCE_INFO pLBInfo = NULL;
  868. ULONG ReturnLength;
  869. NTSTATUS Status;
  870. BYTE *pRedirInfo = NULL;
  871. BYTE *pRedirInfoStart = NULL;
  872. BYTE *LBInfo = NULL;
  873. DWORD LBInfoSize = 0;
  874. DWORD RedirInfoSize = 0;
  875. DWORD ServerAddrLen = 0;
  876. HKEY hKey = NULL;
  877. DWORD Type, DataSize;
  878. WCHAR SDRedirectionIP[IPADDRESSLENGTH];
  879. WCHAR *pszServerIPAddress = NULL;
  880. LONG RegRetVal;
  881. // We need to send redirection packet with LB_NOREDIRECT set to the client
  882. // to let it know the server address it actually connects to (for later
  883. // auto-reconnect use)
  884. // Get the client load balance capability info. We continue onward
  885. // to do a session directory query only when the client supports
  886. // redirection and has not already been redirected to this server.
  887. pLBInfo = (PTS_LOAD_BALANCE_INFO)MemAlloc(sizeof(TS_LOAD_BALANCE_INFO));
  888. if (NULL == pLBInfo) {
  889. goto Cleanup;
  890. }
  891. memset(pLBInfo, 0, sizeof(TS_LOAD_BALANCE_INFO));
  892. Status = IcaStackIoControl(pTargetWinStation->hStack,
  893. IOCTL_TS_STACK_QUERY_LOAD_BALANCE_INFO,
  894. NULL, 0,
  895. pLBInfo, sizeof(TS_LOAD_BALANCE_INFO),
  896. &ReturnLength);
  897. // Send the redirection packet to client if this is not a redirected connection
  898. // and client support redirect-version4 and above
  899. if (NT_SUCCESS(Status)
  900. && !pLBInfo->bRequestedSessionIDFieldValid &&
  901. (pLBInfo->ClientRedirectionVersion >= TS_CLUSTER_REDIRECTION_VERSION4)) {
  902. // Construct and send redirection packet
  903. // Load registry keys.
  904. RegRetVal = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_TS_CLUSTERSETTINGS, 0,
  905. KEY_READ, &hKey);
  906. if (RegRetVal != ERROR_SUCCESS) {
  907. goto Cleanup;
  908. }
  909. // Query for the IP address used for SD redirection
  910. DataSize = sizeof(SDRedirectionIP);
  911. RegRetVal = RegQueryValueExW(hKey, REG_TS_CLUSTER_REDIRECTIONIP,
  912. NULL, &Type, (BYTE *)SDRedirectionIP, &DataSize);
  913. RegCloseKey(hKey);
  914. if (RegRetVal != ERROR_SUCCESS) {
  915. SDRedirectionIP[0] = L'\0';
  916. DBGPRINT(("TERMSRV: Failed RegQuery for RedirectionIP for SD - "
  917. "err=%u, DataSize=%u, type=%u\n",
  918. RegRetVal, DataSize, Type));
  919. goto Cleanup;
  920. }
  921. pszServerIPAddress = SDRedirectionIP;
  922. RedirInfoSize = sizeof(TS_CLIENT_REDIRECTION_INFO);
  923. // Setup the server addr
  924. if (g_SessDirUseServerAddr ||
  925. pLBInfo->bClientRequireServerAddr) {
  926. ServerAddrLen = (DWORD)((wcslen(pszServerIPAddress) + 1) *
  927. sizeof(WCHAR));
  928. RedirInfoSize += (ServerAddrLen + sizeof(ULONG));
  929. DBGPRINT(("TERMSRV: SessDirCheckRedir: size=%d, "
  930. "addr=%S\n", ServerAddrLen,
  931. (WCHAR *)pszServerIPAddress));
  932. }
  933. else {
  934. DBGPRINT(("TERMSRV: SessDirCheckRedir no server "
  935. "address: g_SessDirUseServerAddr = %d, "
  936. "bClientRequireServerAddr = %d\n",
  937. g_SessDirUseServerAddr,
  938. pLBInfo->bClientRequireServerAddr));
  939. }
  940. // Setup the load balance info
  941. if ((pLBInfo->bClientRequireServerAddr == 0) &&
  942. SessDirGetLBInfo(
  943. pszServerIPAddress, &LBInfoSize, &LBInfo)) {
  944. if (LBInfo) {
  945. DBGPRINT(("TERMSRV: SessDirCheckRedir: size=%d, "
  946. "info=%S\n", LBInfoSize,
  947. (WCHAR *)LBInfo));
  948. RedirInfoSize += (LBInfoSize + sizeof(ULONG));
  949. }
  950. }
  951. else {
  952. DBGPRINT(("TERMSRV: SessDirCheckRedir failed: "
  953. "size=%d, info=%S\n", LBInfoSize,
  954. (WCHAR *)LBInfo));
  955. }
  956. // Setup the load balance IOCTL
  957. pRedirInfoStart = pRedirInfo = new BYTE[RedirInfoSize];
  958. TS_CLIENT_REDIRECTION_INFO *pClientRedirInfo =
  959. (TS_CLIENT_REDIRECTION_INFO *)pRedirInfo;
  960. if (pRedirInfo != NULL) {
  961. pClientRedirInfo->Flags = 0;
  962. pRedirInfo += sizeof(TS_CLIENT_REDIRECTION_INFO);
  963. if (ServerAddrLen) {
  964. *((ULONG UNALIGNED*)(pRedirInfo)) =
  965. ServerAddrLen;
  966. memcpy((BYTE*)(pRedirInfo + sizeof(ULONG)),
  967. (BYTE*)pszServerIPAddress,
  968. ServerAddrLen);
  969. pRedirInfo += ServerAddrLen + sizeof(ULONG);
  970. pClientRedirInfo->Flags |= TARGET_NET_ADDRESS;
  971. }
  972. if (LBInfoSize) {
  973. *((ULONG UNALIGNED*)(pRedirInfo)) = LBInfoSize;
  974. memcpy((BYTE*)(pRedirInfo + sizeof(ULONG)),
  975. LBInfo, LBInfoSize);
  976. pRedirInfo += LBInfoSize + sizeof(ULONG);
  977. pClientRedirInfo->Flags |= LOAD_BALANCE_INFO;
  978. }
  979. pClientRedirInfo->Flags |= LB_NOREDIRECT;
  980. }
  981. else {
  982. Status = STATUS_NO_MEMORY;
  983. goto Cleanup;
  984. }
  985. Status = IcaStackIoControl(pTargetWinStation->hStack,
  986. IOCTL_TS_STACK_SEND_CLIENT_REDIRECTION,
  987. pClientRedirInfo, RedirInfoSize,
  988. NULL, 0,
  989. &ReturnLength);
  990. if (NT_SUCCESS(Status)) {
  991. // do nothing here
  992. }
  993. else {
  994. // The stack returned failure. Continue
  995. // the current connection.
  996. TRACE((hTrace,TC_ICASRV,TT_API1,
  997. "TERMSRV: Failed STACK_CLIENT_REDIR, "
  998. "SessionID=%u, Status=0x%X\n",
  999. pTargetWinStation->LogonId, Status));
  1000. }
  1001. Cleanup:
  1002. // Cleanup the buffers
  1003. if (LBInfo != NULL) {
  1004. SysFreeString((BSTR)LBInfo);
  1005. LBInfo = NULL;
  1006. }
  1007. if (pRedirInfo != NULL) {
  1008. delete [] pRedirInfoStart;
  1009. pRedirInfoStart = NULL;
  1010. }
  1011. }
  1012. if (pLBInfo != NULL) {
  1013. MemFree(pLBInfo);
  1014. pLBInfo = NULL;
  1015. }
  1016. hr = pTSSD->NotifyReconnectLocalSession(pReconnInfo);
  1017. if (FAILED(hr)) {
  1018. DBGPRINT(("TERMSRV: SessDirNotifyReconn: Call failed, "
  1019. "hr=0x%X\n", hr));
  1020. PostErrorValueEvent(EVENT_TS_SESSDIR_FAIL_UPDATE, hr);
  1021. }
  1022. ReleaseTSSD();
  1023. }
  1024. }
  1025. /****************************************************************************/
  1026. // SessDirNotifyLogoff
  1027. //
  1028. // Called on logoff to inform the session directory.
  1029. /****************************************************************************/
  1030. void SessDirNotifyLogoff(DWORD SessionID)
  1031. {
  1032. HRESULT hr;
  1033. ITSSessionDirectory *pTSSD;
  1034. pTSSD = GetTSSD();
  1035. // We can get called even when the directory is inactive.
  1036. if (pTSSD != NULL) {
  1037. hr = pTSSD->NotifyDestroyLocalSession(SessionID);
  1038. if (FAILED(hr)) {
  1039. DBGPRINT(("TERMSRV: SessDirNotifyLogoff: Call failed, "
  1040. "hr=0x%X\n", hr));
  1041. PostErrorValueEvent(EVENT_TS_SESSDIR_FAIL_UPDATE, hr);
  1042. }
  1043. ReleaseTSSD();
  1044. }
  1045. }
  1046. /****************************************************************************/
  1047. // SessDirNotifyReconnectPending
  1048. //
  1049. // Called to inform the session directory a session should start soon on
  1050. // another machine in the cluster (for Directory Integrity Service).
  1051. /****************************************************************************/
  1052. void SessDirNotifyReconnectPending(WCHAR *ServerName)
  1053. {
  1054. HRESULT hr;
  1055. ITSSessionDirectory *pTSSD;
  1056. pTSSD = GetTSSD();
  1057. // We can get called even when the directory is inactive.
  1058. if (pTSSD != NULL) {
  1059. hr = pTSSD->NotifyReconnectPending(ServerName);
  1060. if (FAILED(hr)) {
  1061. DBGPRINT(("TERMSRV: SessDirNotifyReconnectPending: Call failed, "
  1062. "hr=0x%X\n", hr));
  1063. PostErrorValueEvent(EVENT_TS_SESSDIR_FAIL_UPDATE, hr);
  1064. }
  1065. ReleaseTSSD();
  1066. }
  1067. }
  1068. /****************************************************************************/
  1069. // SessDirWaitForRepopulate
  1070. //
  1071. // Wait for session directory to complete repopulate
  1072. /****************************************************************************/
  1073. void SessDirWaitForRepopulate()
  1074. {
  1075. HRESULT hr;
  1076. ITSSessionDirectory *pTSSD;
  1077. #if DBG
  1078. DWORD dwStartTime;
  1079. #endif
  1080. // no waiting so just return
  1081. if( g_WaitForRepopulate == 0 ) {
  1082. return;
  1083. }
  1084. pTSSD = GetTSSD();
  1085. // We can get called even when the directory is inactive.
  1086. if (pTSSD != NULL) {
  1087. #if DBG
  1088. dwStartTime = GetTickCount();
  1089. #endif
  1090. hr = pTSSD->WaitForRepopulate(g_WaitForRepopulate);
  1091. if (FAILED(hr)) {
  1092. PostErrorValueEvent(EVENT_TS_SESSDIR_FAIL_UPDATE, hr);
  1093. DBGPRINT(("TERMSRV: WaitForRepopulate: Call failed, "
  1094. "hr=0x%X\n", hr));
  1095. }
  1096. #if DBG
  1097. DBGPRINT(("SessDirWaitForRepopulate() takes %d ms\n", GetTickCount() - dwStartTime) );
  1098. #endif
  1099. ReleaseTSSD();
  1100. }
  1101. }
  1102. /****************************************************************************/
  1103. // SessDirGetDisconnectedSessions
  1104. //
  1105. // Returns in the provided TSSD_DisconnectedSessionInfo buffer space
  1106. // up to TSSD_MaxDisconnectedSessions' worth of disconnected sessions
  1107. // from the session directory. Returns the number of sessions returned, which
  1108. // can be zero.
  1109. /****************************************************************************/
  1110. unsigned SessDirGetDisconnectedSessions(
  1111. WCHAR *UserName,
  1112. WCHAR *Domain,
  1113. TSSD_DisconnectedSessionInfo Info[TSSD_MaxDisconnectedSessions])
  1114. {
  1115. DWORD NumSessions = 0;
  1116. HRESULT hr;
  1117. ITSSessionDirectory *pTSSD;
  1118. pTSSD = GetTSSD();
  1119. // We can get called even when the directory is inactive.
  1120. if (pTSSD != NULL) {
  1121. hr = pTSSD->GetUserDisconnectedSessions(UserName, Domain,
  1122. &NumSessions, Info);
  1123. if (FAILED(hr)) {
  1124. DBGPRINT(("TERMSRV: SessDirGetDiscSessns: Call failed, "
  1125. "hr=0x%X\n", hr));
  1126. PostErrorValueEvent(EVENT_TS_SESSDIR_FAIL_QUERY, hr);
  1127. }
  1128. ReleaseTSSD();
  1129. }
  1130. return NumSessions;
  1131. }
  1132. /****************************************************************************/
  1133. // SessDirGetLBInfo
  1134. //
  1135. // Call the SessDirEx COM object interface, using the server address, get
  1136. // the opaque load balance info back, will send the info to the client.
  1137. /****************************************************************************/
  1138. BOOL SessDirGetLBInfo(
  1139. WCHAR *ServerAddress,
  1140. DWORD* pLBInfoSize,
  1141. PBYTE* pLBInfo)
  1142. {
  1143. ITSSessionDirectoryEx *pTSSDEx;
  1144. HRESULT hr;
  1145. static BOOL EventLogged = FALSE;
  1146. *pLBInfoSize = 0;
  1147. *pLBInfo = NULL;
  1148. pTSSDEx = GetTSSDEx();
  1149. if (pTSSDEx != NULL) {
  1150. hr = pTSSDEx->GetLoadBalanceInfo(ServerAddress, (BSTR *)pLBInfo);
  1151. if(SUCCEEDED(hr))
  1152. {
  1153. *pLBInfoSize = SysStringByteLen((BSTR)(*pLBInfo));
  1154. }
  1155. else
  1156. {
  1157. DBGPRINT(("TERMSRV: SessDirGetLBInfo: Call failed, "
  1158. "hr=0x%X\n", hr));
  1159. if (EventLogged == FALSE) {
  1160. PostErrorValueEvent(EVENT_TS_SESSDIR_FAIL_LBQUERY, hr);
  1161. EventLogged = TRUE;
  1162. }
  1163. }
  1164. ReleaseTSSDEx();
  1165. }
  1166. else {
  1167. DBGPRINT(("TERMSRV: SessDirGetLBInfo: Call failed, pTSSDEx is NULL "));
  1168. hr = E_FAIL;
  1169. }
  1170. return SUCCEEDED(hr);
  1171. }
  1172. #define SERVER_ADDRESS_LENGTH 64
  1173. /****************************************************************************/
  1174. // IsSameAsCurrentIP
  1175. //
  1176. // Determines whether the IP address given is the same as the current machine.
  1177. // Returning FALSE in case of error is not a problem--the client will
  1178. // just get redirected right back here.
  1179. /****************************************************************************/
  1180. BOOL IsSameAsCurrentIP(WCHAR *SessionIPAddress)
  1181. {
  1182. // Get the server adresses.
  1183. int RetVal;
  1184. unsigned long NumericalSessionIPAddr = 0;
  1185. char achComputerName[256];
  1186. DWORD dwComputerNameSize;
  1187. PBYTE pServerAddrByte;
  1188. PBYTE pSessionAddrByte;
  1189. ADDRINFO *AddrInfo, *AI;
  1190. struct sockaddr_in *pIPV4addr;
  1191. char AnsiSessionIPAddress[SERVER_ADDRESS_LENGTH];
  1192. // Compute integer for the server address.
  1193. // First, get ServerAddress as an ANSI string.
  1194. RetVal = WideCharToMultiByte(CP_ACP, 0, SessionIPAddress, -1,
  1195. AnsiSessionIPAddress, SERVER_ADDRESS_LENGTH, NULL, NULL);
  1196. if (RetVal == 0) {
  1197. DBGPRINT(("IsSameServerIP: WideCharToMB failed %d\n", GetLastError()));
  1198. return FALSE;
  1199. }
  1200. // Now, get the numerical server address.
  1201. // Now, use inet_addr to turn into an unsigned long.
  1202. NumericalSessionIPAddr = inet_addr(AnsiSessionIPAddress);
  1203. if (NumericalSessionIPAddr == INADDR_NONE) {
  1204. DBGPRINT(("IsSameServerIP: inet_addr failed\n"));
  1205. return FALSE;
  1206. }
  1207. pSessionAddrByte = (PBYTE) &NumericalSessionIPAddr;
  1208. dwComputerNameSize = sizeof(achComputerName);
  1209. if (!GetComputerNameA(achComputerName,&dwComputerNameSize)) {
  1210. return FALSE;
  1211. }
  1212. RetVal = getaddrinfo(achComputerName, NULL, NULL, &AddrInfo);
  1213. if (RetVal != 0) {
  1214. DBGPRINT (("Cannot resolve address, error: %d\n", RetVal));
  1215. return FALSE;
  1216. }
  1217. else {
  1218. // Compare all server adresses with client till a match is found.
  1219. // Currently only works for IPv4.
  1220. for (AI = AddrInfo; AI != NULL; AI = AI->ai_next) {
  1221. if (AI->ai_family == AF_INET) {
  1222. if (AI->ai_addrlen >= sizeof(struct sockaddr_in)) {
  1223. pIPV4addr = (struct sockaddr_in *) AI->ai_addr;
  1224. pServerAddrByte = (PBYTE)&pIPV4addr->sin_addr;
  1225. if (RtlEqualMemory(pSessionAddrByte, pServerAddrByte, 4)) {
  1226. return TRUE;
  1227. }
  1228. }
  1229. }
  1230. }
  1231. }
  1232. return FALSE;
  1233. }
  1234. /****************************************************************************/
  1235. // SessDirCheckRedirectClient
  1236. //
  1237. // Performs the set of steps needed to get the client's clustering
  1238. // capabilities, get the disconnected session list, and apply client
  1239. // redirection policy. Returns TRUE if the client was redirected, FALSE if
  1240. // the current WinStation transfer should be continued.
  1241. /****************************************************************************/
  1242. BOOL SessDirCheckRedirectClient(
  1243. PWINSTATION pTargetWinStation,
  1244. TS_LOAD_BALANCE_INFO *pLBInfo)
  1245. {
  1246. BOOL rc = FALSE;
  1247. ULONG ReturnLength;
  1248. unsigned i, NumSessions;
  1249. NTSTATUS Status;
  1250. ITSSessionDirectory *pTSSD;
  1251. BOOL fLogonUsingUPN = FALSE;
  1252. BOOL fNeedToRedirect = FALSE;
  1253. pTSSD = GetTSSD();
  1254. pTargetWinStation->NumClusterDiscSessions = 0;
  1255. if (pTSSD != NULL) {
  1256. if (pLBInfo->bClientSupportsRedirection &&
  1257. !pLBInfo->bRequestedSessionIDFieldValid) {
  1258. // The client has not been redirected to this machine. See if we
  1259. // have disconnected sessions in the database for redirecting.
  1260. NumSessions = pTargetWinStation->NumClusterDiscSessions =
  1261. SessDirGetDisconnectedSessions(
  1262. pLBInfo->UserName,
  1263. pLBInfo->Domain,
  1264. pTargetWinStation->ClusterDiscSessions);
  1265. if (pTargetWinStation->NumClusterDiscSessions > 0) {
  1266. // trevorfo: Applying policy here for reconnection to only one session
  1267. // (whichever is first). More general policy requires a selection UI at the
  1268. // client or in WinLogon.
  1269. // Find the first session in the list that matches the
  1270. // client's session requirements. Namely, we filter based
  1271. // on the client's TS protocol, wire protocol, and application
  1272. // type.
  1273. for (i = 0; i < NumSessions; i++) {
  1274. if ((pLBInfo->ProtocolType ==
  1275. pTargetWinStation->ClusterDiscSessions[i].
  1276. TSProtocol) &&
  1277. (!_wcsicmp(pLBInfo->InitialProgram,
  1278. pTargetWinStation->ClusterDiscSessions[i].
  1279. ApplicationType))) {
  1280. break;
  1281. }
  1282. }
  1283. if (i == NumSessions) {
  1284. TRACE((hTrace,TC_ICASRV,TT_API1,
  1285. "TERMSRV: SessDirCheckRedir: No matching sessions "
  1286. "found\n"));
  1287. }
  1288. else {
  1289. // If the session is not on this server, redirect the
  1290. // client. See notes above about use of
  1291. // _IcaStackIoControl().
  1292. if (!IsSameAsCurrentIP(pTargetWinStation->
  1293. ClusterDiscSessions[i].ServerAddress)) {
  1294. fNeedToRedirect = TRUE;
  1295. }
  1296. }
  1297. }
  1298. if (fNeedToRedirect ||
  1299. (pLBInfo->ClientRedirectionVersion >= TS_CLUSTER_REDIRECTION_VERSION4)) {
  1300. BYTE *pRedirInfo = NULL;
  1301. BYTE *pRedirInfoStart = NULL;
  1302. BYTE *LBInfo = NULL;
  1303. DWORD LBInfoSize = 0;
  1304. DWORD RedirInfoSize = 0;
  1305. DWORD ServerAddrLen = 0;
  1306. DWORD DomainSize = 0;
  1307. DWORD UserNameSize = 0;
  1308. DWORD PasswordSize = 0;
  1309. HKEY hKey = NULL;
  1310. DWORD Type, DataSize;
  1311. WCHAR SDRedirectionIP[IPADDRESSLENGTH];
  1312. WCHAR *pszServerIPAddress = NULL;
  1313. LONG RegRetVal;
  1314. // Even fNeedToRedirect is FALSE, we still need to send redirection packet
  1315. // with LB_NOREDIRECT set to the client to let it know the server address
  1316. // it actually connects to (for later auto-reconnect use)
  1317. if (!fNeedToRedirect) {
  1318. // Load registry keys.
  1319. RegRetVal = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_TS_CLUSTERSETTINGS, 0,
  1320. KEY_READ, &hKey);
  1321. if (RegRetVal != ERROR_SUCCESS) {
  1322. goto Cleanup;
  1323. }
  1324. // Query for the IP address used for SD redirection
  1325. DataSize = sizeof(SDRedirectionIP);
  1326. RegRetVal = RegQueryValueExW(hKey, REG_TS_CLUSTER_REDIRECTIONIP,
  1327. NULL, &Type, (BYTE *)SDRedirectionIP, &DataSize);
  1328. RegCloseKey(hKey);
  1329. if (RegRetVal != ERROR_SUCCESS) {
  1330. SDRedirectionIP[0] = L'\0';
  1331. DBGPRINT(("TERMSRV: Failed RegQuery for RedirectionIP for SD - "
  1332. "err=%u, DataSize=%u, type=%u\n",
  1333. RegRetVal, DataSize, Type));
  1334. goto Cleanup;
  1335. }
  1336. pszServerIPAddress = SDRedirectionIP;
  1337. }
  1338. else {
  1339. pszServerIPAddress = pTargetWinStation->ClusterDiscSessions[i].ServerAddress;
  1340. }
  1341. RedirInfoSize = sizeof(TS_CLIENT_REDIRECTION_INFO);
  1342. // Setup the server addr
  1343. if (g_SessDirUseServerAddr ||
  1344. pLBInfo->bClientRequireServerAddr) {
  1345. ServerAddrLen = (DWORD)((wcslen(pszServerIPAddress) + 1) *
  1346. sizeof(WCHAR));
  1347. RedirInfoSize += (ServerAddrLen + sizeof(ULONG));
  1348. DBGPRINT(("TERMSRV: SessDirCheckRedir: size=%d, "
  1349. "addr=%S\n", ServerAddrLen,
  1350. (WCHAR *)pszServerIPAddress));
  1351. }
  1352. else {
  1353. DBGPRINT(("TERMSRV: SessDirCheckRedir no server "
  1354. "address: g_SessDirUseServerAddr = %d, "
  1355. "bClientRequireServerAddr = %d\n",
  1356. g_SessDirUseServerAddr,
  1357. pLBInfo->bClientRequireServerAddr));
  1358. }
  1359. // Setup the load balance info
  1360. if ((pLBInfo->bClientRequireServerAddr == 0) &&
  1361. SessDirGetLBInfo(
  1362. pszServerIPAddress, &LBInfoSize, &LBInfo)) {
  1363. if (LBInfo) {
  1364. DBGPRINT(("TERMSRV: SessDirCheckRedir: size=%d, "
  1365. "info=%S\n", LBInfoSize,
  1366. (WCHAR *)LBInfo));
  1367. RedirInfoSize += (LBInfoSize + sizeof(ULONG));
  1368. }
  1369. }
  1370. else {
  1371. DBGPRINT(("TERMSRV: SessDirCheckRedir failed: "
  1372. "size=%d, info=%S\n", LBInfoSize,
  1373. (WCHAR *)LBInfo));
  1374. }
  1375. // Only send domain, username and password info if client
  1376. // redirection version is 3 and above
  1377. if ((pLBInfo->ClientRedirectionVersion >= TS_CLUSTER_REDIRECTION_VERSION3) &&
  1378. fNeedToRedirect) {
  1379. //domain
  1380. if (pLBInfo->Domain) {
  1381. DomainSize = (DWORD)(wcslen(pLBInfo->Domain) + 1) * sizeof(WCHAR);
  1382. RedirInfoSize += DomainSize + sizeof(ULONG);
  1383. }
  1384. if( pTargetWinStation && pTargetWinStation->pNewNotificationCredentials &&
  1385. wcschr(pTargetWinStation->pNewNotificationCredentials->UserName, L'@') ) {
  1386. // User is logon using UPN address, we need to send back same UPN in case the target machine
  1387. // does not have the domain in the logon dialog list.
  1388. // WINLOGON calls TS's WinStationUpdateClientCachedCredentialsWorker() which sets up
  1389. // UPN address
  1390. UserNameSize = (DWORD)(wcslen(pTargetWinStation->pNewNotificationCredentials->UserName) + 1 ) * sizeof(WCHAR);
  1391. RedirInfoSize += UserNameSize + sizeof(ULONG);
  1392. fLogonUsingUPN = TRUE;
  1393. }
  1394. else if (pLBInfo->UserName) {
  1395. UserNameSize = (DWORD)(wcslen(pLBInfo->UserName) + 1) * sizeof(WCHAR);
  1396. RedirInfoSize += UserNameSize + sizeof(ULONG);
  1397. }
  1398. //password
  1399. if (pLBInfo->Password) {
  1400. PasswordSize = (DWORD)(wcslen(pLBInfo->Password) + 1) * sizeof(WCHAR);
  1401. RedirInfoSize += PasswordSize + sizeof(ULONG);
  1402. }
  1403. }
  1404. // Setup the load balance IOCTL
  1405. pRedirInfoStart = pRedirInfo = new BYTE[RedirInfoSize];
  1406. TS_CLIENT_REDIRECTION_INFO *pClientRedirInfo =
  1407. (TS_CLIENT_REDIRECTION_INFO *)pRedirInfo;
  1408. if (pRedirInfo != NULL) {
  1409. if (fNeedToRedirect) {
  1410. pClientRedirInfo->SessionID =
  1411. pTargetWinStation->ClusterDiscSessions[i].
  1412. SessionID;
  1413. }
  1414. pClientRedirInfo->Flags = 0;
  1415. pRedirInfo += sizeof(TS_CLIENT_REDIRECTION_INFO);
  1416. if (ServerAddrLen) {
  1417. *((ULONG UNALIGNED*)(pRedirInfo)) =
  1418. ServerAddrLen;
  1419. memcpy((BYTE*)(pRedirInfo + sizeof(ULONG)),
  1420. (BYTE*)pszServerIPAddress,
  1421. ServerAddrLen);
  1422. pRedirInfo += ServerAddrLen + sizeof(ULONG);
  1423. pClientRedirInfo->Flags |= TARGET_NET_ADDRESS;
  1424. }
  1425. if (LBInfoSize) {
  1426. *((ULONG UNALIGNED*)(pRedirInfo)) = LBInfoSize;
  1427. memcpy((BYTE*)(pRedirInfo + sizeof(ULONG)),
  1428. LBInfo, LBInfoSize);
  1429. pRedirInfo += LBInfoSize + sizeof(ULONG);
  1430. pClientRedirInfo->Flags |= LOAD_BALANCE_INFO;
  1431. }
  1432. if (UserNameSize) {
  1433. *((ULONG UNALIGNED*)(pRedirInfo)) = UserNameSize;
  1434. if( TRUE == fLogonUsingUPN ) {
  1435. memcpy((BYTE*)(pRedirInfo + sizeof(ULONG)),
  1436. (BYTE*)(pTargetWinStation->pNewNotificationCredentials->UserName), UserNameSize);
  1437. }
  1438. else {
  1439. memcpy((BYTE*)(pRedirInfo + sizeof(ULONG)),
  1440. (BYTE*)(pLBInfo->UserName), UserNameSize);
  1441. }
  1442. pRedirInfo += UserNameSize + sizeof(ULONG);
  1443. pClientRedirInfo->Flags |= LB_USERNAME;
  1444. }
  1445. if (DomainSize) {
  1446. *((ULONG UNALIGNED*)(pRedirInfo)) = DomainSize;
  1447. memcpy((BYTE*)(pRedirInfo + sizeof(ULONG)),
  1448. (BYTE*)(pLBInfo->Domain), DomainSize);
  1449. pRedirInfo += DomainSize + sizeof(ULONG);
  1450. pClientRedirInfo->Flags |= LB_DOMAIN;
  1451. }
  1452. if (PasswordSize) {
  1453. *((ULONG UNALIGNED*)(pRedirInfo)) = PasswordSize;
  1454. memcpy((BYTE*)(pRedirInfo + sizeof(ULONG)),
  1455. (BYTE*)(pLBInfo->Password), PasswordSize);
  1456. pRedirInfo += PasswordSize + sizeof(ULONG);
  1457. pClientRedirInfo->Flags |= LB_PASSWORD;
  1458. }
  1459. if (pTargetWinStation->fSmartCardLogon) {
  1460. pClientRedirInfo->Flags |= LB_SMARTCARD_LOGON;
  1461. }
  1462. if (!fNeedToRedirect) {
  1463. pClientRedirInfo->Flags |= LB_NOREDIRECT;
  1464. }
  1465. }
  1466. else {
  1467. Status = STATUS_NO_MEMORY;
  1468. // The stack returned failure. Continue
  1469. // the current connection.
  1470. TRACE((hTrace,TC_ICASRV,TT_API1,
  1471. "TERMSRV: Failed STACK_CLIENT_REDIR, "
  1472. "SessionID=%u, Status=0x%X\n",
  1473. pTargetWinStation->LogonId, Status));
  1474. PostErrorValueEvent(
  1475. EVENT_TS_SESSDIR_FAIL_CLIENT_REDIRECT,
  1476. Status);
  1477. goto Cleanup;
  1478. }
  1479. Status = IcaStackIoControl(pTargetWinStation->hStack,
  1480. IOCTL_TS_STACK_SEND_CLIENT_REDIRECTION,
  1481. pClientRedirInfo, RedirInfoSize,
  1482. NULL, 0,
  1483. &ReturnLength);
  1484. if (NT_SUCCESS(Status)) {
  1485. // Notify session directory
  1486. //
  1487. // There is a relatively benign race condition here.
  1488. // If the second server logs in the user completely,
  1489. // it may end up hitting the session directory
  1490. // before this statement executes. In that case,
  1491. // the directory integrity service may end up
  1492. // pinging the machine once.
  1493. if (fNeedToRedirect) {
  1494. SessDirNotifyReconnectPending(pTargetWinStation->
  1495. ClusterDiscSessions[i].ServerAddress);
  1496. // Drop the current connection.
  1497. rc = TRUE;
  1498. }
  1499. }
  1500. else {
  1501. // The stack returned failure. Continue
  1502. // the current connection.
  1503. TRACE((hTrace,TC_ICASRV,TT_API1,
  1504. "TERMSRV: Failed STACK_CLIENT_REDIR, "
  1505. "SessionID=%u, Status=0x%X\n",
  1506. pTargetWinStation->LogonId, Status));
  1507. PostErrorValueEvent(
  1508. EVENT_TS_SESSDIR_FAIL_CLIENT_REDIRECT,
  1509. Status);
  1510. }
  1511. Cleanup:
  1512. // Cleanup the buffers
  1513. if (LBInfo != NULL) {
  1514. SysFreeString((BSTR)LBInfo);
  1515. LBInfo = NULL;
  1516. }
  1517. if (pRedirInfo != NULL) {
  1518. delete [] pRedirInfoStart;
  1519. pRedirInfoStart = NULL;
  1520. }
  1521. }
  1522. }
  1523. ReleaseTSSD();
  1524. }
  1525. return rc;
  1526. }
  1527. /****************************************************************************/
  1528. // SetTSSD
  1529. //
  1530. // These three functions ensure protected access to the session directory
  1531. // provider at all times. SetTSSD sets the pointer and increments the
  1532. // reference count to 1.
  1533. //
  1534. // SetTSSD returns:
  1535. // 0 on success
  1536. // -1 if failed because there was still a reference count on the COM object.
  1537. // This could happen if set was called too quickly after the final release
  1538. // to attempt to delete the object, as there still may be pending calls using
  1539. // the COM object.
  1540. // -2 if failed because the critical section is not initialized. This
  1541. // shouldn't be reached in normal operation, because Init is the only
  1542. // function that can call Set, and it bails if it fails the creation of the
  1543. // critical section.
  1544. /****************************************************************************/
  1545. int SetTSSD(ITSSessionDirectory *pTSSD)
  1546. {
  1547. int retval = 0;
  1548. if (g_bCritSecsInitialized != FALSE) {
  1549. EnterCriticalSection(&g_CritSecComObj);
  1550. if (g_nComObjRefCount == 0) {
  1551. ASSERT(g_pTSSDPriv == NULL);
  1552. g_pTSSDPriv = pTSSD;
  1553. g_nComObjRefCount = 1;
  1554. }
  1555. else {
  1556. DBGPRINT(("TERMSRV: SetTSSD: obj ref count not 0!\n"));
  1557. retval = -1;
  1558. }
  1559. LeaveCriticalSection(&g_CritSecComObj);
  1560. }
  1561. else {
  1562. ASSERT(g_bCritSecsInitialized == TRUE);
  1563. retval = -2;
  1564. }
  1565. return retval;
  1566. }
  1567. /****************************************************************************/
  1568. // GetTSSD
  1569. //
  1570. // GetTSSD returns a pointer to the session directory provider, if any, and
  1571. // increments the reference count if there is one.
  1572. /****************************************************************************/
  1573. ITSSessionDirectory *GetTSSD()
  1574. {
  1575. ITSSessionDirectory *pTSSD = NULL;
  1576. if (g_bCritSecsInitialized != FALSE) {
  1577. EnterCriticalSection(&g_CritSecComObj);
  1578. if (g_pTSSDPriv != NULL) {
  1579. g_nComObjRefCount += 1;
  1580. }
  1581. else {
  1582. ASSERT(g_nComObjRefCount == 0);
  1583. }
  1584. pTSSD = g_pTSSDPriv;
  1585. LeaveCriticalSection(&g_CritSecComObj);
  1586. }
  1587. return pTSSD;
  1588. }
  1589. /****************************************************************************/
  1590. // ReleaseTSSD
  1591. //
  1592. // ReleaseTSSD decrements the reference count of the session directory provider
  1593. // after a thread has finished using it, or when it is going to be deleted.
  1594. //
  1595. // If the reference count goes to zero, the pointer to the session directory
  1596. // provider is set to NULL.
  1597. /****************************************************************************/
  1598. void ReleaseTSSD()
  1599. {
  1600. ITSSessionDirectory *killthispTSSD = NULL;
  1601. if (g_bCritSecsInitialized != FALSE) {
  1602. EnterCriticalSection(&g_CritSecComObj);
  1603. ASSERT(g_nComObjRefCount != 0);
  1604. if (g_nComObjRefCount != 0) {
  1605. g_nComObjRefCount -= 1;
  1606. if (g_nComObjRefCount == 0) {
  1607. killthispTSSD = g_pTSSDPriv;
  1608. g_pTSSDPriv = NULL;
  1609. }
  1610. }
  1611. LeaveCriticalSection(&g_CritSecComObj);
  1612. }
  1613. // Now, release the session directory provider if our temppTSSD is NULL.
  1614. // We didn't want to release it while holding the critical section because
  1615. // that might create a deadlock in the recovery thread. Well, it did once.
  1616. if (killthispTSSD != NULL)
  1617. killthispTSSD->Release();
  1618. }
  1619. /****************************************************************************/
  1620. // SetTSSDEx
  1621. //
  1622. // These three functions ensure protected access to the session directory
  1623. // provider at all times. SetTSSDEx sets the pointer and increments the
  1624. // reference count to 1.
  1625. //
  1626. // SetTSSDEx returns:
  1627. // 0 on success
  1628. // -1 if failed because there was still a reference count on the COM object.
  1629. // This could happen if set was called too quickly after the final release
  1630. // to attempt to delete the object, as there still may be pending calls using
  1631. // the COM object.
  1632. /****************************************************************************/
  1633. int SetTSSDEx(ITSSessionDirectoryEx *pTSSDEx)
  1634. {
  1635. int retval = 0;
  1636. EnterCriticalSection(&g_CritSecComObj);
  1637. if (g_nTSSDExObjRefCount == 0) {
  1638. ASSERT(g_pTSSDExPriv == NULL);
  1639. g_pTSSDExPriv = pTSSDEx;
  1640. g_nTSSDExObjRefCount = 1;
  1641. }
  1642. else {
  1643. DBGPRINT(("TERMSRV: SetTSSDEx: obj ref count not 0!\n"));
  1644. retval = -1;
  1645. }
  1646. LeaveCriticalSection(&g_CritSecComObj);
  1647. return retval;
  1648. }
  1649. /****************************************************************************/
  1650. // GetTSSDEx
  1651. //
  1652. // GetTSSDEx returns a pointer to the session directory provider, if any, and
  1653. // increments the reference count if there is one.
  1654. /****************************************************************************/
  1655. ITSSessionDirectoryEx *GetTSSDEx()
  1656. {
  1657. ITSSessionDirectoryEx *pTSSDEx = NULL;
  1658. if (g_bCritSecsInitialized != FALSE) {
  1659. EnterCriticalSection(&g_CritSecComObj);
  1660. if (g_pTSSDExPriv != NULL) {
  1661. g_nTSSDExObjRefCount += 1;
  1662. }
  1663. else {
  1664. ASSERT(g_nTSSDExObjRefCount == 0);
  1665. }
  1666. pTSSDEx = g_pTSSDExPriv;
  1667. LeaveCriticalSection(&g_CritSecComObj);
  1668. }
  1669. return pTSSDEx;
  1670. }
  1671. /****************************************************************************/
  1672. // ReleaseTSSDEx
  1673. //
  1674. // ReleaseTSSDEx decrements the reference count of the session directory
  1675. // provider after a thread has finished using it, or when it is going to be
  1676. // deleted.
  1677. //
  1678. // If the reference count goes to zero, the pointer to the session directory
  1679. // provider is set to NULL.
  1680. /****************************************************************************/
  1681. void ReleaseTSSDEx()
  1682. {
  1683. ITSSessionDirectoryEx *killthispTSSDEx = NULL;
  1684. EnterCriticalSection(&g_CritSecComObj);
  1685. ASSERT(g_nTSSDExObjRefCount != 0);
  1686. if (g_nTSSDExObjRefCount != 0) {
  1687. g_nTSSDExObjRefCount -= 1;
  1688. if (g_nTSSDExObjRefCount == 0) {
  1689. killthispTSSDEx = g_pTSSDExPriv;
  1690. g_pTSSDExPriv = NULL;
  1691. }
  1692. }
  1693. LeaveCriticalSection(&g_CritSecComObj);
  1694. // Now, release the session directory provider if our temppTSSD is NULL.
  1695. // We didn't want to release it while holding the critical section because
  1696. // that might create a deadlock in the recovery thread. Well, it did once.
  1697. if (killthispTSSDEx != NULL)
  1698. killthispTSSDEx->Release();
  1699. }
  1700. /*****************************************************************************
  1701. ****************************************************************************/
  1702. DWORD SessDirOpenSessionDirectory( LPWSTR pszServerName )
  1703. {
  1704. ITSSessionDirectory *pTSSD = NULL;
  1705. DWORD Len;
  1706. DWORD Type;
  1707. WCHAR CLSIDStr[CLSIDLENGTH + 1]; // CLSIDLENGTH is limit, one extra for NULL
  1708. CLSID TSSDCLSID;
  1709. DWORD Status = ERROR_SUCCESS;
  1710. HKEY hKeyTermSrv = NULL;
  1711. HRESULT hr = S_OK;
  1712. // Load registry keys.
  1713. Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_CONTROL_TSERVER, 0,
  1714. KEY_READ, &hKeyTermSrv);
  1715. if (Status != ERROR_SUCCESS)
  1716. {
  1717. // Return Error Code as it is
  1718. goto Exit;
  1719. }
  1720. CLSIDStr[CLSIDLENGTH] = L'\0';
  1721. // Get the CLSID of the session directory object to instantiate.
  1722. Len = sizeof(CLSIDStr) - sizeof(CLSIDStr[0]);
  1723. Status = RegQueryValueEx(hKeyTermSrv, REG_TS_SESSDIRCLSID, NULL, &Type,
  1724. (BYTE *)CLSIDStr, &Len);
  1725. if( Status != ERROR_SUCCESS )
  1726. {
  1727. // Return Error Code as it is
  1728. goto Exit;
  1729. }
  1730. if( Type != REG_SZ || wcslen(CLSIDStr) == 0 )
  1731. {
  1732. // we have invalid data in registry, likely cause is setup not done.
  1733. Status = ERROR_INVALID_DATA;
  1734. goto Exit;
  1735. }
  1736. hr = CLSIDFromString(CLSIDStr, &TSSDCLSID);
  1737. if ( SUCCEEDED(hr) )
  1738. {
  1739. // Get the instance of TSSessionDirectory interface
  1740. hr = CoCreateInstance(TSSDCLSID, NULL,
  1741. CLSCTX_INPROC_SERVER, IID_ITSSessionDirectory,
  1742. (void **)&pTSSD);
  1743. if (SUCCEEDED(hr)) {
  1744. // Call PingSD to make the RPC call to SD
  1745. hr = pTSSD->PingSD(pszServerName);
  1746. pTSSD->Release();
  1747. }
  1748. }
  1749. // none of code returns HRESULT.
  1750. Status = HRESULT_CODE(hr);
  1751. Exit:
  1752. if (hKeyTermSrv) {
  1753. RegCloseKey(hKeyTermSrv);
  1754. }
  1755. return Status;
  1756. }
  1757. #pragma warning (pop)