//+------------------------------------------------------------------- // // File: service.cxx // // Contents: APIs to simplify RPC setup // // Functions: // // History: 23-Nov-92 Rickhi // 20-Feb-95 Rickhi Major Simplification for DCOM // //-------------------------------------------------------------------- #include #include // CRpcService #include // IOrCallback #include // MIDL_user_allocate #include // LOCK/UNLOCK etc #include // GetLocalEntry #include // gpsaSecurity #include // gRemUnknownIf BOOL gSpeedOverMem = FALSE; // Trade memory for speed. BOOL gfListening = FALSE; // Server is/isn't listening BOOL gfDefaultStrings = FALSE; // Using precomputed string bindings BOOL gfLrpc = FALSE; // Registered for ncalrpc #ifdef _CHICAGO_ BOOL gfMswmsg = FALSE; // Registered for mswmsg #endif DWORD gdwEndPoint = 0; DWORD gdwPsaMaxSize = 0; DUALSTRINGARRAY *gpsaCurrentProcess = NULL; const DWORD MAX_LOCAL_SB = 23; #ifndef _CHICAGO_ SECURITY_DESCRIPTOR LrpcSecurityDescriptor; BOOL fLrpcSDInitialized = FALSE; #endif // interface structure for IRemUnknown extern const RPC_SERVER_INTERFACE gRemUnknownIf; #if DBG==1 //+------------------------------------------------------------------- // // Function: DisplayAllStringBindings, private // // Synopsis: prints the stringbindings to the debugger // // Notes: This function requires the caller to hold gComLock. // // History: 23-Nov-93 Rickhi Created // //-------------------------------------------------------------------- void DisplayAllStringBindings(void) { ASSERT_LOCK_HELD if (gpsaCurrentProcess) { LPWSTR pwszNext = gpsaCurrentProcess->aStringArray; LPWSTR pwszEnd = pwszNext + gpsaCurrentProcess->wSecurityOffset; while (pwszNext < pwszEnd) { ComDebOut((DEB_CHANNEL, "pSEp=%x %ws\n", pwszNext, pwszNext)); pwszNext += lstrlenW(pwszNext) + 1; } } } #endif // DBG == 1 //+------------------------------------------------------------------- // // Function: InitializeLrpcSecurity, private // // Synopsis: Create a DACL allowing all access to NCALRPC and MSWMSG // endpoints. // //-------------------------------------------------------------------- void InitializeLrpcSecurity() { #ifndef _CHICAGO_ if (!fLrpcSDInitialized) { // // Since this is static storage, and we always initialize it // to the same values, it does not need to be MT safe. // InitializeSecurityDescriptor(&LrpcSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION); SetSecurityDescriptorDacl(&LrpcSecurityDescriptor, TRUE, NULL, FALSE); fLrpcSDInitialized = TRUE; } #endif } //+------------------------------------------------------------------- // // Function: RegisterLrpc, private // // Synopsis: Register the ncalrpc transport. // //-------------------------------------------------------------------- RPC_STATUS RegisterLrpc() { RPC_STATUS sc; WCHAR pwszEndPoint[12]; InitializeLrpcSecurity(); lstrcpyW( pwszEndPoint, L"OLE" ); _ultow( gdwEndPoint, &pwszEndPoint[3], 16 ); // The second parameter is a hint that tells lrpc whether or not it // can preallocate additional resources (threads). sc = RpcServerUseProtseqEp(L"ncalrpc", RPC_C_PROTSEQ_MAX_REQS_DEFAULT + 1, pwszEndPoint, #ifndef _CHICAGO_ &LrpcSecurityDescriptor); #else NULL); #endif // Assume that duplicate endpoint means we registered the endpoint and // got unload and reloaded instead of it meaning someone else registered // the endpoint. if (sc == RPC_S_DUPLICATE_ENDPOINT) { gfLrpc = TRUE; return RPC_S_OK; } else if (sc == RPC_S_OK) { #ifndef _CHICAGO_ // Tell RPC to use this endpoint for mswmsg replies. sc = I_RpcSetWMsgEndpoint( pwszEndPoint ); if (sc == RPC_S_OK) #endif gfLrpc = TRUE; } return sc; } #ifdef _CHICAGO_ //+------------------------------------------------------------------- // // Function: RegisterMswmsg, private // // Synopsis: Register the mswmsg transport. // // Notes: The caller must hold gComLock. // //-------------------------------------------------------------------- RPC_STATUS RegisterMswmsg() { RPC_STATUS sc; WCHAR pwszEndPoint[12]; ASSERT_LOCK_HELD InitializeLrpcSecurity(); lstrcpyW( pwszEndPoint, L"MSG" ); _ultow( gdwEndPoint, &pwszEndPoint[3], 16 ); sc = RpcServerUseProtseqEp(L"mswmsg", RPC_C_PROTSEQ_MAX_REQS_DEFAULT, pwszEndPoint, &LrpcSecurityDescriptor); // Assume that duplicate endpoint means we registered the endpoint and // got unload and reloaded instead of it meaning someone else registered // the endpoint. if (sc == RPC_S_OK || sc == RPC_S_DUPLICATE_ENDPOINT) { gfMswmsg = TRUE; return RPC_S_OK; } else return sc; } #endif //+------------------------------------------------------------------- // // Function: CheckClientMswmsg, public // // Synopsis: For the MSWMSG transport, we must call RpcServerUseProtseqEp // on the client side. // // Notes: The caller must hold gComLock. // // History: 27 Sept 95 AlexMit Created // //-------------------------------------------------------------------- RPC_STATUS CheckClientMswmsg( WCHAR *pProtseq, DWORD *pFlags ) { RPC_STATUS sc = RPC_S_OK; ASSERT_LOCK_HELD // Set the MSWMSG flag correctly. #ifdef _CHICAGO_ if (lstrlenW (pProtseq) >= 6 && memcmp ( L"mswmsg", pProtseq, 6 * sizeof (WCHAR)) == 0) #else if (IsSTAThread() && (*pFlags & OXIDF_MACHINE_LOCAL)) #endif *pFlags |= OXIDF_MSWMSG; // Find out if the transport is MSWMSG. if ((*pFlags & OXIDF_MSWMSG) #ifdef _CHICAGO_ && IsSTAThread() && !gfMswmsg #endif ) { // Get a unique number and convert it to a string endpoint. if (gdwEndPoint == 0) gdwEndPoint = CoGetCurrentProcess(); if (gdwEndPoint == 0) return E_FAIL; // Register mswmsg. #ifdef _CHICAGO_ sc = RegisterMswmsg(); #else sc = RegisterLrpc(); #endif } return sc; } //+------------------------------------------------------------------- // // Function: GetLocalEndpoint, public // // Synopsis: Get the endpoint for the local protocol sequence // for the local OXID. // // Notes: This function takes gComLock. // // History: 6 May 95 AlexMit Created // //-------------------------------------------------------------------- LPWSTR GetLocalEndpoint() { ComDebOut((DEB_MARSHAL,"Entering GetLocalEndpoint.\n")); LPWSTR pwszLocalEndpoint = NULL; LOCK StartListen(); if (gfListening) { // OLEFFFFFFFF // maximum 12 character including the null, 24 bytes. pwszLocalEndpoint = (LPWSTR) PrivMemAlloc( 24 ); if (pwszLocalEndpoint != NULL) { Win4Assert( gdwEndPoint != 0 ); lstrcpyW( pwszLocalEndpoint, L"OLE" ); _ultow( gdwEndPoint, &pwszLocalEndpoint[3], 16 ); } } UNLOCK ComDebOut((DEB_MARSHAL,"Leaving GetLocalEndpoint Endpoint: 0x%x\n", pwszLocalEndpoint)); return pwszLocalEndpoint; } //+------------------------------------------------------------------- // // Function: DefaultStringBindings, private // // Synopsis: Create a string binding with entries for just ncalrpc // and mswmsg // // Notes: This function requires the caller to hold gComLock. // //-------------------------------------------------------------------- RPC_STATUS DefaultStringBindings() { ULONG cChar; ASSERT_LOCK_HELD // If mswmsg has been used, reserve space for the string // mswmsg:[MSGnnnnnnnn] #ifdef _CHICAGO_ if (gfMswmsg) cChar = 22; else #endif cChar = 0; // If ncalrpc has been used, reserve space for the string // ncalrpc:[OLEnnnnnnnn] if (gfLrpc) cChar += 24; // Allocate memory. Reserve space for an empty security binding. cChar += 3; gpsaCurrentProcess = (DUALSTRINGARRAY *) PrivMemAlloc( SASIZE(cChar) ); // Give up if the allocation failed. if (gpsaCurrentProcess == NULL) return RPC_S_OUT_OF_RESOURCES; // If mswmsg has been used, make up a string for it. #ifdef _CHICAGO_ if (gfMswmsg) { lstrcpyW( gpsaCurrentProcess->aStringArray, L"mswmsg:[MSG" ); _ultow( gdwEndPoint, &gpsaCurrentProcess->aStringArray[11], 16 ); cChar = lstrlenW( gpsaCurrentProcess->aStringArray ); gpsaCurrentProcess->aStringArray[cChar++] = L']'; gpsaCurrentProcess->aStringArray[cChar++] = 0; } else #endif cChar = 0; // If ncalrpc has been used, make up a string for it. if (gfLrpc) { lstrcpyW( &gpsaCurrentProcess->aStringArray[cChar], L"ncalrpc:[OLE" ); _ultow( gdwEndPoint, &gpsaCurrentProcess->aStringArray[cChar+12], 16 ); cChar += lstrlenW( &gpsaCurrentProcess->aStringArray[cChar] ); gpsaCurrentProcess->aStringArray[cChar++] = L']'; gpsaCurrentProcess->aStringArray[cChar++] = 0; } // Stick on an empty security binding. gpsaCurrentProcess->aStringArray[cChar++] = 0; gpsaCurrentProcess->wSecurityOffset = (USHORT) cChar; gpsaCurrentProcess->aStringArray[cChar++] = 0; gpsaCurrentProcess->aStringArray[cChar++] = 0; gpsaCurrentProcess->wNumEntries = (USHORT) cChar; gfDefaultStrings = TRUE; return RPC_S_OK; } //+------------------------------------------------------------------- // // Function: InquireStringBindings, private // // Synopsis: Get and server binding handles from RPC and convert them // into a string array. // // Notes: This function requires the caller to hold gComLock. // // History: 23 May 95 AlexMit Created // //-------------------------------------------------------------------- BOOL InquireStringBindings( WCHAR *pProtseq ) { ASSERT_LOCK_HELD BOOL fFound = FALSE; DWORD cbProtseq; RPC_BINDING_VECTOR *pBindVect = NULL; RPC_STATUS sc = RpcServerInqBindings(&pBindVect); if (sc == S_OK) { LPWSTR *apwszFullStringBinding; ULONG *aulStrLen; ULONG ulTotalStrLen = MAX_LOCAL_SB; // Total string lengths ULONG j = 0; // BindString we're using if (pProtseq != NULL) cbProtseq = lstrlenW( pProtseq ) * sizeof(WCHAR); else cbProtseq = 0; apwszFullStringBinding = (LPWSTR *) PrivMemAlloc( pBindVect->Count * sizeof(LPWSTR) ); aulStrLen = (ULONG *) PrivMemAlloc( pBindVect->Count * sizeof(ULONG) ); if (apwszFullStringBinding != NULL && aulStrLen != NULL) { // iterate over the handles to get the string bindings // and dynamic endpoints for all available protocols. for (ULONG i=0; iCount; i++) { LPWSTR pwszStringBinding = NULL; apwszFullStringBinding[j] = NULL; aulStrLen[j] = 0; sc = RpcBindingToStringBinding(pBindVect->BindingH[i], &pwszStringBinding); Win4Assert(sc == S_OK && "RpcBindingToStringBinding"); if (sc == S_OK) { // Determine is this is the protseq we are looking for. if (memcmp( pProtseq, pwszStringBinding, cbProtseq ) == 0) fFound = TRUE; // Skip ncalrpc because rot needs to know the // format of the ncalrpc endpoint. if (lstrlenW (pwszStringBinding) >= 7 && memcmp ( L"ncalrpc", pwszStringBinding, 7*sizeof(WCHAR)) != 0) { // record the string lengths for later. include room // for the NULL terminator. apwszFullStringBinding[j] = pwszStringBinding; aulStrLen[j] = lstrlenW(apwszFullStringBinding[j])+1; ulTotalStrLen += aulStrLen[j]; j++; } else { RpcStringFree( &pwszStringBinding ); } } } // for // now that all the string bindings and endpoints have been // accquired, allocate a DUALSTRINGARRAY large enough to hold them // all and copy them into the structure. if (ulTotalStrLen > 0) { void *pNew = PrivMemAlloc( sizeof(DUALSTRINGARRAY) + (ulTotalStrLen+1)*sizeof(WCHAR) ); if (pNew) { PrivMemFree( gpsaCurrentProcess ); gpsaCurrentProcess = (DUALSTRINGARRAY *) pNew; LPWSTR pwszNext = gpsaCurrentProcess->aStringArray; // Copy in ncalrpc:[OLEnnnnnnnn] if (gfLrpc) { lstrcpyW( pwszNext, L"ncalrpc:[OLE" ); _ultow( gdwEndPoint, &pwszNext[12], 16 ); lstrcatW( pwszNext, L"]" ); pwszNext += lstrlenW(pwszNext) + 1; } // copy in the strings for (i=0; iwSecurityOffset = pwszNext - gpsaCurrentProcess->aStringArray + 1; gpsaCurrentProcess->wNumEntries = gpsaCurrentProcess->wSecurityOffset + 2; } else { sc = RPC_S_OUT_OF_RESOURCES; } } else { // no binding strings. this is an error. ComDebOut((DEB_ERROR, "No Rpc ProtSeq/EndPoints Generated\n")); sc = RPC_S_NO_PROTSEQS; } // free the full string bindings we allocated above for (i=0; ihServerSTA ); if (sc != RPC_S_OK) sc = HRESULT_FROM_WIN32(sc); } } // If something failed, make sure everything gets cleaned up. if (FAILED(sc)) { UNLOCK UnregisterDcomInterfaces(); LOCK } ASSERT_LOCK_HELD ComDebOut(((sc == S_OK) ? DEB_MARSHAL : DEB_ERROR, "[OUT] StartListen hr: 0x%x\n", sc)); return sc; } //+------------------------------------------------------------------- // // Function: GetStringBindings, public // // Synopsis: Return an array of strings bindings for this process // // Notes: This function takes gComLock. // // History: 23-Nov-93 Rickhi Created // //-------------------------------------------------------------------- HRESULT GetStringBindings( DUALSTRINGARRAY **psaStrings ) { TRACECALL(TRACE_RPC, "GetStringBindings"); ComDebOut((DEB_CHANNEL, "[IN] GetStringBindings\n")); #ifdef _CHICAGO_ #error Register MSWMSG per thread. #endif *psaStrings = NULL; LOCK HRESULT hr = StartListen(); if (SUCCEEDED(hr)) { hr = CopyStringArray(gpsaCurrentProcess, gpsaSecurity, psaStrings); } UNLOCK ComDebOut((DEB_CHANNEL, "[OUT] GetStringBindings hr:%x\n", hr)); return hr; } //+------------------------------------------------------------------- // // Function: CopyStringArray, public // // Synopsis: Combines the string bindings from the first DUALSTRINGARRAY // with the security bindings from the second DUALSTRINGARRAY // (if present) into a new DUALSTRINGARRAY. // // History: 23-Nov-93 Rickhi Created // //-------------------------------------------------------------------- HRESULT CopyStringArray(DUALSTRINGARRAY *psaStringBinding, DUALSTRINGARRAY *psaSecurity, DUALSTRINGARRAY **ppsaNew) { // compute size of string bindings USHORT lSizeSB = SASIZE(psaStringBinding->wNumEntries); // compute size of additional security strings USHORT lSizeSC = (psaSecurity == NULL) ? 0 : psaSecurity->wNumEntries - psaSecurity->wSecurityOffset; *ppsaNew = (DUALSTRINGARRAY *) PrivMemAlloc( lSizeSB + lSizeSC * sizeof(USHORT)); if (*ppsaNew != NULL) { // copy in the string bindings memcpy(*ppsaNew, psaStringBinding, lSizeSB); if (psaSecurity != NULL) { // copy in the security strings, and adjust the overall length. memcpy(&(*ppsaNew)->aStringArray[psaStringBinding->wSecurityOffset], &psaSecurity->aStringArray[psaSecurity->wSecurityOffset], lSizeSC*sizeof(USHORT)); (*ppsaNew)->wNumEntries = psaStringBinding->wSecurityOffset + lSizeSC; } return S_OK; } return E_OUTOFMEMORY; } //+------------------------------------------------------------------- // // Function: UnregisterDcomInterfaces // // Synopsis: Unregister the object resolver callback function and mark // DCOM as no longer accepting remote calls. // // Notes: This function requires that the caller guarentee // serialization without taking gComLock. // // History: 23-Nov-93 Rickhi Created // //-------------------------------------------------------------------- SCODE UnregisterDcomInterfaces(void) { ComDebOut((DEB_CHANNEL, "[IN] UnregisterDcomInterfaces\n")); RPC_STATUS sc = RPC_S_OK; ASSERT_LOCK_RELEASED if (gfListening) { // Unregister IOrCallback. This can result in calls being dispatched. // Do not hold the lock around this call. sc = RpcServerUnregisterIf(_IOrCallback_ServerIfHandle, 0, 1 ); // Unregister IRemUnknown. This can result in calls being dispatched. // Do not hold the lock around this call. sc = RpcServerUnregisterIf((RPC_SERVER_INTERFACE *)&gRemUnknownIf, 0, 1); gfListening = FALSE; } gSpeedOverMem = FALSE; if (sc != RPC_S_OK) sc = HRESULT_FROM_WIN32(sc); ComDebOut((DEB_CHANNEL, "[OUT] UnregisterDcomInterfaces hr:%x\n", sc)); return sc; } //+------------------------------------------------------------------- // // Function: UseProtseq // // Synopsis: Use the specified protseq and return a list of all string // bindings. // // History: 25 May 95 AlexMit Created // //-------------------------------------------------------------------- error_status_t _UseProtseq( handle_t hRpc, wchar_t *pwstrProtseq, DUALSTRINGARRAY **ppsaNewBindings, DUALSTRINGARRAY **ppsaSecurity ) { BOOL fInUse = FALSE; RPC_STATUS sc = RPC_S_OK; ASSERT_LOCK_RELEASED LOCK // Make sure security is initialized. sc = DefaultAuthnServices(); // If we have never inquired string bindings, inquire them before doing // anything else. if (sc == RPC_S_OK && gfDefaultStrings) { fInUse = InquireStringBindings( pwstrProtseq ); gfDefaultStrings = FALSE; } if (sc == RPC_S_OK && !fInUse) { // Special case ncalrpc. if (lstrcmpW( pwstrProtseq, L"ncalrpc" ) == 0) sc = RegisterLrpc(); #ifdef _CHICAGO_ // Special case mswmsg. else if (lstrcmpW( pwstrProtseq, L"mswmsg" ) == 0) { if (!gfMswmsg) sc = RegisterMswmsg(); } #endif // Register all other protocol sequences. else { sc = RpcServerUseProtseq(pwstrProtseq, RPC_C_PROTSEQ_MAX_REQS_DEFAULT, NULL); } if (sc != RPC_S_OK) ComDebOut((DEB_CHANNEL, "Could not register protseq %ws: 0x%x\n", pwstrProtseq, sc )); // Return the latest string bindings. Ignore failures. InquireStringBindings( NULL ); } // Generate a copy to return. CopyStringArray( gpsaCurrentProcess, NULL, ppsaNewBindings ); CopyStringArray( gpsaSecurity, NULL, ppsaSecurity ); UNLOCK ASSERT_LOCK_RELEASED return RPC_S_OK; }