/*++ Copyright (C) 1998-2001 Microsoft Corporation Module Name: UBSKMRSH.CPP Abstract: Unbound Sink Marshaling History: --*/ #include "precomp.h" #include <stdio.h> #include "ubskmrsh.h" #include <fastall.h> #include <cominit.h> #define WBEM_S_NEW_STYLE 0x400FF //**************************************************************************** //**************************************************************************** // PS FACTORY //**************************************************************************** //**************************************************************************** //*************************************************************************** // // CUnboundSinkFactoryBuffer::XUnboundSinkFactory::CreateProxy // // DESCRIPTION: // // Creates a facelet. Also sets the outer unknown since the proxy is going to be // aggregated. // // RETURN VALUE: // // S_OK all is well // //*************************************************************************** STDMETHODIMP CUnboundSinkFactoryBuffer::XUnboundSinkFactory::CreateProxy(IN IUnknown* pUnkOuter, IN REFIID riid, OUT IRpcProxyBuffer** ppProxy, void** ppv) { if(riid != IID_IWbemUnboundObjectSink) { *ppProxy = NULL; *ppv = NULL; return E_NOINTERFACE; } CUnboundSinkProxyBuffer* pProxy = new CUnboundSinkProxyBuffer(m_pObject->m_pLifeControl, pUnkOuter); SCODE sc = E_OUTOFMEMORY; if ( NULL != pProxy ) { pProxy->QueryInterface(IID_IRpcProxyBuffer, (void**)ppProxy); sc = pProxy->QueryInterface(riid, (void**)ppv); } return sc; } //*************************************************************************** // // CUnboundSinkFactoryBuffer::XUnboundSinkFactory::CreateStub // // DESCRIPTION: // // Creates a stublet. Also passes a pointer to the clients IWbemUnboundObjectSink // interface. // // RETURN VALUE: // // S_OK all is well // //*************************************************************************** STDMETHODIMP CUnboundSinkFactoryBuffer::XUnboundSinkFactory::CreateStub(IN REFIID riid, IN IUnknown* pUnkServer, OUT IRpcStubBuffer** ppStub) { if(riid != IID_IWbemUnboundObjectSink) { *ppStub = NULL; return E_NOINTERFACE; } CUnboundSinkStubBuffer* pStub = new CUnboundSinkStubBuffer(m_pObject->m_pLifeControl, NULL); if ( NULL != pStub ) { pStub->QueryInterface(IID_IRpcStubBuffer, (void**)ppStub); // Pass the pointer to the clients object if(pUnkServer) { HRESULT hres = (*ppStub)->Connect(pUnkServer); if(FAILED(hres)) { delete pStub; *ppStub = NULL; } return hres; } else { return S_OK; } } else { return E_OUTOFMEMORY; } } //*************************************************************************** // // void* CUnboundSinkFactoryBuffer::GetInterface(REFIID riid) // // DESCRIPTION: // // CUnboundSinkFactoryBuffer is derived from CUnk. Since CUnk handles the QI calls, // all classes derived from it must support this function. // // RETURN VALUE: // // S_OK all is well // //*************************************************************************** void* CUnboundSinkFactoryBuffer::GetInterface(REFIID riid) { if(riid == IID_IPSFactoryBuffer) return &m_XUnboundSinkFactory; else return NULL; } //**************************************************************************** //**************************************************************************** // PROXY //**************************************************************************** //**************************************************************************** //*************************************************************************** // // CUnboundSinkProxyBuffer::CUnboundSinkProxyBuffer // ~CUnboundSinkProxyBuffer::CUnboundSinkProxyBuffer // // DESCRIPTION: // // Constructor and destructor. The main things to take care of are the // old style proxy, and the channel // // RETURN VALUE: // // S_OK all is well // //*************************************************************************** CUnboundSinkProxyBuffer::CUnboundSinkProxyBuffer(CLifeControl* pControl, IUnknown* pUnkOuter) : m_pControl(pControl), m_pUnkOuter(pUnkOuter), m_lRef(0), m_XUnboundSinkFacelet(this), m_pChannel(NULL), m_pOldProxy( NULL ), m_pOldProxyUnboundSink( NULL ), m_fRemote( false ) { m_pControl->ObjectCreated(this); m_StubType = UNKNOWN; } CUnboundSinkProxyBuffer::~CUnboundSinkProxyBuffer() { // This MUST be released first if ( NULL != m_pOldProxyUnboundSink ) { m_pOldProxyUnboundSink->Release(); } if ( NULL != m_pOldProxy ) { m_pOldProxy->Release(); } if(m_pChannel) m_pChannel->Release(); m_pControl->ObjectDestroyed(this); } ULONG STDMETHODCALLTYPE CUnboundSinkProxyBuffer::AddRef() { return InterlockedIncrement(&m_lRef); } ULONG STDMETHODCALLTYPE CUnboundSinkProxyBuffer::Release() { long lRef = InterlockedDecrement(&m_lRef); if(lRef == 0) delete this; return lRef; } HRESULT STDMETHODCALLTYPE CUnboundSinkProxyBuffer::QueryInterface(REFIID riid, void** ppv) { if(riid == IID_IUnknown || riid == IID_IRpcProxyBuffer) { *ppv = (IRpcProxyBuffer*)this; } else if(riid == IID_IWbemUnboundObjectSink) { *ppv = (IWbemUnboundObjectSink*)&m_XUnboundSinkFacelet; } else return E_NOINTERFACE; ((IUnknown*)*ppv)->AddRef(); return S_OK; } //*************************************************************************** // // HRESULT STDMETHODCALLTYPE CUnboundSinkProxyBuffer::XUnboundSinkFacelet:: // QueryInterface(REFIID riid, void** ppv) // // DESCRIPTION: // // Supports querries for interfaces. The only thing unusual is that // this object is aggregated by the proxy manager and so some interface // requests are passed to the outer IUnknown interface. // // RETURN VALUE: // // S_OK all is well // //*************************************************************************** HRESULT STDMETHODCALLTYPE CUnboundSinkProxyBuffer::XUnboundSinkFacelet:: QueryInterface(REFIID riid, void** ppv) { // All other interfaces are delegated to the UnkOuter if( riid == IID_IRpcProxyBuffer ) { // Trick #2: this is an internal interface that should not be delegated! // =================================================================== return m_pObject->QueryInterface(riid, ppv); } else if ( riid == IID_IClientSecurity ) { // We handle this here in the facelet AddRef(); *ppv = (IClientSecurity*) this; return S_OK; } else { return m_pObject->m_pUnkOuter->QueryInterface(riid, ppv); } } ////////////////////////////// // IClientSecurity Methods // ////////////////////////////// HRESULT STDMETHODCALLTYPE CUnboundSinkProxyBuffer::XUnboundSinkFacelet:: QueryBlanket( IUnknown* pProxy, DWORD* pAuthnSvc, DWORD* pAuthzSvc, OLECHAR** pServerPrincName, DWORD* pAuthnLevel, DWORD* pImpLevel, void** pAuthInfo, DWORD* pCapabilities ) { HRESULT hr = S_OK; // Return the security as stored in the pUnkOuter. IClientSecurity* pCliSec; // We pass through to the PUNKOuter hr = m_pObject->m_pUnkOuter->QueryInterface( IID_IClientSecurity, (void**) &pCliSec ); if ( SUCCEEDED( hr ) ) { hr = pCliSec->QueryBlanket( pProxy, pAuthnSvc, pAuthzSvc, pServerPrincName, pAuthnLevel, pImpLevel, pAuthInfo, pCapabilities ); pCliSec->Release(); } return hr; } HRESULT STDMETHODCALLTYPE CUnboundSinkProxyBuffer::XUnboundSinkFacelet:: SetBlanket( IUnknown* pProxy, DWORD AuthnSvc, DWORD AuthzSvc, OLECHAR* pServerPrincName, DWORD AuthnLevel, DWORD ImpLevel, void* pAuthInfo, DWORD Capabilities ) { HRESULT hr = S_OK; IClientSecurity* pCliSec; // First set the security explicitly on our IUnknown, then we will Set the blanket // on ourselves using the punkOuter (It's tricky but it works...uh...we think). // This will enable us to make calls to QueryInterface(), AddRef()/Release() that // may have to go remote // Only set the IUnknown blanket if we are remoting and it appears that the authinfo contains // credentials if ( m_pObject->m_fRemote && DoesContainCredentials( (COAUTHIDENTITY*) pAuthInfo ) ) { // This will enable us to make calls to QueryInterface(), AddRef()/Release() that // may have to go remote hr = CoSetProxyBlanket( m_pObject->m_pUnkOuter, AuthnSvc, AuthzSvc, pServerPrincName, AuthnLevel, ImpLevel, pAuthInfo, Capabilities ); } if ( SUCCEEDED( hr ) ) { // We pass through to the PUNKOuter hr = m_pObject->m_pUnkOuter->QueryInterface( IID_IClientSecurity, (void**) &pCliSec ); if ( SUCCEEDED( hr ) ) { hr = pCliSec->SetBlanket( pProxy, AuthnSvc, AuthzSvc, pServerPrincName, AuthnLevel, ImpLevel, pAuthInfo, Capabilities ); pCliSec->Release(); } } // If Set Blanket on IUnknown return hr; } HRESULT STDMETHODCALLTYPE CUnboundSinkProxyBuffer::XUnboundSinkFacelet:: CopyProxy( IUnknown* pProxy, IUnknown** ppCopy ) { HRESULT hr = S_OK; IClientSecurity* pCliSec; // We pass through to the PUNKOuter hr = m_pObject->m_pUnkOuter->QueryInterface( IID_IClientSecurity, (void**) &pCliSec ); if ( SUCCEEDED( hr ) ) { hr = pCliSec->CopyProxy( pProxy, ppCopy ); pCliSec->Release(); } return hr; } //*************************************************************************** // // HRESULT STDMETHODCALLTYPE CUnboundSinkProxyBuffer::XUnboundSinkFacelet:: // Indicate( LONG lObjectCount, IWbemClassObject** ppObjArray ) // // DESCRIPTION: // // Proxies the IWbemUnboundObjectSink::Indicate calls. Note that if the stub is an // old style, then the old proxy/stub pair in wbemsvc.dll is used for backward // compatibility. // // RETURN VALUE: // // S_OK all is well // //*************************************************************************** HRESULT STDMETHODCALLTYPE CUnboundSinkProxyBuffer::XUnboundSinkFacelet:: IndicateToConsumer( IWbemClassObject* pLogicalConsumer, LONG lObjectCount, IWbemClassObject** ppObjArray ) { HRESULT hr = S_OK; // Make sure the lObjectCount parameter and the array pointer make sense if ( lObjectCount < 0 ) { return WBEM_E_INVALID_PARAMETER; } else if ( lObjectCount == 0 && NULL != ppObjArray ) { return WBEM_E_INVALID_PARAMETER; } else if ( lObjectCount > 0 && NULL == ppObjArray ) { return WBEM_E_INVALID_PARAMETER; } CInCritSec ics(&m_cs); // If the stublet is an old style, just let the old proxy handle it if(m_pObject->m_StubType == OLD) return m_pObject->m_pOldProxyUnboundSink->IndicateToConsumer( pLogicalConsumer, lObjectCount, ppObjArray ); // If the stublet is unknown, send just the first object and check the return // code to determine what is on the other side. if(m_pObject->m_StubType == UNKNOWN) { hr = m_pObject->m_pOldProxyUnboundSink->IndicateToConsumer( pLogicalConsumer, 1, ppObjArray ); // bump up pointer to the next object so that it isnt sent more than once lObjectCount--; ppObjArray++; if(hr == WBEM_S_NEW_STYLE) { m_pObject->m_StubType = NEW; } else { // We have an old client, set the stub type and send any remaining objects m_pObject->m_StubType = OLD; if(lObjectCount > 0) hr = m_pObject->m_pOldProxyUnboundSink->IndicateToConsumer( pLogicalConsumer, lObjectCount, ppObjArray ); return hr; } } if(lObjectCount < 1) return S_OK; // if all done, then just return. // Create a packet and some data for it to use. Then calculate // the length of the packet DWORD dwLength; GUID* pguidClassIds = new GUID[lObjectCount]; BOOL* pfSendFullObject = new BOOL[lObjectCount]; // arrays will be deleted when we drop out of scope. CVectorDeleteMe<GUID> delpguidClassIds( pguidClassIds ); CVectorDeleteMe<BOOL> delpfSendFullObject( pfSendFullObject ); CWbemUnboundSinkIndicatePacket packet; hr = packet.CalculateLength( pLogicalConsumer, lObjectCount, ppObjArray, &dwLength, m_pObject->m_ClassToIdMap, pguidClassIds, pfSendFullObject ); // Declare the message structure RPCOLEMESSAGE msg; memset(&msg, 0, sizeof(msg)); msg.cbBuffer = dwLength; // This is the id of the Invoke function. This MUST be set before calling GetBuffer, or // it will fail. msg.iMethod = 3; // allocate the channel buffer and marshal the data into it HRESULT hres = m_pObject->GetChannel()->GetBuffer(&msg, IID_IWbemUnboundObjectSink); if(FAILED(hres)) return hres; // Setup the packet for marshaling hr = packet.MarshalPacket( (LPBYTE)msg.Buffer, dwLength, pLogicalConsumer, lObjectCount, ppObjArray, pguidClassIds, pfSendFullObject); // Send the data to the stub only if the marshaling was successful if ( SUCCEEDED( hr ) ) { DWORD dwRes; hr = m_pObject->GetChannel()->SendReceive(&msg, &dwRes); if(FAILED(hr)) { if(msg.Buffer) m_pObject->GetChannel()->FreeBuffer(&msg); return dwRes; } // We appear to be ok, so get HRESULT LPBYTE pbData = (LPBYTE) msg.Buffer; hr = *((HRESULT*) pbData); m_pObject->GetChannel()->FreeBuffer(&msg); } else { // Clean up the buffer -- Marshaling the packet failed if(msg.Buffer) m_pObject->GetChannel()->FreeBuffer(&msg); } return hr; } //*************************************************************************** // // STDMETHODIMP CUnboundSinkProxyBuffer::Connect(IRpcChannelBuffer* pChannel) // // DESCRIPTION: // // Called during the initialization of the proxy. The channel buffer is passed // to this routine. // // RETURN VALUE: // // S_OK all is well // //*************************************************************************** STDMETHODIMP CUnboundSinkProxyBuffer::Connect(IRpcChannelBuffer* pChannel) { // get a pointer to the old UnboundSink which is in WBEMSVC.DLL this allows // for backward compatibility IPSFactoryBuffer* pIPS; // Establish the marshaling context DWORD dwCtxt = 0; pChannel->GetDestCtx( &dwCtxt, NULL ); m_fRemote = ( dwCtxt == MSHCTX_DIFFERENTMACHINE ); // This is tricky --- The old proxys/stub stuff is actually registered under the // IID_IWbemObjectSink in wbemcli_p.cpp. This single class id, is backpointered // by ProxyStubClsId32 entries for all the standard WBEM interfaces. HRESULT hr = CoGetClassObject( IID_IWbemObjectSink, CLSCTX_INPROC_HANDLER | CLSCTX_INPROC_SERVER, NULL, IID_IPSFactoryBuffer, (void**) &pIPS ); // We aggregated it --- WE OWN IT! hr = pIPS->CreateProxy( this, IID_IWbemUnboundObjectSink, &m_pOldProxy, (void**) &m_pOldProxyUnboundSink ); pIPS->Release(); // Save a reference to the channel hr = m_pOldProxy->Connect( pChannel ); if(m_pChannel) return E_UNEXPECTED; m_pChannel = pChannel; if(m_pChannel) m_pChannel->AddRef(); return S_OK; } //*************************************************************************** // // STDMETHODIMP CUnboundSinkProxyBuffer::Disconnect(IRpcChannelBuffer* pChannel) // // DESCRIPTION: // // Called when the proxy is being disconnected. It just frees various pointers. // // RETURN VALUE: // // S_OK all is well // //*************************************************************************** void STDMETHODCALLTYPE CUnboundSinkProxyBuffer::Disconnect() { // Old Proxy code if(m_pOldProxy) m_pOldProxy->Disconnect(); // Complete the Disconnect by releasing our references to the // old proxy pointers. The old Proxy UnboundSink MUST be released first. if ( NULL != m_pOldProxyUnboundSink ) { m_pOldProxyUnboundSink->Release(); m_pOldProxyUnboundSink = NULL; } if ( NULL != m_pOldProxy ) { m_pOldProxy->Release(); m_pOldProxy = NULL; } if(m_pChannel) m_pChannel->Release(); m_pChannel = NULL; } //**************************************************************************** //**************************************************************************** // STUB //**************************************************************************** //**************************************************************************** //*************************************************************************** // // void* CUnboundSinkFactoryBuffer::GetInterface(REFIID riid) // // DESCRIPTION: // // CUnboundSinkFactoryBuffer is derived from CUnk. Since CUnk handles the QI calls, // all classes derived from this must support this function. // // RETURN VALUE: // // S_OK all is well // //*************************************************************************** void* CUnboundSinkStubBuffer::GetInterface(REFIID riid) { if(riid == IID_IRpcStubBuffer) return &m_XUnboundSinkStublet; else return NULL; } CUnboundSinkStubBuffer::XUnboundSinkStublet::XUnboundSinkStublet(CUnboundSinkStubBuffer* pObj) : CImpl<IRpcStubBuffer, CUnboundSinkStubBuffer>(pObj), m_pServer(NULL), m_lConnections( 0 ) { m_bFirstIndicate = true; } CUnboundSinkStubBuffer::XUnboundSinkStublet::~XUnboundSinkStublet() { if(m_pServer) m_pServer->Release(); if ( NULL != m_pObject->m_pOldStub ) { m_pObject->m_pOldStub->Release(); m_pObject->m_pOldStub = NULL; } } //*************************************************************************** // // STDMETHODIMP CUnboundSinkStubBuffer::Connect(IUnknown* pUnkServer) // // DESCRIPTION: // // Called during the initialization of the stub. The pointer to the // IWbemObject UnboundSink object is passed in. // // RETURN VALUE: // // S_OK all is well // //*************************************************************************** STDMETHODIMP CUnboundSinkStubBuffer::XUnboundSinkStublet::Connect(IUnknown* pUnkServer) { if(m_pServer) return E_UNEXPECTED; HRESULT hres = pUnkServer->QueryInterface(IID_IWbemUnboundObjectSink, (void**)&m_pServer); if(FAILED(hres)) return E_NOINTERFACE; // get a pointer to the old stub which is in WBEMSVC.DLL this allows // for backward compatibility IPSFactoryBuffer* pIPS; // This is tricky --- The old proxys/stub stuff is actually registered under the // IID_IWbemObjectSink in wbemcli_p.cpp. This single class id, is backpointered // by ProxyStubClsId32 entries for all the standard WBEM interfaces. HRESULT hr = CoGetClassObject( IID_IWbemObjectSink, CLSCTX_INPROC_HANDLER | CLSCTX_INPROC_SERVER, NULL, IID_IPSFactoryBuffer, (void**) &pIPS ); hr = pIPS->CreateStub( IID_IWbemUnboundObjectSink, m_pServer, &m_pObject->m_pOldStub ); pIPS->Release(); // Successful connection m_lConnections++; return S_OK; } //*************************************************************************** // // void STDMETHODCALLTYPE CUnboundSinkStubBuffer::XUnboundSinkStublet::Disconnect() // // DESCRIPTION: // // Called when the stub is being disconnected. It frees the IWbemUnboundObjectSink // pointer. // // RETURN VALUE: // // S_OK all is well // //*************************************************************************** void STDMETHODCALLTYPE CUnboundSinkStubBuffer::XUnboundSinkStublet::Disconnect() { // Inform the listener of the disconnect // ===================================== HRESULT hres = S_OK; if(m_pObject->m_pOldStub) m_pObject->m_pOldStub->Disconnect(); if(m_pServer) { m_pServer->Release(); m_pServer = NULL; } // Successful disconnect m_lConnections--; } //*************************************************************************** // // STDMETHODIMP CUnboundSinkStubBuffer::XUnboundSinkStublet::Invoke(RPCOLEMESSAGE* pMessage, // IRpcChannelBuffer* pChannel) // // DESCRIPTION: // // Called when a method reaches the stublet. This checks the method id and // then branches to specific code for the Indicate, or SetStatus calls. // // RETURN VALUE: // // S_OK all is well // //*************************************************************************** STDMETHODIMP CUnboundSinkStubBuffer::XUnboundSinkStublet::Invoke(RPCOLEMESSAGE* pMessage, IRpcChannelBuffer* pChannel) { // SetStatus is a pass through to the old layer if ( pMessage->iMethod == 3 ) return IndicateToConsumer_Stub( pMessage, pChannel ); else return RPC_E_SERVER_CANTUNMARSHAL_DATA; } //*************************************************************************** // // HRESULT CUnboundSinkStubBuffer::XUnboundSinkStublet::IndicateToConsumer_Stub( RPCOLEMESSAGE* pMessage, // IRpcChannelBuffer* pBuffer ) // // DESCRIPTION: // // Handles the Indicate function in the stublet. // // RETURN VALUE: // // S_OK all is well // //*************************************************************************** HRESULT CUnboundSinkStubBuffer::XUnboundSinkStublet::IndicateToConsumer_Stub( RPCOLEMESSAGE* pMessage, IRpcChannelBuffer* pBuffer ) { HRESULT hr = RPC_E_SERVER_CANTUNMARSHAL_DATA; SCODE sc; // Determine if an old style, or new style packet has arrived CWbemUnboundSinkIndicatePacket packet( (LPBYTE) pMessage->Buffer, pMessage->cbBuffer); sc = packet.IsValid(); bool bOldStyle = (S_OK != packet.IsValid()); if(bOldStyle) { // Pass the call in using the old style stub hr = m_pObject->m_pOldStub->Invoke( pMessage, pBuffer ); if(hr == S_OK && m_bFirstIndicate) { // Let proxy know that we can handle the new style by returning a special return code. *(( HRESULT __RPC_FAR * )pMessage->Buffer) = WBEM_S_NEW_STYLE; m_bFirstIndicate = false; return hr; } return hr; } m_bFirstIndicate = false; // Got some new style data. Unmarshall it. long lObjectCount = 0; IWbemClassObject* pLogicalConsumer = NULL; IWbemClassObject ** pObjArray = NULL; sc = packet.UnmarshalPacket( pLogicalConsumer, lObjectCount, pObjArray, m_ClassCache ); // Only continue if the Unmarshaling succeeded. If it failed, we still want // the sc to go back to the other side if ( SUCCEEDED( sc ) ) { // Call the acual UnboundSink sc = m_pServer->IndicateToConsumer( pLogicalConsumer, lObjectCount, pObjArray ); for ( int nCtr = 0; nCtr < lObjectCount; nCtr++ ) { pObjArray[nCtr]->Release(); } delete [] pObjArray; // Done with the logical consumer if ( NULL != pLogicalConsumer ) { pLogicalConsumer->Release(); } } // Send the results back pMessage->cbBuffer = sizeof(HRESULT); hr = pBuffer->GetBuffer( pMessage, IID_IWbemUnboundObjectSink ); if ( SUCCEEDED( hr ) ) { ((HRESULT*)pMessage->Buffer)[0] = sc; } else { hr = sc; } return hr; } IRpcStubBuffer* STDMETHODCALLTYPE CUnboundSinkStubBuffer::XUnboundSinkStublet::IsIIDSupported( REFIID riid) { if(riid == IID_IWbemUnboundObjectSink) { // Don't AddRef(). At least that's what the sample on // Inside DCOM p.341 does. //AddRef(); // ?? not sure return this; } else return NULL; } ULONG STDMETHODCALLTYPE CUnboundSinkStubBuffer::XUnboundSinkStublet::CountRefs() { // See Page 340-41 in Inside DCOM return m_lConnections; } STDMETHODIMP CUnboundSinkStubBuffer::XUnboundSinkStublet::DebugServerQueryInterface(void** ppv) { if(m_pServer == NULL) return E_UNEXPECTED; *ppv = m_pServer; return S_OK; } void STDMETHODCALLTYPE CUnboundSinkStubBuffer::XUnboundSinkStublet::DebugServerRelease(void* pv) { }