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.
716 lines
16 KiB
716 lines
16 KiB
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (c) Microsoft Corporation
|
|
//
|
|
// SYNOPSIS
|
|
//
|
|
// This file implements the PerfMon DLL for IAS.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <ias.h>
|
|
#include <iasinfo.h>
|
|
#include <iasutil.h>
|
|
|
|
#include <loadperf.h>
|
|
#include <newop.cpp>
|
|
|
|
#include <iasperf.h>
|
|
#include <perflib.h>
|
|
#include <resource.h>
|
|
#include <stats.h>
|
|
|
|
//////////
|
|
// Schema for the performance objects supported by this DLL.
|
|
//////////
|
|
extern PerfCollectorDef PERF_SCHEMA;
|
|
|
|
//////////
|
|
// The performance collector.
|
|
//////////
|
|
PerfCollector theCollector;
|
|
|
|
//////////
|
|
// Last start time of the server -- used to detect restarts.
|
|
//////////
|
|
LARGE_INTEGER theLastStart;
|
|
|
|
//////////
|
|
// Computes the server time counters.
|
|
//////////
|
|
PDWORD
|
|
WINAPI
|
|
ComputeServerTimes(
|
|
PDWORD dst
|
|
) throw ()
|
|
{
|
|
if (theStats->seServer.liStartTime.QuadPart)
|
|
{
|
|
LARGE_INTEGER now, elapsed;
|
|
GetSystemTimeAsFileTime((LPFILETIME)&now);
|
|
|
|
elapsed.QuadPart = now.QuadPart - theStats->seServer.liStartTime.QuadPart;
|
|
elapsed.QuadPart /= 10000000;
|
|
*dst++ = elapsed.LowPart;
|
|
|
|
elapsed.QuadPart = now.QuadPart - theStats->seServer.liResetTime.QuadPart;
|
|
elapsed.QuadPart /= 10000000;
|
|
*dst++ = elapsed.LowPart;
|
|
}
|
|
else
|
|
{
|
|
// If the start time is zero, then the server's not running.
|
|
*dst++ = 0;
|
|
*dst++ = 0;
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
|
|
//////////
|
|
// Creates instances for any newly added clients.
|
|
//////////
|
|
VOID
|
|
WINAPI
|
|
PopulateInstances(
|
|
PerfObjectType& sink
|
|
) throw ()
|
|
{
|
|
for (DWORD i = sink.size(); i < theStats->dwNumClients; ++i)
|
|
{
|
|
WCHAR buf[16];
|
|
sink.addInstance(ias_inet_htow(theStats->ceClients[i].dwAddress, buf));
|
|
}
|
|
}
|
|
|
|
//////////
|
|
// Computes derived authentication counters from raw counters.
|
|
//////////
|
|
VOID
|
|
WINAPI
|
|
DeriveAuthCounters(
|
|
PDWORD dst
|
|
) throw ()
|
|
{
|
|
// Compute packets received.
|
|
DWORD rcvd = 0;
|
|
for (DWORD i = 0; i < 6; ++i) rcvd += dst[i];
|
|
dst[9] = rcvd;
|
|
|
|
// Compute packets sent.
|
|
DWORD sent = 0;
|
|
for (DWORD j = 6; j < 9; ++j) sent += dst[j];
|
|
dst[10] = sent;
|
|
|
|
// Copy raw counters into rate counters.
|
|
memcpy(dst + 11, dst, sizeof(DWORD) * 11);
|
|
}
|
|
|
|
//////////
|
|
// Computes derived accounting counters from raw counters.
|
|
//////////
|
|
VOID
|
|
WINAPI
|
|
DeriveAcctCounters(
|
|
PDWORD dst
|
|
) throw ()
|
|
{
|
|
// Compute packets received.
|
|
DWORD rcvd = 0;
|
|
for (DWORD i = 0; i < 7; ++i) rcvd += dst[i];
|
|
dst[8] = rcvd;
|
|
|
|
// Compute packets sent.
|
|
DWORD sent = 0;
|
|
for (DWORD j = 7; j < 8; ++j) sent += dst[j];
|
|
dst[9] = sent;
|
|
|
|
// Copy raw counters into rate counters.
|
|
memcpy(dst + 10, dst, sizeof(DWORD) * 10);
|
|
}
|
|
|
|
//////////
|
|
// Callback for the authentication server object.
|
|
//////////
|
|
VOID WINAPI AuthServerDataSource(PerfObjectType& sink)
|
|
{
|
|
PDWORD p = ComputeServerTimes(sink[0].getCounters());
|
|
|
|
*p++ = theStats->seServer.dwCounters[radiusAuthServTotalInvalidRequests];
|
|
*p++ = theStats->seServer.dwCounters[radiusAuthServTotalInvalidRequests];
|
|
|
|
memset(p, 0, sizeof(DWORD) * 9);
|
|
|
|
for (DWORD i = 0; i < theStats->dwNumClients; ++i)
|
|
{
|
|
for (DWORD j = 0; j < 9; ++j)
|
|
{
|
|
p[j] += theStats->ceClients[i].dwCounters[j];
|
|
}
|
|
}
|
|
|
|
DeriveAuthCounters(p);
|
|
}
|
|
|
|
//////////
|
|
// Callback for the authentication clients object.
|
|
//////////
|
|
VOID WINAPI AuthClientDataSource(PerfObjectType& sink)
|
|
{
|
|
PopulateInstances(sink);
|
|
|
|
for (DWORD i = 0; i < theStats->dwNumClients; ++i)
|
|
{
|
|
PDWORD dst = sink[i].getCounters();
|
|
|
|
memcpy(dst, theStats->ceClients[i].dwCounters, sizeof(DWORD) * 9);
|
|
|
|
DeriveAuthCounters(dst);
|
|
}
|
|
}
|
|
|
|
//////////
|
|
// Callback for the accounting server object.
|
|
//////////
|
|
VOID WINAPI AcctServerDataSource(PerfObjectType& sink)
|
|
{
|
|
PDWORD p = ComputeServerTimes(sink[0].getCounters());
|
|
|
|
*p++ = theStats->seServer.dwCounters[radiusAccServTotalInvalidRequests];
|
|
*p++ = theStats->seServer.dwCounters[radiusAccServTotalInvalidRequests];
|
|
|
|
memset(p, 0, sizeof(DWORD) * 8);
|
|
|
|
for (DWORD i = 0; i < theStats->dwNumClients; ++i)
|
|
{
|
|
for (DWORD j = 0; j < 8; ++j)
|
|
{
|
|
p[j] += theStats->ceClients[i].dwCounters[j + 9];
|
|
}
|
|
}
|
|
|
|
DeriveAcctCounters(p);
|
|
}
|
|
|
|
//////////
|
|
// Callback for the accounting clients object.
|
|
//////////
|
|
VOID WINAPI AcctClientDataSource(PerfObjectType& sink)
|
|
{
|
|
PopulateInstances(sink);
|
|
|
|
for (DWORD i = 0; i < theStats->dwNumClients; ++i)
|
|
{
|
|
PDWORD dst = sink[i].getCounters();
|
|
|
|
memcpy(dst, theStats->ceClients[i].dwCounters + 9, sizeof(DWORD) * 8);
|
|
|
|
DeriveAcctCounters(dst);
|
|
}
|
|
}
|
|
|
|
//////////
|
|
// Creates instances for any newly added remote servers.
|
|
//////////
|
|
VOID
|
|
WINAPI
|
|
PopulateServers(
|
|
PerfObjectType& sink
|
|
) throw ()
|
|
{
|
|
for (DWORD i = sink.size(); i < theProxy->dwNumRemoteServers; ++i)
|
|
{
|
|
WCHAR buf[16];
|
|
sink.addInstance(
|
|
ias_inet_htow(theProxy->rseRemoteServers[i].dwAddress, buf)
|
|
);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
WINAPI
|
|
DeriveProxyAuthCounters(
|
|
PDWORD dst
|
|
) throw ()
|
|
{
|
|
// Compute packets received.
|
|
dst[12] = + dst[radiusAuthClientAccessAccepts]
|
|
+ dst[radiusAuthClientAccessRejects]
|
|
+ dst[radiusAuthClientAccessChallenges]
|
|
+ dst[radiusAuthClientUnknownTypes];
|
|
|
|
// Compute requests pending.
|
|
dst[13] = + dst[radiusAuthClientAccessRequests]
|
|
- dst[radiusAuthClientAccessAccepts]
|
|
- dst[radiusAuthClientAccessRejects]
|
|
- dst[radiusAuthClientAccessChallenges]
|
|
+ dst[radiusAuthClientMalformedAccessResponses]
|
|
+ dst[radiusAuthClientBadAuthenticators]
|
|
+ dst[radiusAuthClientPacketsDropped]
|
|
- dst[radiusAuthClientTimeouts];
|
|
|
|
// Copy raw counters into rate counters.
|
|
memcpy(dst + 14, dst + 2, sizeof(DWORD) * 10);
|
|
}
|
|
|
|
VOID
|
|
WINAPI
|
|
DeriveProxyAcctCounters(
|
|
PDWORD dst
|
|
) throw ()
|
|
{
|
|
// Compute packets received.
|
|
dst[10] = + dst[radiusAccClientResponses - 12]
|
|
+ dst[radiusAccClientUnknownTypes - 12];
|
|
|
|
// Compute requests pending.
|
|
dst[11] = + dst[radiusAccClientRequests - 12]
|
|
- dst[radiusAccClientResponses - 12]
|
|
+ dst[radiusAccClientMalformedResponses - 12]
|
|
+ dst[radiusAccClientBadAuthenticators - 12]
|
|
+ dst[radiusAccClientPacketsDropped - 12]
|
|
- dst[radiusAccClientTimeouts - 12];
|
|
|
|
// Copy raw counters into rate counters.
|
|
memcpy(dst + 12, dst + 2, sizeof(DWORD) * 8);
|
|
}
|
|
|
|
//////////
|
|
// Callback for the authentication proxy object.
|
|
//////////
|
|
VOID WINAPI AuthProxyDataSource(PerfObjectType& sink)
|
|
{
|
|
PDWORD p = sink[0].getCounters();
|
|
|
|
p[0] = theProxy->peProxy.dwCounters[radiusAuthClientInvalidAddresses];
|
|
p[1] = theProxy->peProxy.dwCounters[radiusAuthClientInvalidAddresses];
|
|
|
|
memset(p + 2, 0, sizeof(DWORD) * 10);
|
|
|
|
for (DWORD i = 0; i < theProxy->dwNumRemoteServers; ++i)
|
|
{
|
|
for (DWORD j = 2; j < 12; ++j)
|
|
{
|
|
p[j] += theProxy->rseRemoteServers[i].dwCounters[j];
|
|
}
|
|
}
|
|
|
|
DeriveProxyAuthCounters(p);
|
|
}
|
|
|
|
//////////
|
|
// Callback for the accounting proxy object.
|
|
//////////
|
|
VOID WINAPI AcctProxyDataSource(PerfObjectType& sink)
|
|
{
|
|
PDWORD p = sink[0].getCounters();
|
|
|
|
p[0] = theProxy->peProxy.dwCounters[radiusAccClientInvalidAddresses];
|
|
p[1] = theProxy->peProxy.dwCounters[radiusAccClientInvalidAddresses];
|
|
|
|
memset(p + 2, 0, sizeof(DWORD) * 8);
|
|
|
|
for (DWORD i = 0; i < theProxy->dwNumRemoteServers; ++i)
|
|
{
|
|
for (DWORD j = 2; j < 10; ++j)
|
|
{
|
|
p[j] += theProxy->rseRemoteServers[i].dwCounters[j + 12];
|
|
}
|
|
}
|
|
|
|
DeriveProxyAcctCounters(p);
|
|
}
|
|
|
|
//////////
|
|
// Callback for the remote authentication servers.
|
|
//////////
|
|
VOID WINAPI AuthRemoteServerDataSource(PerfObjectType& sink)
|
|
{
|
|
PopulateServers(sink);
|
|
|
|
for (DWORD i = 0; i < theProxy->dwNumRemoteServers; ++i)
|
|
{
|
|
PDWORD dst = sink[i].getCounters();
|
|
|
|
memcpy(
|
|
dst,
|
|
theProxy->rseRemoteServers[i].dwCounters,
|
|
sizeof(DWORD) * 12
|
|
);
|
|
|
|
DeriveProxyAuthCounters(dst);
|
|
}
|
|
|
|
}
|
|
|
|
//////////
|
|
// Callback for the remote accounting servers.
|
|
//////////
|
|
VOID WINAPI AcctRemoteServerDataSource(PerfObjectType& sink)
|
|
{
|
|
PopulateServers(sink);
|
|
|
|
for (DWORD i = 0; i < theProxy->dwNumRemoteServers; ++i)
|
|
{
|
|
PDWORD dst = sink[i].getCounters();
|
|
|
|
memcpy(
|
|
dst,
|
|
theProxy->rseRemoteServers[i].dwCounters + 12,
|
|
sizeof(DWORD) * 10
|
|
);
|
|
|
|
DeriveProxyAcctCounters(dst);
|
|
}
|
|
}
|
|
|
|
//////////
|
|
// Reference count for API initialization.
|
|
//////////
|
|
LONG theRefCount;
|
|
|
|
//////////
|
|
// Serialize access to PerfMon.
|
|
//////////
|
|
CRITICAL_SECTION thePerfLock;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// FUNCTION
|
|
//
|
|
// OpenPerformanceData
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
DWORD
|
|
WINAPI
|
|
OpenPerformanceData(
|
|
LPWSTR lpDeviceNames
|
|
)
|
|
{
|
|
EnterCriticalSection(&thePerfLock);
|
|
|
|
DWORD error = NO_ERROR;
|
|
|
|
// Are we already initialized?
|
|
if (theRefCount == 0)
|
|
{
|
|
if (StatsOpen())
|
|
{
|
|
try
|
|
{
|
|
theCollector.open(PERF_SCHEMA);
|
|
|
|
// Everything succeeded, so update theRefCount.
|
|
theRefCount = 1;
|
|
}
|
|
catch (LONG lErr)
|
|
{
|
|
StatsClose();
|
|
|
|
error = (DWORD)lErr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = GetLastError();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Already initialized, so just bump the ref. count.
|
|
++theRefCount;
|
|
}
|
|
|
|
LeaveCriticalSection(&thePerfLock);
|
|
|
|
return error;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// FUNCTION
|
|
//
|
|
// CollectPerformanceData
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
DWORD
|
|
WINAPI
|
|
CollectPerformanceData(
|
|
LPWSTR lpwszValue,
|
|
LPVOID* lppData,
|
|
LPDWORD lpcbBytes,
|
|
LPDWORD lpcObjectTypes
|
|
)
|
|
{
|
|
DWORD error = NO_ERROR;
|
|
|
|
EnterCriticalSection(&thePerfLock);
|
|
|
|
if (theRefCount)
|
|
{
|
|
StatsLock();
|
|
|
|
// If the server has restarted, then
|
|
if (theStats->seServer.liStartTime.QuadPart != theLastStart.QuadPart)
|
|
{
|
|
// ... clear out any old instances.
|
|
theCollector.clear();
|
|
|
|
theLastStart = theStats->seServer.liStartTime;
|
|
}
|
|
|
|
try
|
|
{
|
|
theCollector.collect(
|
|
lpwszValue,
|
|
*lppData,
|
|
*lpcbBytes,
|
|
*lpcObjectTypes
|
|
);
|
|
}
|
|
catch (LONG lErr)
|
|
{
|
|
error = (DWORD)lErr;
|
|
}
|
|
|
|
StatsUnlock();
|
|
}
|
|
else
|
|
{
|
|
error = ERROR_NOT_READY;
|
|
}
|
|
|
|
LeaveCriticalSection(&thePerfLock);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// FUNCTION
|
|
//
|
|
// ClosePerformanceData
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
DWORD
|
|
WINAPI
|
|
ClosePerformanceData()
|
|
{
|
|
EnterCriticalSection(&thePerfLock);
|
|
|
|
DWORD error = NO_ERROR;
|
|
|
|
if (--theRefCount == 0)
|
|
{
|
|
// We're the last man out, so clean-up.
|
|
|
|
StatsClose();
|
|
|
|
try
|
|
{
|
|
theCollector.close();
|
|
}
|
|
catch (LONG lErr)
|
|
{
|
|
error = (DWORD)lErr;
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection(&thePerfLock);
|
|
|
|
return error;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// FUNCTION
|
|
//
|
|
// CreateKey
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// Creates a registry key.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
LONG
|
|
WINAPI
|
|
CreateKey(
|
|
PCWSTR lpSubKey,
|
|
PHKEY phkResult
|
|
)
|
|
{
|
|
DWORD disposition;
|
|
|
|
return RegCreateKeyExW(
|
|
HKEY_LOCAL_MACHINE,
|
|
lpSubKey,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_SET_VALUE,
|
|
NULL,
|
|
phkResult,
|
|
&disposition
|
|
);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// FUNCTION
|
|
//
|
|
// SetStringValue
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// Sets a string value on a registry key.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
LONG
|
|
WINAPI
|
|
SetStringValue(
|
|
HKEY hKey,
|
|
PCWSTR lpValueName,
|
|
DWORD dwType,
|
|
PCWSTR lpData
|
|
)
|
|
{
|
|
return RegSetValueEx(
|
|
hKey,
|
|
lpValueName,
|
|
0,
|
|
dwType,
|
|
(CONST BYTE*)lpData,
|
|
sizeof(WCHAR) * (wcslen(lpData) + 1)
|
|
);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// FUNCTION
|
|
//
|
|
// DllRegisterServer
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// Adds entries to the system registry.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
const WCHAR MODULE[] =
|
|
L"%SystemRoot%\\System32\\iasperf.dll";
|
|
|
|
const WCHAR PERF_KEY[] =
|
|
L"SYSTEM\\CurrentControlSet\\Services\\IAS\\Performance";
|
|
|
|
extern "C"
|
|
STDAPI DllRegisterServer(void)
|
|
{
|
|
LONG error;
|
|
HKEY hKey;
|
|
DWORD disposition;
|
|
|
|
//////////
|
|
// Blow away the existing counters ...
|
|
//////////
|
|
|
|
UnloadPerfCounterTextStringsW(L"LODCTR " IASServiceName, TRUE);
|
|
|
|
//////////
|
|
// Update the PerfMon registry entries.
|
|
//////////
|
|
|
|
error = CreateKey(PERF_KEY, &hKey);
|
|
if (error) { return HRESULT_FROM_WIN32(error); }
|
|
SetStringValue(hKey, L"Library", REG_EXPAND_SZ, MODULE);
|
|
SetStringValue(hKey, L"Open", REG_SZ, L"OpenPerformanceData");
|
|
SetStringValue(hKey, L"Close", REG_SZ, L"ClosePerformanceData");
|
|
SetStringValue(hKey, L"Collect", REG_SZ, L"CollectPerformanceData");
|
|
RegCloseKey(hKey);
|
|
|
|
//////////
|
|
// Install the counters.
|
|
//////////
|
|
|
|
LONG ErrorCode = LoadPerfCounterTextStringsW(L"LODCTR IASPERF.INI", TRUE);
|
|
if (ErrorCode == ERROR_ALREADY_EXISTS) { ErrorCode = NO_ERROR; }
|
|
|
|
return HRESULT_FROM_WIN32(ErrorCode);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// FUNCTION
|
|
//
|
|
// DllUnregisterServer
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// Removes entries from the system registry.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
STDAPI DllUnregisterServer(void)
|
|
{
|
|
LONG error;
|
|
HKEY hKey;
|
|
|
|
/////////
|
|
// Unload the text strings.
|
|
/////////
|
|
|
|
UnloadPerfCounterTextStringsW(L"LODCTR " IASServiceName, TRUE);
|
|
|
|
//////////
|
|
// Delete the PerfMon registry key.
|
|
//////////
|
|
|
|
error = RegOpenKeyExW(
|
|
HKEY_LOCAL_MACHINE,
|
|
L"SYSTEM\\CurrentControlSet\\Services\\IAS",
|
|
0,
|
|
KEY_CREATE_SUB_KEY,
|
|
&hKey
|
|
);
|
|
if (error == NO_ERROR)
|
|
{
|
|
RegDeleteKey(hKey, L"Performance");
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// FUNCTION
|
|
//
|
|
// DllMain
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
extern "C"
|
|
BOOL
|
|
WINAPI
|
|
DllMain(
|
|
HINSTANCE hInstance,
|
|
DWORD dwReason,
|
|
LPVOID /*lpReserved*/
|
|
)
|
|
{
|
|
if (dwReason == DLL_PROCESS_ATTACH)
|
|
{
|
|
DisableThreadLibraryCalls(hInstance);
|
|
|
|
return InitializeCriticalSectionAndSpinCount(&thePerfLock, 0x80001000);
|
|
}
|
|
else if (dwReason == DLL_PROCESS_DETACH)
|
|
{
|
|
DeleteCriticalSection(&thePerfLock);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|