//-------------------------------------------------------------------- // TimeMonitor - implementation // Copyright (C) Microsoft Corporation, 1999 // // Created by: Louis Thomas (louisth), 10-4-99 // // Monitor time servers // #include "pch.h" // precompiled headers //#################################################################### struct ComputerRecord { WCHAR * wszName; bool bIsPdc; // DNS in_addr * rgiaLocalIpAddrs; in_addr * rgiaRemoteIpAddrs; unsigned int nIpAddrs; HRESULT hrIPs; // ICMP HRESULT hrIcmp; DWORD dwIcmpDelay; // NTP NtTimeOffset toOffset; HRESULT hrNtp; NtpRefId refid; unsigned int nStratum; ComputerRecord * pcrReferer; WCHAR * wszReferer; unsigned int nTimeout; // SERVICE bool bDoingService; HRESULT hrService; DWORD dwStartType; DWORD dwCurrentState; }; struct NameHolder { WCHAR * wszName; bool bIsDomain; NameHolder * pnhNext; }; enum AlertType { e_MaxSpreadAlert, e_MinServersAlert, }; struct AlertRecord { AlertType eType; unsigned int nParam1; DWORD dwError; AlertRecord * parNext; }; struct ThreadSharedContext { ComputerRecord ** rgpcrList; unsigned int nComputers; volatile unsigned int nNextComputer; volatile unsigned int nFinishedComputers; }; struct ThreadContext { HANDLE hThread; volatile unsigned int nCurRecord; ThreadSharedContext * ptsc; }; MODULEPRIVATE const DWORD gc_dwTimeout=1000; //#################################################################### //-------------------------------------------------------------------- MODULEPRIVATE inline void ClearLine(void) { wprintf(L" \r"); } //-------------------------------------------------------------------- MODULEPRIVATE void FreeComputerRecord(ComputerRecord * pcr) { if (NULL==pcr) { return; } if (NULL!=pcr->wszName) { LocalFree(pcr->wszName); } if (NULL!=pcr->rgiaLocalIpAddrs) { LocalFree(pcr->rgiaLocalIpAddrs); } if (NULL!=pcr->rgiaRemoteIpAddrs) { LocalFree(pcr->rgiaRemoteIpAddrs); } if (NULL!=pcr->wszReferer) { LocalFree(pcr->wszReferer); } LocalFree(pcr); }; //-------------------------------------------------------------------- MODULEPRIVATE HRESULT AnalyzeComputer(ComputerRecord * pcr) { HRESULT hr; NtpPacket npPacket; NtTimeEpoch teDestinationTimestamp; DebugWPrintf1(L"%s:\n", pcr->wszName); // look up Ip addrs if necessary if (0==pcr->nIpAddrs) { hr=MyGetIpAddrs(pcr->wszName, &pcr->rgiaLocalIpAddrs, &pcr->rgiaRemoteIpAddrs, &pcr->nIpAddrs, NULL); pcr->hrIPs=hr; _JumpIfError(hr, error, "MyGetIpAddrs"); } // do an ICMP ping DebugWPrintf0(L" ICMP: "); hr=MyIcmpPing(&(pcr->rgiaRemoteIpAddrs[0]), gc_dwTimeout, &pcr->dwIcmpDelay); pcr->hrIcmp=hr; // Some machines do not have ping servers, but still serve time. We can still try an NTP ping // if this fails. _IgnoreIfError(hr, "MyIcmpPing"); // do an NTP ping DebugWPrintf0(L" NTP: "); hr=MyNtpPing(&(pcr->rgiaRemoteIpAddrs[0]), pcr->nTimeout, &npPacket, &teDestinationTimestamp); pcr->hrNtp=hr; _JumpIfError(hr, error, "MyNtpPing"); { // calculate the offset NtTimeEpoch teOriginateTimestamp=NtTimeEpochFromNtpTimeEpoch(npPacket.teOriginateTimestamp); NtTimeEpoch teReceiveTimestamp=NtTimeEpochFromNtpTimeEpoch(npPacket.teReceiveTimestamp); NtTimeEpoch teTransmitTimestamp=NtTimeEpochFromNtpTimeEpoch(npPacket.teTransmitTimestamp); NtTimeOffset toLocalClockOffset= (teReceiveTimestamp-teOriginateTimestamp) + (teTransmitTimestamp-teDestinationTimestamp); toLocalClockOffset/=2; pcr->toOffset=toLocalClockOffset; // new referer? if (pcr->refid.value!=npPacket.refid.value || pcr->nStratum!=npPacket.nStratum) { // clean out the old values if (NULL!=pcr->wszReferer) { LocalFree(pcr->wszReferer); pcr->wszReferer=NULL; } pcr->pcrReferer=NULL; pcr->refid.value=npPacket.refid.value; pcr->nStratum=npPacket.nStratum; } } hr=S_OK; error: return hr; } //-------------------------------------------------------------------- MODULEPRIVATE DWORD WINAPI AnalysisThread(void * pvContext) { ThreadContext * ptc=(ThreadContext *)pvContext; while (true) { ptc->nCurRecord=InterlockedIncrement((LONG *)&(ptc->ptsc->nNextComputer))-1; if (ptc->nCurRecordptsc->nComputers) { AnalyzeComputer(ptc->ptsc->rgpcrList[ptc->nCurRecord]); ptc->ptsc->nFinishedComputers++; // atomic } else { break; } } return S_OK; } //-------------------------------------------------------------------- MODULEPRIVATE HRESULT ResolveReferer(ComputerRecord ** rgpcrList, unsigned int nComputers, unsigned int nCur) { HRESULT hr; unsigned int nIndex; HOSTENT * phe; int nChars; // see if it is nobody if (0==rgpcrList[nCur]->refid.value || 1>=rgpcrList[nCur]->nStratum) { // no referer } else if (NULL==rgpcrList[nCur]->wszReferer && NULL==rgpcrList[nCur]->pcrReferer) { // referer not yet determined // first, see if it is someone we are checking for (nIndex=0; nIndexnIpAddrs>0 && rgpcrList[nIndex]->rgiaRemoteIpAddrs[0].S_un.S_addr==rgpcrList[nCur]->refid.value) { rgpcrList[nCur]->pcrReferer=rgpcrList[nIndex]; } } // if we still don't know, do a reverse DNS lookup if (NULL==rgpcrList[nCur]->pcrReferer) { phe=gethostbyaddr((char *)&(rgpcrList[nCur]->refid.value), 4, AF_INET); if (NULL==phe) { // not worth aborting over. _IgnoreLastError("gethostbyaddr"); } else { // save the result as a unicode string nChars=MultiByteToWideChar(CP_ACP, 0, phe->h_name, -1, NULL, 0); if (0==nChars) { _JumpLastError(hr, error, "MultiByteToWideChar(1)"); } rgpcrList[nCur]->wszReferer=(WCHAR *)LocalAlloc(LPTR, sizeof(WCHAR)*nChars); _JumpIfOutOfMemory(hr, error, rgpcrList[nCur]->wszReferer); nChars=MultiByteToWideChar(CP_ACP, 0, phe->h_name, -1, rgpcrList[nCur]->wszReferer, nChars); if (0==nChars) { _JumpLastError(hr, error, "MultiByteToWideChar(2)"); } } // <- end if lookup successful } // <- end if need to to reverse DNS lookup } // <- end if need to determine referer hr=S_OK; error: return hr; } //-------------------------------------------------------------------- MODULEPRIVATE HRESULT ParseCmdLineForComputerNames(CmdArgs * pca, NameHolder ** ppnhList) { HRESULT hr; NameHolder * pnhTemp; WCHAR * wszComputerList; WCHAR * wszDomainName; unsigned int nComputerIndex; unsigned int nDomainIndex; // must be cleaned up NameHolder * pnhList=NULL; NameHolder ** ppnhTail=&pnhList; // check for list of computers while (FindArg(pca, L"computers", &wszComputerList, &nComputerIndex)) { // allocate pnhTemp=(NameHolder *)LocalAlloc(LPTR, sizeof(NameHolder)); _JumpIfOutOfMemory(hr, error, pnhTemp); // link to tail of list *ppnhTail=pnhTemp; ppnhTail=&(pnhTemp->pnhNext); // remember the arg we found pnhTemp->bIsDomain=false; pnhTemp->wszName=wszComputerList; // mark arg as used MarkArgUsed(pca, nComputerIndex); } // check for domain while (FindArg(pca, L"domain", &wszDomainName, &nDomainIndex)) { // allocate pnhTemp=(NameHolder *)LocalAlloc(LPTR, sizeof(NameHolder)); _JumpIfOutOfMemory(hr, error, pnhTemp); // link to tail of list *ppnhTail=pnhTemp; ppnhTail=&(pnhTemp->pnhNext); // remember the arg we found pnhTemp->bIsDomain=true; pnhTemp->wszName=wszDomainName; // mark arg as used MarkArgUsed(pca, nDomainIndex); } // put in the default domain if nothing specified if (NULL==pnhList) { // allocate pnhTemp=(NameHolder *)LocalAlloc(LPTR, sizeof(NameHolder)); _JumpIfOutOfMemory(hr, error, pnhTemp); // link to tail of list *ppnhTail=pnhTemp; ppnhTail=&(pnhTemp->pnhNext); // add default pnhTemp->bIsDomain=true; pnhTemp->wszName=L""; } // successful hr=S_OK; *ppnhList=pnhList; pnhList=NULL; error: while (NULL!=pnhList) { pnhTemp=pnhList; pnhList=pnhTemp->pnhNext; LocalFree(pnhTemp); } return hr; } //-------------------------------------------------------------------- MODULEPRIVATE HRESULT BuildComputerList(NameHolder * pnhList, ComputerRecord *** prgpcrList, unsigned int * pnComputers, unsigned int nTimeout) { HRESULT hr; unsigned int nComputers=0; unsigned int nDcs; unsigned int nPrevComputers; unsigned int nIndex; // must be cleaned up ComputerRecord ** rgpcrList=NULL; DcInfo * rgdiDcList=NULL; ComputerRecord ** rgpcrPrev=NULL; // for each set of names in our list while (NULL!=pnhList) { if (pnhList->bIsDomain) { // get the dc list if (L'\0'==pnhList->wszName[0]) { LocalizedWPrintf2(IDS_W32TM_STATUS_GETTING_DC_LIST_FOR_DEFAULT_DOMAIN, L"\r"); } else { LocalizedWPrintf2(IDS_W32TM_STATUS_GETTING_DC_LIST_FOR, L" %s...\r", pnhList->wszName); } DebugWPrintf0(L"\n"); hr=GetDcList(pnhList->wszName, false, &rgdiDcList, &nDcs); ClearLine(); if (FAILED(hr)) { LocalizedWPrintf2(IDS_W32TM_ERRORTIMEMONITOR_GETDCLIST_FAILED, L" 0x%08X.\n", hr); } _JumpIfError(hr, error, "GetDcList"); // allow for previous list nPrevComputers=nComputers; rgpcrPrev=rgpcrList; rgpcrList=NULL; nComputers+=nDcs; // allocate memory rgpcrList=(ComputerRecord **)LocalAlloc(LPTR, nComputers*sizeof(ComputerRecord *)); _JumpIfOutOfMemory(hr, error, rgpcrList); for (nIndex=nPrevComputers; nIndexwszName=rgdiDcList[nIndex].wszDnsName; rgpcrList[nIndex+nPrevComputers]->nIpAddrs=rgdiDcList[nIndex].nIpAddresses; rgpcrList[nIndex+nPrevComputers]->rgiaLocalIpAddrs=rgdiDcList[nIndex].rgiaLocalIpAddresses; rgpcrList[nIndex+nPrevComputers]->rgiaRemoteIpAddrs=rgdiDcList[nIndex].rgiaRemoteIpAddresses; rgpcrList[nIndex+nPrevComputers]->bIsPdc=rgdiDcList[nIndex].bIsPdc; rgdiDcList[nIndex].wszDnsName=NULL; rgdiDcList[nIndex].rgiaLocalIpAddresses=NULL; rgdiDcList[nIndex].rgiaRemoteIpAddresses=NULL; } } else { // allow for previous list nPrevComputers=nComputers; rgpcrPrev=rgpcrList; rgpcrList=NULL; // count the number of computers in the computer list WCHAR * wszTravel=pnhList->wszName; nComputers=1; while (NULL!=(wszTravel=wcschr(wszTravel, L','))) { nComputers++; wszTravel++; } nComputers+=nPrevComputers; // allocate memory rgpcrList=(ComputerRecord **)LocalAlloc(LPTR, nComputers*sizeof(ComputerRecord *)); _JumpIfOutOfMemory(hr, error, rgpcrList); for (nIndex=nPrevComputers; nIndexwszName; for (nIndex=nPrevComputers; nIndexbIsPdc=true; wszTravel++; } rgpcrList[nIndex]->wszName=(WCHAR *)LocalAlloc(LPTR, sizeof(WCHAR)*(wcslen(wszTravel)+1)); _JumpIfOutOfMemory(hr, error, rgpcrList[nIndex]->wszName); wcscpy(rgpcrList[nIndex]->wszName, wszTravel); wszTravel=wszComma+1; } } pnhList=pnhList->pnhNext; } // Fill in shared computer data: for (nIndex=0; nIndexnTimeout = nTimeout; } // success hr=S_OK; *pnComputers=nComputers; *prgpcrList=rgpcrList; rgpcrList=NULL; error: if (NULL!=rgpcrPrev) { for (nIndex=0; nIndexparNext; LocalFree(parTemp); } } //-------------------------------------------------------------------- MODULEPRIVATE HRESULT ParseCmdLineForAlerts(CmdArgs * pca, AlertRecord ** pparList) { HRESULT hr; WCHAR * rgwszAlertParams[10]; WCHAR * wszAlert; unsigned int nAlertIndex; AlertRecord * parTemp; unsigned int nIndex; // must be cleaned up AlertRecord * parList=NULL; AlertRecord ** pparTail=&parList; // check for list of computers while (FindArg(pca, L"alert", &wszAlert, &nAlertIndex)) { // parse out comma separates params nIndex=0; rgwszAlertParams[0]=wszAlert; while (nIndex<10 && NULL!=(rgwszAlertParams[nIndex]=wcschr(rgwszAlertParams[nIndex], L','))) { rgwszAlertParams[nIndex][0]=L'\0'; rgwszAlertParams[nIndex]++; rgwszAlertParams[nIndex+1]=rgwszAlertParams[nIndex]; nIndex++; } // is it "maxspread" if (0==_wcsicmp(wszAlert, L"maxspread")) { // quick validy check on params if (NULL==rgwszAlertParams[0] || NULL==rgwszAlertParams[1] || NULL!=rgwszAlertParams[2]) { LocalizedWPrintf2(IDS_W32TM_ERRORPARAMETER_INCORRECT_NUMBER_FOR_ALERT, L" '%s'.\n", wszAlert); hr=E_INVALIDARG; _JumpError(hr, error, "command line parsing"); } // allocate parTemp=(AlertRecord *)LocalAlloc(LPTR, sizeof(AlertRecord)); _JumpIfOutOfMemory(hr, error, parTemp); // link to tail of list *pparTail=parTemp; pparTail=&(parTemp->parNext); // remember the args we found parTemp->eType=e_MaxSpreadAlert; parTemp->nParam1=wcstoul(rgwszAlertParams[0],NULL,0); parTemp->dwError=wcstoul(rgwszAlertParams[1],NULL,0); // is it "minservers } else if (0==_wcsicmp(wszAlert, L"minservers")) { // quick validy check on params if (NULL==rgwszAlertParams[0] || NULL==rgwszAlertParams[1] || NULL!=rgwszAlertParams[2]) { LocalizedWPrintf2(IDS_W32TM_ERRORPARAMETER_INCORRECT_NUMBER_FOR_ALERT, L" '%s'.\n", wszAlert); hr=E_INVALIDARG; _JumpError(hr, error, "command line parsing"); } // allocate parTemp=(AlertRecord *)LocalAlloc(LPTR, sizeof(AlertRecord)); _JumpIfOutOfMemory(hr, error, parTemp); // link to tail of list *pparTail=parTemp; pparTail=&(parTemp->parNext); // remember the args we found parTemp->eType=e_MinServersAlert; parTemp->nParam1=wcstoul(rgwszAlertParams[0],NULL,0); parTemp->dwError=wcstoul(rgwszAlertParams[1],NULL,0); } else { wprintf(L"Alert '%s' unknown.\n", wszAlert); hr=E_INVALIDARG; _JumpError(hr, error, "command line parsing"); } if (!(parTemp->dwError&0x80000000)) { // check sign bit wprintf(L"Retval not negative for alert '%s'.\n", wszAlert); hr=E_INVALIDARG; _JumpError(hr, error, "command line parsing"); } // mark arg as used MarkArgUsed(pca, nAlertIndex); } // <- end FindArg loop // success hr=S_OK; *pparList=parList; parList=NULL; error: if (NULL!=parList) { FreeAlertRecords(parList); } return hr; } //-------------------------------------------------------------------- MODULEPRIVATE HRESULT CheckForAlerts(ComputerRecord ** rgpcrList, unsigned int nComputers, AlertRecord * parList) { HRESULT hr; unsigned int nIndex; for (; NULL!=parList; parList=parList->parNext) { if (e_MaxSpreadAlert==parList->eType) { // see how big the spread is NtTimeOffset toMax; NtTimeOffset toMin; bool bFirst=true; for (nIndex=0; nIndexhrIPs && S_OK==rgpcrList[nIndex]->hrIcmp && S_OK==rgpcrList[nIndex]->hrNtp) { if (bFirst) { toMin=toMax=rgpcrList[nIndex]->toOffset; bFirst=false; } else { if (toMin>rgpcrList[nIndex]->toOffset) { toMin=rgpcrList[nIndex]->toOffset; } if (toMaxtoOffset) { toMax=rgpcrList[nIndex]->toOffset; } } } } if (bFirst) { // no valid data! // ignore this alert continue; } unsigned __int64 qwSpread=(unsigned __int64)(toMax.qw-toMin.qw); if (qwSpread>((unsigned __int64)(parList->nParam1))*10000000) { DWORD dwFraction=(DWORD)(qwSpread%10000000); qwSpread/=10000000; wprintf(L"** ALERT: Current spread %I64u.%07us is greater than maximum\n" L" spread %us. Returning 0x%08X\n", qwSpread, dwFraction, parList->nParam1, parList->dwError); hr=parList->dwError; _JumpError(hr, error, "maxspread alert evaluation"); } } else if (e_MinServersAlert==parList->eType) { // see how many usable servers there are unsigned int nServers=0; for (nIndex=0; nIndexhrIPs && S_OK==rgpcrList[nIndex]->hrIcmp && S_OK==rgpcrList[nIndex]->hrNtp) { nServers++; } } if (nServersnParam1) { wprintf(L"** ALERT: Current usable servers (%u) is less than the minimum\n" L" usable servers (%u). Returning 0x%08X\n", nServers, parList->nParam1, parList->dwError); hr=parList->dwError; _JumpError(hr, error, "e_MinServersAlert alert evaluation"); } } else { // unknown alert type _MyAssert(false); } } // <- end alert checking loop hr=S_OK; error: return hr; } //#################################################################### //-------------------------------------------------------------------- void PrintHelpTimeMonitor(void) { UINT idsText[] = { IDS_W32TM_MONITORHELP_LINE1, IDS_W32TM_MONITORHELP_LINE2, IDS_W32TM_MONITORHELP_LINE3, IDS_W32TM_MONITORHELP_LINE4, IDS_W32TM_MONITORHELP_LINE5, IDS_W32TM_MONITORHELP_LINE6, IDS_W32TM_MONITORHELP_LINE7, IDS_W32TM_MONITORHELP_LINE8, IDS_W32TM_MONITORHELP_LINE9, IDS_W32TM_MONITORHELP_LINE10, IDS_W32TM_MONITORHELP_LINE11, IDS_W32TM_MONITORHELP_LINE12, IDS_W32TM_MONITORHELP_LINE13, IDS_W32TM_MONITORHELP_LINE14, IDS_W32TM_MONITORHELP_LINE15, IDS_W32TM_MONITORHELP_LINE16, IDS_W32TM_MONITORHELP_LINE17, IDS_W32TM_MONITORHELP_LINE18, IDS_W32TM_MONITORHELP_LINE19, IDS_W32TM_MONITORHELP_LINE20, IDS_W32TM_MONITORHELP_LINE21, IDS_W32TM_MONITORHELP_LINE22, IDS_W32TM_MONITORHELP_LINE23, IDS_W32TM_MONITORHELP_LINE24, IDS_W32TM_MONITORHELP_LINE25 }; for (int n=0; n50) { LocalizedWPrintf2(IDS_W32TM_ERRORTIMEMONITOR_INVALID_NUMBER_THREADS, L" (%u).\n", nThreads); hr=E_INVALIDARG; _JumpError(hr, error, "command line parsing"); } } else { nThreads=3; } // get timeout to use for NTP ping if (FindArg(pca, L"timeout", &wszTimeout, &nTimeout)) { MarkArgUsed(pca, nTimeout); nTimeout=wcstoul(wszTimeout, NULL, 0); nTimeout*=1000; } else { nTimeout = gc_dwTimeout; } // all args should be parsed if (pca->nArgs!=pca->nNextArg) { LocalizedWPrintf(IDS_W32TM_ERRORGENERAL_UNEXPECTED_PARAMS); for(; pca->nArgs!=pca->nNextArg; pca->nNextArg++) { wprintf(L" %s", pca->rgwszArgs[pca->nNextArg]); } wprintf(L"\n"); hr=E_INVALIDARG; _JumpError(hr, error, "command line parsing"); } // // build list of computers to analyze // hr=BuildComputerList(pnhList, &rgpcrList, &nComputers, nTimeout); _JumpIfError(hr, error, "BuildComputerList"); // // Do Analysis // // analyze each of the computers if (nThreads>nComputers) { nThreads=nComputers; } if (nThreads<=1) { for (nIndex=0; nIndexwszName, nIndex+1, nComputers); DebugWPrintf0(L"\n"); hr=AnalyzeComputer(rgpcrList[nIndex]); // errors are saved in the ComputerRecord and reported later } } else { // get ready to use threads DWORD dwThreadID; tscContext.nComputers=nComputers; tscContext.rgpcrList=rgpcrList; tscContext.nNextComputer=0; tscContext.nFinishedComputers=0; rgtcThreads=(ThreadContext *)LocalAlloc(LPTR, nThreads*sizeof(ThreadContext)); _JumpIfOutOfMemory(hr, error, rgtcThreads); for (nIndex=0; nIndexrefid.rgnIpAddr[0], rgpcrList[nIndex]->refid.rgnIpAddr[1], rgpcrList[nIndex]->refid.rgnIpAddr[2], rgpcrList[nIndex]->refid.rgnIpAddr[3], nIndex+1, nComputers); DebugWPrintf0(L"\n"); hr=ResolveReferer(rgpcrList, nComputers, nIndex); _JumpIfError(hr, error, "ResolveReferer"); // only fatal errors are returned } ClearLine(); // if there is a PDC, base the offsets from that pcrOffsetsFrom=NULL; for (nIndex=0; nIndexbIsPdc) { pcrOffsetsFrom=rgpcrList[nIndex]; unsigned int nSubIndex; NtTimeOffset toPdc=rgpcrList[nIndex]->toOffset; for (nSubIndex=0; nSubIndextoOffset-=toPdc; } break; } } // // print the results // for (nIndex=0; nIndexwszName, rgpcrList[nIndex]->bIsPdc?L" *** PDC ***":L""); if (0==rgpcrList[nIndex]->nIpAddrs) { if (HRESULT_FROM_WIN32(WSAHOST_NOT_FOUND)==rgpcrList[nIndex]->hrIPs) { wprintf(L" [error WSAHOST_NOT_FOUND]\n"); } else { wprintf(L" [error 0x%08X]\n", rgpcrList[nIndex]->hrIPs); } // don't bother with anything else if this doesn't work continue; } else { wprintf(L" [%u.%u.%u.%u]:\n", rgpcrList[nIndex]->rgiaRemoteIpAddrs[0].S_un.S_un_b.s_b1, rgpcrList[nIndex]->rgiaRemoteIpAddrs[0].S_un.S_un_b.s_b2, rgpcrList[nIndex]->rgiaRemoteIpAddrs[0].S_un.S_un_b.s_b3, rgpcrList[nIndex]->rgiaRemoteIpAddrs[0].S_un.S_un_b.s_b4 ); } // display an ICMP ping wprintf(L" ICMP: "); if (FAILED(rgpcrList[nIndex]->hrIcmp)) { if (HRESULT_FROM_WIN32(IP_REQ_TIMED_OUT)==rgpcrList[nIndex]->hrIcmp) { wprintf(L"error IP_REQ_TIMED_OUT - no response in %ums\n", gc_dwTimeout); } else { wprintf(L"error 0x%08X\n",rgpcrList[nIndex]->hrIcmp); } // NOTE: we could still have successfully done an NTP ping, even if an ICMP // ping fails, as some servers disable ICMP. } else { wprintf(L"%ums delay.\n", rgpcrList[nIndex]->dwIcmpDelay); } // display an NTP ping wprintf(L" NTP: "); if (FAILED(rgpcrList[nIndex]->hrNtp)) { if (HRESULT_FROM_WIN32(WSAECONNRESET)==rgpcrList[nIndex]->hrNtp) { wprintf(L"error WSAECONNRESET - no server listening on NTP port\n"); } else if (HRESULT_FROM_WIN32(ERROR_TIMEOUT)==rgpcrList[nIndex]->hrNtp) { wprintf(L"error ERROR_TIMEOUT - no response from server in %ums\n", rgpcrList[nIndex]->nTimeout); } else { wprintf(L"error 0x%08X\n" ,rgpcrList[nIndex]->hrNtp); } } else { // display the offset DWORD dwSecFraction; NtTimeOffset toLocalClockOffset=rgpcrList[nIndex]->toOffset; WCHAR * wszSign; if (toLocalClockOffset.qw<0) { toLocalClockOffset=-toLocalClockOffset; wszSign=L"-"; } else { wszSign=L"+"; } dwSecFraction=(DWORD)(toLocalClockOffset.qw%10000000); toLocalClockOffset/=10000000; wprintf(L"%s%I64u.%07us offset from %s\n", wszSign, toLocalClockOffset.qw, dwSecFraction, ((NULL!=pcrOffsetsFrom)?pcrOffsetsFrom->wszName:L"local clock")); // deterine and display the referer WCHAR * wszReferer; WCHAR wszRefName[7]; if (0==rgpcrList[nIndex]->refid.value) { wszReferer=L"unspecified / unsynchronized"; } else if (1>=rgpcrList[nIndex]->nStratum) { wszReferer=wszRefName; wszRefName[0]=L'\''; wszRefName[1]=rgpcrList[nIndex]->refid.rgnName[0]; wszRefName[2]=rgpcrList[nIndex]->refid.rgnName[1]; wszRefName[3]=rgpcrList[nIndex]->refid.rgnName[2]; wszRefName[4]=rgpcrList[nIndex]->refid.rgnName[3]; wszRefName[5]=L'\''; wszRefName[6]=0; } else if (NULL!=rgpcrList[nIndex]->pcrReferer) { wszReferer=rgpcrList[nIndex]->pcrReferer->wszName; } else if (NULL!=rgpcrList[nIndex]->wszReferer) { wszReferer=rgpcrList[nIndex]->wszReferer; } else { wszReferer=L"(unknown)"; } wprintf(L" RefID: %s [%u.%u.%u.%u]\n", wszReferer, rgpcrList[nIndex]->refid.rgnIpAddr[0], rgpcrList[nIndex]->refid.rgnIpAddr[1], rgpcrList[nIndex]->refid.rgnIpAddr[2], rgpcrList[nIndex]->refid.rgnIpAddr[3] ); // BUGBUG: change not approved for beta2, checkin to beta 3: // wprintf(L" Stratum: %d\n", rgpcrList[nIndex]->nStratum); } } // <- end ComputerRecord display loop hr=CheckForAlerts(rgpcrList, nComputers, parList); _JumpIfError(hr, error, "CheckForAlerts"); hr=S_OK; error: if (NULL!=rgpcrList) { for (nIndex=0; nIndexpnhNext; LocalFree(pnhTemp); } if (true==bSocketLayerOpen) { CloseSocketLayer(); } if (NULL!=parList) { FreeAlertRecords(parList); } if (NULL!=rgtcThreads) { // clean up threads tscContext.nNextComputer=tscContext.nComputers; // indicate to stop for (nIndex=0; nIndex