// NCProvider.cpp : Implementation of CNCProvider #include "precomp.h" #include "NCProv.h" #include "NCProvider.h" #include "NCDefs.h" #include #include "Buffer.h" #include "dutils.h" #include "NCObjAPI.h" #include #define COUNTOF(x) (sizeof(x)/sizeof(x[0])) // We'll need this for our inproc clients. CPipeToProvMap g_mapPipeToProv; ///////////////////////////////////////////////////////////////////////////// // CNCProvider CNCProvider::CNCProvider() : m_heventDone(NULL), m_heventConnect(NULL), m_hPipe( NULL ), m_hthreadConnect(NULL), m_heventNewQuery(NULL), m_heventCancelQuery(NULL), m_heventAccessCheck(NULL), m_hConnection(NULL), m_pProv(NULL) { InitializeCriticalSection(&m_cs); } CNCProvider::~CNCProvider() { DeleteCriticalSection(&m_cs); } void CNCProvider::FinalRelease() { // // do potentially time consuming cleanup in this function rather than // DTOR. Reason is that ATL decrements the module ref count before calling // the DTOR. This means that a call to DllCanUnloadNow will return TRUE // while there is still a call executing in the module. The race condition // is that the module could be unloaded while it is still being executed. // ATL will call FinalRelease() before decrementing the module refcount // making this race condition much smaller. COM addresses this race // condition by waiting for a bit to unload the module after returning // TRUE. This wait can be controlled by the delay unload param to // CoFreeUnusedLibrariesEx(). This allows the call to the last Release() // of the COM object to finish, before being unloaded. // if ( m_hthreadConnect ) { SetEvent(m_heventDone); WaitForSingleObject( m_hthreadConnect, INFINITE ); CloseHandle(m_hthreadConnect); } if (m_heventDone) CloseHandle(m_heventDone); if (m_heventNewQuery) WmiDestroyObject(m_heventNewQuery); if (m_heventCancelQuery) WmiDestroyObject(m_heventCancelQuery); if (m_heventAccessCheck) WmiDestroyObject(m_heventAccessCheck); if (m_hConnection) WmiEventSourceDisconnect(m_hConnection); delete m_pProv; } HRESULT STDMETHODCALLTYPE CNCProvider::Initialize( /* [in] */ LPWSTR pszUser, /* [in] */ LONG lFlags, /* [in] */ LPWSTR pszNamespace, /* [in] */ LPWSTR pszLocale, /* [in] */ IWbemServices __RPC_FAR *pNamespace, /* [in] */ IWbemContext __RPC_FAR *pCtx, /* [in] */ IWbemProviderInitSink __RPC_FAR *pInitSink) { m_pProv = new CProvInfo; if ( m_pProv == NULL ) { return WBEM_E_OUT_OF_MEMORY; } m_pProv->SetNamespace(pNamespace); m_hConnection = WmiEventSourceConnect( L"root\\cimv2", L"Standard Non-COM Event Provider", FALSE, 64000, 1000, NULL, NULL ); if ( m_hConnection == NULL ) { return WBEM_E_OUT_OF_MEMORY; } m_heventNewQuery = WmiCreateObjectWithFormat( m_hConnection, L"MSFT_NC_NewQuery", WMI_CREATEOBJ_LOCKABLE, L"Namespace!s! ProviderName!s! Result!d! QueryLanguage!s! " L"Query!s! ID!d! "); m_heventCancelQuery = WmiCreateObjectWithFormat( m_hConnection, L"MSFT_NC_CancelQuery", WMI_CREATEOBJ_LOCKABLE, L"Namespace!s! ProviderName!s! Result!d! ID!d!"); m_heventAccessCheck = WmiCreateObjectWithFormat( m_hConnection, L"MSFT_NC_AccessCheck", WMI_CREATEOBJ_LOCKABLE, L"Namespace!s! ProviderName!s! Result!d! QueryLanguage!s! " L"Query!s! Sid!c[]!"); if ( m_heventNewQuery == NULL || m_heventCancelQuery == NULL || m_heventAccessCheck == NULL ) { return WBEM_E_OUT_OF_MEMORY; } m_heventDone = CreateEvent( NULL, TRUE, FALSE, NULL); if ( m_heventDone == NULL ) { return HRESULT_FROM_WIN32( GetLastError() ); } try { m_strNamespace = pszNamespace; } catch( _com_error ) { return WBEM_E_OUT_OF_MEMORY; } // Tell Windows Management our initialization status. return pInitSink->SetStatus( WBEM_S_INITIALIZED, 0 ); } HRESULT STDMETHODCALLTYPE CNCProvider::SetRegistrationObject( LONG lFlags, IWbemClassObject __RPC_FAR *pProvReg) { _variant_t vName; if (SUCCEEDED(pProvReg->Get( L"Name", 0, &vName, NULL, NULL))) { m_strProvider = V_BSTR(&vName); } return S_OK; } HRESULT STDMETHODCALLTYPE CNCProvider::AccessCheck( /* [in] */ WBEM_CWSTR wszQueryLanguage, /* [in] */ WBEM_CWSTR wszQuery, /* [in] */ long lSidLength, /* [unique][size_is][in] */ const BYTE __RPC_FAR *pSid) { HRESULT hr; try { hr = m_pProv->AccessCheck( wszQueryLanguage, wszQuery, lSidLength, (LPBYTE) pSid); WmiSetAndCommitObject( m_heventAccessCheck, WMI_SENDCOMMIT_SET_NOT_REQUIRED, // Data follows... (LPCWSTR) m_strNamespace, (LPCWSTR) m_strProvider, hr, wszQueryLanguage, wszQuery, pSid, lSidLength); } catch(...) { hr = WBEM_E_FAILED; } return hr; } HRESULT STDMETHODCALLTYPE CNCProvider::NewQuery( /* [in] */ DWORD dwID, /* [in] */ WBEM_WSTR wszQueryLanguage, /* [in] */ WBEM_WSTR wszQuery) { HRESULT hr; try { hr = m_pProv->NewQuery(dwID, wszQueryLanguage, wszQuery); } catch(...) { hr = WBEM_E_FAILED; } #if 1 WmiSetAndCommitObject( m_heventNewQuery, WMI_SENDCOMMIT_SET_NOT_REQUIRED, // Data follows... (LPCWSTR) m_strNamespace, (LPCWSTR) m_strProvider, hr, wszQueryLanguage, wszQuery, dwID); #endif return hr; } HRESULT STDMETHODCALLTYPE CNCProvider::CancelQuery( /* [in] */ DWORD dwID) { try { // Get rid of the query item(s). m_pProv->CancelQuery(dwID); } catch(...) { } WmiSetAndCommitObject( m_heventCancelQuery, WMI_SENDCOMMIT_SET_NOT_REQUIRED, // Data follows... (LPCWSTR) m_strNamespace, (LPCWSTR) m_strProvider, S_OK, dwID); return S_OK; } HRESULT STDMETHODCALLTYPE CNCProvider::ProvideEvents( /* [in] */ IWbemObjectSink __RPC_FAR *pSink, /* [in] */ long lFlags) { DWORD dwID; IWbemEventSink *pEventSink = NULL; HRESULT hr; if (SUCCEEDED(pSink->QueryInterface( IID_IWbemEventSink, (LPVOID*) &pEventSink))) { m_pProv->SetSink(pEventSink); pEventSink->Release(); if (!m_hthreadConnect) { m_hthreadConnect = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE) ConnectThreadProc, this, 0, &dwID); } hr = S_OK; } else hr = WBEM_E_FAILED; return hr; } DWORD WINAPI CNCProvider::ConnectThreadProc(CNCProvider *pThis) { if (SUCCEEDED(CoInitializeEx(NULL, COINIT_MULTITHREADED))) { pThis->ConnectLoop(); CoUninitialize(); } return 0; } // ConnectToNewClient(HANDLE, LPOVERLAPPED) // This function is called to start an overlapped connect operation. // It returns TRUE if an operation is pending or FALSE if the // connection has been completed. BOOL CNCProvider::ConnectToNewClient(HANDLE hPipe, LPOVERLAPPED lpo) { BOOL bConnected, bPendingIO = FALSE; // Start an overlapped connection for this pipe instance. bConnected = ConnectNamedPipe(hPipe, lpo); // Overlapped ConnectNamedPipe should return zero. if (bConnected) return FALSE; switch (GetLastError()) { // The overlapped connection in progress. case ERROR_IO_PENDING: bPendingIO = TRUE; break; // Client is already connected, so signal an event. case ERROR_PIPE_CONNECTED: SetEvent(lpo->hEvent); break; // If an error occurs during the connect operation... default: return FALSE; } return bPendingIO; } #define PIPE_SIZE 64000 BOOL CNCProvider::CreateAndConnectInstance(LPOVERLAPPED lpoOverlap, BOOL bFirst) { SECURITY_ATTRIBUTES sa; sa.nLength = sizeof( SECURITY_ATTRIBUTES ); sa.bInheritHandle = FALSE; LPWSTR lpwszSD = L"D:" // DACL L"(A;;GA;;;SY)" // Allow local system full control L"(A;;GRGW;;;LS)" // Allow local service Read/Write L"(A;;GRGW;;;NS)"; // Allow network service Read/Write if ( ConvertStringSecurityDescriptorToSecurityDescriptor( lpwszSD, SDDL_REVISION_1, &(sa.lpSecurityDescriptor), NULL ) ) { long lFlags = PIPE_ACCESS_DUPLEX | // read/write access FILE_FLAG_OVERLAPPED; // overlapped mode if( bFirst ) { lFlags |= FILE_FLAG_FIRST_PIPE_INSTANCE; } m_hPipe = CreateNamedPipe( m_szNamedPipe, // pipe name lFlags, PIPE_TYPE_MESSAGE | // message-type pipe PIPE_READMODE_MESSAGE | // message read mode PIPE_WAIT, // blocking mode PIPE_UNLIMITED_INSTANCES, // unlimited instances PIPE_SIZE, // output buffer size PIPE_SIZE, // input buffer size 0, // client time-out &sa ); // security per above if ( INVALID_HANDLE_VALUE == m_hPipe ) { return FALSE; } } else { return FALSE; } // // Make sure that the pipe is owned by us // Call a subroutine to connect to the new client. // return ConnectToNewClient(m_hPipe, lpoOverlap); } void CNCProvider::ConnectLoop() { // Init our provider info which will tell our comless providers that // we're ready. try { m_pProv->Init(m_strNamespace, m_strProvider); } catch( CX_MemoryException ) { return; } m_heventConnect = CreateEvent( NULL, // no security attribute TRUE, // manual reset event TRUE, // initial state = signaled NULL); // unnamed event object //m_pServerPost = new CPostBuffer(this); // TODO: We need to indicate an error here. if (!m_heventConnect) return; swprintf( m_szNamedPipe, L"\\\\.\\pipe\\" OBJNAME_NAMED_PIPE L"%s%s", (LPCWSTR) m_pProv->m_strBaseNamespace, (LPCWSTR) m_pProv->m_strBaseName); OVERLAPPED oConnect; BOOL bSuccess, bPendingIO; HANDLE hWait[2] = { m_heventDone, m_heventConnect }; DWORD dwRet; oConnect.hEvent = m_heventConnect; bPendingIO = CreateAndConnectInstance(&oConnect, TRUE); // first instance g_mapPipeToProv.AddPipeProv(m_szNamedPipe, this); while ((dwRet = WaitForMultipleObjectsEx(2, hWait, FALSE, INFINITE, TRUE)) != WAIT_OBJECT_0) { if ( dwRet == WAIT_FAILED ) { break; } switch(dwRet) { case 1: { if (bPendingIO) { DWORD dwBytes; bSuccess = GetOverlappedResult( m_hPipe, // pipe handle &oConnect, // OVERLAPPED structure &dwBytes, // bytes transferred FALSE); // does not wait // TODO: This is an error, but what to do? if (!bSuccess) break; } CPipeClient *pInfo = new CPipeClient(this, m_hPipe); if (pInfo) { bSuccess = ReadFileEx( pInfo->m_hPipe, pInfo->m_bufferRecv.m_pBuffer, pInfo->m_bufferRecv.m_dwSize, &pInfo->m_info.overlap, (LPOVERLAPPED_COMPLETION_ROUTINE) CompletedReadRoutine); if (!bSuccess) DisconnectAndClose(pInfo); } bPendingIO = CreateAndConnectInstance(&oConnect, FALSE); break; } case WAIT_IO_COMPLETION: break; } } g_mapPipeToProv.RemovePipeProv(m_szNamedPipe); CloseHandle(m_hPipe); CloseHandle(m_heventConnect); } void CNCProvider::DisconnectAndClose(CClientInfo *pInfo) { m_pProv->RemoveClient(pInfo); } //#define NO_WINMGMT //#define NO_INDICATE //#define NO_DECODE #ifdef NO_INDICATE DWORD m_dwEvents = 0; #endif void WINAPI CNCProvider::CompletedReadRoutine( DWORD dwErr, DWORD nBytesRead, LPOVERLAPPED pOverlap) { CPipeClient *pInfo = ((OLAP_AND_CLIENT*) pOverlap)->pInfo; CNCProvider *pThis = pInfo->m_pProvider; #ifndef _DEBUG try #endif { #ifndef NO_DECODE if (nBytesRead) { pInfo->PostBuffer(pInfo->m_bufferRecv.m_pBuffer, nBytesRead); } #endif } #ifndef _DEBUG catch(...) { } #endif // The read operation has finished, so write a response (if no // error occurred). if (dwErr == 0) { BOOL bSuccess; bSuccess = ReadFileEx( pInfo->m_hPipe, pInfo->m_bufferRecv.m_pBuffer, pInfo->m_bufferRecv.m_dwSize, pOverlap, (LPOVERLAPPED_COMPLETION_ROUTINE) CompletedReadRoutine); if (!bSuccess) pThis->DisconnectAndClose(pInfo); } else pThis->DisconnectAndClose(pInfo); } ///////////////////////////////////////////////////////////////////////////// // Inproc helper stuff /* ~CPipeToProvMap::CPipeToProvMap() { } */ void CPipeToProvMap::AddPipeProv(LPCWSTR szPipeName, CNCProvider *pProv) { Lock(); (*this)[szPipeName] = pProv; Unlock(); } void CPipeToProvMap::RemovePipeProv(LPCWSTR szPipeName) { Lock(); CBstrToProviderIterator item = g_mapPipeToProv.find(szPipeName); if (item != g_mapPipeToProv.end()) { //CNCProvider *pProv = (*item).second; //pProv->m_pServerPost->SetProvider(NULL); erase(item); } Unlock(); } BOOL WINAPI InprocConnect( LPCWSTR szPipeName, IPostBuffer *pClientPost, IPostBuffer **ppServerPost) { BOOL bRet; g_mapPipeToProv.Lock(); CBstrToProviderIterator item = g_mapPipeToProv.find(szPipeName); if (item != g_mapPipeToProv.end()) { CNCProvider *pProv = (*item).second; CInprocClient *pInfo = new CInprocClient(pProv, pClientPost); //pProv->m_prov.AddClient(pInfo); pInfo->AddRef(); *ppServerPost = pInfo; bRet = TRUE; } else bRet = FALSE; g_mapPipeToProv.Unlock(); return bRet; }