windows-nt/Source/XPSP1/NT/inetsrv/iis/svcs/loadbal/serv/boot.cxx
2020-09-26 16:20:57 +08:00

3777 lines
89 KiB
C++

/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
boot.cxx
Abstract:
Load balancing service
Author:
Philippe Choquier (phillich)
--*/
#define INITGUID
extern "C" {
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
}
#include <ole2.h>
#include <windows.h>
#include <winsock2.h>
#include <olectl.h>
#include <pdh.h>
#pragma warning( disable:4200 )
#include "pdhitype.h"
#include "pdhidef.h"
#include <pdhmsg.h>
#include <stdio.h>
#include <ctype.h>
#include <time.h>
#include <IisLb.h>
#include <lbxbf.hxx>
#include <lbcnfg.hxx>
#include <bootimp.hxx>
#include <IisLbs.hxx>
#include <lbmsg.h>
#include <dcomperm.h>
#include <iisnatio.h>
#include <pudebug.h>
#if DBG
#define _NOISY_DEBUG
#endif
#if defined(_NOISY_DEBUG)
#define DEBUG_BUFFER char achE[128]
#define DBG_PRINTF(a) sprintf a; OutputDebugString( achE )
#define DBG_PUTS(a) OutputDebugString(a)
#else
#define DEBUG_BUFFER
#define DBG_PRINTF(a)
#define DBG_PUTS(a)
#endif
#define RETURNCODETOHRESULT(rc) \
(((rc) < 0x10000) \
? HRESULT_FROM_WIN32(rc) \
: (rc))
#define DEFAULT_IP_PORT 80
#define COLLECT_TIMEOUT 20 // in seconds
#define SLEEP_INTERVAL 1000 // for server state query, in ms
#define MAX_SLEEP (10*1000) // driver startup time, in ms
typedef
PDH_STATUS
(__stdcall *PFN_OPEN_QUERY)(
IN LPVOID pv,
IN DWORD dwUserData,
IN HQUERY *phQuery);
//
// Globals
//
DWORD g_dwRefCount = 0;
DWORD g_dwComRegister;
DWORD g_bInitialized = FALSE;
HANDLE g_UserToKernelUpdateEvent;
BOOL g_fStopUserToKernelThread = FALSE;
HANDLE g_hUserToKernelThread = NULL;
WSADATA g_WSAData;
HMODULE g_hPdh;
PFN_OPEN_QUERY g_pfnPdhOpenQuery = NULL;
LPBYTE g_pAcl = NULL;
DWORD g_dwPdhVersion = 0;
CComputerPerfCounters g_PerfmonCounterList;
CRITICAL_SECTION g_csPerfList;
// guarantee structure integrity
CRITICAL_SECTION g_csIpListCheckpoint;
// for write access w/o modifying structure
CRITICAL_SECTION g_csIpListUpdate;
CRITICAL_SECTION g_csAcl;
CRITICAL_SECTION g_csKernelThreadUpdate;
CKernelIpMapHelper g_KernelIpMap;
// set to TRUE if load in each server info is available load
BOOL g_fNormalized = FALSE;
CIPMap g_IpMap;
GENERIC_MAPPING g_FileGenericMapping =
{
FILE_READ_DATA,
FILE_WRITE_DATA,
FILE_EXECUTE,
FILE_ALL_ACCESS
};
//
// Private prototypes
//
HRESULT InitOleSecurity(
);
BOOL
LbAccessCheck(
DWORD dwAccess
);
VOID
FlushLbAccessCheck(
);
HRESULT
SetLbDriverState(
BOOL fStart
);
BOOL
CheckQueryMachineStatus(
HQUERY hQuery
);
/////
BOOL
IncludeServerName(
LPWSTR pszPath
)
/*++
Routine Description:
Check if path includes server names
Arguments:
pszPath - UNC path
Return Value:
TRUE if path refers to server, otherwise FALSE
--*/
{
return pszPath[0] == L'\\' && pszPath[1] == L'\\';
}
CIisLb::CIisLb(
)
/*++
Routine Description:
CIisLb constructor
Arguments:
None
Return Value:
Nothing
--*/
{
m_dwRefCount = 0;
}
CIisLb::~CIisLb(
)
/*++
Routine Description:
CIisLb destructor
Arguments:
None
Return Value:
Nothing
--*/
{
}
HRESULT
CIisLb::QueryInterface(
REFIID riid,
void **ppObject
)
/*++
Routine Description:
CIisLb QueryInterface
Arguments:
riid - Interface ID
ppObject - updated with ptr to interface on success
Return Value:
status
--*/
{
if (riid==IID_IUnknown || riid==IID_IMSIisLb) {
*ppObject = (IMSIisLb *) this;
}
else {
return E_NOINTERFACE;
}
AddRef();
return NO_ERROR;
}
ULONG
CIisLb::AddRef(
)
/*++
Routine Description:
CIisLb add reference to COM interface
Arguments:
None
Return Value:
reference count
--*/
{
DWORD dwRefCount;
InterlockedIncrement((long *)&g_dwRefCount);
dwRefCount = InterlockedIncrement((long *)&m_dwRefCount);
return dwRefCount;
}
ULONG
CIisLb::Release(
)
/*++
Routine Description:
CIisLb release reference to COM interface
delete object when reference count drops to zero
Arguments:
None
Return Value:
reference count
--*/
{
DWORD dwRefCount;
InterlockedDecrement((long *)&g_dwRefCount);
dwRefCount = InterlockedDecrement((long *)&m_dwRefCount);
if (dwRefCount == 0)
{
delete this;
return 0;
}
return dwRefCount;
}
HRESULT STDMETHODCALLTYPE
CIisLb::GetIpList(
/*[in]*/ DWORD dwBufferSize,
/*[out, size_is(dwBufferSize)]*/ unsigned char *pbBuffer,
/*[out]*/ DWORD *pdwMDRequiredBufferSize )
/*++
Routine Description:
Get the IP mapping list in a serialized format
Arguments:
dwBufferSize - size of pbBuffer
pbBuffer - buffer updated with serialized IP mapping list
pdwMDRequiredBufferSize - updated with required size if dwBufferSize too small
Return Value:
COM status
will be Win32 error ERROR_INSUFFICIENT_BUFFER if dwBufferSize too small
--*/
{
XBF xbf;
BOOL fSt;
EnterCriticalSection( &g_csIpListCheckpoint );
fSt = g_IpMap.Serialize( &xbf );
LeaveCriticalSection( &g_csIpListCheckpoint );
if ( fSt )
{
*pdwMDRequiredBufferSize = xbf.GetUsed();
if ( dwBufferSize < xbf.GetUsed() )
{
return RETURNCODETOHRESULT( ERROR_INSUFFICIENT_BUFFER );
}
memcpy( pbBuffer, xbf.GetBuff(), xbf.GetUsed() );
return S_OK;
}
return E_FAIL;
}
HRESULT STDMETHODCALLTYPE
CIisLb::SetIpList(
/*[in]*/ DWORD dwBufferSize,
/*[in, size_is(dwBufferSize)]*/ unsigned char *pbBuffer )
/*++
Routine Description:
Set the IP mapping list from serialized format
Arguments:
dwBufferSize - size of pbBuffer
pbBuffer - buffer containing serialized IP mapping list
Return Value:
COM status
--*/
{
EnterCriticalSection( &g_csIpListCheckpoint );
if ( g_IpMap.Unserialize( &pbBuffer, &dwBufferSize ) &&
IpListToKernelConfig( &g_IpMap ) &&
g_IpMap.Save( HKEY_LOCAL_MACHINE, IISLOADBAL_REGISTRY_KEY, IPLIST_REGISTRY_VALUE ) )
{
LeaveCriticalSection( &g_csIpListCheckpoint );
//
// update kernel configuration
//
SetEvent( g_UserToKernelUpdateEvent );
return S_OK;
}
LeaveCriticalSection( &g_csIpListCheckpoint );
return E_FAIL;
}
HRESULT STDMETHODCALLTYPE
CIisLb::GetPerfmonCounters(
/*[in]*/ DWORD dwBufferSize,
/*[out, size_is(dwBufferSize)]*/ unsigned char *pbBuffer,
/*[out]*/ DWORD *pdwMDRequiredBufferSize )
/*++
Routine Description:
Get the perfmon counter list in a serialized format
Arguments:
dwBufferSize - size of pbBuffer
pbBuffer - buffer updated with serialized perfmon counter list
pdwMDRequiredBufferSize - updated with required size if dwBufferSize too small
Return Value:
COM status
will be Win32 error ERROR_INSUFFICIENT_BUFFER if dwBufferSize too small
--*/
{
XBF xbf;
if ( KernelConfigToPerfmonCounters( &xbf ) )
{
*pdwMDRequiredBufferSize = xbf.GetUsed();
if ( dwBufferSize < xbf.GetUsed() )
{
return RETURNCODETOHRESULT( ERROR_INSUFFICIENT_BUFFER );
}
memcpy( pbBuffer, xbf.GetBuff(), xbf.GetUsed() );
return S_OK;
}
return E_FAIL;
}
HRESULT STDMETHODCALLTYPE
CIisLb::SetPerfmonCounters(
/*[in]*/ DWORD dwBufferSize,
/*[in, size_is(dwBufferSize)]*/ unsigned char *pbBuffer )
/*++
Routine Description:
Set the perfmon counter list from serialized format
Arguments:
dwBufferSize - size of pbBuffer
pbBuffer - buffer containing serialized perfmon counter list
Return Value:
COM status
--*/
{
CComputerPerfCounters PerfmonCounters;
if ( PerfmonCounters.Unserialize( &pbBuffer, &dwBufferSize ) &&
PerfmonCountersToKernelConfig( &PerfmonCounters ) &&
PerfmonCounters.Save( HKEY_LOCAL_MACHINE, IISLOADBAL_REGISTRY_KEY, PERFMON_REGISTRY_VALUE ) )
{
return S_OK;
}
return E_FAIL;
}
HRESULT STDMETHODCALLTYPE CIisLb::GetStickyDuration(
/*[out]*/ LPDWORD pdwStickyDuration )
/*++
Routine Description:
Get the sticky binding duration ( public IP -> private IP )
Arguments:
pdwStickyDuration - updated with sticky mode duration
Return Value:
COM status
--*/
{
*pdwStickyDuration = g_KernelIpMap.GetStickyDuration();
return S_OK;
}
HRESULT STDMETHODCALLTYPE
CIisLb::SetStickyDuration(
/*[in]*/ DWORD dwStickyDuration )
/*++
Routine Description:
Set the sticky binding duration ( public IP -> private IP )
Arguments:
dwStickyDuration - sticky mode duration
Return Value:
COM status
--*/
{
XBF xbf;
g_KernelIpMap.SetStickyDuration( dwStickyDuration );
if ( Serialize( &xbf, (DWORD)dwStickyDuration ) &&
SaveBlobToReg( HKEY_LOCAL_MACHINE, IISLOADBAL_REGISTRY_KEY, STICKY_REGISTRY_VALUE, &xbf ) )
{
return S_OK;
}
return E_FAIL;
}
HRESULT STDMETHODCALLTYPE
CIisLb::GetIpEndpointList(
/*[in]*/ DWORD dwBufferSize,
/*[out, size_is(dwBufferSize)]*/ unsigned char *pbBuffer,
/*[out]*/ DWORD *pdwMDRequiredBufferSize )
/*++
Routine Description:
Get the IP endpoint list in a serialized format
Arguments:
dwBufferSize - size of pbBuffer
pbBuffer - buffer updated with serialized IP mapping list
pdwMDRequiredBufferSize - updated with required size if dwBufferSize too small
Return Value:
COM status
will be Win32 error ERROR_INSUFFICIENT_BUFFER if dwBufferSize too small
--*/
{
XBF xbf;
CIpEndpointList ieEndpointList;
if ( ieEndpointList.BuildListFromLocalhost() &&
ieEndpointList.Serialize( &xbf ) )
{
*pdwMDRequiredBufferSize = xbf.GetUsed();
if ( dwBufferSize < xbf.GetUsed() )
{
return RETURNCODETOHRESULT( ERROR_INSUFFICIENT_BUFFER );
}
memcpy( pbBuffer, xbf.GetBuff(), xbf.GetUsed() );
return S_OK;
}
return E_FAIL;
}
HRESULT STDMETHODCALLTYPE
CIisLb::SetDriverState(
/*[in]*/ DWORD dwState
)
/*++
Routine Description:
Set the driver state ( started or stopped )
Arguments:
dwState - either SERVICE_RUNNING to start or SERVICE_STOPPED to stop
Return Value:
COM status
--*/
{
HRESULT hresReturn = S_OK;
DWORD dwId;
EnterCriticalSection( &g_csKernelThreadUpdate );
if ( dwState == SERVICE_STOPPED )
{
if ( g_hUserToKernelThread )
{
g_fStopUserToKernelThread = TRUE;
SetEvent( g_UserToKernelUpdateEvent );
WaitForSingleObject( g_hUserToKernelThread, INFINITE );
CloseHandle( g_hUserToKernelThread );
g_hUserToKernelThread = NULL;
}
}
else
{
if ( g_hUserToKernelThread == NULL )
{
if ( (g_UserToKernelUpdateEvent = CreateEvent(
NULL,
FALSE,
FALSE,
NULL )) == NULL ||
(g_hUserToKernelThread = CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE)UserToKernelThread,
(LPVOID)g_UserToKernelUpdateEvent,
0,
&dwId )) == NULL )
{
hresReturn = RETURNCODETOHRESULT( GetLastError() );
}
}
}
LeaveCriticalSection( &g_csKernelThreadUpdate );
return hresReturn;
}
CIisLbSrvFactory::CIisLbSrvFactory(
)
/*++
Routine Description:
CIisLbSrvFactory constructor
Arguments:
None
Return Value:
Nothing
--*/
{
m_dwRefCount=0;
}
CIisLbSrvFactory::~CIisLbSrvFactory(
)
/*++
Routine Description:
CIisLbSrvFactory destructor
Arguments:
None
Return Value:
Nothing
--*/
{
}
HRESULT
CIisLbSrvFactory::CreateInstance(
IUnknown *pUnkOuter,
REFIID riid,
void ** ppObject)
/*++
Routine Description:
Create instances of CIisLb
Arguments:
pUnkOuter - controling unknown
riid - Interface ID in
ppObject - updated with ptr to interface on success
Return Value:
COM status
--*/
{
CIisLb* pBl;
if (pUnkOuter != NULL) {
return CLASS_E_NOAGGREGATION;
}
if ( !LbAccessCheck( FILE_READ_DATA ) ) {
return RETURNCODETOHRESULT( GetLastError() );
}
FlushLbAccessCheck();
if ( (pBl = new CIisLb) == NULL ) {
return E_NOINTERFACE;
}
if (FAILED(pBl->QueryInterface(riid, ppObject))) {
delete pBl;
return E_NOINTERFACE;
}
return NO_ERROR;
}
HRESULT
CIisLbSrvFactory::LockServer(
BOOL fLock
)
/*++
Routine Description:
Lock COM DLL
Arguments:
fLock - TRUE to lock, FALSE to unlock
Return Value:
COM status
--*/
{
if (fLock) {
InterlockedIncrement((long *)&g_dwRefCount);
}
else {
InterlockedDecrement((long *)&g_dwRefCount);
}
return NO_ERROR;
}
HRESULT
CIisLbSrvFactory::QueryInterface(
REFIID riid,
void **ppObject
)
/*++
Routine Description:
CIisLbSrvFactory QueryInterface
Arguments:
riid - Interface ID
ppObject - updated with ptr to interface on success
Return Value:
status
--*/
{
if (riid==IID_IUnknown || riid == IID_IClassFactory) {
*ppObject = (IClassFactory *) this;
}
else {
return E_NOINTERFACE;
}
AddRef();
return NO_ERROR;
}
ULONG
CIisLbSrvFactory::AddRef(
)
/*++
Routine Description:
CIisLbSrvFactory add reference to COM interface
Arguments:
None
Return Value:
reference count
--*/
{
DWORD dwRefCount;
InterlockedIncrement((long *)&g_dwRefCount);
dwRefCount = InterlockedIncrement((long *)&m_dwRefCount);
return dwRefCount;
}
ULONG
CIisLbSrvFactory::Release(
)
/*++
Routine Description:
CIisLbSrvFactory release reference to COM interface
delete object when reference count drops to zero
Arguments:
None
Return Value:
reference count
--*/
{
DWORD dwRefCount;
InterlockedDecrement((long *)&g_dwRefCount);
dwRefCount = InterlockedDecrement((long *)&m_dwRefCount);
if (dwRefCount == 0)
{
delete this;
}
return dwRefCount;
}
STDAPI BootDllRegisterServer(
)
/*++
Routine Description:
Register COM objects
Arguments:
None
Return Value:
COM status
--*/
{
HKEY hKeyCLSID, hKeyInproc32;
HKEY hKeyIF, hKeyStub32;
HKEY hKeyAppExe, hKeyAppID, hKeyTemp;
DWORD dwDisposition;
//
// register AppExe
//
HRESULT hr;
//
// register CLSID
//
if (RegCreateKeyEx(HKEY_CLASSES_ROOT,
TEXT("CLSID\\{a9da4430-65c5-11d1-a700-00a0c922e752}"),
NULL, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
&hKeyCLSID, &dwDisposition)!=ERROR_SUCCESS) {
return E_UNEXPECTED;
}
if (RegSetValueEx(hKeyCLSID, TEXT(""), NULL, REG_SZ, (BYTE*) TEXT("IIS load balancing configuration"), sizeof(TEXT("load balancing configuration")))!=ERROR_SUCCESS) {
RegCloseKey(hKeyCLSID);
return E_UNEXPECTED;
}
if (RegSetValueEx(hKeyCLSID, TEXT("AppID"), NULL, REG_SZ, (BYTE*) TEXT("{a9da4430-65c5-11d1-a700-00a0c922e752}"), sizeof(TEXT("{a9da4430-65c5-11d1-a700-00a0c922e752}")))!=ERROR_SUCCESS) {
RegCloseKey(hKeyCLSID);
return E_UNEXPECTED;
}
{
if (RegSetValueEx(hKeyCLSID, TEXT("LocalService"), NULL, REG_SZ, (BYTE*) TEXT(SZSERVICENAME), sizeof(TEXT(SZSERVICENAME)))!=ERROR_SUCCESS) {
RegCloseKey(hKeyCLSID);
return E_UNEXPECTED;
}
}
RegCloseKey(hKeyCLSID);
//
// AppID
//
//
// register CLSID
//
if (RegCreateKeyEx(HKEY_CLASSES_ROOT,
TEXT("AppID\\{a9da4430-65c5-11d1-a700-00a0c922e752}"),
NULL, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
&hKeyCLSID, &dwDisposition)!=ERROR_SUCCESS) {
return E_UNEXPECTED;
}
if (RegSetValueEx(hKeyCLSID, TEXT(""), NULL, REG_SZ, (BYTE*) TEXT("IIS load balancing configuration"), sizeof(TEXT("IIS load balancing configuration")))!=ERROR_SUCCESS) {
RegCloseKey(hKeyCLSID);
return E_UNEXPECTED;
}
{
if (RegSetValueEx(hKeyCLSID, TEXT("LocalService"), NULL, REG_SZ, (BYTE*) TEXT(SZSERVICENAME), sizeof(TEXT(SZSERVICENAME)))!=ERROR_SUCCESS) {
RegCloseKey(hKeyCLSID);
return E_UNEXPECTED;
}
}
RegCloseKey(hKeyCLSID);
//
// Assign ACL to CLSID ( a la dcomcnfg ) granting access only to admins, current user
// and system
//
ChangeAppIDAccessACL( "{a9da4430-65c5-11d1-a700-00a0c922e752}",
"administrators",
TRUE,
TRUE );
//
// Main Interface
//
if (RegCreateKeyEx(HKEY_CLASSES_ROOT,
TEXT("CLSID\\{996d0030-65c5-11d1-a700-00a0c922e752}"),
NULL, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
&hKeyCLSID, &dwDisposition)!=ERROR_SUCCESS) {
return E_UNEXPECTED;
}
if (RegSetValueEx(hKeyCLSID, TEXT(""), NULL, REG_SZ, (BYTE*) TEXT(SZSERVICENAME), sizeof(TEXT(SZSERVICENAME)))!=ERROR_SUCCESS) {
RegCloseKey(hKeyCLSID);
return E_UNEXPECTED;
}
if (RegCreateKeyEx(hKeyCLSID,
"InprocServer32",
NULL, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
&hKeyInproc32, &dwDisposition)!=ERROR_SUCCESS) {
RegCloseKey(hKeyCLSID);
return E_UNEXPECTED;
}
if (RegSetValueEx(hKeyInproc32, TEXT(""), NULL, REG_SZ, (BYTE*) "IISLBP.DLL", sizeof(TEXT("IISLBP.DLL")))!=ERROR_SUCCESS) {
RegCloseKey(hKeyInproc32);
RegCloseKey(hKeyCLSID);
return E_UNEXPECTED;
}
if (RegSetValueEx(hKeyInproc32, TEXT("ThreadingModel"), NULL, REG_SZ, (BYTE*) "Both", sizeof("Both")-1 )!=ERROR_SUCCESS) {
RegCloseKey(hKeyInproc32);
RegCloseKey(hKeyCLSID);
return E_UNEXPECTED;
}
RegCloseKey(hKeyInproc32);
RegCloseKey(hKeyCLSID);
//
// register Interfaces
//
//
// Main Interface
//
if (RegCreateKeyEx(HKEY_CLASSES_ROOT,
TEXT("Interface\\{996d0030-65c5-11d1-a700-00a0c922e752}"),
NULL, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
&hKeyIF, &dwDisposition)!=ERROR_SUCCESS) {
return E_UNEXPECTED;
}
if (RegSetValueEx(hKeyIF, TEXT(""), NULL, REG_SZ, (BYTE*) TEXT(SZSERVICENAME), sizeof(TEXT(SZSERVICENAME)))!=ERROR_SUCCESS) {
RegCloseKey(hKeyIF);
return E_UNEXPECTED;
}
if (RegCreateKeyEx(hKeyIF,
"ProxyStubClsid32",
NULL, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
&hKeyStub32, &dwDisposition)!=ERROR_SUCCESS) {
RegCloseKey(hKeyIF);
return E_UNEXPECTED;
}
if (RegSetValueEx(hKeyStub32, TEXT(""), NULL, REG_SZ, (BYTE*)"{996d0030-65c5-11d1-a700-00a0c922e752}", sizeof("{996d0030-65c5-11d1-a700-00a0c922e752}") )!=ERROR_SUCCESS) {
RegCloseKey(hKeyStub32);
RegCloseKey(hKeyIF);
return E_UNEXPECTED;
}
RegCloseKey(hKeyStub32);
RegCloseKey(hKeyIF);
return S_OK;
}
STDAPI
BootDllUnregisterServer(
void
)
/*++
Routine Description:
Unregister COM objects
Arguments:
None
Return Value:
COM status
--*/
{
//
// register AppID
//
RegDeleteKey(HKEY_CLASSES_ROOT, TEXT("AppID\\{a9da4430-65c5-11d1-a700-00a0c922e752}"));
//
// register CLSID
//
RegDeleteKey(HKEY_CLASSES_ROOT, TEXT("CLSID\\{a9da4430-65c5-11d1-a700-00a0c922e752}"));
//
// Main Interfaces
//
RegDeleteKey(HKEY_CLASSES_ROOT, TEXT("CLSID\\{996d0030-65c5-11d1-a700-00a0c922e752}\\InprocServer32"));
RegDeleteKey(HKEY_CLASSES_ROOT, TEXT("CLSID\\{996d0030-65c5-11d1-a700-00a0c922e752}"));
//
// deregister Interfaces
//
RegDeleteKey(HKEY_CLASSES_ROOT, TEXT("Interface\\{996d0030-65c5-11d1-a700-00a0c922e752}\\ProxyStubClsid32"));
RegDeleteKey(HKEY_CLASSES_ROOT, TEXT("Interface\\{996d0030-65c5-11d1-a700-00a0c922e752}"));
return S_OK;
}
BOOL
InitGlobals(
)
/*++
Routine Description:
Initialize globals variables
Arguments:
None
Return Value:
TRUE if success, otherwise FALSE
--*/
{
INITIALIZE_CRITICAL_SECTION( &g_csPerfList );
INITIALIZE_CRITICAL_SECTION( &g_csIpListCheckpoint );
INITIALIZE_CRITICAL_SECTION( &g_csIpListUpdate );
INITIALIZE_CRITICAL_SECTION( &g_csAcl );
INITIALIZE_CRITICAL_SECTION( &g_csKernelThreadUpdate );
//
// NT4 uses PdhOpenQuery, NT5 has Ansi/Unicode versions only
//
if ( (g_hPdh = LoadLibrary( "pdh.dll" )) )
{
g_pfnPdhOpenQuery = (PFN_OPEN_QUERY)GetProcAddress( g_hPdh,
"PdhOpenQuery" );
if ( g_pfnPdhOpenQuery == NULL )
{
g_pfnPdhOpenQuery = (PFN_OPEN_QUERY)GetProcAddress( g_hPdh,
"PdhOpenQueryA" );
if ( g_pfnPdhOpenQuery == NULL )
{
FreeLibrary( g_hPdh );
g_hPdh = NULL;
}
}
}
if ( g_pfnPdhOpenQuery == NULL )
{
WCHAR achErr[32];
LPCWSTR pA[1];
pA[0] = achErr;
_itow( GetLastError(), achErr, 10 );
ReportIisLbEvent( EVENTLOG_ERROR_TYPE,
IISLB_EVENT_PDH_ACCESS_ERROR,
1,
pA );
}
if ( g_pfnPdhOpenQuery == NULL ||
!g_KernelIpMap.Init() )
{
DeleteCriticalSection( &g_csPerfList );
DeleteCriticalSection( &g_csIpListCheckpoint );
DeleteCriticalSection( &g_csIpListUpdate );
DeleteCriticalSection( &g_csAcl );
DeleteCriticalSection( &g_csKernelThreadUpdate );
return FALSE;
}
PdhGetDllVersion( &g_dwPdhVersion );
return TRUE;
}
BOOL
TerminateGlobals(
)
/*++
Routine Description:
Terminate globals variables
Arguments:
None
Return Value:
TRUE if success, otherwise FALSE
--*/
{
//
// stop perfmon threads
//
EnterCriticalSection( &g_csIpListCheckpoint );
while ( g_KernelIpMap.ServerCount() )
{
//
// do no wait for perfmon thread to stop : timeout is 0
//
g_KernelIpMap.StopServerThread( 0, 0 );
EnterCriticalSection( &g_csIpListUpdate );
g_KernelIpMap.RemoveServer( 0 );
LeaveCriticalSection( &g_csIpListUpdate );
}
LeaveCriticalSection( &g_csIpListCheckpoint );
if ( g_pAcl != NULL )
{
LocalFree( g_pAcl );
g_pAcl = NULL;
}
DeleteCriticalSection( &g_csAcl );
DeleteCriticalSection( &g_csPerfList );
DeleteCriticalSection( &g_csIpListCheckpoint );
DeleteCriticalSection( &g_csIpListUpdate );
DeleteCriticalSection( &g_csKernelThreadUpdate );
FreeLibrary( g_hPdh );
return TRUE;
}
BOOL
InitComLb(
)
/*++
Routine Description:
Init COM interface support & user mode <> kernel communication
Arguments:
None
Return Value:
TRUE if success, otherwise FALSE
--*/
{
HRESULT hr;
BOOL bReturn = TRUE;
DWORD dwId;
DEBUG_BUFFER;
if ( WSAStartup( MAKEWORD( 2, 0), &g_WSAData ) != 0 )
{
DBG_PUTS( "can't init WSA\n" );
return FALSE;
}
CoInitializeEx( NULL, COINIT_MULTITHREADED );
InitOleSecurity();
DBG_PUTS("InitComLb");
{
CIisLbSrvFactory *pADMClassFactory = new CIisLbSrvFactory;
if ( pADMClassFactory == NULL )
{
DBG_PUTS("InitComLb:OOM");
CoUninitialize();
bReturn = FALSE;
}
else {
if ( !InitGlobals() )
{
DBG_PUTS("InitComLb:Init");
CoUninitialize();
bReturn = FALSE;
}
else
{
//
// register the class-object with OLE
//
hr = CoRegisterClassObject(CLSID_MSIisLb, pADMClassFactory,
CLSCTX_SERVER, REGCLS_MULTIPLEUSE, &g_dwComRegister);
if (FAILED(hr))
{
DBG_PRINTF(( achE, "InitComLb:CoRegisterClassObject %08x %d", hr, hr ));
bReturn = FALSE;
CoUninitialize();
delete pADMClassFactory;
TerminateGlobals();
}
else
{
//
// register COM object
//
if ( (g_UserToKernelUpdateEvent = CreateEvent(
NULL,
FALSE,
FALSE,
NULL )) == NULL ||
(g_hUserToKernelThread = CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE)UserToKernelThread,
(LPVOID)g_UserToKernelUpdateEvent,
0,
&dwId )) == NULL )
{
DBG_PUTS("InitComLb:CreateEvent/CreateThread");
CoRevokeClassObject( g_dwComRegister );
CoUninitialize();
TerminateGlobals();
bReturn = FALSE;
}
else
{
g_bInitialized = TRUE;
}
}
}
}
}
return bReturn;
}
BOOL
TerminateComLb(
)
/*++
Routine Description:
Terminate COM interface support & user mode <> kernel communication
Arguments:
None
Return Value:
TRUE if success, otherwise FALSE
--*/
{
if (g_bInitialized)
{
g_bInitialized = FALSE;
CoRevokeClassObject( g_dwComRegister );
// Stop user<>kernel mode thread
EnterCriticalSection( &g_csKernelThreadUpdate );
if ( g_hUserToKernelThread )
{
g_fStopUserToKernelThread = TRUE;
SetEvent( g_UserToKernelUpdateEvent );
WaitForSingleObject( g_hUserToKernelThread, INFINITE );
CloseHandle( g_hUserToKernelThread );
g_hUserToKernelThread = NULL;
}
LeaveCriticalSection( &g_csKernelThreadUpdate );
TerminateGlobals();
CoUninitialize();
WSACleanup();
}
return TRUE;
}
BOOL
KernelConfigToIpList(
XBF* pxbf
)
/*++
Routine Description:
Retrieve serialized IP mapping configuration from kernel mode configuration
Arguments:
pxbf - ptr to buffer updated with serialized configuration
Return Value:
TRUE if success, otherwise FALSE
--*/
{
CIPMap IpMap;
CKernelIpEndpointEx*pIp;
UINT iPublic;
UINT iServ;
UINT iRef;
WCHAR achAddr[128];
EnterCriticalSection( &g_csIpListCheckpoint );
for ( iServ = 0 ;
iServ < g_KernelIpMap.ServerCount() ;
++iServ )
{
IpMap.AddComputer( g_KernelIpMap.GetServerName(iServ) );
}
// update public IP addresses list
for ( iPublic = 0 ;
iPublic < g_KernelIpMap.PublicIpCount() ;
++iPublic )
{
pIp = g_KernelIpMap.GetPublicIpPtr( iPublic );
if ( !g_KernelIpMap.IpEndpointToString( (CKernelIpEndpoint*)pIp, achAddr, sizeof(achAddr) ) ||
!IpMap.AddIpPublic( achAddr, L"", pIp->m_dwSticky, 0 ) )
{
LeaveCriticalSection( &g_csIpListCheckpoint );
return FALSE;
}
}
// update private IP address for each ( server, public IP ) pairs
for ( iServ = 0 ;
iServ < g_KernelIpMap.ServerCount() ;
++iServ )
{
for ( iPublic = 0 ;
iPublic < g_KernelIpMap.PublicIpCount() ;
++iPublic )
{
if ( (iRef = *g_KernelIpMap.GetPrivateIpRef( g_KernelIpMap.GetServerPtr( iServ ),
iPublic )) == (UINT)-1 )
{
if ( !IpMap.SetIpPrivate( iServ, iPublic, L"", L"" ) )
{
LeaveCriticalSection( &g_csIpListCheckpoint );
return FALSE;
}
}
else if ( !g_KernelIpMap.IpEndpointToString(
(CKernelIpEndpoint*)g_KernelIpMap.GetPrivateIpEndpoint( iRef ),
achAddr,
sizeof(achAddr) ) ||
!IpMap.SetIpPrivate( iServ, iPublic, achAddr, L"" ) )
{
LeaveCriticalSection( &g_csIpListCheckpoint );
return FALSE;
}
}
}
LeaveCriticalSection( &g_csIpListCheckpoint );
return IpMap.Serialize( pxbf );
}
BOOL
IpListToKernelConfig(
CIPMap* pIpMap
)
/*++
Routine Description:
Set kernel mode configuration from serialized IP mapping configuration
Arguments:
pIpMap - IP mapping configuration
Return Value:
TRUE if success, otherwise FALSE
--*/
{
BOOL fSt = TRUE;
LPBOOL pbToBeAdded = NULL;
LPBOOL pbToBeDeleted = NULL;
UINT iCurrent;
UINT iNew;
LPWSTR pCurrent;
LPWSTR pNew;
DEBUG_BUFFER;
EnterCriticalSection( &g_csIpListCheckpoint );
DBG_PRINTF(( achE, "%d servers", pIpMap->ComputerCount() ));
if ( (pbToBeAdded = (LPBOOL)LocalAlloc( LPTR, sizeof(BOOL)*pIpMap->ComputerCount())) == NULL ||
(pbToBeDeleted = (LPBOOL)LocalAlloc( LPTR, sizeof(BOOL)*g_KernelIpMap.ServerCount())) == NULL )
{
fSt = FALSE;
goto cleanup;
}
// update to be deleted list
for ( iCurrent = 0 ;
iCurrent < g_KernelIpMap.ServerCount() ;
++iCurrent )
{
pbToBeDeleted[iCurrent] = TRUE;
pCurrent = g_KernelIpMap.GetServerName( iCurrent );
for ( iNew = 0 ;
iNew < pIpMap->ComputerCount() ;
++iNew )
{
pIpMap->EnumComputer( iNew, &pNew );
if ( !_wcsicmp( pCurrent, pNew ) )
{
pbToBeDeleted[iCurrent] = FALSE;
break;
}
}
if ( pbToBeDeleted[iCurrent] )
{
//
// Not an error to be unable to stop thread : might be stuck doing RPC
// as computer name will be removed from list ( protected by g_csIpListUpdate )
// thread will not find computer and will eventually exit.
//
g_KernelIpMap.StopServerThread( iCurrent, STOP_PERFMON_THREAD_TIMEOUT );
EnterCriticalSection( &g_csIpListUpdate );
g_KernelIpMap.RemoveServer( iCurrent );
LeaveCriticalSection( &g_csIpListUpdate );
--iCurrent; // because iCurrent was deleted
}
}
// update to be added list
for ( iNew = 0 ;
iNew < pIpMap->ComputerCount() ;
++iNew )
{
pbToBeAdded[iNew] = TRUE;
pIpMap->EnumComputer( iNew, &pNew );
for ( iCurrent = 0 ;
iCurrent < g_KernelIpMap.ServerCount() ;
++iCurrent )
{
pCurrent = g_KernelIpMap.GetServerName( iCurrent );
if ( !_wcsicmp( pCurrent, pNew ) )
{
pbToBeAdded[iNew] = FALSE;
break;
}
}
if ( pbToBeAdded[iNew] )
{
EnterCriticalSection( &g_csIpListUpdate );
fSt = g_KernelIpMap.AddServer( pNew );
LeaveCriticalSection( &g_csIpListUpdate );
if ( !fSt )
{
break;
}
if ( !(fSt = g_KernelIpMap.StartServerThread( iCurrent )) )
{
break;
}
}
}
// update public IP list
if ( fSt )
{
EnterCriticalSection( &g_csIpListUpdate );
fSt = g_KernelIpMap.SetPublicIpList( pIpMap );
LeaveCriticalSection( &g_csIpListUpdate );
}
cleanup:
if ( pbToBeAdded )
{
LocalFree( pbToBeAdded );
}
if ( pbToBeDeleted )
{
LocalFree( pbToBeDeleted );
}
LeaveCriticalSection( &g_csIpListCheckpoint );
return fSt;
}
BOOL
KernelConfigToPerfmonCounters(
XBF* pxbf
)
/*++
Routine Description:
Retrieve serialized perfmon counters configuration from kernel mode configuration
Arguments:
pxbf - ptr to buffer updated with serialized configuration
Return Value:
TRUE if success, otherwise FALSE
--*/
{
BOOL fSt;
EnterCriticalSection( &g_csPerfList );
fSt = g_PerfmonCounterList.Serialize( pxbf );
LeaveCriticalSection( &g_csPerfList );
return fSt;
}
BOOL
PerfmonCountersToKernelConfig(
CComputerPerfCounters* pPerfmonCounters
)
/*++
Routine Description:
Set kernel mode configuration from perfmon counters configuration
Arguments:
pPerfmonCounters - perfmon counters configuration
Return Value:
TRUE if success, otherwise FALSE
--*/
{
XBF xbf;
UINT iS;
EnterCriticalSection( &g_csPerfList );
if ( !pPerfmonCounters->Serialize( &xbf ) ||
!g_PerfmonCounterList.Unserialize( &xbf ) )
{
return FALSE;
}
LeaveCriticalSection( &g_csPerfList );
// refresh perf list for each computer
for ( iS = 0 ;
iS < g_KernelIpMap.ServerCount() ;
++iS )
{
//
// if can't stop thread then do not restart it.
//
if ( !g_KernelIpMap.StopServerThread( iS, STOP_PERFMON_THREAD_TIMEOUT ) ||
!g_KernelIpMap.StartServerThread( iS ) )
{
continue;
}
}
return TRUE;
}
CKernelIpMapHelper::CKernelIpMapHelper(
)
/*++
Routine Description:
CKernelIpMapHelper constructor
Arguments:
None
Return Value:
Nothing
--*/
{
}
BOOL
CKernelIpMapHelper::Init(
)
/*++
Routine Description:
Initialize kernel configuration object
Arguments:
None
Return Value:
TRUE if success, otherwise FALSE
--*/
{
CComputerPerfCounters PerfmonCounters;
DWORD dwStickyDuration;
LPBYTE pB;
DWORD dwC;
XBF xbf;
DEBUG_BUFFER;
if ( (m_pKernelIpMap = (CKernelIpMap*)LocalAlloc( LMEM_FIXED, sizeof(CKernelIpMap) )) == NULL )
{
DBG_PUTS("InitComLb:OOM");
return FALSE;
}
m_pKernelIpMap->m_dwServerCount = 0;
m_pKernelIpMap->m_dwPublicIpCount = 0;
m_pKernelIpMap->m_dwPrivateIpCount = 0;
m_pKernelIpMap->m_dwStickyDuration = DEFAULT_STICKY_DURATION;
if ( g_IpMap.Load( HKEY_LOCAL_MACHINE,
IISLOADBAL_REGISTRY_KEY,
IPLIST_REGISTRY_VALUE ) )
{
if ( !IpListToKernelConfig( &g_IpMap ) )
{
DBG_PUTS("!IpListToKernelConfig");
return FALSE;
}
}
else if ( GetLastError() != ERROR_FILE_NOT_FOUND )
{
DBG_PRINTF(( achE, "Err read registry line %d : %08x %d\n", __LINE__, GetLastError(), GetLastError() ));
return FALSE;
}
if ( PerfmonCounters.Load( HKEY_LOCAL_MACHINE,
IISLOADBAL_REGISTRY_KEY,
PERFMON_REGISTRY_VALUE ) )
{
if ( !PerfmonCountersToKernelConfig( &PerfmonCounters ) )
{
DBG_PUTS("!IpListToKernelConfig");
return FALSE;
}
}
else if ( GetLastError() != ERROR_FILE_NOT_FOUND )
{
DBG_PRINTF(( achE, "Err read registry line %d : %08x %d\n", __LINE__, GetLastError(), GetLastError() ));
return FALSE;
}
if ( LoadBlobFromReg( HKEY_LOCAL_MACHINE,
IISLOADBAL_REGISTRY_KEY,
STICKY_REGISTRY_VALUE,
&xbf ) )
{
pB = xbf.GetBuff();
dwC = xbf.GetUsed();
if ( !Unserialize( &pB, &dwC, &dwStickyDuration ) )
{
DBG_PUTS("!SetStickyDuration");
return FALSE;
}
g_KernelIpMap.SetStickyDuration( dwStickyDuration );
}
else if ( GetLastError() != ERROR_FILE_NOT_FOUND )
{
DBG_PRINTF(( achE, "Err read registry line %d : %08x %d\n", __LINE__, GetLastError(), GetLastError() ));
return FALSE;
}
return TRUE;
}
BOOL
CKernelIpMapHelper::SetPublicIpList(
CIPMap* pIpMap
)
/*++
Routine Description:
Update public IP list from CIPMap object
Arguments:
pIpMap - public -> private IP addresses mapping
Return Value:
TRUE if success, otherwise FALSE
--*/
{
UINT iNew;
LPWSTR pNewEntry;
CKernelIpEndpoint* pIp;
UINT cNewSize;
LPBYTE pNew;
UINT iP;
UINT iS;
CKernelServerDescription* pNewServer;
CKernelServerDescription* pOldServer;
CKernelIpEndpoint ieNew;
UINT iPrv;
LPWSTR pName;
DWORD dwSticky;
DWORD dwAttr;
DEBUG_BUFFER;
// build private IP list of unique private IP in pIpMap
// ( as array of CKernelIpEndpoint )
ResetPrivateIpList();
for ( iS = 0 ;
iS < ServerCount() ;
++iS )
{
for ( iP = 0 ;
iP < pIpMap->IpPublicCount() ;
++iP )
{
if ( !pIpMap->GetIpPrivate( iS, iP, &pNewEntry, &pName ) ||
!StringToIpEndpoint( pNewEntry, &ieNew ) )
{
return FALSE;
}
// do not add if private Ip address is NULL, i.e. invalid
if ( ieNew.m_dwIpAddress != 0 &&
!CheckAndAddPrivateIp( &ieNew, TRUE, NULL ) )
{
return FALSE;
}
}
}
// resize array
cNewSize = GetSize( ServerCount(), pIpMap->IpPublicCount(), PrivateIpListCount() );
DBG_PRINTF(( achE, "SetPublicIpList: SrvCount=%d pub=%d prv=%d sz=%d\n",
ServerCount(), pIpMap->IpPublicCount(), PrivateIpListCount(), cNewSize ));
if ( (pNew = (LPBYTE)LocalAlloc( LMEM_FIXED, cNewSize )) == NULL )
{
return FALSE;
}
*(CKernelIpMap*)pNew = *m_pKernelIpMap;
((CKernelIpMap*)pNew)->m_dwPublicIpCount = pIpMap->IpPublicCount();
// store private IP list
for ( iP = 0 ;
EnumPrivateIpList( iP, &pIp ) ;
++iP )
{
memcpy( GetPrivateIpEndpoint( iP, pNew ), pIp, sizeof(CKernelIpEndpoint) );
}
((CKernelIpMap*)pNew)->m_dwPrivateIpCount = iP;
// load private IP addresses
for ( iS = 0 ;
iS < ServerCount() ;
++iS )
{
pNewServer = GetServerPtr( iS, pNew, pIpMap->IpPublicCount(), PrivateIpListCount() );
pOldServer = GetServerPtr( iS );
memcpy( pNewServer, pOldServer, sizeof(CKernelServerDescription) );
for ( iP = 0 ;
iP < pIpMap->IpPublicCount() ;
++iP )
{
// transcode private IP addr
if ( !pIpMap->GetIpPrivate( iS, iP, &pNewEntry, &pName ) ||
!StringToIpEndpoint( pNewEntry, &ieNew) )
{
return FALSE;
}
DBG_PRINTF(( achE, "(s=%d,i=%d) -> %ws, IP = %08x\n",
iS, iP, pNewEntry, ieNew.m_dwIpAddress ));
if ( ieNew.m_dwIpAddress == 0 )
{
// if no mapping then store as invalid index
iPrv = (UINT)-1;
}
else if ( !CheckAndAddPrivateIp( &ieNew, FALSE, &iPrv ) ||
iPrv == (UINT)-1 )
{
return FALSE;
}
*GetPrivateIpRef( pNewServer, iP ) = iPrv;
DBG_PRINTF(( achE, "pNew=%08x, pNewServer=%08x, IpRef(%d)=%08x : %u\n",
pNew, pNewServer, iP, GetPrivateIpRef( pNewServer, iP ), iPrv ));
}
}
LocalFree(m_pKernelIpMap);
m_pKernelIpMap = (CKernelIpMap*)pNew;
// store public ip addresses
for ( iNew = 0 ;
iNew < pIpMap->IpPublicCount() ;
++iNew )
{
pIpMap->EnumIpPublic( iNew, &pNewEntry, &pName, &dwSticky, &dwAttr );
if ( !StringToIpEndpoint( pNewEntry, (CKernelIpEndpoint*)GetPublicIpPtr( iNew ) ) )
{
return FALSE;
}
GetPublicIpPtr( iNew )->m_dwSticky = dwSticky;
}
return TRUE;
}
BOOL
CKernelIpMapHelper::StringToIpEndpoint(
LPWSTR pNew,
CKernelIpEndpoint* pIp
)
/*++
Routine Description:
Convert string ( IP_ADDR:PORT_# ) to IP endpoint object ( sockaddr_in )
Arguments:
pNew - string representation of IP address + port #
pIp - updated with IP address + port # in sockaddr_in
Return Value:
TRUE if success, otherwise FALSE
--*/
{
SOCKADDR sockaddr;
int csockaddr;
LPWSTR pPort;
BOOL fSt = FALSE;
DEBUG_BUFFER;
if ( pPort = wcschr( pNew, L':' ) )
{
*pPort = L'\0';
csockaddr = sizeof(sockaddr);
if ( WSAStringToAddressW( pNew, AF_INET, NULL, &sockaddr, &csockaddr ) == 0 )
{
memcpy( &pIp->m_dwIpAddress,
&((sockaddr_in*)&sockaddr)->sin_addr,
sizeof(pIp->m_dwIpAddress) );
pIp->m_usPort = htons((u_short)_wtoi(pPort +1));
fSt = TRUE;
}
else
{
DBG_PRINTF(( achE, "WSAStringToAddress error: %d\n", WSAGetLastError() ));
}
*pPort = L':';
}
else if ( *pNew )
{
csockaddr = sizeof(sockaddr);
if ( WSAStringToAddressW( pNew, AF_INET, NULL, &sockaddr, &csockaddr ) == 0 )
{
memcpy( &pIp->m_dwIpAddress,
&((sockaddr_in*)&sockaddr)->sin_addr,
sizeof(pIp->m_dwIpAddress) );
pIp->m_usPort = htons(DEFAULT_IP_PORT);
fSt = TRUE;
}
else
{
DBG_PRINTF(( achE, "WSAStringToAddress error: %d\n", WSAGetLastError() ));
}
}
else
{
memset( &pIp->m_dwIpAddress,
'\0',
sizeof(pIp->m_dwIpAddress) );
pIp->m_usPort = 0;
fSt = TRUE;
}
return fSt;
}
BOOL
CKernelIpMapHelper::IpEndpointToString(
CKernelIpEndpoint* pIp,
LPWSTR pAddr,
DWORD cAddr
)
/*++
Routine Description:
Convert IP endpoint object ( sockaddr_in ) to string ( IP_ADDR:PORT_# )
Arguments:
pIp - IP address + port # in sockaddr_in
pAddr - updated with string representation of IP address + port # on success
cAddr - size of pAddr on input
Return Value:
TRUE if success, otherwise FALSE
--*/
{
SOCKADDR sockaddr;
BOOL fSt = FALSE;
if ( pIp->m_dwIpAddress != 0 )
{
((sockaddr_in*)&sockaddr)->sin_family = AF_INET;
memcpy( &((sockaddr_in*)&sockaddr)->sin_addr,
&pIp->m_dwIpAddress,
sizeof(pIp->m_dwIpAddress) );
((sockaddr_in*)&sockaddr)->sin_port = pIp->m_usPort;
if ( WSAAddressToStringW( &sockaddr, sizeof(sockaddr), NULL, pAddr, &cAddr ) == 0 )
{
fSt = TRUE;
}
}
else
{
*pAddr = L'\0';
fSt = TRUE;
}
return fSt;
}
BOOL
CKernelIpMapHelper::AddServer(
LPWSTR pNewServer
)
/*++
Routine Description:
Add a server to kernel configuration
Arguments:
pNewServer - server name
Return Value:
TRUE if success, otherwise FALSE
--*/
{
LPBYTE pNew;
UINT cNewSize;
UINT iS;
DEBUG_BUFFER;
cNewSize = GetSize( ServerCount()+1, PublicIpCount(), PrivateIpCount() );
DBG_PRINTF(( achE, "cNewSize = %d", cNewSize ));
if ( (pNew = (LPBYTE)LocalAlloc( LMEM_FIXED, cNewSize )) == NULL )
{
return FALSE;
}
if ( m_ServerNames.AddEntry( pNewServer ) == INDEX_ERROR ||
m_PerfmonThreadHandle.AddPtr( NULL ) == INDEX_ERROR ||
m_PerfmonThreadStopEvent.AddPtr( NULL ) == INDEX_ERROR )
{
return FALSE;
}
// copy existing
memcpy( pNew, m_pKernelIpMap, GetSize(ServerCount(), PublicIpCount(), PrivateIpCount() ) );
DBG_PRINTF(( achE, "memcpy( %08x, %08x, %d", pNew, m_pKernelIpMap, GetSize(ServerCount(), PublicIpCount(), PrivateIpCount() ) ));
LocalFree(m_pKernelIpMap);
m_pKernelIpMap = (CKernelIpMap*)pNew;
// init new entry
memset( GetServerPtr( ServerCount() ), '\0', GetKernelServerDescriptionSize() );
DBG_PRINTF(( achE, "memset( %08x, %d", GetServerPtr( ServerCount() ), GetKernelServerDescriptionSize() ));
for ( iS = 0 ;
iS < LOADBAL_SAMPLES ;
++iS )
{
GetServerPtr( ServerCount() )->m_flLoadAvail[iS] = 0;
}
DBG_PRINTF(( achE, "memset( %08x, %d", GetServerPtr( ServerCount() ), GetKernelServerDescriptionSize() ));
++m_pKernelIpMap->m_dwServerCount;
DBG_PRINTF(( achE, "m_dwServerCount %d", m_pKernelIpMap->m_dwServerCount ));
return TRUE;
}
BOOL
CKernelIpMapHelper::RemoveServer(
UINT i
)
/*++
Routine Description:
Remove a server from kernel configuration
Arguments:
i - server index ( 0-based )
Return Value:
TRUE if success, otherwise FALSE
--*/
{
LPBYTE pNew;
UINT cNewSize;
if ( !m_ServerNames.DeleteEntry( i ) ||
!m_PerfmonThreadHandle.DeletePtr( i ) ||
!m_PerfmonThreadStopEvent.DeletePtr( i ) )
{
return FALSE;
}
// remove CKernelServerDescription entry
// do not reallocate storage ( SetPublicIpList() will typically be called after
// this function )
memmove( GetServerPtr(i),
GetServerPtr(i+1),
GetKernelServerDescriptionSize() * (ServerCount()-i-1) );
--m_pKernelIpMap->m_dwServerCount;
return TRUE;
}
BOOL
CKernelIpMapHelper::StopServerThread(
DWORD iServer,
DWORD dwTimeout
)
/*++
Routine Description:
Stop the thread monitoring perfmon counters for a specific server
Arguments:
iServer - server index ( 0-based )
Return Value:
TRUE if success, otherwise FALSE
--*/
{
BOOL fSt = FALSE;
// request thread to stop
if ( SetEvent( (HANDLE)m_PerfmonThreadStopEvent.GetPtr( iServer ) ) &&
WaitForSingleObject( (HANDLE)m_PerfmonThreadHandle.GetPtr( iServer ), dwTimeout ) == WAIT_OBJECT_0 )
{
fSt = TRUE;
}
// close thread handle
if ( (HANDLE)m_PerfmonThreadHandle.GetPtr( iServer ) )
{
CloseHandle( (HANDLE)m_PerfmonThreadHandle.GetPtr( iServer ) );
m_PerfmonThreadHandle.SetPtr( iServer, NULL );
}
m_PerfmonThreadStopEvent.SetPtr( iServer, NULL );
return fSt;
}
BOOL
CKernelIpMapHelper::StartServerThread(
DWORD iServer
)
/*++
Routine Description:
Start the thread monitoring perfmon counters for a specific server
Arguments:
iServer - server index ( 0-based )
Return Value:
TRUE if success, otherwise FALSE
--*/
{
CPerfmonThreadControlBlock* pCB = NULL;
HANDLE hThread;
DWORD dwId;
if ( pCB = new CPerfmonThreadControlBlock)
{
if ( ( pCB->m_hStopEvent = CreateEvent( NULL, TRUE, FALSE, NULL )) == NULL )
{
goto cleanup;
}
if ( !pCB->m_ServerName.Set( GetServerName( iServer ) ) )
{
goto cleanup;
}
GetServerPtr( iServer )->m_cNbSamplesAvail = 0;
GetServerPtr( iServer )->m_dwHeartbeat = UNAVAIL_HEARTBEAT;
if ( (hThread = CreateThread( NULL,
0,
(LPTHREAD_START_ROUTINE)PermonThread,
(LPVOID)pCB,
0,
&dwId )) == NULL )
{
goto cleanup;
}
m_PerfmonThreadHandle.SetPtr( iServer, (LPVOID)hThread );
m_PerfmonThreadStopEvent.SetPtr( iServer, (LPVOID)pCB->m_hStopEvent );
return TRUE;
}
cleanup:
if ( pCB )
{
if ( pCB->m_hStopEvent != NULL )
{
CloseHandle( pCB->m_hStopEvent );
}
delete pCB;
}
return FALSE;
}
extern "C" DWORD WINAPI
PermonThread(
LPVOID pV
)
/*++
Routine Description:
Thread monitoring perfmon counters for a specific server
update & reference g_KernelIpMap
reference g_csPerfList
Arguments:
pV - ptr to CPerfmonThreadControlBlock
This function will have to cleanup resources associated with this control
block on thread exit.
Return Value:
NT status
--*/
{
HQUERY hQuery;
PDH_STATUS pdhStatus;
CPtrXBF CounterArray;
CPtrXBF Weight;
DWORD dwWeight;
HCOUNTER hCounter;
LPWSTR pszPerfCounter;
LPDWORD pdwWeight = NULL;
UINT cComputerName;
CAllocString ComputerPlusPerfmonCounter;
UINT iPerf;
UINT iPerfName;
BOOL fCreatedCounters = FALSE;
UINT iServ;
UINT cPerf;
double dbAvail;
double dbAcc;
UINT cInst;
PDH_FMT_COUNTERVALUE fmtValue;
DWORD ctrType;
UINT iSample = 0;
CKernelServerDescription* pServerInfo;
LPWSTR pDel;
WCHAR ch;
WCHAR achInstance[32];
UINT iInstance;
DWORD dwZero;
DWORD dwSizeInstanceList;
WCHAR achInstanceList[3000];
LPWSTR pIns;
LPWSTR pszPerfServer;
time_t tStartCollect;
BOOL fNormalized = FALSE;
DEBUG_BUFFER;
CPerfmonThreadControlBlock* pCB = (CPerfmonThreadControlBlock*)pV;
cComputerName = wcslen( pCB->m_ServerName.Get() );
for ( ;
;
)
{
// query counters
if ( !fCreatedCounters )
{
DBG_PRINTF(( achE, "%ws: create counters\n",
pCB->m_ServerName.Get() ));
if ( g_PerfmonCounterList.PerfCounterCount() == 0 )
{
goto waitnext;
}
pdhStatus = g_pfnPdhOpenQuery (0, 0, &hQuery);
if ( pdhStatus != ERROR_SUCCESS )
{
return pdhStatus;
}
#if 0
pdhStatus = PdhConnectMachineW( pCB->m_ServerName.Get() );
DBG_PRINTF(( achE, "%ws: connect to machine : %x\n",
pCB->m_ServerName.Get(), pdhStatus ));
#endif
CounterArray.Reset();
Weight.Reset();
EnterCriticalSection( &g_csPerfList );
for ( iPerfName = 0, iPerf = 0 ;
g_PerfmonCounterList.EnumPerfCounter( iPerfName, &pszPerfServer, &pszPerfCounter, &dwWeight ) ;
++iPerfName )
{
// if server name was specified in perf counter list, check we are using
// this server
if ( pszPerfServer != NULL )
{
// skip possible leading "\\"
if ( *pszPerfServer == L'\\' )
{
pszPerfServer += (sizeof(L"\\\\") / sizeof(WCHAR)) -1;
}
if ( _wcsicmp( pszPerfServer, pCB->m_ServerName.Get() ) )
{
continue;
}
}
// combine computer name & perf name
ComputerPlusPerfmonCounter.Reset();
if ( !IncludeServerName( pszPerfCounter ) &&
(!ComputerPlusPerfmonCounter.Set( L"\\\\" ) ||
!ComputerPlusPerfmonCounter.Append( pCB->m_ServerName.Get() )) )
{
eallocname:
LeaveCriticalSection( &g_csPerfList );
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// If this is a multi-instance object, we need to enumerate
// all instances. Each instance will be a separate counter, but all of the
// counters for a given object will be averaged before being added to the
// composite load.
//
if ( (pDel = wcschr( pszPerfCounter, L'(' )) &&
iswdigit( pDel[1] ) )
{
dwSizeInstanceList = sizeof(achInstanceList) / sizeof(WCHAR);
dwZero = 0;
*pDel = L'\0';
pdhStatus = PdhEnumObjectItemsW( NULL,
pCB->m_ServerName.Get(),
pszPerfCounter + 1,
NULL, // don't need Counter list
&dwZero,
achInstanceList,
&dwSizeInstanceList,
100,
0 );
if ( pdhStatus == ERROR_SUCCESS &&
dwSizeInstanceList )
{
for ( pIns = achInstanceList ;
*pIns;
pIns += wcslen( pIns ) + 1 )
{
//
// do not consider meta instances ( e.g. _Total )
//
if ( *pIns == '_' )
{
continue;
}
ComputerPlusPerfmonCounter.Reset();
//
// Add an instance
//
if ( !IncludeServerName( pszPerfCounter ) &&
(!ComputerPlusPerfmonCounter.Set( L"\\\\" ) ||
!ComputerPlusPerfmonCounter.Append( pCB->m_ServerName.Get() )) )
{
*pDel = L'(';
goto eallocname;
}
if ( !ComputerPlusPerfmonCounter.Append( pszPerfCounter ) ||
!ComputerPlusPerfmonCounter.Append( L"(" ) ||
!ComputerPlusPerfmonCounter.Append( pIns ) ||
!ComputerPlusPerfmonCounter.Append( wcschr(pDel+1,')') ) )
{
*pDel = L'(';
goto eallocname;
}
if ( (pdhStatus = PdhAddCounterW( hQuery,
ComputerPlusPerfmonCounter.Get(),
0,
&hCounter )) != ERROR_SUCCESS )
{
DBG_PRINTF(( achE, "%ws: PdhAddCounterW %ws failed : %x\n",
pCB->m_ServerName.Get(),
ComputerPlusPerfmonCounter.Get(),
pdhStatus ));
break;
}
//
// All instances after 1st one will have dwWeight set to -1
//
if ( CounterArray.AddPtr( (LPVOID)hCounter ) == INDEX_ERROR ||
Weight.AddPtr( (LPVOID)dwWeight ) == INDEX_ERROR )
{
*pDel = L'(';
goto eallocname;
}
dwWeight = (DWORD)-1;
++iPerf;
}
*pDel = L'(';
}
else
{
if ( pdhStatus == ERROR_SUCCESS )
{
pdhStatus = PDH_CSTATUS_NO_OBJECT;
}
// can't enumerate instances
*pDel = '(';
WCHAR achErr[32];
LPCWSTR pA[3];
pA[0] = pszPerfCounter;
pA[1] = pCB->m_ServerName.Get();
pA[2] = achErr;
_itow( pdhStatus, achErr, 10 );
ReportIisLbEvent( EVENTLOG_ERROR_TYPE,
IISLB_EVENT_COUNTER_ENUM_ERROR,
1,
pA );
DBG_PRINTF(( achE, "%ws: PdhEnumObjectItemsW %ws failed : %x\n",
pCB->m_ServerName.Get(), pszPerfCounter, pdhStatus ));
break;
}
}
else
{
if ( !ComputerPlusPerfmonCounter.Append( pszPerfCounter ) )
{
goto eallocname;
}
if ( (pdhStatus = PdhAddCounterW( hQuery,
ComputerPlusPerfmonCounter.Get(),
0,
&hCounter )) != ERROR_SUCCESS )
{
DBG_PRINTF(( achE, "%ws: PdhAddCounterW %ws failed : %x\n",
pCB->m_ServerName.Get(),
ComputerPlusPerfmonCounter.Get(),
pdhStatus ));
break;
}
if ( CounterArray.AddPtr( (LPVOID)hCounter ) == INDEX_ERROR ||
Weight.AddPtr( (LPVOID)dwWeight ) == INDEX_ERROR )
{
goto eallocname;
}
++iPerf;
}
if ( pdhStatus != ERROR_SUCCESS )
{
break;
}
}
if ( pdhStatus == ERROR_SUCCESS )
{
fCreatedCounters = TRUE;
cPerf = iPerf;
DBG_PRINTF(( achE, "%ws: created counters = %d, perf count %d\n",
pCB->m_ServerName.Get(), fCreatedCounters, cPerf ));
}
else
{
CounterArray.Reset();
Weight.Reset();
PdhCloseQuery (hQuery);
}
LeaveCriticalSection( &g_csPerfList );
}
if ( fCreatedCounters )
{
tStartCollect = time( NULL );
pdhStatus = PdhCollectQueryData( hQuery );
if ( time(NULL) < tStartCollect + COLLECT_TIMEOUT &&
pdhStatus == ERROR_SUCCESS &&
CheckQueryMachineStatus( hQuery ) )
{
// build normalized load avail
dbAvail = 0;
for ( iPerf = 0 ;
iPerf < cPerf ;
)
{
dwWeight = (DWORD)Weight.GetPtr(iPerf); // BUGBUG64
for ( cInst = 0, dbAcc = 0 ;
;
)
{
pdhStatus = PdhGetFormattedCounterValue(
(HCOUNTER)CounterArray.GetPtr(iPerf),
PDH_FMT_DOUBLE,
&ctrType,
&fmtValue );
if ( pdhStatus == ERROR_SUCCESS &&
fmtValue.CStatus == PDH_CSTATUS_VALID_DATA )
{
dbAcc += fmtValue.doubleValue;
++cInst;
}
else
{
DBG_PRINTF(( achE, "%ws: failed format counter, status %x cstatus %x\n",
pCB->m_ServerName.Get(), pdhStatus, fmtValue.CStatus ));
goto err_acc_cnt;
}
//
// Loop for all instances of a given counter name
//
if ( ++iPerf == cPerf ||
(DWORD)Weight.GetPtr(iPerf) != (DWORD)-1 ) // BUGBUG64
{
break;
}
}
if (pdhStatus == ERROR_SUCCESS)
{
// add average of all instances
// if not an availability flag : in such a case the fact
// that we successfully read the counter is enough
if ( dwWeight == LB_WEIGHT_NORMALIZED_FLAG )
{
DBG_PRINTF(( achE, "%ws: raw normalized load is %f\n",
pCB->m_ServerName.Get(),
((dbAcc/cInst)) ));
dbAvail += ((dbAcc/cInst));
fNormalized = TRUE;
}
else if ( dwWeight != LB_WEIGHT_AVAILABILITY_FLAG )
{
DBG_PRINTF(( achE, "%ws: raw load is %f weighted load is %f\n",
pCB->m_ServerName.Get(),
((dbAcc/cInst)),
((dbAcc/cInst)) * dwWeight ));
dbAvail += ((dbAcc/cInst)) * dwWeight;
}
}
}
}
else
{
// failed to access counters :
// force re-open
err_acc_cnt:
DBG_PRINTF(( achE, "%ws: failed to get counters, time %d, status %x\n",
pCB->m_ServerName.Get(), time(NULL) - tStartCollect, pdhStatus ));
if ( fCreatedCounters &&
cPerf )
{
WCHAR achErr[32];
LPCWSTR pA[2];
pA[0] = pCB->m_ServerName.Get();
pA[1] = achErr;
_itow( pdhStatus, achErr, 10 );
ReportIisLbEvent( EVENTLOG_ERROR_TYPE,
IISLB_EVENT_COUNTER_COLLECT_ERROR,
2,
pA );
}
for ( iPerf = 0 ;
iPerf < cPerf ;
++iPerf )
{
pdhStatus = PdhRemoveCounter( (HCOUNTER)CounterArray.GetPtr(iPerf) );
}
CounterArray.Reset();
Weight.Reset();
PdhCloseQuery( hQuery );
fCreatedCounters = FALSE;
dbAvail = 0;
}
}
else
{
dbAvail = 0;
}
// locate server name in list
EnterCriticalSection( &g_csIpListUpdate );
for ( iServ = 0 ;
iServ < g_KernelIpMap.ServerCount() &&
_wcsicmp( pCB->m_ServerName.Get(), g_KernelIpMap.GetServerName( iServ ) ) ;
++iServ )
{
}
// update load
if ( iServ < g_KernelIpMap.ServerCount() )
{
pServerInfo = g_KernelIpMap.GetServerPtr( iServ );
g_fNormalized = fNormalized;
if ( fCreatedCounters )
{
DBG_PRINTF(( achE, "%ws: updated slot %d, %f counters\n", pCB->m_ServerName.Get(), iSample, dbAvail ));
pServerInfo->m_flLoadAvail[iSample] = (float)dbAvail;
if ( ++iSample == LOADBAL_SAMPLES )
{
iSample = 0;
}
if ( pServerInfo->m_cNbSamplesAvail < LOADBAL_SAMPLES )
{
++pServerInfo->m_cNbSamplesAvail;
}
pServerInfo->m_dwHeartbeat = ~UNAVAIL_HEARTBEAT;
}
else
{
//
// No counter available. Reset count of available sample
//
pServerInfo->m_cNbSamplesAvail = 0;
iSample = 0;
pServerInfo->m_dwHeartbeat = UNAVAIL_HEARTBEAT;
}
}
else
{
// can't find computer : exit thread
LeaveCriticalSection( &g_csIpListUpdate );
break;
}
LeaveCriticalSection( &g_csIpListUpdate );
waitnext:
if ( WaitForSingleObject( pCB->m_hStopEvent, PERFMON_SLEEP_TIME ) == WAIT_OBJECT_0 )
{
// stop request
break;
}
}
// close counters
if ( fCreatedCounters )
{
PdhCloseQuery( hQuery );
}
CloseHandle( pCB->m_hStopEvent );
delete pCB;
return 0;
}
extern "C" DWORD WINAPI
UserToKernelThread(
LPVOID pV
)
/*++
Routine Description:
Thread updating kernel mode info with user mode configuration
through IOCTL
Arguments:
pV - event handle set by caller to request this thread to stop
Return Value:
NT status
--*/
{
CKernelServerDescription* pS;
UINT iS;
UINT iL;
UINT iDup;
float flLoadAvail;
float flTotalLoadAvail;
BOOL fSt;
HANDLE hDevice;
DWORD dwOutBytes;
UINT cInitialRetries;
BOOL fSomeServUnavailable;
DWORD dwWaitTime;
IPREF iRef;
HANDLE hDriver;
DEBUG_BUFFER;
// open driver
SetLbDriverState( TRUE );
hDriver = CreateFile( "\\\\.\\" SZDRIVERNAME,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0,
OPEN_EXISTING,
0,
0 );
if ( hDriver == INVALID_HANDLE_VALUE )
{
WCHAR achErr[32];
LPCWSTR pA[1];
pA[0] = achErr;
_itow( GetLastError(), achErr, 10 );
DBG_PRINTF(( achE, "CreateFile to access driver %s failed: %u\n", SZDRIVERNAME, GetLastError() ));
ReportIisLbEvent( EVENTLOG_ERROR_TYPE,
IISLB_EVENT_DRIVEROPEN_ERROR,
1,
pA );
goto cleanup;
}
cInitialRetries = 30;
for ( ;
;
)
{
EnterCriticalSection( &g_csIpListCheckpoint );
//
// Compute average for all servers
//
for ( iS = 0, flTotalLoadAvail = 0 ;
iS < g_KernelIpMap.ServerCount() ;
++iS )
{
pS = g_KernelIpMap.GetServerPtr( iS );
for ( iL = 0, flLoadAvail = 0 ;
iL < pS->m_cNbSamplesAvail ;
++iL )
{
flLoadAvail += pS->m_flLoadAvail[iL];
}
pS->m_flLoadAvailAvg = iL ? (flLoadAvail / iL) : 0;
DBG_PRINTF(( achE, "%d: avg load is %f\n", iS, pS->m_flLoadAvailAvg ));
flTotalLoadAvail += pS->m_flLoadAvailAvg;
}
//
// Per Server Available load is Total / PerServerLoad
//
fSomeServUnavailable = FALSE;
for ( iS = 0;
iS < g_KernelIpMap.ServerCount() ;
++iS )
{
pS = g_KernelIpMap.GetServerPtr( iS );
if( pS->m_dwHeartbeat == UNAVAIL_HEARTBEAT )
{
pS->m_flLoadAvailAvg = 0;
fSomeServUnavailable = TRUE;
}
else
{
if ( !g_fNormalized )
{
if ( pS->m_flLoadAvailAvg )
{
pS->m_flLoadAvailAvg = flTotalLoadAvail / pS->m_flLoadAvailAvg;
}
else
{
if ( flTotalLoadAvail )
{
pS->m_flLoadAvailAvg = flTotalLoadAvail;
}
else
{
//
// Total load is 0 : avail load should be !0 and identical
// on all servers.
//
pS->m_flLoadAvailAvg = LOADBAL_NORMALIZED_TOTAL;
}
}
}
}
DBG_PRINTF(( achE, "%d: avail load is %f\n", iS, pS->m_flLoadAvailAvg ));
}
//
// recompute total
//
flTotalLoadAvail = 0;
for ( iS = 0 ;
iS < g_KernelIpMap.ServerCount() ;
++iS )
{
pS = g_KernelIpMap.GetServerPtr( iS );
flTotalLoadAvail += pS->m_flLoadAvailAvg;
if ( !(fSomeServUnavailable && cInitialRetries) )
{
pS->m_dwHeartbeat = UNAVAIL_HEARTBEAT;
}
}
//
// Normalize each server availability so that total availability is
// LOADBAL_NORMALIZED_TOTAL
//
for ( iS = 0;
iS < g_KernelIpMap.ServerCount() ;
++iS )
{
pS = g_KernelIpMap.GetServerPtr( iS );
if ( flTotalLoadAvail )
{
pS->m_dwLoadAvail = (DWORD)((pS->m_flLoadAvailAvg * LOADBAL_NORMALIZED_TOTAL) / flTotalLoadAvail);
}
else
{
pS->m_dwLoadAvail = 0;
}
pS->m_LoadbalancedLoadAvail = pS->m_dwLoadAvail;
DBG_PRINTF(( achE, "Srv %d, available %d\n", iS, pS->m_dwLoadAvail ));
}
if ( fSomeServUnavailable &&
cInitialRetries )
{
dwWaitTime = 1000;
--cInitialRetries;
}
else
{
cInitialRetries = 0;
//
// set all prv IP index to 0
//
for ( iL = 0 ;
iL < g_KernelIpMap.PrivateIpCount() ;
++iL )
{
g_KernelIpMap.GetPrivateIpEndpoint( iL )->m_dwRefCount = 0;
}
//
// scan all public IP addr:port. Set m_usUniquePort to port only once
// for each port used in public IP list
// i.e each port will be present only once in m_usUniquePort list
// needed so that driver knows if notify / unnotify request to NAT is needed
//
for ( iL = 0 ;
iL < g_KernelIpMap.PublicIpCount() ;
++iL )
{
g_KernelIpMap.GetPublicIpPtr( iL )->m_usUniquePort =
g_KernelIpMap.GetPublicIpPtr( iL )->m_usPort;
for ( iDup = 0 ;
iDup < iL ;
++iDup )
{
if ( g_KernelIpMap.GetPublicIpPtr( iL )->m_usPort ==
g_KernelIpMap.GetPublicIpPtr( iDup )->m_usPort )
{
g_KernelIpMap.GetPublicIpPtr( iL )->m_usUniquePort = 0;
break;
}
}
//
// m_dwNotifyPort is used by driver to build notify/unnotify list
//
g_KernelIpMap.GetPublicIpPtr( iL )->m_dwNotifyPort =
g_KernelIpMap.GetPublicIpPtr( iL )->m_usUniquePort;
g_KernelIpMap.GetPublicIpPtr( iL )->m_pvDirectorHandle = NULL;
}
//
// scan all pub / servers, inc count for prv if avail load != 0
// kernel will use it to check if prv IP in use
//
for ( iS = 0;
iS < g_KernelIpMap.ServerCount() ;
++iS )
{
pS = g_KernelIpMap.GetServerPtr( iS );
if ( pS->m_dwLoadAvail )
{
for ( iL = 0 ;
iL < g_KernelIpMap.PublicIpCount() ;
++iL )
{
iRef = *g_KernelIpMap.GetPrivateIpRef( pS, iL );
if ( iRef != -1 )
{
++g_KernelIpMap.GetPrivateIpEndpoint( iRef )->m_dwRefCount;
}
}
}
}
// IOCTL to load balancing driver
fSt = DeviceIoControl( hDriver,
IOCTL_IISNATIO_SET_CONFIG,
g_KernelIpMap.GetBuffer(),
g_KernelIpMap.GetSize(),
NULL,
0,
&dwOutBytes,
NULL );
if ( !fSt )
{
WCHAR achErr[32];
LPCWSTR pA[1];
pA[0] = achErr;
_itow( GetLastError(), achErr, 10 );
DBG_PRINTF(( achE, "IoCtl to access driver failed: %u\n", GetLastError() ));
ReportIisLbEvent( EVENTLOG_ERROR_TYPE,
IISLB_EVENT_IOCTL_ERROR,
1,
pA );
LeaveCriticalSection( &g_csIpListCheckpoint );
break;
}
else
{
DBG_PRINTF(( achE, "IoCtl to access driver success\n" ));
}
dwWaitTime = USER_TO_KERNEL_INTERVAL;
}
LeaveCriticalSection( &g_csIpListCheckpoint );
if ( WaitForSingleObject( (HANDLE)pV, dwWaitTime ) == WAIT_OBJECT_0 &&
g_fStopUserToKernelThread )
{
break;
}
}
cleanup:
//close driver
if ( hDriver != INVALID_HANDLE_VALUE )
{
CloseHandle( hDriver );
}
SetLbDriverState( FALSE );
CloseHandle( (HANDLE)pV );
return 0;
}
BOOL
CKernelIpMapHelper::CheckAndAddPrivateIp(
CKernelIpEndpoint* pEndpoint,
BOOL fAddIfNotPresent,
LPUINT piFound
)
/*++
Routine Description:
look for IP Endpoint in private IP list, optionaly returns its index
optionaly add it to list if not found
Arguments:
pEndpoint - Endpoint to look for and optionaly add
fAddIfNotPresent - TRUE to add if endpoint not found in list
piFound - (optional, can be NULL ) updated with index if found, otherwise -1
Return Value:
TRUE if no error ( even if not found ), otherwise FALSE
--*/
{
UINT iLoop;
CKernelIpEndpoint* pLoopEndpoint;
for ( iLoop = 0 ;
EnumPrivateIpList( iLoop, &pLoopEndpoint ) ;
++iLoop )
{
if ( !memcmp( pLoopEndpoint, pEndpoint, sizeof(CKernelIpEndpoint) ) )
{
if ( piFound )
{
*piFound = iLoop;
}
return TRUE;
}
}
if ( piFound )
{
*piFound = (UINT)-1;
}
if ( fAddIfNotPresent )
{
if ( !m_PrivateIpList.Append( (LPBYTE)pEndpoint, (DWORD)sizeof(CKernelIpEndpoint) ) )
{
return FALSE;
}
}
return TRUE;
}
BOOL
CKernelIpMapHelper::EnumPrivateIpList(
UINT iIndex,
CKernelIpEndpoint** ppEndpoint
)
/*++
Routine Description:
Enumerate endpoints in private IP list
Arguments:
iIndex - 0 based index
ppEndpoint - updated with read-only ptr to endpoint
Return Value:
TRUE if no error, otherwise FALSE ( end of list )
--*/
{
if ( iIndex < PrivateIpListCount() )
{
*ppEndpoint = (CKernelIpEndpoint*)(m_PrivateIpList.GetBuff()) + iIndex;
return TRUE;
}
return FALSE;
}
BOOL
ReportIisLbEvent(
WORD wType,
DWORD dwEventId,
WORD cNbStr,
LPCWSTR* pStr
)
/*++
Routine Description:
Log an event based on type, ID and insertion strings
Arguments:
wType -- event type ( error, warning, information )
dwEventId -- event ID ( as defined by the .mc file )
cNbStr -- nbr of LPWSTR in the pStr array
pStr -- insertion strings
Returns:
TRUE if success, FALSE if failure
--*/
{
BOOL fSt = TRUE;
HANDLE hEventLog = NULL;
hEventLog = RegisterEventSourceW(NULL,L"IISLB");
if ( hEventLog != NULL )
{
if (!ReportEventW(hEventLog, // event log handle
wType, // event type
0, // category zero
(DWORD) dwEventId, // event identifier
NULL, // no user security identifier
cNbStr, // count of substitution strings (may be no strings)
// less ProgName (argv[0]) and Event ID (argv[1])
0, // no data
(LPCWSTR *) pStr, // address of string array
NULL)) // address of data
{
fSt = FALSE;
}
DeregisterEventSource( hEventLog );
}
else
{
fSt = FALSE;
}
return fSt;
}
/*===================================================================
InitOleSecurity
Setup for and call CoInitializeSecurity. This will avoid problems with
DCOM security on sites that have no default security.
Parameters:
None
Returns:
Retail -- Always returns NOERROR, failures are ignored.
Debug -- DBG_ASSERTs on error and returns error code
Side effects:
Sets desktop
===================================================================*/
HRESULT InitOleSecurity(
)
{
HRESULT hr = NOERROR;
DWORD err;
hr = CoInitializeSecurity(
NULL,
-1,
NULL,
NULL,
RPC_C_AUTHN_LEVEL_CONNECT,
RPC_C_IMP_LEVEL_IDENTIFY,
NULL,
EOAC_NONE,
NULL );
if (SUCCEEDED(hr))
{
err = GetLastError();
hr = HRESULT_FROM_WIN32(err);
}
else
{
DBG_PUTS( "Failed to set OLE security\n" );
}
return(NOERROR);
}
BOOL
LbAccessCheck(
DWORD dwAccess
)
/*++
Routine Description:
Perform security access check
Arguments:
dwAccess - access type FILE_READ_DATA / FILE_WRITE_DATA
Returns:
TRUE on success, FALSE on failure
--*/
{
BOOL bReturn = TRUE;
HANDLE hAccTok = NULL;
DWORD dwRef;
LPBYTE pAcl;
HKEY registryKey;
DWORD valueType;
DWORD valueSize;
EnterCriticalSection( &g_csAcl );
if ( (pAcl = g_pAcl) == NULL )
{
if ( RegOpenKeyEx( HKEY_CLASSES_ROOT,
"APPID\\{a9da4430-65c5-11d1-a700-00a0c922e752}",
0,
KEY_READ,
&registryKey ) == ERROR_SUCCESS )
{
valueSize = 0;
if ( RegQueryValueEx( registryKey,
"AccessPermission",
NULL,
&valueType,
NULL,
&valueSize ) == ERROR_SUCCESS &&
( g_pAcl = (LPBYTE)LocalAlloc( LMEM_FIXED, valueSize )) != NULL )
{
if ( RegQueryValueEx( registryKey,
"AccessPermission",
NULL,
&valueType,
g_pAcl,
&valueSize ) != ERROR_SUCCESS )
{
LocalFree( g_pAcl );
g_pAcl = NULL;
}
else
{
pAcl = g_pAcl;
}
}
RegCloseKey( registryKey );
}
}
if ( pAcl )
{
IServerSecurity* pSec;
//
// test if already impersonated ( inprocess call w/o marshalling )
// If not call DCOM to retrieve security context & impersonate, then
// extract access token.
//
if ( OpenThreadToken( GetCurrentThread(),
TOKEN_ALL_ACCESS,
TRUE,
&hAccTok ) )
{
pSec = NULL;
}
else
{
if ( SUCCEEDED( CoGetCallContext( IID_IServerSecurity, (void**)&pSec ) ) )
{
pSec->ImpersonateClient();
if ( !OpenThreadToken( GetCurrentThread(),
TOKEN_EXECUTE|TOKEN_QUERY,
TRUE,
&hAccTok ) )
{
hAccTok = NULL;
}
}
else
{
pSec = NULL;
}
}
if ( hAccTok )
{
DWORD dwAcc;
//
// Protected properties require EXECUTE access rights instead of WRITE
//
if ( dwAccess & FILE_WRITE_DATA )
{
dwAcc = COM_RIGHTS_EXECUTE;
}
else
{
dwAcc = COM_RIGHTS_EXECUTE;
}
DWORD dwGrantedAccess;
BYTE PrivSet[400];
DWORD cbPrivilegeSet = sizeof(PrivSet);
BOOL fAccessGranted;
if ( !AccessCheck( pAcl,
hAccTok,
dwAcc,
&g_FileGenericMapping,
(PRIVILEGE_SET *) &PrivSet,
&cbPrivilegeSet,
&dwGrantedAccess,
&fAccessGranted ) ||
!fAccessGranted )
{
//
// If read access denied, retry with restricted read right
// only if not called from GetAll()
//
bReturn = FALSE;
}
CloseHandle( hAccTok );
}
else
{
//
// For now, assume failure to get IServerSecurity means we are
// in the SYSTEM context, so grant access.
//
bReturn = TRUE;
}
if ( pSec )
{
pSec->RevertToSelf();
pSec->Release();
}
}
else
{
//
// No ACL : access check succeed
//
bReturn = TRUE;
}
if ( !bReturn )
{
SetLastError( ERROR_ACCESS_DENIED );
}
LeaveCriticalSection( &g_csAcl );
return bReturn;
}
VOID
FlushLbAccessCheck(
)
/*++
Routine Description:
Flush access check ACL cache
Arguments:
None
Returns:
Nothing
--*/
{
EnterCriticalSection( &g_csAcl );
if ( g_pAcl != NULL )
{
LocalFree( g_pAcl );
g_pAcl = NULL;
}
LeaveCriticalSection( &g_csAcl );
}
HRESULT
WaitForServiceStatus(
SC_HANDLE schDependent,
DWORD dwDesiredServiceState
)
/*++
Routine Description:
Wait for status of specified service to match desired state
with MAX_SLEEP timeout
Arguments:
schDependent - service/driver to monitor
dwDesiredServiceState - service/driver state to wait for
Returns:
STATUS_SUCCESS if success, otherwise error status
--*/
{
DWORD dwSleepTotal = 0;
SERVICE_STATUS ssDependent;
DEBUG_BUFFER;
while ( dwSleepTotal < MAX_SLEEP )
{
if ( QueryServiceStatus( schDependent, &ssDependent ) )
{
if ( ssDependent.dwCurrentState == dwDesiredServiceState )
{
return S_OK;
}
else
{
//
// Still pending...
//
Sleep( SLEEP_INTERVAL );
dwSleepTotal += SLEEP_INTERVAL;
DBG_PRINTF(( achE, "WaitForServiceStatus sleep, state is %u waiting for %u",
ssDependent.dwCurrentState, dwDesiredServiceState ));
}
}
else
{
return RETURNCODETOHRESULT( GetLastError() );
}
}
return RETURNCODETOHRESULT( ERROR_SERVICE_REQUEST_TIMEOUT );
}
HRESULT
SetLbDriverState(
BOOL fStart
)
/*++
Routine Description:
Start/Stop Load balancing driver
Arguments:
fStart - TRUE to start driver, FALSE to stop
Returns:
STATUS_SUCCESS if success, otherwise error status
--*/
{
SC_HANDLE schSCM;
SC_HANDLE schDependent;
HRESULT hresReturn = S_OK;
SERVICE_STATUS ssDependent;
DEBUG_BUFFER;
schSCM = OpenSCManager( NULL,
NULL,
SC_MANAGER_ALL_ACCESS);
if ( schSCM == NULL )
{
hresReturn = RETURNCODETOHRESULT(GetLastError());
}
else
{
schDependent = OpenService( schSCM,
SZDRIVERNAME,
SERVICE_ALL_ACCESS );
if (schDependent != NULL)
{
if ( fStart )
{
if ( !StartService( schDependent, 0, NULL ) )
{
if ( GetLastError() != ERROR_SERVICE_ALREADY_RUNNING )
{
hresReturn = RETURNCODETOHRESULT( GetLastError() );
DBG_PRINTF(( achE, "SetLbDriverState:StartService %u", GetLastError() ));
}
}
else
{
DBG_PRINTF(( achE, "SetLbDriverState:StartService success\n" ));
}
}
else
{
if ( ControlService( schDependent, SERVICE_CONTROL_STOP, &ssDependent ) ||
GetLastError() == ERROR_INVALID_SERVICE_CONTROL )
{
hresReturn = WaitForServiceStatus( schDependent, SERVICE_STOPPED );
}
else
{
if ( GetLastError() != ERROR_SERVICE_NOT_ACTIVE )
{
hresReturn = RETURNCODETOHRESULT( GetLastError() );
DBG_PRINTF(( achE, "SetLbDriverState:ControlService %u", GetLastError() ));
}
}
CloseServiceHandle( schDependent );
}
}
else
{
hresReturn = RETURNCODETOHRESULT(GetLastError());
DBG_PRINTF(( achE, "SetLbDriverState:OpenService %u", GetLastError() ));
}
CloseServiceHandle( schSCM );
}
return hresReturn;
}
BOOL
CheckQueryMachineStatus(
HQUERY hQuery
)
/*++
Routine Description:
Check machine status in query
This is not the same as checking the result of PdhCollectQueryData() and CStatus
of each counter, as PDH library will attempt to reconnect before reporting an
invalid status, so 1st query after disconnect will give back success status.
Arguments:
hQuery - PDH query
Returns:
TRUE if all machine status OK, otherwise FALSE
--*/
{
PPDHI_QUERY_MACHINE pQMachine;
PPDHI_QUERY pQuery = (PPDHI_QUERY)hQuery;
DEBUG_BUFFER;
//
// Check that the structure is something we understand
//
if ( g_dwPdhVersion == ((DWORD)((PDH_CVERSION_WIN40) + 0x0003)) )
{
pQMachine = pQuery->pFirstQMachine;
while (pQMachine != NULL)
{
DBG_PRINTF(( achE, "Machine %ws status %x\n",
pQMachine->pMachine->szName,
pQMachine->pMachine->dwStatus ));
if ( pQMachine->pMachine->dwStatus != ERROR_SUCCESS )
{
return FALSE;
}
pQMachine = pQMachine->pNext;
}
}
return TRUE;
}