/*++ Copyright (c) 1996-1997 Microsoft Corporation Module Name: Manager.cxx Abstract: InProc OR entry points Author: Satish Thatte [SatishT] Feb-07-1996 --*/ #include // // Update the channel hook list if it has changed in the registry. // void UpdateChannelHooks( LONG *pcChannelHook, GUID **paChannelHook ) { DWORD cChannelHook = gpSecVals->s_cChannelHook; if (cChannelHook == 0) { *pcChannelHook = 0; *paChannelHook = NULL; return; } GUID * aChannelHook = gpSecVals->s_aChannelHook; // Return the channel hook list. *paChannelHook = (GUID *) MIDL_user_allocate(cChannelHook * sizeof(GUID)); if (*paChannelHook != NULL) { *pcChannelHook = cChannelHook; memcpy(*paChannelHook, aChannelHook, cChannelHook * sizeof(GUID)); } else *pcChannelHook = 0; } // // Manager (server-side) calls to the local OR interface. lclor.idl // error_status_t ConnectDCOM( IN OUT HPROCESS *phProcess, OUT ULONG *pdwTimeoutInSeconds, OUT MID *pLocalMid, OUT ULONG *pfConnectFlags, OUT DWORD *pAuthnLevel, OUT DWORD *pImpLevel, OUT DWORD *pThreadID ) { CProcess *hProcess; *phProcess = NULL; // in case of failure ORSTATUS status = StartDCOM(); if (status != OR_OK) { if (status != OR_I_REPEAT_START) { return status; } else { status = OR_OK; } } *pdwTimeoutInSeconds = BaseTimeoutInterval; *pLocalMid = gLocalMID; // Fill in security parameters. *pfConnectFlags = 0; if (gpSecVals->s_fEnableDCOM == FALSE) *pfConnectFlags |= CONNECT_DISABLEDCOM; if (gpSecVals->s_fMutualAuth) *pfConnectFlags |= CONNECT_MUTUALAUTH; if (gpSecVals->s_fSecureRefs) *pfConnectFlags |= CONNECT_SECUREREF; *pAuthnLevel = gpSecVals->s_lAuthnLevel; *pImpLevel = gpSecVals->s_lImpLevel; if (status != OR_OK) return status; CProtectSharedMemory protector; // locks through rest of lexical scope *pThreadID = (*gpNextThreadID)++; hProcess = new CProcess( AllocateId() ); if (hProcess) { gpProcess = *phProcess = hProcess; status = gpProcessTable->Add(hProcess); // BUGBUG: and rundown an old process, // if there is one, with the same _processID } else { status = OR_NOMEM; } OrDbgDetailPrint(("OR: Client connected\n")); CheckORdata(); return(status); } void UninitializeGlobals(); // private routine for resources release error_status_t Disconnect( IN OUT HPROCESS *phProcess ) { ASSERT(*phProcess!=NULL); CProcess *hProcess = *phProcess; { CProtectSharedMemory protector; // locks through rest of lexical scope // special scope needed since we delete // the global mutex immediately after CheckORdata(); // DBG only // run down context handle for current process hProcess->Rundown(); gpProcessTable->Remove(*hProcess); *phProcess = NULL; // Do this before uninitializing globals! CheckORdata(); // DBG only // this does not delete gpMutex since we are holding it // however, it sets gpMutex to NULL so no other thread can // take it in this process UninitializeGlobals(); // release global resources } return OR_OK; } error_status_t AllocateReservedIds( IN LONG cIdsToReserve, OUT ID *pidReservedBase ) /*++ Routine Description: Called by local clients to reserve a range of IDs which will not conflict with any other local IDs. Arguments: cIdsToReserve - Number of IDs to reserve. pidReservedBase - Starting value of the reserved IDs. The lower DWORD of this can be increatmented to generate cIdsToReserve unique IDs. Return Value: OR_OK --*/ { UINT type; if (cIdsToReserve > 10 || cIdsToReserve < 0) { cIdsToReserve = 10; } CProtectSharedMemory protector; // locks through rest of lexical scope CheckORdata(); *pidReservedBase = AllocateId(cIdsToReserve); CheckORdata(); return(OR_OK); } // // simple helper // // this is needed because if a 16 bit app is the first to initialize DCOM, // a temporary empty DSA for the local OR is created because remote protocols // cannot be initialized. The temporary DSA must still be honored after a // 32 bit app has reinitialized the DSA for the local OR to its true value // BOOL IsLocalDSA( DUALSTRINGARRAY *pdsaServerObjectResolverBindings ) { // special case the NULL binding -- this may be left over from before // remote init and thus may not be in the table if (dsaCompare(gpLocalDSA,pdsaServerObjectResolverBindings) || pdsaServerObjectResolverBindings->wNumEntries == 4) // NULL DSA grandfathered in { return TRUE; } else { return FALSE; } } handle_t remote_resolve_handle; // handle for RPCSS RemoteResolveOXID call UCHAR * RpcssStringBinding = (UCHAR *) "ncalrpc:[epmapper]\0"; // Helper to initialize the remote_resolve_handle RPC_STATUS InitResolveHandleIfNecessary() { static BOOL fBindingInitialized = FALSE; if (fBindingInitialized) return RPC_S_OK; RPC_STATUS status = RpcBindingFromStringBindingA( RpcssStringBinding, &remote_resolve_handle ); if (status == RPC_S_OK) { fBindingInitialized = TRUE; } return status; } // Assumes that the gpMutex is held ORSTATUS FindOrCreateMid( DUALSTRINGARRAY *pdsaObjectResolverBindings, USHORT wProtseqId, CMid * &pMid ) { ORSTATUS status = OR_OK; if (IsLocalDSA(pdsaObjectResolverBindings)) { pMid = gpLocalMid; return OR_OK; } // Attempt to lookup MID CMidKey midkey(pdsaObjectResolverBindings); pMid = gpMidTable->Lookup(midkey); if (NULL == pMid) // must create one { // The CMid constructor releases the shared memory lock, so someone else // might get in and create the very same MID object at the same time pMid = new CMid(pdsaObjectResolverBindings, status, wProtseqId); // We initialize local MID autologically, // therefore this has to be a remote MID if (pMid && status == OR_OK) { status = gpMidTable->Add(pMid); if (status == OR_I_DUPLICATE) { // someone beat us to the punch, use theirs delete pMid; // can't use this one pMid = gpMidTable->Lookup(midkey); ASSERT(pMid != NULL); status = OR_OK; } else if (status != OR_OK) { delete pMid; pMid = NULL; return status; } ASSERT(status == OR_OK); } else { status = (status == OR_OK) ? OR_NOMEM : status; } } return status; } // Assumes that the gpMutex is held ORSTATUS CallRpcssToResolveOxid( IN OXID Oxid, IN CMid *pMid, OUT COxid * &pOxid ) { ORSTATUS status = OR_OK; { CTempReleaseSharedMemory temp; // release mutex before taking gComLock HRESULT hr = StartRPCSS(); if (FAILED(hr)) { return OR_INTERNAL_ERROR; } } status = InitResolveHandleIfNecessary(); // call RPCSS if (status == RPC_S_OK) { CTempReleaseSharedMemory temp; status = RemoteResolveOXID(Oxid,(DWORD)pMid); if (status != RPC_S_OK) { return status; } } // if OK, then the Oxid is in the table now pOxid = gpOxidTable->Lookup(CId2Key(Oxid, pMid->GetMID())); ASSERT(pOxid); return OR_OK; } // Assumes that the gpMutex is held ORSTATUS FindOrCreateOxid( IN OXID Oxid, IN CMid *pMid, IN long fApartment, IN OUT OXID_INFO& OxidInfo, IN USHORT wProtseqId, IN BOOL fSCMRequest, // is this a register request from SCM? OUT COxid * &pOxid ) { ORSTATUS status = OR_OK; MID Mid = pMid->GetMID(); pOxid = gpOxidTable->Lookup(CId2Key(Oxid, Mid)); if (pOxid != NULL) { if (fSCMRequest) { if (gfThisIsRPCSS) { MIDL_user_free(OxidInfo.psa); // free the original } else { CoTaskMemFree(OxidInfo.psa); // ORPC uses CoTaskMemAlloc } } return OR_OK; } else if (pMid->IsLocal()) { return OR_BADOXID; // local OXID should be registered by server } // Not found, and not local MID // Need to allocate the OXID. First step is to resolve it remotely if ( !fSCMRequest ) // genuine resolve request { if ( !gfThisIsRPCSS ) { return CallRpcssToResolveOxid(Oxid,pMid,pOxid); } else // we are RPCSS { OxidInfo.psa = NULL; // just to be sure // Call remote resolver to resolve the OXID // This call releases the shared memory lock, so someone else // might get in and create the very same OXID object at the same time status = pMid->ResolveRemoteOxid( // This op will also replace the Oxid, // psa with one in shared memory &OxidInfo ); if (status != OR_OK) { MIDL_user_free(OxidInfo.psa); return OR_BADOXID; } } } ASSERT(status == OR_OK); DUALSTRINGARRAY *pdsaT = dsaSMCopy(OxidInfo.psa); if (!pdsaT) { return OR_NOMEM; } if (gfThisIsRPCSS) { MIDL_user_free(OxidInfo.psa); // free the original and replace } else { CoTaskMemFree(OxidInfo.psa); // ORPC uses CoTaskMemAlloc } OxidInfo.psa = pdsaT; // with shared memory compressed copy wProtseqId = fSCMRequest ? wProtseqId : pMid->ProtseqOfServer(); pOxid = new COxid( Oxid, pMid, wProtseqId, OxidInfo ); if (pOxid == NULL) return OR_NOMEM; // remote OXID belongs to ping process .. if ((status = gpPingProcess->OwnOxid(pOxid)) == OR_OK) { status = gpOxidTable->Add(pOxid); } if (status == OR_I_DUPLICATE) { // this never got inserted anywhere, so we must delete it // explicitly because refcounting never got involved delete pOxid; // someone beat us to the punch, use theirs pOxid = gpOxidTable->Lookup(CId2Key(Oxid, Mid)); ASSERT(pOxid != NULL); status = OR_OK; } else if (status != OR_OK) { // something else went wrong, back out of the whole thing OrMemFree(OxidInfo.psa); gpPingProcess->DisownOxid(pOxid,FALSE); gpOxidTable->Remove(*pOxid); pOxid = NULL; } return status; } error_status_t GetOXID( IN HPROCESS hProcess, IN OXID Oxid, IN DUALSTRINGARRAY *pdsaServerObjectResolverBindings, IN long fApartment, IN USHORT wProtseqId, IN OUT OXID_INFO& OxidInfo, OUT MID &Mid, OPTIONAL IN BOOL fSCMRequest // is this a register request from SCM? ) /*++ Routine Description: Discovers the OXID_INFO for an oxid. Will find local OXIDs without any help from resolver process. It needs OR bindings in order to resolve remote OXIDs. REVIEW: Should the resolver process be involved in this? Arguments: hProcess - The context handle of the process. Oxid - The OXID (a uuid) to resolve. pdsaServerObjectResolverBindings - Compressed string bindings to the OR on the server's machine. fApartment - non-zero if the client is aparment model. OxidInfo - If successful this will contain information about the oxid and an expanded string binding to the server oxid's process. Mid - The machine ID assigned locally for the remote machine. This is obviously meaningful only for remote OXIDs. Return Value: OR_NOMEM - Common. OR_BADOXID - Unable to resolve it. OR_OK - Success. --*/ { ComDebOut((DEB_OXID, "GetOXID OXID = %08x\n",Oxid)); COxid *pOxid; CMid *pMid; ORSTATUS status = OR_OK; if (!pdsaServerObjectResolverBindings) pdsaServerObjectResolverBindings = gpLocalDSA; ASSERT(dsaValid(pdsaServerObjectResolverBindings)); CProtectSharedMemory protector; // locks through rest of lexical scope CheckORdata(); #if DBG if (hProcess) hProcess->IsValid(); // don't validate for fake SCM calls #endif status = FindOrCreateMid( pdsaServerObjectResolverBindings, wProtseqId, pMid ); if (status != OR_OK) return status; ASSERT(pMid); // otherwise we would have returned by now Mid = pMid->GetMID(); status = FindOrCreateOxid( Oxid, pMid, fApartment, OxidInfo, wProtseqId, fSCMRequest, pOxid ); ASSERT( (status != OR_OK) || pOxid ); CheckORdata(); if (status == OR_OK) { return pOxid->GetInfo(&OxidInfo); } else { return status; } } error_status_t ClientAddOID( IN HPROCESS hProcess, IN OID Oid, IN OXID Oxid, IN MID Mid ) /*++ Routine Description: Updates the set of OIDs in use by a process. Arguments: hProcess - Context handle for the process. Oid - OID to add. Oxid - OXID to which OID belongs. Mid - MID for location of OXID server. Return Value: OR_OK - All updates completed ok. OR_BADOXID - The Oxid was not found OR_BADOID - The Oid could not be created or found Notes: Unlike the NT resolver, there is no possibility that the Oxid is not in the gpOxidTable (since client and server Oxid objects are in the same table). --*/ { ComDebOut((DEB_OXID, "ClientAddOID\nOID = %08x\nOXID = %08x\nMID = %08x\n", Oid,Oxid,Mid)); ORSTATUS status = OR_OK; BOOL fNeedRpcss = FALSE; CProtectSharedMemory protector; // locks through rest of lexical scope CheckORdata(); #if DBG hProcess->IsValid(); #endif // Lookup up the oxid owning this new oid. COxid *pOxid = gpOxidTable->Lookup(CId2Key(Oxid,Mid)); if (NULL == pOxid) { return OR_BADOXID; } CMid *pMid = pOxid->GetMid(); // Find or create the oid. COid *pOid = gpOidTable->Lookup(CId2Key(Oid,Mid)); if (NULL == pOid) { if (pMid->IsLocal()) // Local OID should be registered by server { return OR_BADOID; } pOid = new COid(pOxid,Oid); if (NULL == pOid) { return OR_NOMEM; } status = gpOidTable->Add(pOid); if (status != OR_OK) { delete pOid; return status; } // Need to lazy start RPCSS fNeedRpcss = TRUE; } ASSERT(status == OR_OK); // We need to call OwnOid irrespective of whether we found the Oid // in the gpOidTable because this Oid may be remote, and it may have // been DisOwned by its Oxid but is now being used by a new client status = pOxid->OwnOid(pOid); if (status == OR_OK && !pMid->IsLocal()) { status = pMid->AddClientOid(pOid); } if (status == OR_OK || status == OR_I_DUPLICATE) { status = hProcess->AddOid(pOid); if (status == OR_I_DUPLICATE) { status = OR_OK; } } else { pOxid->DisownOid(pOid); } if ((status == OR_OK) && fNeedRpcss) { CTempReleaseSharedMemory temp; // release mutex before taking gComLock HRESULT hr = StartRPCSS(); if (FAILED(hr)) { status = OR_INTERNAL_ERROR; } } CheckORdata(); return status; } error_status_t ClientDropOID( IN HPROCESS hProcess, IN OID Oid, IN MID Mid ) /*++ Routine Description: Updates the set of remote OIDs in use by a process. Arguments: hProcess - Context handle for the process. Oid - OID to be removed. Mid - MID to which Oid belongs. Return Value: OR_OK - All updates completed ok. OR_BADOID - The Oid could not be found --*/ { ComDebOut((DEB_OXID, "ClientDropOID\nOID = %08x\nMID = %08x\n", Oid,Mid)); CProtectSharedMemory protector; // locks through rest of lexical scope CheckORdata(); #if DBG hProcess->IsValid(); #endif COid * pOid = gpOidTable->Lookup(CId2Key(Oid,Mid)); if (pOid) { COid *pRemove = hProcess->DropOid(pOid); if (pRemove == NULL) { #if DBG { GUID Moid; MOIDFromOIDAndMID(Oid,Mid,&Moid); ComDebOut((DEB_OXID,"OR: Client process %d tried to remove moid %I which \ it didn't own\n", hProcess->GetProcessID(), &Moid)); } #endif // DBG return OR_BADOID; } else { ASSERT(pRemove == pOid); CheckORdata(); return OR_OK; } } else { #if DBG { GUID Moid; MOIDFromOIDAndMID(Oid,Mid,&Moid); ComDebOut((DEB_OXID,"OR: Client process %d tried to remove moid %I which \ doesn't exist\n", hProcess->GetProcessID(), &Moid)); } #endif // DBG return OR_BADOID; } } error_status_t ServerAllocateOXID( IN HPROCESS hProcess, IN long fApartment, IN OXID_INFO *pOxidInfo, IN DUALSTRINGARRAY *pdsaStringBindings, OUT OXID &Oxid ) /*++ Routine Description: Allocates an OXID and 0 or more OIDs from the OR. Arguments: hProcess - The context handle of the process containing the OXID. fApartment - is the server threading model apartment or free OxidInfo - The OXID_INFO structure for the OXID without bindings. pdsaStringBindings - Expanded string binding of the server. Oxid - The OXID registered and returned. Return Value: OR_OK - success. OR_NOMEM - Allocation of OXID failed. --*/ { ComDebOut((DEB_OXID, "ServerAllocateOXID\n")); ORSTATUS status = OR_OK; COxid *pNewOxid; // Save the string bindings back to the process ASSERT(dsaValid(pdsaStringBindings)); CProtectSharedMemory protector; // locks through rest of lexical scope CheckORdata(); #if DBG hProcess->IsValid(); #endif status = hProcess->ProcessBindings(pdsaStringBindings); if (status != OR_OK) { return(status); } pNewOxid = new COxid( hProcess, *pOxidInfo, fApartment ); if (NULL == pNewOxid) { return(OR_NOMEM); } Oxid = pNewOxid->GetOXID(); // Add to process and lookup table. status = hProcess->OwnOxid(pNewOxid); VALIDATE((status, OR_NOMEM, 0)); if (OR_OK == status) { status = gpOxidTable->Add(pNewOxid); if (status != OR_OK) { delete pNewOxid; return status; } ComDebOut((DEB_OXID, "OXID successfully allocated: %08x\n", Oxid)); } CheckORdata(); return(status); } error_status_t ServerAllocateOID( IN HPROCESS hProcess, IN OXID Oxid, OUT OID &Oid ) /*++ Routine Description: Registers an OID on behalf of an existing OXID. Arguments: hProcess - The context handle of the process containing the OXID and OIDs. Oxid - The OXID associated with the OID (assumed local of course). Oid - The OID to be allocated and returned. Return Value: OR_OK (0) - Success. OR_NOMEM - OXID or one or more OIDs --*/ { ComDebOut((DEB_OXID, "ServerAllocateOID, OXID = %08x\n", Oxid)); CProtectSharedMemory protector; // locks through rest of lexical scope CheckORdata(); #if DBG hProcess->IsValid(); #endif COxid *pOxid = gpOxidTable->Lookup(CId2Key(Oxid,gLocalMID)); ORSTATUS status; if (NULL == pOxid) { return(OR_BADOXID); } COid *pOid = new COid(pOxid); if (NULL == pOid) { return OR_NOMEM; } status = pOxid->OwnOid(pOid); if (status == OR_OK) { Oid = pOid->GetOID(); // out parameter status = gpOidTable->Add(pOid); if (status != OR_OK) { COid *pTemp = pOxid->DisownOid(pOid); ASSERT(pTemp != NULL); return status; } // If the server doesn't want to keep the OID alive, // this OID may rundown in six minutes unless // someone references it in the meantime... ComDebOut((DEB_OXID, "OID successfully allocated: %08x at offset = %x\n", Oid,pOid)); pOxid->StartRundownThreadIfNecessary(); CheckORdata(); return OR_OK; } else { return OR_NOMEM; } } error_status_t ServerFreeOXID( IN HPROCESS hProcess, IN OXID Oxid, IN ULONG cOids, IN OID aOids[]) /*++ Routine Description: Delete an OXID registered by the server, and all OIDs belonging to this OXID. Arguments: hProcess - The context handle of the process containing the OXID and OIDs. Oxid - The OXID to be deleted (assumed local). cOids - The number of OIDs to be deleted. aOids - array of OIDs to be deleted. Return Value: OR_OK (0) - Success. OR_BADOXID - OXID does not exist. OR_NOACCESS - OXID does not belong to this process. OR_BADOID - OID does not exist or does not belong to this OXID. --*/ { ComDebOut((DEB_OXID, "ServerFreeOXID: %08x MID = %x\n", Oxid,gLocalMID)); CProtectSharedMemory protector; // locks through rest of lexical scope CheckORdata(); #if DBG hProcess->IsValid(); #endif COxid *pOxid = gpOxidTable->Lookup(CId2Key(Oxid,gLocalMID)); if (NULL != pOxid) { #if DBG OXID Oxid = pOxid->GetOXID(); // get this before pOxid potentially disappears #endif hProcess->DisownOxid(pOxid,TRUE); // this call is on the server's thread // in the apartment case and in the server's // process in both threading cases ComDebOut((DEB_OXID, "OXID successfully removed: %08x\n", Oxid)); CheckORdata(); return OR_OK; } else { return OR_BADOXID; } }