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.

2396 lines
57 KiB

  1. /*++
  2. Copyright (c) 1994 Microsoft Corporation
  3. Module Name:
  4. llssrv.c
  5. Abstract:
  6. Main routine to setup the exception handlers and initialize everything
  7. to listen to LPC and RPC port requests.
  8. Author:
  9. Arthur Hanson (arth) Dec 07, 1994
  10. Environment:
  11. Revision History:
  12. Jeff Parham (jeffparh) 05-Dec-1995
  13. o Added certificate database support.
  14. o Expanded file load time (the update limit sent to the service
  15. controller) to account for certificate database loading.
  16. o Reordered initialization such that the license purchase subsystem
  17. is initialized before the service subsystem. (The service
  18. subsystem now uses the license subsystem.)
  19. o Increased internal version number.
  20. --*/
  21. #include <nt.h>
  22. #include <ntlsa.h>
  23. #include <ntsam.h>
  24. #include <ntrtl.h>
  25. #include <nturtl.h>
  26. #include <windows.h>
  27. #include <stdio.h>
  28. #include <stdlib.h>
  29. #include <process.h>
  30. #include <tchar.h>
  31. #define COBJMACROS
  32. #include <objbase.h>
  33. #include <iads.h>
  34. #include <adshlp.h>
  35. #include <adserr.h>
  36. #include <lm.h>
  37. #include <alertmsg.h>
  38. #include <winsock2.h>
  39. #include <dsgetdc.h>
  40. #include <ntdsapi.h>
  41. #include "llsapi.h"
  42. #include "debug.h"
  43. #include "llsutil.h"
  44. #include "llssrv.h"
  45. #include "service.h"
  46. #include "registry.h"
  47. #include "mapping.h"
  48. #include "msvctbl.h"
  49. #include "svctbl.h"
  50. #include "perseat.h"
  51. #include "purchase.h"
  52. #include "server.h"
  53. #include "repl.h"
  54. #include "scaven.h"
  55. #include "llsrpc_s.h"
  56. #include "certdb.h"
  57. BOOL CompareMachineName(
  58. LPCWSTR pwszName1,
  59. LPCWSTR pwszName2);
  60. VOID DNSToFlatName(
  61. LPCWSTR pwszDNSName,
  62. DWORD ccBufferLen,
  63. LPWSTR pwszBuffer);
  64. NTSTATUS GetDCInfo(
  65. DWORD ccDomain,
  66. WCHAR wszDomain[],
  67. DOMAIN_CONTROLLER_INFO ** ppDCInfo);
  68. HRESULT GetSiteServer(
  69. LPCWSTR pwszDomain,
  70. LPCWSTR pwszSiteName,
  71. BOOL fIsDC,
  72. LPWSTR * ppwszSiteServer);
  73. HRESULT
  74. BecomeSiteServer(
  75. DS_NAME_RESULT **ppDsResult,
  76. IADs *pADs2,
  77. LPWSTR *ppwszDN,
  78. LPCWSTR pwszDomain);
  79. LPWSTR GetSiteServerFromRegistry(
  80. VOID);
  81. LPWSTR GetEnterpriseServerFromRegistry(
  82. VOID);
  83. HRESULT GetLicenseSettingsObject(
  84. LPCWSTR pwszSiteName,
  85. LPCWSTR pwszConfigContainer,
  86. IADs ** ppADs);
  87. HRESULT GetSiteObject(
  88. LPCWSTR pwszSiteName,
  89. LPCWSTR pwszConfigContainer,
  90. IADsContainer ** ppADsContainer);
  91. HRESULT CreateLicenseSettingsObject(
  92. LPCWSTR pwszSiteName,
  93. LPCWSTR pwszConfigContainer,
  94. IADs ** ppADs);
  95. BOOL IsDC(
  96. VOID);
  97. VOID LLSRpcInit();
  98. BOOLEAN LLSpLPCInitialize(
  99. VOID);
  100. VOID LoadAll();
  101. VOID SetSiteRegistrySettings(
  102. LPCWSTR pwszSiteServer);
  103. NTSTATUS FilePrintTableInit();
  104. #define INTERNAL_VERSION 0x0006
  105. #define DEFAULT_LICENSE_CHECK_TIME 24
  106. #define DEFAULT_REPLICATION_TIME 12 * 60 * 60
  107. CONFIG_RECORD ConfigInfo;
  108. RTL_CRITICAL_SECTION ConfigInfoLock;
  109. #if DBG
  110. DWORD TraceFlags = 0;
  111. #endif
  112. //
  113. // this event is signalled when the service should end
  114. //
  115. HANDLE hServerStopEvent = NULL;
  116. TCHAR MyDomain[MAX_COMPUTERNAME_LENGTH + 2];
  117. ULONG MyDomainSize;
  118. BOOL IsMaster = FALSE;
  119. //
  120. // Files
  121. //
  122. TCHAR MappingFileName[MAX_PATH + 1];
  123. TCHAR UserFileName[MAX_PATH + 1];
  124. TCHAR LicenseFileName[MAX_PATH + 1];
  125. TCHAR CertDbFileName[MAX_PATH + 1];
  126. extern SERVICE_STATUS_HANDLE sshStatusHandle;
  127. RTL_CRITICAL_SECTION g_csLock;
  128. volatile BOOL g_fInitializationComplete = FALSE;
  129. volatile BOOL g_fDoingInitialization = FALSE;
  130. HANDLE g_hThrottleConfig = NULL;
  131. /////////////////////////////////////////////////////////////////////////
  132. NTSTATUS
  133. GetDCInfo(
  134. DWORD ccDomain,
  135. WCHAR wszDomain[],
  136. DOMAIN_CONTROLLER_INFO ** ppDCInfo
  137. )
  138. /*++
  139. Routine Description:
  140. Arguments:
  141. Return Value:
  142. None.
  143. --*/
  144. {
  145. DWORD Status;
  146. Status = DsGetDcNameW(NULL,
  147. NULL,
  148. NULL,
  149. NULL,
  150. DS_DIRECTORY_SERVICE_PREFERRED | DS_RETURN_FLAT_NAME | DS_BACKGROUND_ONLY,
  151. ppDCInfo);
  152. if (Status == STATUS_SUCCESS) {
  153. wcsncpy(wszDomain, (*ppDCInfo)->DomainName, ccDomain);
  154. }
  155. else {
  156. *wszDomain = L'\0';
  157. *ppDCInfo = NULL;
  158. }
  159. return(Status);
  160. } // GetDCInfo
  161. /////////////////////////////////////////////////////////////////////////
  162. DWORD
  163. LlsTimeGet(
  164. )
  165. /*++
  166. Routine Description:
  167. Arguments:
  168. Return Value:
  169. Seconds since midnight.
  170. --*/
  171. {
  172. DWORD Seconds;
  173. SYSTEMTIME SysTime;
  174. GetLocalTime(&SysTime);
  175. Seconds = (SysTime.wHour * 24 * 60) + (SysTime.wMinute * 60) + (SysTime.wSecond);
  176. return Seconds;
  177. } // LlsTimeGet
  178. /////////////////////////////////////////////////////////////////////////
  179. VOID
  180. ConfigInfoRegistryUpdate( )
  181. /*++
  182. Routine Description:
  183. Arguments:
  184. Return Value:
  185. --*/
  186. {
  187. DWORD ReplicationType, ReplicationTime;
  188. #if DBG
  189. if (TraceFlags & TRACE_FUNCTION_TRACE)
  190. dprintf(TEXT("LLS TRACE: ConfigInfoRegistryUpdate\n"));
  191. #endif
  192. #if DELAY_INITIALIZATION
  193. EnsureInitialized();
  194. #endif
  195. ConfigInfoUpdate(NULL);
  196. RtlEnterCriticalSection(&ConfigInfoLock);
  197. //
  198. // Update values from Registry
  199. //
  200. ReplicationTime = ConfigInfo.ReplicationTime;
  201. ReplicationType = ConfigInfo.ReplicationType;
  202. ConfigInfoRegistryInit( &ConfigInfo.ReplicationType,
  203. &ConfigInfo.ReplicationTime,
  204. &ConfigInfo.LogLevel,
  205. &ConfigInfo.PerServerCapacityWarning );
  206. if ( (ConfigInfo.ReplicationTime == 0) && (LLS_REPLICATION_TYPE_TIME != ConfigInfo.ReplicationType) )
  207. ConfigInfo.ReplicationTime = DEFAULT_REPLICATION_TIME;
  208. //
  209. // Adjust replication time if it has changed
  210. //
  211. if ((ReplicationTime != ConfigInfo.ReplicationTime) || (ReplicationType != ConfigInfo.ReplicationType))
  212. ReplicationTimeSet();
  213. RtlLeaveCriticalSection(&ConfigInfoLock);
  214. } // ConfigInfoRegistryUpdate
  215. /////////////////////////////////////////////////////////////////////////
  216. VOID
  217. ConfigInfoUpdate(
  218. DOMAIN_CONTROLLER_INFO * pDCInfo
  219. )
  220. /*++
  221. Routine Description:
  222. Arguments:
  223. Return Value:
  224. --*/
  225. {
  226. BOOL fIsDC = FALSE;
  227. BOOL fSiteServerFromRegistry = FALSE;
  228. BOOL fInDomain = FALSE;
  229. LPWSTR pwszSiteName = NULL;
  230. LPWSTR pwszSiteServer = NULL;
  231. LPWSTR pwszPropagationTarget = NULL;
  232. DOMAIN_CONTROLLER_INFO * pDCInfoLocal = NULL;
  233. DWORD ReplicationType, ReplicationTime;
  234. TCHAR pDomain[MAX_COMPUTERNAME_LENGTH + 1] = { TEXT('\0') };
  235. DWORD dwWait;
  236. #if DBG
  237. if (TraceFlags & TRACE_FUNCTION_TRACE)
  238. dprintf(TEXT("LLS TRACE: ConfigInfoUpdate2\n"));
  239. #endif
  240. dwWait = WaitForSingleObject(g_hThrottleConfig, 0);
  241. if (dwWait == WAIT_TIMEOUT)
  242. {
  243. // We've already updated in the past 15 minutes; return immediately
  244. return;
  245. }
  246. //
  247. // Get domain/DC information.
  248. //
  249. if (pDCInfo == NULL) {
  250. GetDCInfo(MAX_COMPUTERNAME_LENGTH + 1,
  251. pDomain,
  252. &pDCInfoLocal);
  253. pDCInfo = pDCInfoLocal;
  254. }
  255. else {
  256. //
  257. // Copy the domain name.
  258. //
  259. if (pDCInfo->DomainName != NULL) {
  260. wcsncpy(pDomain, pDCInfo->DomainName, MAX_COMPUTERNAME_LENGTH);
  261. }
  262. }
  263. if (*pDomain) {
  264. fInDomain = TRUE;
  265. if (NO_ERROR != DsGetSiteName(NULL, &pwszSiteName))
  266. {
  267. pwszSiteName = NULL;
  268. }
  269. fIsDC = IsDC();
  270. if (fIsDC && (NULL != pwszSiteName)) {
  271. GetSiteServer(pDomain, pwszSiteName, fIsDC, &pwszSiteServer);
  272. }
  273. }
  274. if (!fIsDC) {
  275. //
  276. // Domain or Workgroup member
  277. //
  278. pwszSiteServer = GetSiteServerFromRegistry();
  279. fSiteServerFromRegistry = TRUE;
  280. }
  281. if ( fIsDC ) {
  282. //
  283. // This server is a DC. Propagate to the site server.
  284. //
  285. if (pwszSiteServer == NULL) {
  286. //
  287. // The attempt to obtain it from the DS failed, default to
  288. // the local registry.
  289. //
  290. pwszSiteServer = GetSiteServerFromRegistry();
  291. fSiteServerFromRegistry = TRUE;
  292. }
  293. pwszPropagationTarget = pwszSiteServer;
  294. }
  295. else if ( fInDomain ) {
  296. //
  297. // This server is a member server. Propagate to a DC, providing
  298. // it is in the same site as this server; else, propagate
  299. // directly to the site server.
  300. //
  301. if (pDCInfo != NULL && pwszSiteName != NULL &&
  302. pDCInfo->DcSiteName != NULL &&
  303. lstrcmpi(pwszSiteName, pDCInfo->DcSiteName) == 0) {
  304. //
  305. // DC and server are in same site. Propagate to DC.
  306. //
  307. // Create DC name copy so the info struct can be freed.
  308. //
  309. if (pDCInfo->DomainControllerName != NULL) {
  310. pwszPropagationTarget = LocalAlloc(
  311. LPTR,
  312. (lstrlen(pDCInfo->DomainControllerName) + 1)
  313. * sizeof(TCHAR));
  314. if (pwszPropagationTarget != NULL) {
  315. lstrcpy(pwszPropagationTarget,
  316. pDCInfo->DomainControllerName);
  317. }
  318. else {
  319. #if DBG
  320. dprintf(TEXT("LLS: DC name allocation failure\n"));
  321. #endif
  322. goto CleanExit;
  323. }
  324. }
  325. }
  326. else {
  327. //
  328. // DC is in another site. Propagate to the site server.
  329. //
  330. if ((NULL == pwszSiteServer)
  331. && (NULL != pwszSiteName)) {
  332. //
  333. // No value found in registry, try Active Directory
  334. //
  335. fSiteServerFromRegistry = FALSE;
  336. GetSiteServer(pDomain, pwszSiteName, fIsDC, &pwszSiteServer);
  337. }
  338. pwszPropagationTarget = pwszSiteServer;
  339. }
  340. }
  341. else {
  342. //
  343. // Standalone server. Propagate directly to the enterprise
  344. // server
  345. //
  346. pwszPropagationTarget = GetEnterpriseServerFromRegistry();
  347. if (pwszPropagationTarget == NULL)
  348. {
  349. //
  350. // Don't have an enterprise server, try site server
  351. //
  352. pwszPropagationTarget = pwszSiteServer;
  353. }
  354. }
  355. //
  356. // Update ConfigInfo fields from information obtained above.
  357. //
  358. RtlEnterCriticalSection(&ConfigInfoLock);
  359. //
  360. // Check if the propagation target is actually this
  361. // machine. i.e., this is the site server.
  362. //
  363. if ((pwszPropagationTarget != NULL) && (*pwszPropagationTarget != 0)) {
  364. if (CompareMachineName(pwszPropagationTarget,
  365. ConfigInfo.ComputerName)) {
  366. //
  367. // This is the site server - don't propagate.
  368. //
  369. if (pwszPropagationTarget != pwszSiteServer) {
  370. LocalFree(pwszPropagationTarget);
  371. }
  372. pwszPropagationTarget = NULL; // For free below.
  373. ConfigInfo.IsMaster = TRUE;
  374. ConfigInfo.Replicate = FALSE;
  375. }
  376. }
  377. //
  378. // Update the SiteServer ConfigInfo field.
  379. //
  380. if (pwszSiteServer != NULL) {
  381. if (ConfigInfo.SiteServer != ConfigInfo.ReplicateTo) {
  382. LocalFree(ConfigInfo.SiteServer);
  383. }
  384. ConfigInfo.SiteServer = pwszSiteServer;
  385. pwszSiteServer = NULL; // For free below.
  386. //
  387. // Update the site related registry values.
  388. //
  389. if (!fSiteServerFromRegistry) {
  390. SetSiteRegistrySettings(ConfigInfo.SiteServer);
  391. }
  392. }
  393. //
  394. // Update the ReplicateTo ConfigInfo field.
  395. //
  396. if ((pwszPropagationTarget != NULL) && (*pwszPropagationTarget != 0)) {
  397. //
  398. // This server is propgagating to the site server or the DC.
  399. //
  400. ConfigInfo.IsMaster = FALSE;
  401. ConfigInfo.Replicate = TRUE;
  402. if ((ConfigInfo.ReplicateTo != NULL) && (ConfigInfo.ReplicateTo != ConfigInfo.SiteServer)) {
  403. LocalFree(ConfigInfo.ReplicateTo);
  404. }
  405. ConfigInfo.ReplicateTo = pwszPropagationTarget;
  406. pwszPropagationTarget = NULL; // For free below.
  407. }
  408. else if (!ConfigInfo.IsMaster) {
  409. //
  410. // Standalone server, and Site Server not specified in registry.
  411. // Do not replicate.
  412. //
  413. ConfigInfo.IsMaster = FALSE;
  414. ConfigInfo.Replicate = FALSE;
  415. }
  416. //
  417. // Update remaining ConfigInfo fields from registry.
  418. //
  419. // NB : Hardcode to *always* use the enterprise - new with NT 5.0.
  420. //
  421. ConfigInfo.UseEnterprise = 1;
  422. ReplicationTime = ConfigInfo.ReplicationTime;
  423. ReplicationType = ConfigInfo.ReplicationType;
  424. ConfigInfoRegistryInit( &ConfigInfo.ReplicationType,
  425. &ConfigInfo.ReplicationTime,
  426. &ConfigInfo.LogLevel,
  427. &ConfigInfo.PerServerCapacityWarning );
  428. //
  429. // Finally, adjust replication time if it has changed
  430. //
  431. if ((ReplicationTime != ConfigInfo.ReplicationTime)
  432. || (ReplicationType != ConfigInfo.ReplicationType)) {
  433. ReplicationTimeSet();
  434. }
  435. IsMaster = ConfigInfo.IsMaster;
  436. RtlLeaveCriticalSection(&ConfigInfoLock);
  437. CleanExit:
  438. if (pDCInfoLocal != NULL) {
  439. NetApiBufferFree(pDCInfoLocal); // Allocated from DsGetDcName
  440. }
  441. if (pwszSiteName != NULL) { // Allocated from DsGetSiteName
  442. NetApiBufferFree(pwszSiteName);
  443. }
  444. if (pwszSiteServer != NULL && pwszSiteServer == pwszPropagationTarget) {
  445. LocalFree(pwszSiteServer);
  446. pwszPropagationTarget = NULL;
  447. }
  448. if (pwszPropagationTarget != NULL) {
  449. LocalFree(pwszPropagationTarget);
  450. }
  451. }
  452. /////////////////////////////////////////////////////////////////////////
  453. BOOL
  454. IsDC(
  455. VOID
  456. )
  457. /*++
  458. Routine Description:
  459. Arguments:
  460. Return Value:
  461. --*/
  462. {
  463. NT_PRODUCT_TYPE NtType;
  464. //
  465. // If we aren't a DC, then count us as a member
  466. //
  467. NtType = NtProductLanManNt;
  468. RtlGetNtProductType(&NtType);
  469. if (NtType != NtProductLanManNt)
  470. return(FALSE);
  471. else {
  472. return(TRUE);
  473. }
  474. }
  475. /////////////////////////////////////////////////////////////////////////
  476. NTSTATUS
  477. ConfigInfoInit( )
  478. /*++
  479. Routine Description:
  480. Arguments:
  481. Return Value:
  482. --*/
  483. {
  484. DWORD Size;
  485. TCHAR DataPath[MAX_PATH + 1];
  486. NTSTATUS status;
  487. //
  488. // First zero init the memory
  489. //
  490. memset(&ConfigInfo, 0, sizeof(CONFIG_RECORD));
  491. ConfigInfo.ComputerName = LocalAlloc(LPTR, (MAX_COMPUTERNAME_LENGTH + 1) * sizeof(TCHAR));
  492. ConfigInfo.ReplicateTo = LocalAlloc(LPTR, (MAX_COMPUTERNAME_LENGTH + 3) * sizeof(TCHAR));
  493. ConfigInfo.EnterpriseServer = LocalAlloc(LPTR, (MAX_COMPUTERNAME_LENGTH + 3) * sizeof(TCHAR));
  494. ConfigInfo.SystemDir = LocalAlloc(LPTR, (MAX_PATH + 1) * sizeof(TCHAR));
  495. if ((ConfigInfo.ComputerName == NULL) || (ConfigInfo.ReplicateTo == NULL) || (ConfigInfo.EnterpriseServer == NULL) || (ConfigInfo.SystemDir == NULL) ) {
  496. ASSERT(FALSE);
  497. }
  498. ConfigInfo.Version = INTERNAL_VERSION;
  499. GetLocalTime(&ConfigInfo.Started);
  500. //
  501. // LastReplicated is just for display, LlsReplTime is what is used to
  502. // Calculate it.
  503. GetLocalTime(&ConfigInfo.LastReplicated);
  504. ConfigInfo.LastReplicatedSeconds = DateSystemGet();
  505. if (ConfigInfo.SystemDir != NULL)
  506. {
  507. GetSystemDirectory(ConfigInfo.SystemDir, MAX_PATH);
  508. lstrcat(ConfigInfo.SystemDir, TEXT("\\"));
  509. }
  510. ConfigInfo.IsMaster = FALSE;
  511. ConfigInfo.Replicate = FALSE;
  512. ConfigInfo.IsReplicating = FALSE;
  513. ConfigInfo.PerServerCapacityWarning = TRUE;
  514. ConfigInfo.ReplicationType = REPLICATE_DELTA;
  515. ConfigInfo.ReplicationTime = DEFAULT_REPLICATION_TIME;
  516. if (ConfigInfo.ComputerName != NULL)
  517. {
  518. Size = MAX_COMPUTERNAME_LENGTH + 1;
  519. GetComputerName(ConfigInfo.ComputerName, &Size);
  520. }
  521. status = RtlInitializeCriticalSection(&ConfigInfoLock);
  522. if (!NT_SUCCESS(status))
  523. return status;
  524. ConfigInfo.LogLevel = 0;
  525. if (ConfigInfo.SystemDir != NULL)
  526. {
  527. //
  528. // Create File paths
  529. //
  530. lstrcpy(MappingFileName, ConfigInfo.SystemDir);
  531. lstrcat(MappingFileName, TEXT(LLS_FILE_SUBDIR));
  532. lstrcat(MappingFileName, TEXT("\\"));
  533. lstrcat(MappingFileName, TEXT(MAP_FILE_NAME));
  534. lstrcpy(UserFileName, ConfigInfo.SystemDir);
  535. lstrcat(UserFileName, TEXT(LLS_FILE_SUBDIR));
  536. lstrcat(UserFileName, TEXT("\\"));
  537. lstrcat(UserFileName, TEXT(USER_FILE_NAME));
  538. lstrcpy(CertDbFileName, ConfigInfo.SystemDir);
  539. lstrcat(CertDbFileName, TEXT(LLS_FILE_SUBDIR));
  540. lstrcat(CertDbFileName, TEXT("\\"));
  541. lstrcat(CertDbFileName, TEXT(CERT_DB_FILE_NAME));
  542. lstrcpy(LicenseFileName, ConfigInfo.SystemDir);
  543. lstrcat(LicenseFileName, TEXT(LICENSE_FILE_NAME));
  544. //
  545. // Make sure our directory is there.
  546. //
  547. lstrcpy(DataPath, ConfigInfo.SystemDir);
  548. lstrcat(DataPath, TEXT(LLS_FILE_SUBDIR));
  549. CreateDirectory(DataPath, NULL);
  550. } else
  551. {
  552. MappingFileName[0] = 0;
  553. UserFileName[0] = 0;
  554. CertDbFileName[0] = 0;
  555. LicenseFileName[0] = 0;
  556. }
  557. return STATUS_SUCCESS;
  558. } // ConfigInfoInit
  559. /////////////////////////////////////////////////////////////////////////
  560. DWORD WINAPI
  561. LLSTopLevelExceptionHandler(
  562. struct _EXCEPTION_POINTERS *ExceptionInfo
  563. )
  564. /*++
  565. Routine Description:
  566. The Top Level exception filter for LLSMain.exe.
  567. This ensures the entire process will be cleaned up if any of
  568. the threads fail. Since LLSMain.exe is a distributed application,
  569. it is better to fail the entire process than allow random threads
  570. to continue executing.
  571. Arguments:
  572. ExceptionInfo - Identifies the exception that occurred.
  573. Return Values:
  574. EXCEPTION_EXECUTE_HANDLER - Terminate the process.
  575. EXCEPTION_CONTINUE_SEARCH - Continue processing as though this filter
  576. was never called.
  577. --*/
  578. {
  579. HANDLE hModule;
  580. //
  581. // Raise an alert
  582. //
  583. hModule = LoadLibraryA("netapi32");
  584. if ( hModule != NULL ) {
  585. NET_API_STATUS (NET_API_FUNCTION *NetAlertRaiseExFunction)
  586. (LPTSTR, LPVOID, DWORD, LPTSTR);
  587. NetAlertRaiseExFunction =
  588. (NET_API_STATUS (NET_API_FUNCTION *) (LPTSTR, LPVOID, DWORD, LPTSTR))
  589. GetProcAddress(hModule, "NetAlertRaiseEx");
  590. if ( NetAlertRaiseExFunction != NULL ) {
  591. NTSTATUS Status;
  592. UNICODE_STRING Strings;
  593. char message[ALERTSZ + sizeof(ADMIN_OTHER_INFO)];
  594. PADMIN_OTHER_INFO admin = (PADMIN_OTHER_INFO) message;
  595. //
  596. // Build the variable data
  597. //
  598. admin->alrtad_errcode = ALERT_UnhandledException;
  599. admin->alrtad_numstrings = 0;
  600. Strings.Buffer = (LPWSTR) ALERT_VAR_DATA(admin);
  601. Strings.Length = 0;
  602. Strings.MaximumLength = ALERTSZ;
  603. Status = RtlIntegerToUnicodeString(
  604. (ULONG)ExceptionInfo->ExceptionRecord->ExceptionCode,
  605. 16,
  606. &Strings );
  607. if ( NT_SUCCESS(Status) ) {
  608. if ( Strings.Length + sizeof(WCHAR) >= Strings.MaximumLength) {
  609. Status = STATUS_BUFFER_TOO_SMALL;
  610. } else {
  611. admin->alrtad_numstrings++;
  612. *(Strings.Buffer+(Strings.Length/sizeof(WCHAR))) = L'\0';
  613. Strings.Length += sizeof(WCHAR);
  614. Status = RtlAppendUnicodeToString( &Strings, L"LLS" );
  615. }
  616. }
  617. if ( NT_SUCCESS(Status) ) {
  618. if ( Strings.Length + sizeof(WCHAR) >= Strings.MaximumLength) {
  619. Status = STATUS_BUFFER_TOO_SMALL;
  620. } else {
  621. admin->alrtad_numstrings++;
  622. *(Strings.Buffer+(Strings.Length/sizeof(WCHAR))) = L'\0';
  623. Strings.Buffer += (Strings.Length/sizeof(WCHAR)) + 1;
  624. Strings.MaximumLength -= Strings.Length + sizeof(WCHAR);
  625. Strings.Length = 0;
  626. Status = RtlIntPtrToUnicodeString(
  627. (ULONG_PTR)(ExceptionInfo->ExceptionRecord->ExceptionAddress),
  628. 16,
  629. &Strings );
  630. }
  631. }
  632. if ( NT_SUCCESS(Status) ) {
  633. if ( Strings.Length + sizeof(WCHAR) >= Strings.MaximumLength) {
  634. Status = STATUS_BUFFER_TOO_SMALL;
  635. } else {
  636. admin->alrtad_numstrings++;
  637. *(Strings.Buffer+(Strings.Length/sizeof(WCHAR))) = L'\0';
  638. Strings.Buffer += (Strings.Length/sizeof(WCHAR)) + 1;
  639. (VOID) (*NetAlertRaiseExFunction)(
  640. ALERT_ADMIN_EVENT,
  641. message,
  642. (DWORD)((PCHAR)Strings.Buffer -
  643. (PCHAR)message),
  644. L"LLS" );
  645. }
  646. }
  647. }
  648. (VOID) FreeLibrary( hModule );
  649. }
  650. //
  651. // Just continue processing the exception.
  652. //
  653. return EXCEPTION_CONTINUE_SEARCH;
  654. } // LLSTopLevelExceptionHandler
  655. DWORD
  656. ServiceStartDelayed(
  657. )
  658. /*++
  659. Routine Description:
  660. Do the stuff that used to be done at service startup time,
  661. but can wait until the first RPC.
  662. Arguments:
  663. None.
  664. Return Values:
  665. None.
  666. --*/
  667. {
  668. NTSTATUS dwErr = STATUS_SUCCESS;
  669. ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, NSERVICEWAITHINT);
  670. //
  671. // Create the FilePrint table
  672. //
  673. dwErr = FilePrintTableInit();
  674. ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, NSERVICEWAITHINT);
  675. if (!NT_SUCCESS(dwErr))
  676. goto Cleanup;
  677. // Initialize the Service Table
  678. dwErr = LicenseListInit();
  679. ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, NSERVICEWAITHINT);
  680. if (!NT_SUCCESS(dwErr))
  681. goto Cleanup;
  682. dwErr = MasterServiceListInit();
  683. ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, NSERVICEWAITHINT);
  684. if (!NT_SUCCESS(dwErr))
  685. goto Cleanup;
  686. dwErr = LocalServiceListInit();
  687. ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, NSERVICEWAITHINT);
  688. if (!NT_SUCCESS(dwErr))
  689. goto Cleanup;
  690. dwErr = ServiceListInit();
  691. ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, NSERVICEWAITHINT);
  692. if (!NT_SUCCESS(dwErr))
  693. goto Cleanup;
  694. dwErr = MappingListInit();
  695. ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, NSERVICEWAITHINT);
  696. if (!NT_SUCCESS(dwErr))
  697. goto Cleanup;
  698. // Initialize the Per-Seat Table
  699. dwErr = UserListInit();
  700. ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, NSERVICEWAITHINT);
  701. if (!NT_SUCCESS(dwErr))
  702. goto Cleanup;
  703. // Initialize the Service Table
  704. dwErr = ServerListInit();
  705. ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, NSERVICEWAITHINT);
  706. if (!NT_SUCCESS(dwErr))
  707. goto Cleanup;
  708. // Initialize the Certificate Database
  709. dwErr = CertDbInit();
  710. ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, NSERVICEWAITHINT);
  711. if (!NT_SUCCESS(dwErr))
  712. goto Cleanup;
  713. // Load data files
  714. LoadAll();
  715. ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, NSERVICEWAITHINT);
  716. Cleanup:
  717. return dwErr;
  718. }
  719. DWORD
  720. EnsureInitialized (
  721. VOID
  722. )
  723. {
  724. DWORD dwErr;
  725. // Most common case is we're already initialized. Perform a quick
  726. // check for this.
  727. //
  728. if (g_fInitializationComplete)
  729. {
  730. return NOERROR;
  731. }
  732. dwErr = NOERROR;
  733. // Make no assumptions about how many threads may be trying to
  734. // initialize us at the same time.
  735. //
  736. RtlEnterCriticalSection (&g_csLock);
  737. // Need to re-check after acquiring the lock because another thread
  738. // may have just finished initializing and released the lock allowing
  739. // us to get it.
  740. //
  741. if ((!g_fInitializationComplete) && (!g_fDoingInitialization))
  742. {
  743. // set this now so this thread can't call ServiceStartDelayed twice
  744. g_fDoingInitialization = TRUE;
  745. dwErr = ServiceStartDelayed();
  746. g_fInitializationComplete = TRUE;
  747. }
  748. RtlLeaveCriticalSection (&g_csLock);
  749. return dwErr;
  750. }
  751. /////////////////////////////////////////////////////////////////////////
  752. VOID
  753. ServiceStart (
  754. DWORD dwArgc,
  755. LPTSTR *lpszArgv
  756. )
  757. /*++
  758. Routine Description:
  759. The code that starts everything, is really the main().
  760. Arguments:
  761. None. *** argc and argv unused ***
  762. Return Values:
  763. None.
  764. --*/
  765. {
  766. DWORD dwWait;
  767. NTSTATUS Status = STATUS_SUCCESS;
  768. BOOLEAN EnableAlignmentFaults = TRUE;
  769. KPRIORITY BasePriority;
  770. HANDLE hThread = NULL;
  771. BOOL fCoInitialized = FALSE;
  772. LARGE_INTEGER liWait;
  773. BOOL fRet;
  774. ///////////////////////////////////////////////////
  775. //
  776. // Service initialization
  777. //
  778. //
  779. // Report the status to the service control manager.
  780. //
  781. if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, NSERVICEWAITHINT)) // wait hint
  782. goto Cleanup;
  783. //
  784. // Define a top-level exception handler for the entire process.
  785. //
  786. (VOID) SetErrorMode( SEM_FAILCRITICALERRORS );
  787. //
  788. // Report the status to the service control manager.
  789. //
  790. if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, NSERVICEWAITHINT)) // wait hint
  791. goto Cleanup;
  792. (VOID) SetUnhandledExceptionFilter( &LLSTopLevelExceptionHandler );
  793. //
  794. // Run the LLS in the foreground.
  795. //
  796. // Several processes which depend on the LLS (like the lanman server)
  797. // run in the foreground. If we don't run in the foreground, they'll
  798. // starve waiting for us.
  799. //
  800. BasePriority = FOREGROUND_BASE_PRIORITY;
  801. Status = NtSetInformationProcess(
  802. NtCurrentProcess(),
  803. ProcessBasePriority,
  804. &BasePriority,
  805. sizeof(BasePriority)
  806. );
  807. // BUGBUG: ignore error for now; may be caused by running as NetworkService
  808. #if 0
  809. if (!NT_SUCCESS(Status))
  810. {
  811. goto Cleanup;
  812. }
  813. #endif
  814. //
  815. // Report the status to the service control manager.
  816. //
  817. if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, NSERVICEWAITHINT)) // wait hint
  818. goto Cleanup;
  819. //
  820. // Create an event to throttle ConfigInfoUpdate
  821. //
  822. g_hThrottleConfig
  823. = CreateWaitableTimer(NULL, // SecurityAttributes,
  824. FALSE, // bManualReset
  825. NULL // lpName
  826. );
  827. if (NULL == g_hThrottleConfig)
  828. {
  829. Status = GetLastError();
  830. goto Cleanup;
  831. }
  832. liWait.QuadPart = (LONGLONG) (-1); // Start immediately
  833. fRet = SetWaitableTimer(g_hThrottleConfig,
  834. &liWait,
  835. 1000*60*15, // lPeriod, 15 minutes
  836. NULL, // pfnCompletionRoutine
  837. NULL, // lpArgToCompletionRoutine
  838. FALSE // fResume (from suspended)
  839. );
  840. if (!fRet)
  841. {
  842. Status = GetLastError();
  843. goto Cleanup;
  844. }
  845. //
  846. // Start separate thread to contact the DS
  847. //
  848. hThread = CreateThread(NULL,
  849. 0,
  850. (LPTHREAD_START_ROUTINE) ConfigInfoInit,
  851. NULL,
  852. 0,
  853. NULL);
  854. Status = RtlInitializeCriticalSection(&g_csLock);
  855. if (!NT_SUCCESS(Status))
  856. {
  857. goto Cleanup;
  858. }
  859. //
  860. // Report the status to the service control manager.
  861. //
  862. if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, NSERVICEWAITHINT)) // wait hint
  863. goto Cleanup;
  864. //
  865. // Create the event object. The control handler function signals
  866. // this event when it receives the "stop" control code.
  867. //
  868. hServerStopEvent = CreateEvent(
  869. NULL, // no security attributes
  870. TRUE, // manual reset event
  871. FALSE, // not-signalled
  872. NULL); // no name
  873. if ( hServerStopEvent == NULL)
  874. goto Cleanup;
  875. //
  876. // Report the status to the service control manager.
  877. //
  878. if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, NSERVICEWAITHINT)) // wait hint
  879. goto Cleanup;
  880. //
  881. // Report the status to the service control manager.
  882. //
  883. if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, NSERVICEWAITHINT)) // wait hint
  884. goto Cleanup;
  885. // Initialize Replication...
  886. ReplicationInit();
  887. //
  888. // Report the status to the service control manager.
  889. //
  890. if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, NSERVICEWAITHINT)) // wait hint
  891. goto Cleanup;
  892. // Initialize the Registry values...
  893. RegistryInit();
  894. //
  895. // Report the status to the service control manager.
  896. //
  897. if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, NSERVICEWAITHINT)) // wait hint
  898. goto Cleanup;
  899. // Initialize scavenger thread...
  900. ScavengerInit();
  901. //
  902. // Report the status to the service control manager.
  903. //
  904. if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, NSERVICEWAITHINT)) // wait hint
  905. goto Cleanup;
  906. //
  907. // wait for ConfigInfoInit to complete before accepting clients
  908. //
  909. while (hThread != NULL)
  910. {
  911. dwWait = WaitForSingleObject(hThread,NSERVICEWAITHINT/2);
  912. //
  913. // Report the status to the service control manager.
  914. //
  915. if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, NSERVICEWAITHINT)) // wait hint
  916. goto Cleanup;
  917. if (dwWait == WAIT_OBJECT_0)
  918. {
  919. GetExitCodeThread(hThread,&Status);
  920. // Check if critsec creation failed
  921. if (!NT_SUCCESS(Status))
  922. goto Cleanup;
  923. CloseHandle(hThread);
  924. hThread = NULL;
  925. break;
  926. }
  927. }
  928. // Initialize RegistryMonitor thread...
  929. RegistryStartMonitor();
  930. //
  931. // Report the status to the service control manager.
  932. //
  933. if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, NSERVICEWAITHINT)) // wait hint
  934. goto Cleanup;
  935. // Initialize COM
  936. if (!FAILED(CoInitialize(NULL)))
  937. {
  938. fCoInitialized = TRUE;
  939. }
  940. // Do all the stuff that used to be delayed
  941. EnsureInitialized();
  942. // Initialize RPC Stuff... (start accepting clients)
  943. LLSRpcInit();
  944. //
  945. // End of initialization
  946. //
  947. ////////////////////////////////////////////////////////
  948. //
  949. // Tell SCM we are up and running!
  950. //
  951. if (!ReportStatusToSCMgr( SERVICE_RUNNING, NO_ERROR, 0))
  952. goto Cleanup;
  953. ////////////////////////////////////////////////////////
  954. //
  955. // Service is now running, perform work until shutdown
  956. //
  957. dwWait = WaitForSingleObject(hServerStopEvent, INFINITE);
  958. Cleanup:
  959. if (fCoInitialized)
  960. CoUninitialize();
  961. if (hThread != NULL)
  962. CloseHandle(hThread);
  963. if (hServerStopEvent)
  964. CloseHandle(hServerStopEvent);
  965. if (sshStatusHandle)
  966. ReportStatusToSCMgr( SERVICE_STOPPED, NO_ERROR, 0);
  967. } // ServiceStart
  968. /////////////////////////////////////////////////////////////////////////
  969. VOID ServiceStop()
  970. /*++
  971. Routine Description:
  972. Stops the service.
  973. If a ServiceStop procedure is going to take longer than 3 seconds to
  974. execute, it should spawn a thread to execute the stop code, and return.
  975. Otherwise, the ServiceControlManager will believe that the service has
  976. stopped responding.
  977. Arguments:
  978. None.
  979. Return Values:
  980. None.
  981. --*/
  982. {
  983. if ( hServerStopEvent )
  984. SetEvent(hServerStopEvent);
  985. } // ServiceStop
  986. #define FIND_DNSNAME_SEPARATOR(pwsz) { \
  987. while (*pwsz && *pwsz != TEXT('.')) { \
  988. pwsz++; \
  989. } \
  990. }
  991. /////////////////////////////////////////////////////////////////////////
  992. VOID
  993. DNSToFlatName(
  994. LPCWSTR pwszDNSName,
  995. DWORD ccBufferLen,
  996. LPWSTR pwszBuffer
  997. )
  998. /*++
  999. Routine Description:
  1000. Arguments:
  1001. pwszDNSName
  1002. ccBufferLen
  1003. pwszBuffer
  1004. Return Value:
  1005. --*/
  1006. {
  1007. LPWSTR pwszFlatName = (LPWSTR)pwszDNSName;
  1008. SIZE_T ccFlatNameLen;
  1009. ASSERT(pwszDNSName != NULL);
  1010. FIND_DNSNAME_SEPARATOR(pwszFlatName);
  1011. ccFlatNameLen = (DWORD)(pwszFlatName - pwszDNSName);
  1012. if (ccFlatNameLen && ccFlatNameLen < ccBufferLen) {
  1013. lstrcpyn(pwszBuffer, pwszDNSName, (int)ccFlatNameLen + 1);
  1014. pwszBuffer[ccFlatNameLen] = TEXT('\0');
  1015. }
  1016. else {
  1017. *pwszBuffer = TEXT('\0');
  1018. }
  1019. }
  1020. /////////////////////////////////////////////////////////////////////////
  1021. BOOL
  1022. CompareMachineName(
  1023. LPCWSTR pwszName1,
  1024. LPCWSTR pwszName2
  1025. )
  1026. /*++
  1027. Routine Description:
  1028. Arguments:
  1029. pwszName1
  1030. pwszName2
  1031. Return Value:
  1032. TRUE -- The names match.
  1033. FALSE -- Otherwise.
  1034. --*/
  1035. {
  1036. TCHAR szFlatName[MAX_COMPUTERNAME_LENGTH + 3];
  1037. LPWSTR pwszTmp1 = (LPWSTR)pwszName1;
  1038. LPWSTR pwszTmp2 = (LPWSTR)pwszName2;
  1039. if (pwszName1 == NULL || pwszName2 == NULL) {
  1040. return FALSE;
  1041. }
  1042. //
  1043. // Identify if both/either name are DNS names by checking for the
  1044. // existence of a '.' separator.
  1045. //
  1046. FIND_DNSNAME_SEPARATOR(pwszTmp1);
  1047. FIND_DNSNAME_SEPARATOR(pwszTmp2);
  1048. if ((*pwszTmp1 && *pwszTmp2) || (!*pwszTmp1 && !*pwszTmp2)) {
  1049. //
  1050. // Non-differing name types. Both are either DNS or flat.
  1051. //
  1052. return (lstrcmpi(pwszName1, pwszName2) == 0);
  1053. }
  1054. else if (*pwszTmp1) {
  1055. //
  1056. // Name 1 is DNS, name 2 is flat.
  1057. // Convert DNS to flat, then compare.
  1058. //
  1059. DNSToFlatName(pwszName1,
  1060. MAX_COMPUTERNAME_LENGTH + 3,
  1061. szFlatName);
  1062. return (lstrcmpi(szFlatName, pwszName2) == 0);
  1063. }
  1064. else {
  1065. //
  1066. // Name 2 is DNS, name 1 is flat.
  1067. // Convert DNS to flat, then compare.
  1068. //
  1069. DNSToFlatName(pwszName2,
  1070. MAX_COMPUTERNAME_LENGTH + 3,
  1071. szFlatName);
  1072. return (lstrcmpi(szFlatName, pwszName1) == 0);
  1073. }
  1074. }
  1075. #define REG_LS_PARAMETERS \
  1076. TEXT("System\\CurrentControlSet\\Services\\LicenseService\\Parameters")
  1077. #define REG_LS_SITESERVER \
  1078. TEXT("SiteServer")
  1079. #define REG_LS_ENTERPRISESERVER \
  1080. TEXT("EnterpriseServer")
  1081. #define REG_LS_USEENTERPRISE \
  1082. TEXT("UseEnterprise")
  1083. /////////////////////////////////////////////////////////////////////////
  1084. LPWSTR
  1085. GetSiteServerFromRegistry(
  1086. VOID
  1087. )
  1088. /*++
  1089. Routine Description:
  1090. Arguments:
  1091. None.
  1092. Return Value:
  1093. --*/
  1094. {
  1095. HKEY hKey = NULL;
  1096. DWORD dwType, dwSize;
  1097. LPWSTR pwszSiteServer = NULL;
  1098. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  1099. REG_LS_PARAMETERS,
  1100. 0,
  1101. KEY_READ,
  1102. &hKey) == ERROR_SUCCESS) {
  1103. //
  1104. // Allocate SiteServer on the heap since it could be quite large.
  1105. //
  1106. dwSize = 0;
  1107. if (RegQueryValueEx(hKey,
  1108. REG_LS_SITESERVER,
  1109. NULL,
  1110. &dwType,
  1111. (LPBYTE)NULL,
  1112. &dwSize) == ERROR_SUCCESS)
  1113. {
  1114. pwszSiteServer = LocalAlloc(LPTR, dwSize);
  1115. if (pwszSiteServer != NULL) {
  1116. if (RegQueryValueEx(hKey,
  1117. REG_LS_SITESERVER,
  1118. NULL,
  1119. &dwType,
  1120. (LPBYTE)pwszSiteServer,
  1121. &dwSize) != ERROR_SUCCESS) {
  1122. LocalFree(pwszSiteServer);
  1123. pwszSiteServer = NULL;
  1124. }
  1125. }
  1126. }
  1127. RegCloseKey(hKey);
  1128. }
  1129. return pwszSiteServer;
  1130. }
  1131. /////////////////////////////////////////////////////////////////////////
  1132. LPWSTR
  1133. GetEnterpriseServerFromRegistry(
  1134. VOID
  1135. )
  1136. /*++
  1137. Routine Description:
  1138. Arguments:
  1139. None.
  1140. Return Value:
  1141. --*/
  1142. {
  1143. HKEY hKey = NULL;
  1144. DWORD dwType, dwSize;
  1145. LPWSTR pwszEnterpriseServer = NULL;
  1146. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  1147. REG_LS_PARAMETERS,
  1148. 0,
  1149. KEY_READ,
  1150. &hKey) == ERROR_SUCCESS) {
  1151. //
  1152. // Allocate EnterpriseServer on the heap since it could be quite large.
  1153. //
  1154. dwSize = 0;
  1155. if (RegQueryValueEx(hKey,
  1156. REG_LS_ENTERPRISESERVER,
  1157. NULL,
  1158. &dwType,
  1159. (LPBYTE)NULL,
  1160. &dwSize) == ERROR_SUCCESS)
  1161. {
  1162. pwszEnterpriseServer = LocalAlloc(LPTR, dwSize);
  1163. if (pwszEnterpriseServer != NULL) {
  1164. if (RegQueryValueEx(hKey,
  1165. REG_LS_ENTERPRISESERVER,
  1166. NULL,
  1167. &dwType,
  1168. (LPBYTE)pwszEnterpriseServer,
  1169. &dwSize) != ERROR_SUCCESS) {
  1170. LocalFree(pwszEnterpriseServer);
  1171. pwszEnterpriseServer = NULL;
  1172. }
  1173. }
  1174. }
  1175. RegCloseKey(hKey);
  1176. }
  1177. return pwszEnterpriseServer;
  1178. }
  1179. /////////////////////////////////////////////////////////////////////////
  1180. VOID
  1181. SetSiteRegistrySettings(
  1182. LPCWSTR pwszSiteServer
  1183. )
  1184. /*++
  1185. Routine Description:
  1186. Arguments:
  1187. pwszSiteServer
  1188. Return Value:
  1189. --*/
  1190. {
  1191. HKEY hKey;
  1192. DWORD dwSize;
  1193. DWORD dwType = REG_SZ;
  1194. DWORD dwUseEnterprise = 1;
  1195. TCHAR szSiteServerFlatName[MAX_COMPUTERNAME_LENGTH + 1];
  1196. ASSERT(pwszSiteServer != NULL);
  1197. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  1198. REG_LS_PARAMETERS,
  1199. 0,
  1200. KEY_WRITE,
  1201. &hKey) == ERROR_SUCCESS) {
  1202. //
  1203. // Write the SiteServer value.
  1204. //
  1205. dwSize = (lstrlen(pwszSiteServer) + 1) * sizeof(TCHAR);
  1206. RegSetValueEx(hKey,
  1207. REG_LS_SITESERVER,
  1208. 0,
  1209. dwType,
  1210. (LPBYTE)pwszSiteServer,
  1211. dwSize);
  1212. RegCloseKey(hKey);
  1213. }
  1214. }
  1215. //
  1216. // Pre-fill the ADSI cache with only the attribute we want, then get it
  1217. // Only use if exactly one attribute is needed
  1218. //
  1219. HRESULT
  1220. GetWithGetInfoEx(
  1221. IADs *pADs,
  1222. LPWSTR wszAttribute,
  1223. VARIANT *pvar
  1224. )
  1225. {
  1226. HRESULT hr;
  1227. hr = ADsBuildVarArrayStr( &wszAttribute, 1, pvar );
  1228. if( SUCCEEDED( hr ) )
  1229. {
  1230. hr = IADs_GetInfoEx( pADs, *pvar, 0L );
  1231. VariantClear( pvar );
  1232. if (SUCCEEDED(hr))
  1233. {
  1234. hr = IADs_Get( pADs, wszAttribute, pvar );
  1235. }
  1236. }
  1237. return hr;
  1238. }
  1239. //
  1240. // Pre-fill the ADSI cache with only the attributes we want, then get them
  1241. // Only use if exactly two attributes are needed
  1242. //
  1243. HRESULT
  1244. GetWithGetInfoEx2(
  1245. IADs *pADs,
  1246. LPWSTR wszAttribute1,
  1247. LPWSTR wszAttribute2,
  1248. VARIANT *pvar1,
  1249. VARIANT *pvar2,
  1250. HRESULT * phr2
  1251. )
  1252. {
  1253. HRESULT hr;
  1254. LPWSTR rgwszAttributes[] = {wszAttribute1,wszAttribute2};
  1255. hr = ADsBuildVarArrayStr( rgwszAttributes, 2, pvar1 );
  1256. if( SUCCEEDED( hr ) )
  1257. {
  1258. hr = IADs_GetInfoEx( pADs, *pvar1, 0L );
  1259. VariantClear( pvar1 );
  1260. if (SUCCEEDED(hr))
  1261. {
  1262. hr = IADs_Get( pADs, wszAttribute1, pvar1 );
  1263. if (SUCCEEDED(hr))
  1264. {
  1265. *phr2 = IADs_Get( pADs, wszAttribute2, pvar2 );
  1266. }
  1267. }
  1268. }
  1269. return hr;
  1270. }
  1271. #define CWSTR_SIZE(x) (sizeof(x) - (sizeof(WCHAR) * 2))
  1272. #define DWSTR_SIZE(x) ((wcslen(x) + 1) * sizeof(WCHAR))
  1273. #define ROOT_DSE_PATH L"LDAP://RootDSE"
  1274. #define CONFIG_CNTNR L"ConfigurationNamingContext"
  1275. #define SITE_SERVER L"siteServer"
  1276. #define DNS_MACHINE_NAME L"dNSHostName"
  1277. #define IS_DELETED L"isDeleted"
  1278. /////////////////////////////////////////////////////////////////////////
  1279. HRESULT
  1280. GetSiteServer(
  1281. LPCWSTR pwszDomain,
  1282. LPCWSTR pwszSiteName,
  1283. BOOL fIsDC,
  1284. LPWSTR * ppwszSiteServer
  1285. )
  1286. /*++
  1287. Routine Description:
  1288. Arguments:
  1289. pwszSiteName
  1290. fIsDC
  1291. ppwszSiteServer
  1292. Return Value:
  1293. --*/
  1294. {
  1295. LPWSTR pwszDN = NULL;
  1296. LPWSTR pwszConfigContainer;
  1297. LPWSTR pwszBindPath;
  1298. IADs * pADs = NULL;
  1299. IADs * pADs2 = NULL;
  1300. IADs * pADs3 = NULL;
  1301. DS_NAME_RESULT * pDsResult = NULL;
  1302. VARIANT var;
  1303. VARIANT var2;
  1304. HRESULT hr, hr2;
  1305. DWORD cbSize;
  1306. DWORD dwRet = 0;
  1307. BOOL fAlreadyTookSiteServer = FALSE;
  1308. BOOL fCoInitialized = FALSE;
  1309. ASSERT(pwszSiteName != NULL);
  1310. ASSERT(ppwszSiteServer != NULL);
  1311. *ppwszSiteServer = NULL;
  1312. VariantInit(&var);
  1313. VariantInit(&var2);
  1314. hr = CoInitialize(NULL);
  1315. if (FAILED(hr)) {
  1316. ERR(hr);
  1317. goto CleanExit;
  1318. }
  1319. fCoInitialized = TRUE;
  1320. //
  1321. // Obtain the path to the configuration container.
  1322. //
  1323. hr = ADsGetObject(ROOT_DSE_PATH, &IID_IADs, (void **)&pADs);
  1324. if (FAILED(hr)) {
  1325. ERR(hr);
  1326. goto CleanExit;
  1327. }
  1328. hr = IADs_Get(pADs, CONFIG_CNTNR, &var);
  1329. if (FAILED(hr)) {
  1330. ERR(hr);
  1331. goto CleanExit;
  1332. }
  1333. if (V_VT(&var) != VT_BSTR) {
  1334. ASSERT(V_VT(&var) == VT_BSTR);
  1335. dwRet = ERROR_INVALID_DATA;
  1336. ERR(dwRet);
  1337. goto CleanExit;
  1338. }
  1339. pwszConfigContainer = var.bstrVal; // For sake of readability.
  1340. //
  1341. // Bind to the license settings object.
  1342. //
  1343. hr = GetLicenseSettingsObject(pwszSiteName,
  1344. pwszConfigContainer,
  1345. &pADs2);
  1346. if (hr == HRESULT_FROM_WIN32(ERROR_DS_NO_SUCH_OBJECT)) {
  1347. //
  1348. // The license settings object doesn't exist. Create it.
  1349. //
  1350. hr = CreateLicenseSettingsObject(pwszSiteName,
  1351. pwszConfigContainer,
  1352. &pADs2);
  1353. }
  1354. if (FAILED(hr)) {
  1355. //
  1356. // Failed to bind or create the license settings object.
  1357. //
  1358. goto CleanExit;
  1359. }
  1360. ASSERT(pADs2 != NULL);
  1361. //
  1362. // Consult the site server property on the license settings object.
  1363. // It's a DN to the machine object of the site server.
  1364. //
  1365. VariantClear(&var);
  1366. hr = GetWithGetInfoEx(pADs2, SITE_SERVER, &var);
  1367. //
  1368. // If the site server property has not been set and this server
  1369. // is a DC, designate this server as the site server.
  1370. //
  1371. if (hr == E_ADS_PROPERTY_NOT_FOUND && fIsDC) {
  1372. dwRet = BecomeSiteServer(&pDsResult,pADs2,&pwszDN,pwszDomain);
  1373. if (dwRet)
  1374. goto CleanExit;
  1375. }
  1376. else if (SUCCEEDED(hr)) {
  1377. if (V_VT(&var) != VT_BSTR) {
  1378. ASSERT(V_VT(&var) == VT_BSTR);
  1379. dwRet = ERROR_INVALID_DATA;
  1380. ERR(dwRet);
  1381. goto CleanExit;
  1382. }
  1383. pwszDN = V_BSTR(&var);
  1384. }
  1385. else {
  1386. goto CleanExit;
  1387. }
  1388. TryNewSiteServer:
  1389. //
  1390. // Bind to the computer object referenced by the Site-Server property.
  1391. //
  1392. if (pwszDN == NULL)
  1393. {
  1394. hr = E_FAIL;
  1395. ERR(hr);
  1396. goto CleanExit;
  1397. }
  1398. // LDAP:// + pwszDN + 1
  1399. pwszBindPath = LocalAlloc(LPTR,
  1400. (wcslen(pwszDN) + 8) * sizeof(WCHAR));
  1401. if (pwszBindPath == NULL) {
  1402. hr = E_OUTOFMEMORY;
  1403. ERR(hr);
  1404. goto CleanExit;
  1405. }
  1406. wsprintf(pwszBindPath, L"LDAP://%ws", pwszDN);
  1407. hr = ADsOpenObject(pwszBindPath,
  1408. NULL,
  1409. NULL,
  1410. ADS_SECURE_AUTHENTICATION | ADS_FAST_BIND | ADS_SERVER_BIND,
  1411. &IID_IADs,
  1412. (void **)&pADs3);
  1413. LocalFree(pwszBindPath);
  1414. if (FAILED(hr)) {
  1415. if (fIsDC && !fAlreadyTookSiteServer)
  1416. {
  1417. //
  1418. // Existing SiteServer is gone, claim it
  1419. //
  1420. if (pDsResult != NULL) {
  1421. DsFreeNameResult(pDsResult);
  1422. }
  1423. dwRet = BecomeSiteServer(&pDsResult,pADs2,&pwszDN,pwszDomain);
  1424. if (dwRet)
  1425. {
  1426. goto CleanExit;
  1427. }
  1428. else
  1429. {
  1430. fAlreadyTookSiteServer = TRUE;
  1431. if (pADs3 != NULL) {
  1432. IADs_Release(pADs3);
  1433. }
  1434. goto TryNewSiteServer;
  1435. }
  1436. } else
  1437. {
  1438. ERR(hr);
  1439. goto CleanExit;
  1440. }
  1441. }
  1442. //
  1443. // Fetch the Machine-DNS-Name property.
  1444. //
  1445. VariantClear(&var);
  1446. hr = GetWithGetInfoEx2(pADs3, DNS_MACHINE_NAME, IS_DELETED, &var, &var2, &hr2);
  1447. if (FAILED(hr)) {
  1448. ERR(hr);
  1449. goto CleanExit;
  1450. }
  1451. if (SUCCEEDED(hr2))
  1452. {
  1453. hr = VariantChangeType(&var2,&var2,0,VT_BOOL);
  1454. if (FAILED(hr)) {
  1455. ERR(hr);
  1456. goto CleanExit;
  1457. }
  1458. if (V_BOOL(&var2))
  1459. {
  1460. // object has been deleted - pretend it isn't set
  1461. hr = E_ADS_PROPERTY_NOT_SET;
  1462. if (fIsDC && !fAlreadyTookSiteServer)
  1463. {
  1464. //
  1465. // Existing SiteServer is gone, claim it
  1466. //
  1467. if (pDsResult != NULL) {
  1468. DsFreeNameResult(pDsResult);
  1469. }
  1470. dwRet = BecomeSiteServer(&pDsResult,pADs2,&pwszDN,pwszDomain);
  1471. if (dwRet)
  1472. {
  1473. goto CleanExit;
  1474. }
  1475. else
  1476. {
  1477. fAlreadyTookSiteServer = TRUE;
  1478. if (pADs3 != NULL) {
  1479. IADs_Release(pADs3);
  1480. }
  1481. goto TryNewSiteServer;
  1482. }
  1483. } else
  1484. {
  1485. ERR(hr);
  1486. goto CleanExit;
  1487. }
  1488. }
  1489. }
  1490. //
  1491. // Allocate a return copy of the DNS-Machine-Name.
  1492. //
  1493. *ppwszSiteServer = (LPWSTR)LocalAlloc(LPTR,
  1494. SysStringByteLen(V_BSTR(&var))
  1495. + sizeof(WCHAR));
  1496. if (*ppwszSiteServer != NULL) {
  1497. lstrcpy(*ppwszSiteServer, V_BSTR(&var));
  1498. }
  1499. else {
  1500. hr = E_OUTOFMEMORY;
  1501. ERR(hr);
  1502. }
  1503. CleanExit:
  1504. // Do not free pwszDN, pwszConfigContainer or pwszBindPath.
  1505. if (pADs != NULL) {
  1506. IADs_Release(pADs);
  1507. }
  1508. if (pADs2 != NULL) {
  1509. IADs_Release(pADs2);
  1510. }
  1511. if (pADs3 != NULL) {
  1512. IADs_Release(pADs3);
  1513. }
  1514. if (pDsResult != NULL) {
  1515. DsFreeNameResult(pDsResult);
  1516. }
  1517. if (dwRet) {
  1518. // If dwRet has no facility, then make into HRESULT
  1519. if (dwRet != ERROR_SUCCESS && HRESULT_CODE(dwRet) == dwRet)
  1520. hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, dwRet);
  1521. }
  1522. VariantClear(&var);
  1523. VariantClear(&var2);
  1524. if (fCoInitialized) {
  1525. CoUninitialize();
  1526. }
  1527. return hr;
  1528. }
  1529. HRESULT
  1530. BecomeSiteServer(
  1531. DS_NAME_RESULT **ppDsResult,
  1532. IADs *pADs2,
  1533. LPWSTR *ppwszDN,
  1534. LPCWSTR pwszDomain
  1535. )
  1536. {
  1537. HANDLE hDS;
  1538. WCHAR wszName[MAX_PATH + 1];
  1539. LPWSTR rgpwszNames[2];
  1540. DWORD dwRet = 0;
  1541. VARIANT var;
  1542. DS_NAME_RESULT * pDsResult = NULL;
  1543. LPWSTR pwszDN = NULL;
  1544. HRESULT hr = S_OK;
  1545. ASSERT(ppDsResult != NULL);
  1546. ASSERT(ppwszDN != NULL);
  1547. VariantInit(&var);
  1548. //
  1549. // Bind to the DS (get a handle for use with DsCrackNames).
  1550. //
  1551. if (ConfigInfo.ComputerName == NULL) {
  1552. hr = E_UNEXPECTED;
  1553. goto CleanExit;
  1554. }
  1555. if (DsBind(NULL, (WCHAR *)pwszDomain, &hDS) == ERROR_SUCCESS) {
  1556. //
  1557. // Request the DS-DN of this server's computer object.
  1558. // Offer the domain\server$ name of this server.
  1559. //
  1560. wsprintf(wszName,
  1561. L"%ws\\%ws$",
  1562. pwszDomain,
  1563. ConfigInfo.ComputerName);
  1564. rgpwszNames[0] = wszName;
  1565. rgpwszNames[1] = NULL;
  1566. if (DsCrackNames(hDS,
  1567. DS_NAME_NO_FLAGS,
  1568. DS_UNKNOWN_NAME,
  1569. DS_FQDN_1779_NAME,
  1570. 1,
  1571. &rgpwszNames[0],
  1572. &pDsResult) == ERROR_SUCCESS) {
  1573. if (pDsResult->rItems[0].status != DS_NAME_NO_ERROR) {
  1574. if (pDsResult->rItems[0].status == DS_NAME_ERROR_RESOLVING) {
  1575. dwRet = ERROR_PATH_NOT_FOUND;
  1576. ERR(dwRet);
  1577. }
  1578. else {
  1579. ERR(pDsResult->rItems[0].status);
  1580. hr = E_FAIL;
  1581. }
  1582. goto CleanExit;
  1583. }
  1584. if (pDsResult->rItems[0].pName == NULL)
  1585. {
  1586. hr = E_FAIL;
  1587. goto CleanExit;
  1588. }
  1589. //
  1590. // Update the site server property on the license
  1591. // settings object.
  1592. //
  1593. VariantInit(&var);
  1594. V_VT(&var) = VT_BSTR;
  1595. V_BSTR(&var) = pwszDN = pDsResult->rItems[0].pName;
  1596. hr = IADs_Put(pADs2, SITE_SERVER, var);
  1597. V_VT(&var) = VT_EMPTY; // For VariantClear below
  1598. if (SUCCEEDED(hr)) {
  1599. hr = IADs_SetInfo(pADs2);
  1600. if (FAILED(hr)) {
  1601. ERR(hr);
  1602. }
  1603. }
  1604. else {
  1605. ERR(hr);
  1606. }
  1607. }
  1608. else {
  1609. dwRet = GetLastError();
  1610. ERR(dwRet);
  1611. }
  1612. DsUnBind(&hDS);
  1613. }
  1614. else {
  1615. dwRet = GetLastError();
  1616. ERR(dwRet);
  1617. }
  1618. CleanExit:
  1619. *ppDsResult = pDsResult;
  1620. *ppwszDN = pwszDN;
  1621. if (!SUCCEEDED(hr) && SUCCEEDED(dwRet))
  1622. dwRet = hr;
  1623. return dwRet;
  1624. }
  1625. #define SITE_FORMAT L"LDAP://CN=%ws,CN=%ws,%ws"
  1626. #define SITES L"sites"
  1627. #define SITE_FORMAT_SIZE CWSTR_SIZE(SITE_FORMAT)
  1628. #define SITES_SIZE CWSTR_SIZE(SITES)
  1629. HRESULT
  1630. GetSiteObject(LPCWSTR pwszSiteName,
  1631. LPCWSTR pwszConfigContainer,
  1632. IADsContainer ** ppADsContainer)
  1633. {
  1634. LPWSTR pwszSite;
  1635. HRESULT hr;
  1636. *ppADsContainer = NULL;
  1637. //
  1638. // Build the X.500 path to the Site object.
  1639. //
  1640. pwszSite = (LPWSTR)LocalAlloc(LPTR,
  1641. SITE_FORMAT_SIZE
  1642. + DWSTR_SIZE(pwszSiteName)
  1643. + SITES_SIZE
  1644. + DWSTR_SIZE(pwszConfigContainer)
  1645. + sizeof(WCHAR));
  1646. if (pwszSite == NULL) {
  1647. hr = E_OUTOFMEMORY;
  1648. ERR(hr);
  1649. goto Exit;
  1650. }
  1651. wsprintf(pwszSite,
  1652. SITE_FORMAT,
  1653. pwszSiteName,
  1654. SITES,
  1655. pwszConfigContainer);
  1656. hr = ADsGetObject(pwszSite,
  1657. &IID_IADsContainer,
  1658. (void **)ppADsContainer);
  1659. LocalFree(pwszSite);
  1660. if (FAILED(hr)) {
  1661. ERR(hr);
  1662. }
  1663. Exit:
  1664. return hr;
  1665. }
  1666. #define LICENSE_SETTINGS L"Licensing Site Settings"
  1667. #define LICENSE_SETTINGS_FORMAT L"LDAP://CN=%ws,CN=%ws,CN=%ws,%ws"
  1668. #define LICENSE_SETTINGS_SIZE CWSTR_SIZE(LICENSE_SETTINGS)
  1669. #define LICENSE_SETTINGS_FORMAT_SIZE CWSTR_SIZE(LICENSE_SETTINGS_FORMAT)
  1670. HRESULT
  1671. GetLicenseSettingsObject(LPCWSTR pwszSiteName,
  1672. LPCWSTR pwszConfigContainer,
  1673. IADs ** ppADs)
  1674. {
  1675. LPWSTR pwszLicenseSettings;
  1676. HRESULT hr;
  1677. *ppADs = NULL;
  1678. //
  1679. // Build the X.500 path to the LicenseSettings object.
  1680. //
  1681. pwszLicenseSettings = (LPWSTR)LocalAlloc(
  1682. LPTR,
  1683. LICENSE_SETTINGS_FORMAT_SIZE
  1684. + LICENSE_SETTINGS_SIZE
  1685. + DWSTR_SIZE(pwszSiteName)
  1686. + SITES_SIZE
  1687. + DWSTR_SIZE(pwszConfigContainer)
  1688. + sizeof(WCHAR));
  1689. if (pwszLicenseSettings == NULL) {
  1690. hr = E_OUTOFMEMORY;
  1691. ERR(hr);
  1692. goto Exit;
  1693. }
  1694. wsprintf(pwszLicenseSettings,
  1695. LICENSE_SETTINGS_FORMAT,
  1696. LICENSE_SETTINGS,
  1697. pwszSiteName,
  1698. SITES,
  1699. pwszConfigContainer);
  1700. hr = ADsOpenObject(pwszLicenseSettings,
  1701. NULL,
  1702. NULL,
  1703. ADS_SECURE_AUTHENTICATION | ADS_FAST_BIND,
  1704. &IID_IADs,
  1705. (void **)ppADs);
  1706. LocalFree(pwszLicenseSettings);
  1707. if (FAILED(hr)) {
  1708. ERR(hr);
  1709. }
  1710. Exit:
  1711. return hr;
  1712. }
  1713. HRESULT
  1714. CreateLicenseSettingsObject(LPCWSTR pwszSiteName,
  1715. LPCWSTR pwszConfigContainer,
  1716. IADs ** ppADs)
  1717. {
  1718. IADsContainer * pADsContainer;
  1719. IADs * pADs;
  1720. IDispatch * pDisp;
  1721. HRESULT hr;
  1722. *ppADs = NULL;
  1723. //
  1724. // Obtain the site container object.
  1725. //
  1726. hr = GetSiteObject(pwszSiteName,
  1727. pwszConfigContainer,
  1728. &pADsContainer);
  1729. if (SUCCEEDED(hr)) {
  1730. //
  1731. // Create the license settings leaf object.
  1732. //
  1733. hr = IADsContainer_Create(pADsContainer,
  1734. LICENSE_SETTINGS,
  1735. LICENSE_SETTINGS,
  1736. &pDisp);
  1737. if (SUCCEEDED(hr)) {
  1738. //
  1739. // Return an IADs to the new license settings object.
  1740. //
  1741. hr = IDispatch_QueryInterface(pDisp,
  1742. &IID_IADs,
  1743. (void **)ppADs);
  1744. if (SUCCEEDED(hr)) {
  1745. //
  1746. // Persist the change via SetInfo.
  1747. //
  1748. hr = IADs_SetInfo(*ppADs);
  1749. if (FAILED(hr)) {
  1750. ERR(hr);
  1751. IADs_Release(*ppADs);
  1752. *ppADs = NULL;
  1753. }
  1754. }
  1755. else {
  1756. ERR(hr);
  1757. }
  1758. IDispatch_Release(pDisp);
  1759. }
  1760. else {
  1761. ERR(hr);
  1762. }
  1763. IADsContainer_Release(pADsContainer);
  1764. }
  1765. else {
  1766. ERR(hr);
  1767. }
  1768. return hr;
  1769. }
  1770. #if DBG
  1771. /////////////////////////////////////////////////////////////////////////
  1772. VOID
  1773. ConfigInfoDebugDump( )
  1774. /*++
  1775. Routine Description:
  1776. Arguments:
  1777. Return Value:
  1778. --*/
  1779. {
  1780. ConfigInfoUpdate(NULL);
  1781. RtlEnterCriticalSection(&ConfigInfoLock);
  1782. dprintf(TEXT("License Logging Service - Version: 0x%lX\n"), ConfigInfo.Version);
  1783. dprintf(TEXT(" Started: %u-%u-%u @ %u:%u:%u\n"),
  1784. (UINT) ConfigInfo.Started.wDay,
  1785. (UINT) ConfigInfo.Started.wMonth,
  1786. (UINT) ConfigInfo.Started.wYear,
  1787. (UINT) ConfigInfo.Started.wHour,
  1788. (UINT) ConfigInfo.Started.wMinute,
  1789. (UINT) ConfigInfo.Started.wSecond );
  1790. dprintf(TEXT(" Replication\n"));
  1791. dprintf(TEXT(" +--------------+\n"));
  1792. if (ConfigInfo.IsMaster)
  1793. dprintf(TEXT(" Master Server\n"));
  1794. else
  1795. dprintf(TEXT(" NOT Master Server\n"));
  1796. if (ConfigInfo.Replicate)
  1797. dprintf(TEXT(" Replicates\n"));
  1798. else
  1799. dprintf(TEXT(" Does not Replicate\n"));
  1800. if (ConfigInfo.IsReplicating)
  1801. dprintf(TEXT(" Currently Replicating\n"));
  1802. else
  1803. dprintf(TEXT(" NOT Currently Replicating\n"));
  1804. dprintf(TEXT(" Replicates To: %s\n"), ConfigInfo.ReplicateTo);
  1805. dprintf(TEXT(" Enterprise Server: %s\n"), ConfigInfo.EnterpriseServer);
  1806. if (ConfigInfo.ReplicationType == REPLICATE_DELTA)
  1807. dprintf(TEXT(" Replicate Every: %lu Seconds\n"), ConfigInfo.ReplicationTime );
  1808. else
  1809. dprintf(TEXT(" Replicate @: %lu\n"), ConfigInfo.ReplicationTime );
  1810. dprintf(TEXT("\n Last Replicated: %u-%u-%u @ %u:%u:%u\n\n"),
  1811. (UINT) ConfigInfo.LastReplicated.wDay,
  1812. (UINT) ConfigInfo.LastReplicated.wMonth,
  1813. (UINT) ConfigInfo.LastReplicated.wYear,
  1814. (UINT) ConfigInfo.LastReplicated.wHour,
  1815. (UINT) ConfigInfo.LastReplicated.wMinute,
  1816. (UINT) ConfigInfo.LastReplicated.wSecond );
  1817. dprintf(TEXT(" Number Servers Currently Replicating: %lu\n"), ConfigInfo.NumReplicating);
  1818. dprintf(TEXT(" Current Backoff Time Delta: %lu\n"), ConfigInfo.BackoffTime);
  1819. dprintf(TEXT(" Current Replication Speed: %lu\n"), ConfigInfo.ReplicationSpeed);
  1820. RtlLeaveCriticalSection(&ConfigInfoLock);
  1821. } // ConfigInfoDebugDump
  1822. #endif