2025-04-27 07:49:33 -04:00

575 lines
16 KiB
C++

//
// Create 2/25/2000 : rajeshr
// cim2xml.cpp : Defines the entry point for the ISAPI Extension DLL
//
#include <windows.h>
#include <httpext.h>
#include <mshtml.h>
#include <msxml.h>
#include <objbase.h>
#include <initguid.h>
#include "provtempl.h"
#include "wmixmlop.h"
#include "cim2xml.h"
// Wrap up the XML String into a Stream and put it in VARIANT
static HRESULT EncodeRequestIntoStream(LPEXTENSION_CONTROL_BLOCK pECB, VARIANT *pVariant);
// This function processes an HTTP Operation
// If the HTTP verb is OPTIONS, then the pDocument argument can be NULL, since it is unused
static void HandleOperation(LPEXTENSION_CONTROL_BLOCK pECB, IXMLDOMDocument *pDocument);
static CProtocolHandlersMap g_protocolHandlerMap;
static HRESULT GetProtocolVersion(LPEXTENSION_CONTROL_BLOCK pECB, BSTR *pstrVersion);
static HRESULT GetProtocolHandler(BSTR strVersion, IWbemXMLOperationsHandler *&pProtocolHandler);
static HRESULT UnloadProtocolHandlers();
static HRESULT LoadProtocolHandlers();
static BOOLEAN CheckManHeader (
const char *pszReceivedManHeader,
const char *pszRequiredManHeader,
DWORD &dwNs);
static LPSTR GetExtensionHeader (char *pszName, BOOLEAN bIsMpostRequest, DWORD dwNs);
static LPCSTR HTTP_MAN_HEADER = "http://www.dmtf.org/cim/mapping/http/v1.0";
static LPCSTR MICROSOFT_MAN_HEADER = "http://www.microsoft.com/wmi";
static LPCSTR HTTP_NS = "ns=";
// Globals
static BSTR WMI_XML_PROTOCOL_VERSION = NULL;
//***************************************************************************
//
// BOOL WINAPI DllMain
//
// DESCRIPTION:
//
// Entry point for DLL.
//
// PARAMETERS:
//
// hModule instance handle
// ulReason why we are being called
// pvReserved reserved
//
// RETURN VALUE:
//
// TRUE if OK.
//
//***************************************************************************
BOOL WINAPI DllMain( HINSTANCE hModule,
DWORD ulReason,
LPVOID lpReserved
)
{
switch (ulReason)
{
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls (hModule);
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
//***************************************************************************
//
// BOOL WINAPI GetExtensionVersion
//
// DESCRIPTION:
//
// Called once by IIS to get version information. Of little consequence.
//
// PARAMETERS:
//
// pVer pointer to a HSE_VERSION_INFO structure that
// will hold the version info.
//
// RETURN VALUE:
//
// TRUE if OK.
//
//***************************************************************************
BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer)
{
// Initialize the COM Library as multi-threaded
if (FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED)))
return FALSE;
if(FAILED(LoadProtocolHandlers()))
return FALSE;
if(!(WMI_XML_PROTOCOL_VERSION = SysAllocString(L"1.0")))
return FALSE;
pVer->dwExtensionVersion = MAKELONG(1, 0);
strcpy(pVer->lpszExtensionDesc, "WMI XML/HTTP ISAPI Extension");
return TRUE;
}
//***************************************************************************
//
// BOOL WINAPI HttpExtensionProc
//
// DESCRIPTION:
//
// Called once by IIS to service a single HTTP request.
//
// PARAMETERS:
//
// pECB pointer to a EXTENSION_CONTROL_BLOCK structure that
// will hold the request.
//
// RETURN VALUE:
//
// HSE_STATUS_SUCCESS request has been processed
//
// NOTES:
//
// Status codes are set in the dwHttpStatusCode field of the ECB
//
//***************************************************************************
DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK pECB)
{
// RAJESHR - The below 2 lines are debugging code that need to be removed
TCHAR szUserName[500];
DWORD dwUserLength = 500;
GetUserName(szUserName, &dwUserLength);
// If the method is OPTIONS, then we dont check the body of the request
if (0 == _stricmp(pECB->lpszMethod, "OPTIONS"))
{
HandleOperation(pECB, NULL);
}
else
{
// Create an XML document for the body of the request
//==============================
IXMLDOMDocument *pDocument = NULL;
if(SUCCEEDED(CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
IID_IXMLDOMDocument, (LPVOID *)&pDocument)))
{
VARIANT_BOOL bResult = VARIANT_TRUE;
// Put the XML String in an IStream and wrap it up in a VARIANT
//=============================================================
VARIANT xmlVariant;
VariantInit (&xmlVariant);
if(SUCCEEDED(EncodeRequestIntoStream(pECB, &xmlVariant)))
{
if(SUCCEEDED(pDocument->put_async(VARIANT_FALSE)) &&
SUCCEEDED(pDocument->put_resolveExternals(VARIANT_FALSE)) &&
SUCCEEDED(pDocument->put_validateOnParse(VARIANT_FALSE)))
{
if(SUCCEEDED(pDocument->load(xmlVariant, &bResult)))
{
if(bResult == VARIANT_TRUE)
{
HandleOperation(pECB, pDocument);
}
else
{
// Return a "400 - Bad Request"
HSE_SEND_HEADER_EX_INFO sendHeader;
sendHeader.pszHeader = "CIMError: request-not-valid";
sendHeader.cchHeader = strlen(sendHeader.pszHeader);
sendHeader.fKeepConn = FALSE;
sendHeader.pszStatus = "400 - Bad Request";
sendHeader.cchStatus = strlen (sendHeader.pszStatus);
pECB->ServerSupportFunction (pECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER_EX,
&sendHeader, 0, 0);
IXMLDOMParseError *pError = NULL;
if(SUCCEEDED(pDocument->get_parseError(&pError)))
{
LONG errorCode = 0;
pError->get_errorCode(&errorCode);
LONG line=0, linepos=0;
BSTR reason=NULL, srcText = NULL;
if(SUCCEEDED(pError->get_line(&line)) &&
SUCCEEDED(pError->get_linepos(&linepos)) &&
SUCCEEDED(pError->get_reason(&reason)) &&
SUCCEEDED(pError->get_srcText(&srcText)))
{
}
pError->Release();
if(reason)
SysFreeString(reason);
if(srcText)
SysFreeString(srcText);
}
}
}
}
VariantClear (&xmlVariant);
}
pDocument->Release();
}
}
return HSE_STATUS_SUCCESS;
}
//***************************************************************************
//
// BOOL WINAPI TerminateExtension
//
// DESCRIPTION:
//
// Called once by IIS to unload the extension.
//
// PARAMETERS:
//
// dwFlags determines nature of request (advisory or mandatory)
//
// RETURN VALUE:
//
// TRUE (always agree to an unload for now)
//
//***************************************************************************
BOOL WINAPI TerminateExtension(DWORD dwFlags )
{
SysFreeString(WMI_XML_PROTOCOL_VERSION);
// Unload all the protocol handlers that we have in out table
UnloadProtocolHandlers();
// We need to unload the COM DLLs that we loaded in this extension
CoFreeUnusedLibraries();
CoUninitialize();
return TRUE;
}
//***************************************************************************
//
// BOOL EncodeRequestIntoStream
//
// Description:
//
// Takes an XML document in byte format and encodes it within an
// IStream.
//
// Parameters:
//
// pECB Pointer to the extension control block
// pVariant Pointer to hold wrapped XML document on return. This needs to
// cleared by the client
//
// Return Value:
//
// TRUE if successful, FALSE otherwise
//
//***************************************************************************
HRESULT EncodeRequestIntoStream(LPEXTENSION_CONTROL_BLOCK pECB, VARIANT *pVariant)
{
HRESULT result = E_FAIL;
IStream *pStream = NULL;
if (SUCCEEDED(result = CreateStreamOnHGlobal(NULL, TRUE, &pStream)))
{
#if 0
FILE *f = fopen ("c:\\temp\\xmllog.xml", "a");
fprintf (f, "%s", pszXmlString);
fflush (f);
#endif
// Not all of the request may be present in the ECB; need to
// keep reading from the input stream
if(SUCCEEDED(result = pStream->Write(pECB->lpbData, pECB->cbAvailable, NULL)))
{
DWORD cbDataRead = pECB->cbAvailable;
// Is there more data than was in the ECB?
if (cbDataRead < pECB->cbTotalBytes)
{
#define XML_BUFSIZ 1024
DWORD dwBufSiz = XML_BUFSIZ;
BYTE pDataBuffer[XML_BUFSIZ];
while ((cbDataRead < pECB->cbTotalBytes) &&
pECB->ReadClient (pECB->ConnID, (LPVOID) pDataBuffer, &dwBufSiz) &&
(0 < dwBufSiz))
{
if (FAILED(result = pStream->Write ((LPVOID) pDataBuffer, dwBufSiz, NULL)))
break;
cbDataRead += dwBufSiz;
dwBufSiz = XML_BUFSIZ;
}
#undef XML_BUFSIZ
}
}
if (SUCCEEDED (result))
{
LARGE_INTEGER offset;
offset.LowPart = offset.HighPart = 0;
pStream->Seek (offset, STREAM_SEEK_SET, NULL);
VariantInit(pVariant);
pVariant->vt = VT_UNKNOWN;
pVariant->punkVal = pStream;
}
else
pStream->Release ();
}
return result;
}
/*
* This function processes an HTTP Operation
* If the HTTP verb is OPTIONS, then the pDocument argument can be NULL, since it is unused
* No Status is returned, since any CIM error goes back in the body of the response, and
* any HTTP Header error is communicated to IIS thry the pECB argument
*/
void HandleOperation(LPEXTENSION_CONTROL_BLOCK pECB, IXMLDOMDocument *pDocument)
{
// We first look at the
// If this is an OPTIONs request, then pDocument will be NULL
// We always return the
// Create a protocol handler based on the CIMPROTOCOL version# and call ProcessHTTPRequest() on it.
// Peek into the XML Document to get the version# of the protocol
// Then we use the version# to get the CLSID of the protocol handler
BSTR strVersion = NULL;
bool bAssumeDefaultVersion = false;
if(FAILED(GetProtocolVersion(pECB, &strVersion)))
bAssumeDefaultVersion = true;
IWbemXMLOperationsHandler *pProtocolHandler = NULL;
if(SUCCEEDED(GetProtocolHandler((bAssumeDefaultVersion)? WMI_XML_PROTOCOL_VERSION : strVersion, pProtocolHandler)))
{
pProtocolHandler->ProcessHTTPRequest(pECB, pDocument);
// No need to Release() it since the CMap::Lookup() does not addref it,
// and we keep it for the duration of our ISAPI extension
// pProtocolHandler->Release();
}
else
{
// Return a "501 - Not implemented" - RAJESHR include the CIMError header here as per the spec above
HSE_SEND_HEADER_EX_INFO sendHeader;
sendHeader.pszHeader = "CIMError: unsupported-protocol-version";
sendHeader.cchHeader = strlen(sendHeader.pszHeader);
sendHeader.fKeepConn = FALSE;
sendHeader.pszStatus = "501 Not Implemented";
sendHeader.cchStatus = strlen (sendHeader.pszStatus);
pECB->ServerSupportFunction (pECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER_EX,
&sendHeader, 0, 0);
}
SysFreeString(strVersion);
}
static HRESULT GetProtocolVersion(LPEXTENSION_CONTROL_BLOCK pECB, BSTR *pstrVersion)
{
// We first check to see if there is a a MicrosoftWMIProtocolVersion header
// Then, we check for the CIMProtocolVersionHeader
// Lastly, we assume 1.0 as the version
// ===============================================
// Start by getting the Man Header in case of an M-POST request
bool bIsMpost = false;
CHAR szTempBuffer [1024];
DWORD dwBufferSize = 1024;
if (0 == _stricmp(pECB->lpszMethod, "M-POST"))
bIsMpost = true;
DWORD dwMicrosoftNs = 0;
DWORD dwDMTFNs = 0;
// Get the namespace identifiers for DMTF and Microsoft namespaces
if(bIsMpost)
{
dwBufferSize = 1024;
if (pECB->GetServerVariable (pECB->ConnID, "HTTP_MAN", szTempBuffer, &dwBufferSize))
{
// Get the Microsoft Man header number
CheckManHeader(szTempBuffer, MICROSOFT_MAN_HEADER, dwMicrosoftNs);
CheckManHeader(szTempBuffer, HTTP_MAN_HEADER, dwDMTFNs);
}
}
// Get the MicorosoftWMIProtocolVersion header
dwBufferSize = 1024;
LPSTR pCIMHeader = GetExtensionHeader ("MicorosoftWMIProtocolVersion", bIsMpost, dwMicrosoftNs);
if (pCIMHeader && pECB->GetServerVariable (pECB->ConnID, pCIMHeader, szTempBuffer, &dwBufferSize) && szTempBuffer)
{
WCHAR wTemp[24];
int len = 0;
if(len = MultiByteToWideChar(CP_ACP, 0, szTempBuffer, strlen(szTempBuffer), wTemp, 24))
{
*pstrVersion = SysAllocStringLen(wTemp, len);
return S_OK;
}
}
delete pCIMHeader;
// Get the CIMProtocolVersion header
dwBufferSize = 1024;
pCIMHeader = NULL;
pCIMHeader = GetExtensionHeader ("CIMProtocolVersion", bIsMpost, dwDMTFNs);
if (pCIMHeader && pECB->GetServerVariable (pECB->ConnID, pCIMHeader, szTempBuffer, &dwBufferSize) && szTempBuffer)
{
WCHAR wTemp[24];
int len = 0;
if(len = MultiByteToWideChar(CP_ACP, 0, szTempBuffer, strlen(szTempBuffer), wTemp, 24))
{
*pstrVersion = SysAllocStringLen(wTemp, len);
return S_OK;
}
}
delete pCIMHeader;
return E_FAIL;
}
// A Macro to skip white spaces - useful in header parsing
#define SKIPWS(x) while (x && isspace (*x)) x++;
static BOOLEAN CheckManHeader (
const char *pszReceivedManHeader,
const char *pszRequiredManHeader,
DWORD &dwNs
)
{
BOOLEAN result = FALSE;
LPCSTR ptr = NULL;
// Get the location of the Man header in the string
if (ptr = strstr(pszReceivedManHeader, pszRequiredManHeader))
{
// Look for the "; ns=XX" string
SKIPWS(ptr)
if (ptr && (ptr = strchr (ptr, ';')) && *(++ptr))
{
SKIPWS(ptr)
if (ptr && (0 == _strnicmp (ptr, HTTP_NS, strlen (HTTP_NS))))
{
// Now we should find ourselves a NS value
ptr += strlen (HTTP_NS);
if (ptr)
{
dwNs = strtol (ptr, NULL, 0);
result = TRUE;
}
}
}
}
return result;
}
static LPSTR GetExtensionHeader (char *pszName, BOOLEAN bIsMpostRequest, DWORD dwNs)
{
LPSTR pszHeader = NULL;
if (bIsMpostRequest)
{
if(pszHeader = new char [strlen("HTTP") + strlen(pszName) + 4])
sprintf (pszHeader, "%s_%02d_%s", "HTTP", dwNs, pszName);
}
else
{
if(pszHeader = new char [strlen("HTTP") + strlen (pszName) + 2])
sprintf (pszHeader, "%s_%s", "HTTP", pszName);
}
return pszHeader;
}
static HRESULT GetProtocolHandler(BSTR strVersion, IWbemXMLOperationsHandler *&pProtocolHandler)
{
if(g_protocolHandlerMap.Lookup(strVersion, pProtocolHandler))
return S_OK;
return E_FAIL;
}
static HRESULT LoadProtocolHandlers()
{
// Go thru the registry key looking for protocol handlers
HKEY hHandlers = NULL;
if(RegOpenKey(HKEY_LOCAL_MACHINE, __TEXT("SOFTWARE\\Microsoft\\WBEM\\xml\\ProtocolHandlers"), &hHandlers) == ERROR_SUCCESS)
{
DWORD dwIndex = 0;
WCHAR pszValueName[24];
DWORD dwValueNameLen = 24;
WCHAR pszValue[128];
DWORD dwValueLen = 128*2;
while(RegEnumValue(hHandlers, dwIndex, pszValueName, &dwValueNameLen, NULL, NULL, (LPBYTE)pszValue, &dwValueLen) == ERROR_SUCCESS)
{
BSTR strValueName = NULL;
if(strValueName = SysAllocString(pszValueName))
{
GUID oNextGUID;
if(UuidFromString(pszValue, (UUID *)(&oNextGUID)) == RPC_S_OK)
{
IWbemXMLOperationsHandler *pNextHandler = NULL;
if(SUCCEEDED(CoCreateInstance(oNextGUID,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemXMLOperationsHandler, (LPVOID *) &pNextHandler)))
{
// Add it to our table
g_protocolHandlerMap.SetAt(strValueName, pNextHandler);
}
}
}
dwIndex ++;
}
RegCloseKey(hHandlers);
}
// We always succeed
return S_OK;
}
static HRESULT UnloadProtocolHandlers()
{
// Go thru our table and release the key and values of the handlers
POSITION p = g_protocolHandlerMap.GetStartPosition();
while(p)
{
BSTR strKey = NULL;
IWbemXMLOperationsHandler *pNextHandler = NULL;
g_protocolHandlerMap.GetNextAssoc(p, strKey, pNextHandler);
SysFreeString(strKey);
pNextHandler->Release();
}
return S_OK;
}
// A hashing function for a BSTR
UINT AFXAPI HashKey(BSTR key)
{
UINT nHash = 0;
while (*key)
nHash = (nHash<<5) + nHash + *key++;
return nHash;
}
BOOL AFXAPI CompareElements(const BSTR* pElement1, const BSTR* pElement2)
{
return ! _wcsicmp(*pElement1, *pElement2);
}