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.

964 lines
34 KiB

  1. //--------------------------------------------------------------------
  2. // TimeMonitor - implementation
  3. // Copyright (C) Microsoft Corporation, 1999
  4. //
  5. // Created by: Louis Thomas (louisth), 10-4-99
  6. //
  7. // Monitor time servers
  8. //
  9. #include "pch.h" // precompiled headers
  10. //####################################################################
  11. struct ComputerRecord {
  12. WCHAR * wszName;
  13. bool bIsPdc;
  14. // DNS
  15. in_addr * rgiaLocalIpAddrs;
  16. in_addr * rgiaRemoteIpAddrs;
  17. unsigned int nIpAddrs;
  18. HRESULT hrIPs;
  19. // ICMP
  20. HRESULT hrIcmp;
  21. DWORD dwIcmpDelay;
  22. // NTP
  23. NtTimeOffset toOffset;
  24. HRESULT hrNtp;
  25. NtpRefId refid;
  26. unsigned int nStratum;
  27. ComputerRecord * pcrReferer;
  28. WCHAR * wszReferer;
  29. unsigned int nTimeout;
  30. // SERVICE
  31. bool bDoingService;
  32. HRESULT hrService;
  33. DWORD dwStartType;
  34. DWORD dwCurrentState;
  35. };
  36. struct NameHolder {
  37. WCHAR * wszName;
  38. bool bIsDomain;
  39. NameHolder * pnhNext;
  40. };
  41. enum AlertType {
  42. e_MaxSpreadAlert,
  43. e_MinServersAlert,
  44. };
  45. struct AlertRecord {
  46. AlertType eType;
  47. unsigned int nParam1;
  48. DWORD dwError;
  49. AlertRecord * parNext;
  50. };
  51. struct ThreadSharedContext {
  52. ComputerRecord ** rgpcrList;
  53. unsigned int nComputers;
  54. volatile unsigned int nNextComputer;
  55. volatile unsigned int nFinishedComputers;
  56. };
  57. struct ThreadContext {
  58. HANDLE hThread;
  59. volatile unsigned int nCurRecord;
  60. ThreadSharedContext * ptsc;
  61. };
  62. MODULEPRIVATE const DWORD gc_dwTimeout=1000;
  63. //####################################################################
  64. //--------------------------------------------------------------------
  65. MODULEPRIVATE inline void ClearLine(void) {
  66. wprintf(L" \r");
  67. }
  68. //--------------------------------------------------------------------
  69. MODULEPRIVATE void FreeComputerRecord(ComputerRecord * pcr) {
  70. if (NULL==pcr) {
  71. return;
  72. }
  73. if (NULL!=pcr->wszName) {
  74. LocalFree(pcr->wszName);
  75. }
  76. if (NULL!=pcr->rgiaLocalIpAddrs) {
  77. LocalFree(pcr->rgiaLocalIpAddrs);
  78. }
  79. if (NULL!=pcr->rgiaRemoteIpAddrs) {
  80. LocalFree(pcr->rgiaRemoteIpAddrs);
  81. }
  82. if (NULL!=pcr->wszReferer) {
  83. LocalFree(pcr->wszReferer);
  84. }
  85. LocalFree(pcr);
  86. };
  87. //--------------------------------------------------------------------
  88. MODULEPRIVATE HRESULT AnalyzeComputer(ComputerRecord * pcr) {
  89. HRESULT hr;
  90. NtpPacket npPacket;
  91. NtTimeEpoch teDestinationTimestamp;
  92. DebugWPrintf1(L"%s:\n", pcr->wszName);
  93. // look up Ip addrs if necessary
  94. if (0==pcr->nIpAddrs) {
  95. hr=MyGetIpAddrs(pcr->wszName, &pcr->rgiaLocalIpAddrs, &pcr->rgiaRemoteIpAddrs, &pcr->nIpAddrs, NULL);
  96. pcr->hrIPs=hr;
  97. _JumpIfError(hr, error, "MyGetIpAddrs");
  98. }
  99. // do an ICMP ping
  100. DebugWPrintf0(L" ICMP: ");
  101. hr=MyIcmpPing(&(pcr->rgiaRemoteIpAddrs[0]), gc_dwTimeout, &pcr->dwIcmpDelay);
  102. pcr->hrIcmp=hr;
  103. // Some machines do not have ping servers, but still serve time. We can still try an NTP ping
  104. // if this fails.
  105. _IgnoreIfError(hr, "MyIcmpPing");
  106. // do an NTP ping
  107. DebugWPrintf0(L" NTP: ");
  108. hr=MyNtpPing(&(pcr->rgiaRemoteIpAddrs[0]), pcr->nTimeout, &npPacket, &teDestinationTimestamp);
  109. pcr->hrNtp=hr;
  110. _JumpIfError(hr, error, "MyNtpPing");
  111. {
  112. // calculate the offset
  113. NtTimeEpoch teOriginateTimestamp=NtTimeEpochFromNtpTimeEpoch(npPacket.teOriginateTimestamp);
  114. NtTimeEpoch teReceiveTimestamp=NtTimeEpochFromNtpTimeEpoch(npPacket.teReceiveTimestamp);
  115. NtTimeEpoch teTransmitTimestamp=NtTimeEpochFromNtpTimeEpoch(npPacket.teTransmitTimestamp);
  116. NtTimeOffset toLocalClockOffset=
  117. (teReceiveTimestamp-teOriginateTimestamp)
  118. + (teTransmitTimestamp-teDestinationTimestamp);
  119. toLocalClockOffset/=2;
  120. pcr->toOffset=toLocalClockOffset;
  121. // new referer?
  122. if (pcr->refid.value!=npPacket.refid.value || pcr->nStratum!=npPacket.nStratum) {
  123. // clean out the old values
  124. if (NULL!=pcr->wszReferer) {
  125. LocalFree(pcr->wszReferer);
  126. pcr->wszReferer=NULL;
  127. }
  128. pcr->pcrReferer=NULL;
  129. pcr->refid.value=npPacket.refid.value;
  130. pcr->nStratum=npPacket.nStratum;
  131. }
  132. }
  133. hr=S_OK;
  134. error:
  135. return hr;
  136. }
  137. //--------------------------------------------------------------------
  138. MODULEPRIVATE DWORD WINAPI AnalysisThread(void * pvContext) {
  139. ThreadContext * ptc=(ThreadContext *)pvContext;
  140. while (true) {
  141. ptc->nCurRecord=InterlockedIncrement((LONG *)&(ptc->ptsc->nNextComputer))-1;
  142. if (ptc->nCurRecord<ptc->ptsc->nComputers) {
  143. AnalyzeComputer(ptc->ptsc->rgpcrList[ptc->nCurRecord]);
  144. ptc->ptsc->nFinishedComputers++; // atomic
  145. } else {
  146. break;
  147. }
  148. }
  149. return S_OK;
  150. }
  151. //--------------------------------------------------------------------
  152. MODULEPRIVATE HRESULT ResolveReferer(ComputerRecord ** rgpcrList, unsigned int nComputers, unsigned int nCur) {
  153. HRESULT hr;
  154. unsigned int nIndex;
  155. HOSTENT * phe;
  156. int nChars;
  157. // see if it is nobody
  158. if (0==rgpcrList[nCur]->refid.value || 1>=rgpcrList[nCur]->nStratum) {
  159. // no referer
  160. } else if (NULL==rgpcrList[nCur]->wszReferer && NULL==rgpcrList[nCur]->pcrReferer) {
  161. // referer not yet determined
  162. // first, see if it is someone we are checking
  163. for (nIndex=0; nIndex<nComputers; nIndex++) {
  164. if (rgpcrList[nIndex]->nIpAddrs>0 &&
  165. rgpcrList[nIndex]->rgiaRemoteIpAddrs[0].S_un.S_addr==rgpcrList[nCur]->refid.value) {
  166. rgpcrList[nCur]->pcrReferer=rgpcrList[nIndex];
  167. }
  168. }
  169. // if we still don't know, do a reverse DNS lookup
  170. if (NULL==rgpcrList[nCur]->pcrReferer) {
  171. phe=gethostbyaddr((char *)&(rgpcrList[nCur]->refid.value), 4, AF_INET);
  172. if (NULL==phe) {
  173. // not worth aborting over.
  174. _IgnoreLastError("gethostbyaddr");
  175. } else {
  176. // save the result as a unicode string
  177. nChars=MultiByteToWideChar(CP_ACP, 0, phe->h_name, -1, NULL, 0);
  178. if (0==nChars) {
  179. _JumpLastError(hr, error, "MultiByteToWideChar(1)");
  180. }
  181. rgpcrList[nCur]->wszReferer=(WCHAR *)LocalAlloc(LPTR, sizeof(WCHAR)*nChars);
  182. _JumpIfOutOfMemory(hr, error, rgpcrList[nCur]->wszReferer);
  183. nChars=MultiByteToWideChar(CP_ACP, 0, phe->h_name, -1, rgpcrList[nCur]->wszReferer, nChars);
  184. if (0==nChars) {
  185. _JumpLastError(hr, error, "MultiByteToWideChar(2)");
  186. }
  187. } // <- end if lookup successful
  188. } // <- end if need to to reverse DNS lookup
  189. } // <- end if need to determine referer
  190. hr=S_OK;
  191. error:
  192. return hr;
  193. }
  194. //--------------------------------------------------------------------
  195. MODULEPRIVATE HRESULT ParseCmdLineForComputerNames(CmdArgs * pca, NameHolder ** ppnhList) {
  196. HRESULT hr;
  197. NameHolder * pnhTemp;
  198. WCHAR * wszComputerList;
  199. WCHAR * wszDomainName;
  200. unsigned int nComputerIndex;
  201. unsigned int nDomainIndex;
  202. // must be cleaned up
  203. NameHolder * pnhList=NULL;
  204. NameHolder ** ppnhTail=&pnhList;
  205. // check for list of computers
  206. while (FindArg(pca, L"computers", &wszComputerList, &nComputerIndex)) {
  207. // allocate
  208. pnhTemp=(NameHolder *)LocalAlloc(LPTR, sizeof(NameHolder));
  209. _JumpIfOutOfMemory(hr, error, pnhTemp);
  210. // link to tail of list
  211. *ppnhTail=pnhTemp;
  212. ppnhTail=&(pnhTemp->pnhNext);
  213. // remember the arg we found
  214. pnhTemp->bIsDomain=false;
  215. pnhTemp->wszName=wszComputerList;
  216. // mark arg as used
  217. MarkArgUsed(pca, nComputerIndex);
  218. }
  219. // check for domain
  220. while (FindArg(pca, L"domain", &wszDomainName, &nDomainIndex)) {
  221. // allocate
  222. pnhTemp=(NameHolder *)LocalAlloc(LPTR, sizeof(NameHolder));
  223. _JumpIfOutOfMemory(hr, error, pnhTemp);
  224. // link to tail of list
  225. *ppnhTail=pnhTemp;
  226. ppnhTail=&(pnhTemp->pnhNext);
  227. // remember the arg we found
  228. pnhTemp->bIsDomain=true;
  229. pnhTemp->wszName=wszDomainName;
  230. // mark arg as used
  231. MarkArgUsed(pca, nDomainIndex);
  232. }
  233. // put in the default domain if nothing specified
  234. if (NULL==pnhList) {
  235. // allocate
  236. pnhTemp=(NameHolder *)LocalAlloc(LPTR, sizeof(NameHolder));
  237. _JumpIfOutOfMemory(hr, error, pnhTemp);
  238. // link to tail of list
  239. *ppnhTail=pnhTemp;
  240. ppnhTail=&(pnhTemp->pnhNext);
  241. // add default
  242. pnhTemp->bIsDomain=true;
  243. pnhTemp->wszName=L"";
  244. }
  245. // successful
  246. hr=S_OK;
  247. *ppnhList=pnhList;
  248. pnhList=NULL;
  249. error:
  250. while (NULL!=pnhList) {
  251. pnhTemp=pnhList;
  252. pnhList=pnhTemp->pnhNext;
  253. LocalFree(pnhTemp);
  254. }
  255. return hr;
  256. }
  257. //--------------------------------------------------------------------
  258. MODULEPRIVATE HRESULT BuildComputerList(NameHolder * pnhList, ComputerRecord *** prgpcrList, unsigned int * pnComputers, unsigned int nTimeout)
  259. {
  260. HRESULT hr;
  261. unsigned int nComputers=0;
  262. unsigned int nDcs;
  263. unsigned int nPrevComputers;
  264. unsigned int nIndex;
  265. // must be cleaned up
  266. ComputerRecord ** rgpcrList=NULL;
  267. DcInfo * rgdiDcList=NULL;
  268. ComputerRecord ** rgpcrPrev=NULL;
  269. // for each set of names in our list
  270. while (NULL!=pnhList) {
  271. if (pnhList->bIsDomain) {
  272. // get the dc list
  273. if (L'\0'==pnhList->wszName[0]) {
  274. LocalizedWPrintf2(IDS_W32TM_STATUS_GETTING_DC_LIST_FOR_DEFAULT_DOMAIN, L"\r");
  275. } else {
  276. LocalizedWPrintf2(IDS_W32TM_STATUS_GETTING_DC_LIST_FOR, L" %s...\r", pnhList->wszName);
  277. }
  278. DebugWPrintf0(L"\n");
  279. hr=GetDcList(pnhList->wszName, false, &rgdiDcList, &nDcs);
  280. ClearLine();
  281. if (FAILED(hr)) {
  282. LocalizedWPrintf2(IDS_W32TM_ERRORTIMEMONITOR_GETDCLIST_FAILED, L" 0x%08X.\n", hr);
  283. }
  284. _JumpIfError(hr, error, "GetDcList");
  285. // allow for previous list
  286. nPrevComputers=nComputers;
  287. rgpcrPrev=rgpcrList;
  288. rgpcrList=NULL;
  289. nComputers+=nDcs;
  290. // allocate memory
  291. rgpcrList=(ComputerRecord **)LocalAlloc(LPTR, nComputers*sizeof(ComputerRecord *));
  292. _JumpIfOutOfMemory(hr, error, rgpcrList);
  293. for (nIndex=nPrevComputers; nIndex<nComputers; nIndex++) {
  294. rgpcrList[nIndex]=(ComputerRecord *)LocalAlloc(LPTR, sizeof(ComputerRecord));
  295. _JumpIfOutOfMemory(hr, error, rgpcrList[nIndex]);
  296. }
  297. // move the computers from the previous list
  298. if (0!=nPrevComputers) {
  299. for (nIndex=0; nIndex<nPrevComputers; nIndex++) {
  300. rgpcrList[nIndex]=rgpcrPrev[nIndex];
  301. }
  302. LocalFree(rgpcrPrev);
  303. rgpcrPrev=NULL;
  304. }
  305. // steal the data from the DC list
  306. for (nIndex=0; nIndex<nDcs; nIndex++) {
  307. rgpcrList[nIndex+nPrevComputers]->wszName=rgdiDcList[nIndex].wszDnsName;
  308. rgpcrList[nIndex+nPrevComputers]->nIpAddrs=rgdiDcList[nIndex].nIpAddresses;
  309. rgpcrList[nIndex+nPrevComputers]->rgiaLocalIpAddrs=rgdiDcList[nIndex].rgiaLocalIpAddresses;
  310. rgpcrList[nIndex+nPrevComputers]->rgiaRemoteIpAddrs=rgdiDcList[nIndex].rgiaRemoteIpAddresses;
  311. rgpcrList[nIndex+nPrevComputers]->bIsPdc=rgdiDcList[nIndex].bIsPdc;
  312. rgdiDcList[nIndex].wszDnsName=NULL;
  313. rgdiDcList[nIndex].rgiaLocalIpAddresses=NULL;
  314. rgdiDcList[nIndex].rgiaRemoteIpAddresses=NULL;
  315. }
  316. } else {
  317. // allow for previous list
  318. nPrevComputers=nComputers;
  319. rgpcrPrev=rgpcrList;
  320. rgpcrList=NULL;
  321. // count the number of computers in the computer list
  322. WCHAR * wszTravel=pnhList->wszName;
  323. nComputers=1;
  324. while (NULL!=(wszTravel=wcschr(wszTravel, L','))) {
  325. nComputers++;
  326. wszTravel++;
  327. }
  328. nComputers+=nPrevComputers;
  329. // allocate memory
  330. rgpcrList=(ComputerRecord **)LocalAlloc(LPTR, nComputers*sizeof(ComputerRecord *));
  331. _JumpIfOutOfMemory(hr, error, rgpcrList);
  332. for (nIndex=nPrevComputers; nIndex<nComputers; nIndex++) {
  333. rgpcrList[nIndex]=(ComputerRecord *)LocalAlloc(LPTR, sizeof(ComputerRecord));
  334. _JumpIfOutOfMemory(hr, error, rgpcrList[nIndex]);
  335. }
  336. // move the computers from the previous list
  337. if (0!=nPrevComputers) {
  338. for (nIndex=0; nIndex<nPrevComputers; nIndex++) {
  339. rgpcrList[nIndex]=rgpcrPrev[nIndex];
  340. }
  341. LocalFree(rgpcrPrev);
  342. rgpcrPrev=NULL;
  343. }
  344. // fill in each record
  345. wszTravel=pnhList->wszName;
  346. for (nIndex=nPrevComputers; nIndex<nComputers; nIndex++) {
  347. WCHAR * wszComma=wcschr(wszTravel, L',');
  348. if (NULL!=wszComma) {
  349. wszComma[0]=L'\0';
  350. }
  351. if (L'*'==wszTravel[0]) {
  352. rgpcrList[nIndex]->bIsPdc=true;
  353. wszTravel++;
  354. }
  355. rgpcrList[nIndex]->wszName=(WCHAR *)LocalAlloc(LPTR, sizeof(WCHAR)*(wcslen(wszTravel)+1));
  356. _JumpIfOutOfMemory(hr, error, rgpcrList[nIndex]->wszName);
  357. wcscpy(rgpcrList[nIndex]->wszName, wszTravel);
  358. wszTravel=wszComma+1;
  359. }
  360. }
  361. pnhList=pnhList->pnhNext;
  362. }
  363. // Fill in shared computer data:
  364. for (nIndex=0; nIndex<nComputers; nIndex++) {
  365. rgpcrList[nIndex]->nTimeout = nTimeout;
  366. }
  367. // success
  368. hr=S_OK;
  369. *pnComputers=nComputers;
  370. *prgpcrList=rgpcrList;
  371. rgpcrList=NULL;
  372. error:
  373. if (NULL!=rgpcrPrev) {
  374. for (nIndex=0; nIndex<nPrevComputers; nIndex++) {
  375. FreeComputerRecord(rgpcrPrev[nIndex]);
  376. }
  377. LocalFree(rgpcrPrev);
  378. }
  379. if (NULL!=rgdiDcList) {
  380. for (nIndex=0; nIndex<nDcs; nIndex++) {
  381. FreeDcInfo(&(rgdiDcList[nIndex]));
  382. }
  383. LocalFree(rgdiDcList);
  384. }
  385. if (NULL!=rgpcrList) {
  386. for (nIndex=0; nIndex<nComputers; nIndex++) {
  387. FreeComputerRecord(rgpcrList[nIndex]);
  388. }
  389. LocalFree(rgpcrList);
  390. }
  391. return hr;
  392. }
  393. //--------------------------------------------------------------------
  394. MODULEPRIVATE void FreeAlertRecords(AlertRecord * parList) {
  395. while (NULL!=parList) {
  396. AlertRecord * parTemp=parList;
  397. parList=parList->parNext;
  398. LocalFree(parTemp);
  399. }
  400. }
  401. //--------------------------------------------------------------------
  402. MODULEPRIVATE HRESULT ParseCmdLineForAlerts(CmdArgs * pca, AlertRecord ** pparList) {
  403. HRESULT hr;
  404. WCHAR * rgwszAlertParams[10];
  405. WCHAR * wszAlert;
  406. unsigned int nAlertIndex;
  407. AlertRecord * parTemp;
  408. unsigned int nIndex;
  409. // must be cleaned up
  410. AlertRecord * parList=NULL;
  411. AlertRecord ** pparTail=&parList;
  412. // check for list of computers
  413. while (FindArg(pca, L"alert", &wszAlert, &nAlertIndex)) {
  414. // parse out comma separates params
  415. nIndex=0;
  416. rgwszAlertParams[0]=wszAlert;
  417. while (nIndex<10 && NULL!=(rgwszAlertParams[nIndex]=wcschr(rgwszAlertParams[nIndex], L','))) {
  418. rgwszAlertParams[nIndex][0]=L'\0';
  419. rgwszAlertParams[nIndex]++;
  420. rgwszAlertParams[nIndex+1]=rgwszAlertParams[nIndex];
  421. nIndex++;
  422. }
  423. // is it "maxspread"
  424. if (0==_wcsicmp(wszAlert, L"maxspread")) {
  425. // quick validy check on params
  426. if (NULL==rgwszAlertParams[0] || NULL==rgwszAlertParams[1] || NULL!=rgwszAlertParams[2]) {
  427. LocalizedWPrintf2(IDS_W32TM_ERRORPARAMETER_INCORRECT_NUMBER_FOR_ALERT, L" '%s'.\n", wszAlert);
  428. hr=E_INVALIDARG;
  429. _JumpError(hr, error, "command line parsing");
  430. }
  431. // allocate
  432. parTemp=(AlertRecord *)LocalAlloc(LPTR, sizeof(AlertRecord));
  433. _JumpIfOutOfMemory(hr, error, parTemp);
  434. // link to tail of list
  435. *pparTail=parTemp;
  436. pparTail=&(parTemp->parNext);
  437. // remember the args we found
  438. parTemp->eType=e_MaxSpreadAlert;
  439. parTemp->nParam1=wcstoul(rgwszAlertParams[0],NULL,0);
  440. parTemp->dwError=wcstoul(rgwszAlertParams[1],NULL,0);
  441. // is it "minservers
  442. } else if (0==_wcsicmp(wszAlert, L"minservers")) {
  443. // quick validy check on params
  444. if (NULL==rgwszAlertParams[0] || NULL==rgwszAlertParams[1] || NULL!=rgwszAlertParams[2]) {
  445. LocalizedWPrintf2(IDS_W32TM_ERRORPARAMETER_INCORRECT_NUMBER_FOR_ALERT, L" '%s'.\n", wszAlert);
  446. hr=E_INVALIDARG;
  447. _JumpError(hr, error, "command line parsing");
  448. }
  449. // allocate
  450. parTemp=(AlertRecord *)LocalAlloc(LPTR, sizeof(AlertRecord));
  451. _JumpIfOutOfMemory(hr, error, parTemp);
  452. // link to tail of list
  453. *pparTail=parTemp;
  454. pparTail=&(parTemp->parNext);
  455. // remember the args we found
  456. parTemp->eType=e_MinServersAlert;
  457. parTemp->nParam1=wcstoul(rgwszAlertParams[0],NULL,0);
  458. parTemp->dwError=wcstoul(rgwszAlertParams[1],NULL,0);
  459. } else {
  460. wprintf(L"Alert '%s' unknown.\n", wszAlert);
  461. hr=E_INVALIDARG;
  462. _JumpError(hr, error, "command line parsing");
  463. }
  464. if (!(parTemp->dwError&0x80000000)) { // check sign bit
  465. wprintf(L"Retval not negative for alert '%s'.\n", wszAlert);
  466. hr=E_INVALIDARG;
  467. _JumpError(hr, error, "command line parsing");
  468. }
  469. // mark arg as used
  470. MarkArgUsed(pca, nAlertIndex);
  471. } // <- end FindArg loop
  472. // success
  473. hr=S_OK;
  474. *pparList=parList;
  475. parList=NULL;
  476. error:
  477. if (NULL!=parList) {
  478. FreeAlertRecords(parList);
  479. }
  480. return hr;
  481. }
  482. //--------------------------------------------------------------------
  483. MODULEPRIVATE HRESULT CheckForAlerts(ComputerRecord ** rgpcrList, unsigned int nComputers, AlertRecord * parList) {
  484. HRESULT hr;
  485. unsigned int nIndex;
  486. for (; NULL!=parList; parList=parList->parNext) {
  487. if (e_MaxSpreadAlert==parList->eType) {
  488. // see how big the spread is
  489. NtTimeOffset toMax;
  490. NtTimeOffset toMin;
  491. bool bFirst=true;
  492. for (nIndex=0; nIndex<nComputers; nIndex++) {
  493. if (S_OK==rgpcrList[nIndex]->hrIPs &&
  494. S_OK==rgpcrList[nIndex]->hrIcmp &&
  495. S_OK==rgpcrList[nIndex]->hrNtp) {
  496. if (bFirst) {
  497. toMin=toMax=rgpcrList[nIndex]->toOffset;
  498. bFirst=false;
  499. } else {
  500. if (toMin>rgpcrList[nIndex]->toOffset) {
  501. toMin=rgpcrList[nIndex]->toOffset;
  502. }
  503. if (toMax<rgpcrList[nIndex]->toOffset) {
  504. toMax=rgpcrList[nIndex]->toOffset;
  505. }
  506. }
  507. }
  508. }
  509. if (bFirst) {
  510. // no valid data!
  511. // ignore this alert
  512. continue;
  513. }
  514. unsigned __int64 qwSpread=(unsigned __int64)(toMax.qw-toMin.qw);
  515. if (qwSpread>((unsigned __int64)(parList->nParam1))*10000000) {
  516. DWORD dwFraction=(DWORD)(qwSpread%10000000);
  517. qwSpread/=10000000;
  518. wprintf(L"** ALERT: Current spread %I64u.%07us is greater than maximum\n"
  519. L" spread %us. Returning 0x%08X\n",
  520. qwSpread, dwFraction, parList->nParam1, parList->dwError);
  521. hr=parList->dwError;
  522. _JumpError(hr, error, "maxspread alert evaluation");
  523. }
  524. } else if (e_MinServersAlert==parList->eType) {
  525. // see how many usable servers there are
  526. unsigned int nServers=0;
  527. for (nIndex=0; nIndex<nComputers; nIndex++) {
  528. if (S_OK==rgpcrList[nIndex]->hrIPs &&
  529. S_OK==rgpcrList[nIndex]->hrIcmp &&
  530. S_OK==rgpcrList[nIndex]->hrNtp) {
  531. nServers++;
  532. }
  533. }
  534. if (nServers<parList->nParam1) {
  535. wprintf(L"** ALERT: Current usable servers (%u) is less than the minimum\n"
  536. L" usable servers (%u). Returning 0x%08X\n",
  537. nServers, parList->nParam1, parList->dwError);
  538. hr=parList->dwError;
  539. _JumpError(hr, error, "e_MinServersAlert alert evaluation");
  540. }
  541. } else {
  542. // unknown alert type
  543. _MyAssert(false);
  544. }
  545. } // <- end alert checking loop
  546. hr=S_OK;
  547. error:
  548. return hr;
  549. }
  550. //####################################################################
  551. //--------------------------------------------------------------------
  552. void PrintHelpTimeMonitor(void) {
  553. UINT idsText[] = {
  554. IDS_W32TM_MONITORHELP_LINE1, IDS_W32TM_MONITORHELP_LINE2,
  555. IDS_W32TM_MONITORHELP_LINE3, IDS_W32TM_MONITORHELP_LINE4,
  556. IDS_W32TM_MONITORHELP_LINE5, IDS_W32TM_MONITORHELP_LINE6,
  557. IDS_W32TM_MONITORHELP_LINE7, IDS_W32TM_MONITORHELP_LINE8,
  558. IDS_W32TM_MONITORHELP_LINE9, IDS_W32TM_MONITORHELP_LINE10,
  559. IDS_W32TM_MONITORHELP_LINE11, IDS_W32TM_MONITORHELP_LINE12,
  560. IDS_W32TM_MONITORHELP_LINE13, IDS_W32TM_MONITORHELP_LINE14,
  561. IDS_W32TM_MONITORHELP_LINE15, IDS_W32TM_MONITORHELP_LINE16,
  562. IDS_W32TM_MONITORHELP_LINE17, IDS_W32TM_MONITORHELP_LINE18,
  563. IDS_W32TM_MONITORHELP_LINE19, IDS_W32TM_MONITORHELP_LINE20,
  564. IDS_W32TM_MONITORHELP_LINE21, IDS_W32TM_MONITORHELP_LINE22,
  565. IDS_W32TM_MONITORHELP_LINE23, IDS_W32TM_MONITORHELP_LINE24,
  566. IDS_W32TM_MONITORHELP_LINE25
  567. };
  568. for (int n=0; n<ARRAYSIZE(idsText); n++) {
  569. LocalizedWPrintf(idsText[n]);
  570. }
  571. }
  572. //--------------------------------------------------------------------
  573. HRESULT TimeMonitor(CmdArgs * pca) {
  574. HRESULT hr;
  575. unsigned int nComputers;
  576. unsigned int nIndex;
  577. unsigned int nThreads;
  578. unsigned int nTimeout;
  579. ComputerRecord * pcrOffsetsFrom;
  580. WCHAR * wszNumThreads;
  581. WCHAR * wszTimeout;
  582. ThreadSharedContext tscContext;
  583. // must be cleaned up
  584. ComputerRecord ** rgpcrList=NULL;
  585. NameHolder * pnhList=NULL;
  586. AlertRecord * parList=NULL;
  587. bool bSocketLayerOpen=false;
  588. ThreadContext * rgtcThreads=NULL;
  589. // init winsock
  590. hr=OpenSocketLayer();
  591. _JumpIfError(hr, error, "OpenSocketLayer");
  592. bSocketLayerOpen=true;
  593. //
  594. // parse command line
  595. //
  596. hr=ParseCmdLineForComputerNames(pca, &pnhList);
  597. _JumpIfError(hr, error, "ParseTimeMonCmdLineForComputerNames");
  598. hr=ParseCmdLineForAlerts(pca, &parList);
  599. _JumpIfError(hr, error, "ParseCmdLineForAlerts");
  600. // get number of threads to use
  601. if (FindArg(pca, L"threads", &wszNumThreads, &nThreads)) {
  602. MarkArgUsed(pca, nThreads);
  603. nThreads=wcstoul(wszNumThreads, NULL, 0);
  604. if (nThreads<1 || nThreads>50) {
  605. LocalizedWPrintf2(IDS_W32TM_ERRORTIMEMONITOR_INVALID_NUMBER_THREADS, L" (%u).\n", nThreads);
  606. hr=E_INVALIDARG;
  607. _JumpError(hr, error, "command line parsing");
  608. }
  609. } else {
  610. nThreads=3;
  611. }
  612. // get timeout to use for NTP ping
  613. if (FindArg(pca, L"timeout", &wszTimeout, &nTimeout)) {
  614. MarkArgUsed(pca, nTimeout);
  615. nTimeout=wcstoul(wszTimeout, NULL, 0);
  616. nTimeout*=1000;
  617. } else {
  618. nTimeout = gc_dwTimeout;
  619. }
  620. // all args should be parsed
  621. if (pca->nArgs!=pca->nNextArg) {
  622. LocalizedWPrintf(IDS_W32TM_ERRORGENERAL_UNEXPECTED_PARAMS);
  623. for(; pca->nArgs!=pca->nNextArg; pca->nNextArg++) {
  624. wprintf(L" %s", pca->rgwszArgs[pca->nNextArg]);
  625. }
  626. wprintf(L"\n");
  627. hr=E_INVALIDARG;
  628. _JumpError(hr, error, "command line parsing");
  629. }
  630. //
  631. // build list of computers to analyze
  632. //
  633. hr=BuildComputerList(pnhList, &rgpcrList, &nComputers, nTimeout);
  634. _JumpIfError(hr, error, "BuildComputerList");
  635. //
  636. // Do Analysis
  637. //
  638. // analyze each of the computers
  639. if (nThreads>nComputers) {
  640. nThreads=nComputers;
  641. }
  642. if (nThreads<=1) {
  643. for (nIndex=0; nIndex<nComputers; nIndex++) {
  644. ClearLine();
  645. wprintf(L"Analyzing %s (%u of %u)...\r", rgpcrList[nIndex]->wszName, nIndex+1, nComputers);
  646. DebugWPrintf0(L"\n");
  647. hr=AnalyzeComputer(rgpcrList[nIndex]);
  648. // errors are saved in the ComputerRecord and reported later
  649. }
  650. } else {
  651. // get ready to use threads
  652. DWORD dwThreadID;
  653. tscContext.nComputers=nComputers;
  654. tscContext.rgpcrList=rgpcrList;
  655. tscContext.nNextComputer=0;
  656. tscContext.nFinishedComputers=0;
  657. rgtcThreads=(ThreadContext *)LocalAlloc(LPTR, nThreads*sizeof(ThreadContext));
  658. _JumpIfOutOfMemory(hr, error, rgtcThreads);
  659. for (nIndex=0; nIndex<nThreads; nIndex++) {
  660. rgtcThreads[nIndex].ptsc=&tscContext;
  661. rgtcThreads[nIndex].nCurRecord=-1;
  662. rgtcThreads[nIndex].hThread=CreateThread(NULL, 0, AnalysisThread, &(rgtcThreads[nIndex]), 0, &dwThreadID);
  663. if (NULL==rgtcThreads[nIndex].hThread) {
  664. _JumpLastError(hr, error, "CreateThread");
  665. }
  666. }
  667. // wait for the threads to finish
  668. while (tscContext.nFinishedComputers<nComputers) {
  669. wprintf(L"Analyzing:");
  670. for (nIndex=0; nIndex<nThreads && nIndex<16; nIndex++) {
  671. unsigned int nCurRecord=rgtcThreads[nIndex].nCurRecord;
  672. if (nCurRecord<nComputers) {
  673. wprintf(L" %2u", nCurRecord+1);
  674. } else {
  675. wprintf(L" --");
  676. }
  677. }
  678. wprintf(L" (%u of %u)\r", tscContext.nFinishedComputers, nComputers);
  679. Sleep(250);
  680. }
  681. }
  682. // resolve referers
  683. for (nIndex=0; nIndex<nComputers; nIndex++) {
  684. ClearLine();
  685. wprintf(L"resolving referer %u.%u.%u.%u (%u of %u)...\r",
  686. rgpcrList[nIndex]->refid.rgnIpAddr[0],
  687. rgpcrList[nIndex]->refid.rgnIpAddr[1],
  688. rgpcrList[nIndex]->refid.rgnIpAddr[2],
  689. rgpcrList[nIndex]->refid.rgnIpAddr[3],
  690. nIndex+1, nComputers);
  691. DebugWPrintf0(L"\n");
  692. hr=ResolveReferer(rgpcrList, nComputers, nIndex);
  693. _JumpIfError(hr, error, "ResolveReferer"); // only fatal errors are returned
  694. }
  695. ClearLine();
  696. // if there is a PDC, base the offsets from that
  697. pcrOffsetsFrom=NULL;
  698. for (nIndex=0; nIndex<nComputers; nIndex++) {
  699. if (rgpcrList[nIndex]->bIsPdc) {
  700. pcrOffsetsFrom=rgpcrList[nIndex];
  701. unsigned int nSubIndex;
  702. NtTimeOffset toPdc=rgpcrList[nIndex]->toOffset;
  703. for (nSubIndex=0; nSubIndex<nComputers; nSubIndex++) {
  704. rgpcrList[nSubIndex]->toOffset-=toPdc;
  705. }
  706. break;
  707. }
  708. }
  709. //
  710. // print the results
  711. //
  712. for (nIndex=0; nIndex<nComputers; nIndex++) {
  713. // print who we are looking at
  714. wprintf(L"%s%s", rgpcrList[nIndex]->wszName, rgpcrList[nIndex]->bIsPdc?L" *** PDC ***":L"");
  715. if (0==rgpcrList[nIndex]->nIpAddrs) {
  716. if (HRESULT_FROM_WIN32(WSAHOST_NOT_FOUND)==rgpcrList[nIndex]->hrIPs) {
  717. wprintf(L" [error WSAHOST_NOT_FOUND]\n");
  718. } else {
  719. wprintf(L" [error 0x%08X]\n", rgpcrList[nIndex]->hrIPs);
  720. }
  721. // don't bother with anything else if this doesn't work
  722. continue;
  723. } else {
  724. wprintf(L" [%u.%u.%u.%u]:\n",
  725. rgpcrList[nIndex]->rgiaRemoteIpAddrs[0].S_un.S_un_b.s_b1,
  726. rgpcrList[nIndex]->rgiaRemoteIpAddrs[0].S_un.S_un_b.s_b2,
  727. rgpcrList[nIndex]->rgiaRemoteIpAddrs[0].S_un.S_un_b.s_b3,
  728. rgpcrList[nIndex]->rgiaRemoteIpAddrs[0].S_un.S_un_b.s_b4
  729. );
  730. }
  731. // display an ICMP ping
  732. wprintf(L" ICMP: ");
  733. if (FAILED(rgpcrList[nIndex]->hrIcmp)) {
  734. if (HRESULT_FROM_WIN32(IP_REQ_TIMED_OUT)==rgpcrList[nIndex]->hrIcmp) {
  735. wprintf(L"error IP_REQ_TIMED_OUT - no response in %ums\n", gc_dwTimeout);
  736. } else {
  737. wprintf(L"error 0x%08X\n",rgpcrList[nIndex]->hrIcmp);
  738. }
  739. // NOTE: we could still have successfully done an NTP ping, even if an ICMP
  740. // ping fails, as some servers disable ICMP.
  741. } else {
  742. wprintf(L"%ums delay.\n", rgpcrList[nIndex]->dwIcmpDelay);
  743. }
  744. // display an NTP ping
  745. wprintf(L" NTP: ");
  746. if (FAILED(rgpcrList[nIndex]->hrNtp)) {
  747. if (HRESULT_FROM_WIN32(WSAECONNRESET)==rgpcrList[nIndex]->hrNtp) {
  748. wprintf(L"error WSAECONNRESET - no server listening on NTP port\n");
  749. } else if (HRESULT_FROM_WIN32(ERROR_TIMEOUT)==rgpcrList[nIndex]->hrNtp) {
  750. wprintf(L"error ERROR_TIMEOUT - no response from server in %ums\n", rgpcrList[nIndex]->nTimeout);
  751. } else {
  752. wprintf(L"error 0x%08X\n" ,rgpcrList[nIndex]->hrNtp);
  753. }
  754. } else {
  755. // display the offset
  756. DWORD dwSecFraction;
  757. NtTimeOffset toLocalClockOffset=rgpcrList[nIndex]->toOffset;
  758. WCHAR * wszSign;
  759. if (toLocalClockOffset.qw<0) {
  760. toLocalClockOffset=-toLocalClockOffset;
  761. wszSign=L"-";
  762. } else {
  763. wszSign=L"+";
  764. }
  765. dwSecFraction=(DWORD)(toLocalClockOffset.qw%10000000);
  766. toLocalClockOffset/=10000000;
  767. wprintf(L"%s%I64u.%07us offset from %s\n", wszSign, toLocalClockOffset.qw, dwSecFraction,
  768. ((NULL!=pcrOffsetsFrom)?pcrOffsetsFrom->wszName:L"local clock"));
  769. // deterine and display the referer
  770. WCHAR * wszReferer;
  771. WCHAR wszRefName[7];
  772. if (0==rgpcrList[nIndex]->refid.value) {
  773. wszReferer=L"unspecified / unsynchronized";
  774. } else if (1>=rgpcrList[nIndex]->nStratum) {
  775. wszReferer=wszRefName;
  776. wszRefName[0]=L'\'';
  777. wszRefName[1]=rgpcrList[nIndex]->refid.rgnName[0];
  778. wszRefName[2]=rgpcrList[nIndex]->refid.rgnName[1];
  779. wszRefName[3]=rgpcrList[nIndex]->refid.rgnName[2];
  780. wszRefName[4]=rgpcrList[nIndex]->refid.rgnName[3];
  781. wszRefName[5]=L'\'';
  782. wszRefName[6]=0;
  783. } else if (NULL!=rgpcrList[nIndex]->pcrReferer) {
  784. wszReferer=rgpcrList[nIndex]->pcrReferer->wszName;
  785. } else if (NULL!=rgpcrList[nIndex]->wszReferer) {
  786. wszReferer=rgpcrList[nIndex]->wszReferer;
  787. } else {
  788. wszReferer=L"(unknown)";
  789. }
  790. wprintf(L" RefID: %s [%u.%u.%u.%u]\n",
  791. wszReferer,
  792. rgpcrList[nIndex]->refid.rgnIpAddr[0],
  793. rgpcrList[nIndex]->refid.rgnIpAddr[1],
  794. rgpcrList[nIndex]->refid.rgnIpAddr[2],
  795. rgpcrList[nIndex]->refid.rgnIpAddr[3]
  796. );
  797. // BUGBUG: change not approved for beta2, checkin to beta 3:
  798. // wprintf(L" Stratum: %d\n", rgpcrList[nIndex]->nStratum);
  799. }
  800. } // <- end ComputerRecord display loop
  801. hr=CheckForAlerts(rgpcrList, nComputers, parList);
  802. _JumpIfError(hr, error, "CheckForAlerts");
  803. hr=S_OK;
  804. error:
  805. if (NULL!=rgpcrList) {
  806. for (nIndex=0; nIndex<nComputers; nIndex++) {
  807. FreeComputerRecord(rgpcrList[nIndex]);
  808. }
  809. LocalFree(rgpcrList);
  810. }
  811. while (NULL!=pnhList) {
  812. NameHolder * pnhTemp=pnhList;
  813. pnhList=pnhList->pnhNext;
  814. LocalFree(pnhTemp);
  815. }
  816. if (true==bSocketLayerOpen) {
  817. CloseSocketLayer();
  818. }
  819. if (NULL!=parList) {
  820. FreeAlertRecords(parList);
  821. }
  822. if (NULL!=rgtcThreads) {
  823. // clean up threads
  824. tscContext.nNextComputer=tscContext.nComputers; // indicate to stop
  825. for (nIndex=0; nIndex<nThreads; nIndex++) {
  826. if (NULL!=rgtcThreads[nIndex].hThread) {
  827. WaitForSingleObject(rgtcThreads[nIndex].hThread, INFINITE);
  828. CloseHandle(rgtcThreads[nIndex].hThread);
  829. }
  830. }
  831. LocalFree(rgtcThreads);
  832. }
  833. if (S_OK!=hr) {
  834. wprintf(L"Exiting with error 0x%08X\n", hr);
  835. }
  836. return hr;
  837. }