/*++ Copyright (C) Microsoft Corporation, 1991 - 1999 Module Name: osfclnt.cxx Abstract: This file contains the client side implementation of the OSF connection oriented RPC protocol engine. Author: Michael Montague (mikemon) 17-Jul-1990 Revision History: Mazhar Mohammed (mazharm) 11-08-1996 - Major re-haul to support async: - Added support for Async RPC, Pipes - Changed it to operate as a state machine - Changed class structure - Got rid of the TRANS classes. Kamen Moutafov (kamenm) Jan-2000 Support for multiple transfer syntaxes Kamen Moutafov (KamenM) Dec 99 - Feb 2000 - Support for cell debugging stuff Kamen Moutafov (KamenM) Mar-2000 Support for extended error info --*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Maximum retries in light of getting a shutdown // or closed in doing a bind or shutdown // #define MAX_RETRIES 3 // #define RPC_IDLE_CLEANUP_AUDIT NEW_SDICT(OSF_CASSOCIATION); MUTEX *AssocDictMutex = NULL; OSF_CASSOCIATION_DICT * AssociationDict; long OsfLingeredAssociations = 0; const long MaxOsfLingeredAssociations = 8; ULONG OsfDestroyedAssociations = 0; const ULONG NumberOfOsfDestroyedAssociationsToSample = 128; // in 100 nano-second intervals, this constant is 2 seconds const DWORD DestroyedOsfAssociationBatchThreshold = 1000 * 10 * 1000 * 2; ULARGE_INTEGER OsfLastDestroyedAssociationsBatchTimestamp; inline BOOL SwapTokenIfNecessary ( IN OSF_BINDING_HANDLE *BindingHandle, OPTIONAL OUT HANDLE *OldToken ) /*++ Routine Description: Checks if the binding handle is named pipe and if the type of named pipe requires token swapping before send. If yes, it swaps the token. Return value is whether the token was swapped. Arguments: BindingHandle - the binding handle on which the send is done. If not present, no swapping is done. OldToken - on output, the old token is saved here. This variable is always initialized and can be counted to be NULL even if no swapping was done. Return Value: non-zero if the token was swapped. zero otherwise. --*/ { if (BindingHandle && IsNamedPipe(BindingHandle->NPType) && IsLocalNamedPipe(BindingHandle->NPType) && IsDynamicNamedPipe(BindingHandle->NPType) ) { return BindingHandle->SwapToken(OldToken); } else return FALSE; } OSF_BINDING_HANDLE::OSF_BINDING_HANDLE ( IN OUT RPC_STATUS * Status ) : BINDING_HANDLE(Status) { ALLOCATE_THIS(OSF_BINDING_HANDLE); ObjectType = OSF_BINDING_HANDLE_TYPE; Association = 0; ReferenceCount = 1; DceBinding = 0; TransInfo = 0; TransAuthInitialized = 0; pToken = 0; } OSF_BINDING_HANDLE::~OSF_BINDING_HANDLE ( ) { OSF_RECURSIVE_ENTRY *RecursiveEntry; DictionaryCursor cursor; if (Association != 0) { Unbind(); // Can't touch Association after this point. } else { delete DceBinding; } RecursiveCalls.Reset(cursor); while ((RecursiveEntry = RecursiveCalls.Next(cursor))) { delete RecursiveEntry->CCall; } } RPC_STATUS OSF_BINDING_HANDLE::AcquireCredentialsForTransport( ) /*++ Function Name:AcquireCredentialsForTransport Parameters: Description: Returns: --*/ { BOOL Result, fTokenFound; unsigned long Size; RPC_STATUS Status; HANDLE ImpersonationToken = 0; TOKEN_STATISTICS TokenStatisticsInformation; // // This function is called only when RPC security is not being used // ASSERT(IsNamedPipe(NPType)); ASSERT(ClientAuthInfo.AuthenticationService == RPC_C_AUTHN_NONE); ASSERT(Association); if (OpenThreadToken (GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_QUERY, TRUE, &ImpersonationToken) == FALSE) { ClientAuthInfo.DefaultLogonId = TRUE; pToken = NULL; Status = GetLastError(); if (Status != ERROR_NO_TOKEN) { RpcpErrorAddRecord(EEInfoGCRuntime, RPC_S_ACCESS_DENIED, EEInfoDLOSF_BINDING_HANDLE__AcquireCredentialsForTransport10, (ULONG)Status); return RPC_S_ACCESS_DENIED; } return RPC_S_OK; } Result = GetTokenInformation( ImpersonationToken, TokenStatistics, &TokenStatisticsInformation, sizeof(TOKEN_STATISTICS), &Size ); if (Result != TRUE) { ClientAuthInfo.DefaultLogonId = TRUE; CloseHandle(ImpersonationToken); RpcpErrorAddRecord(EEInfoGCRuntime, RPC_S_ACCESS_DENIED, EEInfoDLOSF_BINDING_HANDLE__AcquireCredentialsForTransport20, (ULONG)GetLastError()); return RPC_S_ACCESS_DENIED; } ClientAuthInfo.DefaultLogonId = FALSE; Status = Association->FindOrCreateToken( ImpersonationToken, &TokenStatisticsInformation.AuthenticationId, &pToken, &fTokenFound); if (Status != RPC_S_OK) { // // If there is a failure, the callee will free the token // return Status; } if (fTokenFound) { CloseHandle(ImpersonationToken); } ASSERT(pToken); FastCopyLUIDAligned(&ClientAuthInfo.ModifiedId, &pToken->ModifiedId); return RPC_S_OK; } BOOL ReplaceToken( HANDLE NewToken ) /*++ Function Name:ReplaceToken Parameters: Description: Returns: --*/ { NTSTATUS NtStatus; HANDLE hTokenToReplace = NewToken; // // This thread should either have a null token or // the token we captured in Initialize. It cannot have // any other token. // NtStatus = NtSetInformationThread(NtCurrentThread(), ThreadImpersonationToken, &hTokenToReplace, sizeof(HANDLE)); if (!NT_SUCCESS(NtStatus)) { return FALSE; } return TRUE; } inline void RestoreTokenIfNecessary ( IN BOOL fTokenSwapped, IN HANDLE OldToken ) /*++ Routine Description: Restores the old token if necessary. Arguments: fTokenSwapped - non-zero if the token was swapped and OldToken needs to be restored to the thread. Zero otherwise. OldToken - the token that was saved. If fTokenSwapped is zero, this parameter is ignored. Return Value: --*/ { if (fTokenSwapped) { ReplaceToken(OldToken); if (OldToken) { CloseHandle(OldToken); } } } BOOL OSF_BINDING_HANDLE::SwapToken ( HANDLE *OldToken ) /*++ Function Name:SwapToken Parameters: Description: Returns: --*/ { HANDLE ImpersonationToken = 0; HANDLE NewToken ; *OldToken = 0; if (!(ClientAuthInfo.AuthenticationService == RPC_C_AUTHN_NONE && IsNamedPipe(NPType))) { return FALSE; } if (pToken == 0) { NewToken = 0; } else { NewToken = pToken->hToken; } ImpersonationToken = 0; if (OpenThreadToken (GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_QUERY, TRUE, &ImpersonationToken) == FALSE) { ImpersonationToken = 0; } if (ReplaceToken(NewToken) == FALSE) { if (ImpersonationToken) { CloseHandle(ImpersonationToken); } return FALSE; } *OldToken = ImpersonationToken; return TRUE; } RPC_STATUS OSF_BINDING_HANDLE::NegotiateTransferSyntax ( IN OUT PRPC_MESSAGE Message ) /*++ Function Name:NegotiateTransferSyntax Parameters: Description: Negotiate a transfer syntax, if necessary. A bind/alter context may be done in the process. Returns: Status of the operation --*/ { OSF_CCALL *CCall; RPC_STATUS Status; unsigned int NotChangedRetry = 0; unsigned int Retry; RPC_CLIENT_INTERFACE *ClientInterface; BOOL fResult; BOOL fRetry; AssocDictMutex->VerifyNotOwned(); for (;;) { Retry = 0; for (;;) { // // Allocate a call object. // Status = AllocateCCall(&CCall, Message, &fRetry); Message->Handle = (RPC_BINDING_HANDLE) CCall; if (Status == RPC_S_OK) { // by now the Binding in the CCall should have // been fixed or in the async case, NDR20 should have been // used ClientInterface = (RPC_CLIENT_INTERFACE *)Message->RpcInterfaceInformation; if (DoesInterfaceSupportMultipleTransferSyntaxes(ClientInterface)) Message->TransferSyntax = CCall->GetSelectedBinding()->GetTransferSyntaxId(); return Status; } if ((Status != RPC_P_CONNECTION_SHUTDOWN) && (Status != RPC_P_CONNECTION_CLOSED) && (fRetry == FALSE)) { break; } // AllocateCCall sets fRetry to TRUE if it has freed the association. if (this->Association != 0 && fRetry == FALSE) { Association->ShutdownRequested(Status, NULL); } Retry++; if (Retry == MAX_RETRIES) break; } if (Status == EPT_S_NOT_REGISTERED) { BindingMutex.Request(); if (DceBinding == NULL) { // in a scenario where multiple threads make an RPC // call on the same binding handle, it is possible that // even though this thread failed with EPT_S_NOT_REGISTERED // another thread succeeded and transferred the ownership of // the DceBinding to the association. In such case we're // already bound to an association, and all we need is to // loop around and try the call again BindingMutex.Clear(); continue; } // we ran out of endpoints - drop the endpoint set for the next // iteration fResult = DceBinding->MaybeMakePartiallyBound( (PRPC_CLIENT_INTERFACE)Message->RpcInterfaceInformation, InqPointerAtObjectUuid()); if (fResult) { if ( *InquireEpLookupHandle() != 0 ) { EpFreeLookupHandle(*InquireEpLookupHandle()); *InquireEpLookupHandle() = 0; } } BindingMutex.Clear(); break; } if (Status != RPC_S_SERVER_UNAVAILABLE) { break; } // if this is either a static endpoint, or an endpoint resolved through // the interface information, there is no need to iterate if (!fDynamicEndpoint || ((RPC_CLIENT_INTERFACE *)Message->RpcInterfaceInformation)->RpcProtseqEndpointCount) { break; } // // If we reach here, it means that we are iterating through the list // of endpoints obtained from the endpoint mapper. // BindingMutex.Request(); if (ReferenceCount == 1 && Association != 0) { // there is an association (which means the server's endpoint // mapper was contacted), and the refcount is 1 (we're the // only user). We know this is a dynamic endpoint. We have // the following cases to take care of: // - the list of endpoints is exhausted. If this is the case, // we wouldn't have gotten here, as we will get // EPT_S_NOT_REGISTERED from AllocateCCall and we would // have bailed out earlier on the first iteration. Since // the code above would have dropped the endpoints list for // the next iteration, we will be fine // - we're iterating over the list of endpoints in the same // call. In this case, we will be getting server unavailable // from each endpoint for which the server is not there, // and we will move on to the next endpoint, until we exhaust // them and get EPT_S_NOT_REGISTERED DceBinding = Association->DuplicateDceBinding(); if (DceBinding == NULL) { BindingMutex.Clear(); return RPC_S_OUT_OF_MEMORY; } // We should not unbind if DceBinding could not be replicated from the // Association. If we unbind when DceBinding == NULL, then Association // will be set to NULL and we may AV in OSF_BINDING_HANDLE::AllocateCCall // since it is assumed there that either Adssociation != NULL or DceBinding != NULL. Unbind(); // Can't touch Association after this point. RpcpErrorAddRecord(EEInfoGCRuntime, Status, EEInfoDLOSF_BINDING_HANDLE__NegotiateTransferSyntax10, DceBinding->InqEndpoint() ); // whack the endpoint and move on to the next (if any) fResult = DceBinding->MaybeMakePartiallyBound( (PRPC_CLIENT_INTERFACE)Message->RpcInterfaceInformation, InqPointerAtObjectUuid()); if (fResult == FALSE) { NotChangedRetry += 1; RpcpPurgeEEInfo(); } else { // we don't purge here because we want next iterations // to add to the record NotChangedRetry = 0; } } else { // either there is more then one reference to the binding handle, // or the endpoint mapper could not be contacted (i.e. no // association) NotChangedRetry += 1; RpcpPurgeEEInfo(); } BindingMutex.Clear(); if (NotChangedRetry > 4) { return(RPC_S_SERVER_UNAVAILABLE); } } ASSERT(Status != RPC_S_OK); if (Status == RPC_P_CONNECTION_CLOSED || Status == RPC_P_CONNECTION_SHUTDOWN) { return(RPC_S_CALL_FAILED_DNE); } return(Status); } RPC_STATUS OSF_BINDING_HANDLE::GetBuffer ( IN PRPC_MESSAGE Message, IN UUID *ObjectUuid ) /*++ Function Name:GetBuffer Parameters: Description: Ask the call object for a buffer Returns: --*/ { ASSERT(!"We should never be here - the binding handle cannot allocate a buffer"); return RPC_S_INTERNAL_ERROR; } RPC_STATUS OSF_BINDING_HANDLE::AllocateCCall ( OUT OSF_CCALL * *CCall, IN PRPC_MESSAGE Message, OUT BOOL *Retry ) /*++ Function Name:AllocateCCall Parameters: CCall - Returns the allocated Call object Message - The RPC_MESSAGE for this call. Retry - if set on output, the caller should retry the allocation and the Association may have been deleted. Description: Finds an existing association or creates a new one. Asks the association to allocate the call object for us. Returns: RPC_S_OK or an error code. --*/ { OSF_RECURSIVE_ENTRY *RecursiveEntry; CLIENT_AUTH_INFO * AuthInfo; RPC_STATUS Status; BOOL fDynamic = FALSE; CLIENT_AUTH_INFO AuthInfo2; DictionaryCursor cursor; BOOL fBindingHandleReferenceRemoved; ULONG_PTR CallTimeout; *Retry = FALSE; BindingMutex.Request(); // // First we need to check if there is already a recursive Call // for this thread and interface. To make the common case quicker, // we will check to see if there are any Calls in the dictionary // first. ** We will find a recursive call only in the case of callbacks ** // if ( RecursiveCalls.Size() != 0 ) { RecursiveCalls.Reset(cursor); while ( (RecursiveEntry = RecursiveCalls.Next(cursor)) != 0 ) { *CCall = RecursiveEntry->IsThisMyRecursiveCall( GetThreadIdentifier(), (RPC_CLIENT_INTERFACE *) Message->RpcInterfaceInformation); if ( *CCall != 0 ) { BindingMutex.Clear(); if ((*CCall)->CurrentState == Aborted) { return (*CCall)->AsyncStatus; } // // This reference will be removed when the send // for this call is complete // (*CCall)->CurrentState = SendingFirstBuffer; return(RPC_S_OK); } } } if (Association == 0) { // if we don't have an object UUID, and we have a dynamic endpoint, // attempt quick resolution if (InqIfNullObjectUuid() && DceBinding->IsNullEndpoint()) { Association = FindOrCreateAssociation( DceBinding, TransInfo, (RPC_CLIENT_INTERFACE *)Message->RpcInterfaceInformation ); AssocDictMutex->VerifyNotOwned(); // do nothing in both cases. In failure case, we will do full // resolution. In success case, ownership of the dce binding // has passed to the association, and we don't need to copy // the resolved endpoint back } // if we are still NULL, attempt full resolution if (Association == NULL) { Status = OSF_BINDING_HANDLE::InqTransportOption( RPC_C_OPT_CALL_TIMEOUT, &CallTimeout); // this function cannot fail unless it is given invalid // parameters ASSERT(Status == RPC_S_OK); Status = DceBinding->ResolveEndpointIfNecessary( (RPC_CLIENT_INTERFACE *) Message->RpcInterfaceInformation, InqPointerAtObjectUuid(), InquireEpLookupHandle(), FALSE, InqComTimeout(), (ULONG)CallTimeout, &ClientAuthInfo ); if ( Status != RPC_S_OK ) { BindingMutex.Clear(); return(Status); } Association = FindOrCreateAssociation( DceBinding, TransInfo, NULL ); AssocDictMutex->VerifyNotOwned(); if (Association == 0) { BindingMutex.Clear(); return RPC_S_OUT_OF_MEMORY; } } // // Ownership of the DCE binding passes to the association. We are // going to set the field to zero so that no one screws with them. // DceBinding = 0; if (ClientAuthInfo.AuthenticationService == RPC_C_AUTHN_NONE && IsNamedPipe(NPType)) { if (TransAuthInitialized == 0) { Status = AcquireCredentialsForTransport(); if (Status != RPC_S_OK) { BindingMutex.Clear(); return Status; } TransAuthInitialized = 1; } } } else { if (Association->IsValid() == 0) { // Check if the last association to use the binding handle is going away. if (ReferenceCount == 1) { // If this is the last association, we need to copy the DceBinding // back onto the binding handle. DceBinding = Association->DuplicateDceBinding(); if (DceBinding == 0) { Status = RPC_S_OUT_OF_MEMORY; } else { Status = Association->AssociationShutdownError; Unbind(); // Can't touch Association after this point. *Retry = TRUE; } } else { Status = Association->AssociationShutdownError; } BindingMutex.Clear(); // If we called Unbind() then as soon as we clear the mutex, // another thread may come and initialize Association. // The caller can't know whether Association has been // freed here by looking at this->Association upon return. // Fortunately, the path freeing the association is the only path // setting Retry = TRUE and the caller can check this flag. return Status; } } // // We will assume that we are successfully able to allocate a Call, // so we bump the reference count now. // ReferenceCount++; AuthInfo = InquireAuthInformation(); // // If this is a secure BH and it requires DYNAMIC TRACKING, check if // LogonID has changed. If it has changed, get new Credential Handle // if ((AuthInfo != 0) && (AuthInfo->AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE) && (AuthInfo->IdentityTracking == RPC_C_QOS_IDENTITY_DYNAMIC)) { Status = ReAcquireCredentialsIfNecessary(); if (Status != RPC_S_OK) { ReferenceCount -=1; BindingMutex.Clear(); return (Status); } fDynamic = TRUE; AuthInfo = AuthInfo->ShallowCopyTo(&AuthInfo2); AuthInfo->ReferenceCredentials(); } BindingMutex.Clear(); Status = Association->AllocateCCall( this, Message, AuthInfo, CCall, &fBindingHandleReferenceRemoved); if (fDynamic) AuthInfo->PrepareForDestructionAfterShallowCopy(); if ( Status == RPC_S_OK ) { if ((*CCall)->CurrentState != SendingFirstBuffer && (*CCall)->Connection->fExclusive) { OSF_CCALL_STATE myState = (*CCall)->CurrentState; Status = (*CCall)->BindToServer( FALSE // sync bind ); if (Status != RPC_S_OK) { // // Call has not yet started, ok to directly // free the call. // if (myState == NeedOpenAndBind) { (*CCall)->FreeCCall(RPC_S_CALL_FAILED_DNE); } else { (*CCall)->FreeCCall(Status); } } } } else { if (fBindingHandleReferenceRemoved == 0) { BindingMutex.Request(); ReferenceCount -= 1; ASSERT( ReferenceCount != 0 ); BindingMutex.Clear(); } } return Status; } RPC_STATUS OSF_BINDING_HANDLE::BindingCopy ( OUT BINDING_HANDLE * * DestinationBinding, IN UINT MaintainContext ) /*++ Routine Description: We need to copy this binding handle. This is relatively easy to do: we just need to point the copied binding handle to the same association as this binding handle. We also need to tell the association about the new binding handle. Arguments: DestinationBinding - Returns a copy of this binding handle. MaintainContext - Supplies a flag that indicates whether or not context is being maintained over this binding handle. A non-zero value indicates that context is being maintained. Return Value: RPC_S_OUT_OF_MEMORY - This indicates that there is not enough memory to allocate a new binding handle. RPC_S_OK - We successfully copied this binding handle. --*/ { RPC_STATUS Status = RPC_S_OK; OSF_BINDING_HANDLE * Binding; RPC_UUID Uuid; CLIENT_AUTH_INFO * AuthInfo; Binding = new OSF_BINDING_HANDLE(&Status); if ( Status != RPC_S_OK ) { delete Binding; Binding = 0; } if ( Binding == 0 ) { *DestinationBinding = 0; return(RPC_S_OUT_OF_MEMORY); } BindingMutex.Request(); Status = Binding->BINDING_HANDLE::Clone( this ); if (Status != RPC_S_OK) { delete Binding; Binding = 0; *DestinationBinding = 0; BindingMutex.Clear(); return Status; } Binding->ClientAuthInfo.DefaultLogonId = ClientAuthInfo.DefaultLogonId; Binding->NPType = NPType; Binding->fDynamicEndpoint = fDynamicEndpoint; if (pToken) { ASSERT(Association); ASSERT(IsNamedPipe(NPType)); Association->ReferenceToken(pToken); Binding->pToken = pToken; FastCopyLUIDAligned(&(Binding->ClientAuthInfo.ModifiedId), &(pToken->ModifiedId)); } Binding->Association = Association; if ( DceBinding != 0 ) { ASSERT( MaintainContext == 0 ); Binding->DceBinding = DceBinding->DuplicateDceBinding(); } else { Binding->DceBinding = 0; } Binding->TransInfo = TransInfo; if ( Association != 0 ) { Association->IncrementCount(); if ( MaintainContext != 0 ) { Association->MaintainingContext(); } } BindingMutex.Clear(); *DestinationBinding = (BINDING_HANDLE *) Binding; return(RPC_S_OK); } RPC_STATUS OSF_BINDING_HANDLE::BindingFree ( ) /*++ Routine Description: This method gets called when the application calls RpcBindingFree. All we have got to do is to decrement the reference count, and if it has reached zero, delete the binding handle. Return Value: RPC_S_OK - This operation always succeeds. --*/ { BindingMutex.Request(); ReferenceCount -= 1; if ( ReferenceCount == 0 ) { BindingMutex.Clear(); delete this; } else { BindingMutex.Clear(); } return(RPC_S_OK); } RPC_STATUS OSF_BINDING_HANDLE::PrepareBindingHandle ( IN TRANS_INFO * TransInfo, IN DCE_BINDING * DceBinding ) /*++ Routine Description: This method will be called just before a new binding handle is returned to the user. We just stash the transport interface and binding information so we can use it later when the first remote procedure call is made. At that time, we will actually bind to the interface. Arguments: TransportInterface - Supplies a pointer to a data structure describing a loadable transport. DceBinding - Supplies the binding information for this binding handle. --*/ { RPC_STATUS RpcStatus; SECURITY_QUALITY_OF_SERVICE SecQos; BOOL IsLocal; this->TransInfo = (TRANS_INFO *) TransInfo; this->DceBinding = DceBinding; fDynamicEndpoint = DceBinding->IsNullEndpoint(); // let's see if this is named pipe, and if yes, what options do we have if (DceBinding->IsNamedPipeTransport()) { IsLocal = (DceBinding->InqNetworkAddress() == NULL); if (DceBinding->InqNetworkOptions() == NULL) { if (IsLocal) { // if we go through NPFS, the default is dynamic SecQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; } else { // if we go through RDR, the default is static SecQos.ContextTrackingMode = SECURITY_STATIC_TRACKING; } } else { RpcStatus = I_RpcParseSecurity (DceBinding->InqNetworkOptions(), &SecQos); if (RpcStatus != RPC_S_OK) return RpcStatus; } if (IsLocal) { if (SecQos.ContextTrackingMode == SECURITY_STATIC_TRACKING) NPType = nptLocalStatic; else NPType = nptLocalDynamic; } else { if (SecQos.ContextTrackingMode == SECURITY_STATIC_TRACKING) NPType = nptRemoteStatic; else NPType = nptRemoteDynamic; } } else { NPType = nptNotNamedPipe; } Association = 0; return RPC_S_OK; } RPC_STATUS OSF_BINDING_HANDLE::ToStringBinding ( OUT RPC_CHAR * * StringBinding ) /*++ Routine Description: We need to convert the binding handle into a string binding. If the binding handle has not yet been used to make a remote procedure call, then we can just use the information in the binding handle to create the string binding. Otherwise, we need to ask the association to do it for us. Arguments: StringBinding - Returns the string representation of the binding handle. Return Value: RPC_S_OK - The operation completed successfully. RPC_S_OUT_OF_MEMORY - We do not have enough memory available to allocate space for the string binding. --*/ { if ( Association == 0 ) { *StringBinding = DceBinding->StringBindingCompose( InqPointerAtObjectUuid()); if (*StringBinding == 0) return(RPC_S_OUT_OF_MEMORY); return(RPC_S_OK); } return(Association->ToStringBinding(StringBinding, InqPointerAtObjectUuid())); } RPC_STATUS OSF_BINDING_HANDLE::BindingReset ( ) /*++ Routine Description: This routine will set the endpoint of this binding handle to zero, if possible. The binding handle will become partially bound as a result. If a remote procedure call has been made on this binding handle, it will fail as well. Return Value: RPC_S_OK - The binding handle has successfully been made partially bound. RPC_S_WRONG_KIND_OF_BINDING - The binding handle currently has remote procedure calls active. --*/ { BindingMutex.Request(); if ( Association != 0 ) { if ( ReferenceCount != 1 ) { BindingMutex.Clear(); return(RPC_S_WRONG_KIND_OF_BINDING); } DceBinding = Association->DuplicateDceBinding(); if (DceBinding == NULL) { BindingMutex.Clear(); return RPC_S_OUT_OF_MEMORY; } Unbind(); // Can't touch Association after this point. } DceBinding->MakePartiallyBound(); fDynamicEndpoint = TRUE; if ( *InquireEpLookupHandle() != 0 ) { EpFreeLookupHandle(*InquireEpLookupHandle()); *InquireEpLookupHandle() = 0; } BindingMutex.Clear(); return(RPC_S_OK); } ULONG OSF_BINDING_HANDLE::MapAuthenticationLevel ( IN ULONG AuthenticationLevel ) /*++ Routine Description: The connection oriented protocol module supports all authentication levels except for RPC_C_AUTHN_LEVEL_CALL. We just need to map it to RPC_C_AUTHN_LEVEL_PKT. --*/ { UNUSED(this); if ( AuthenticationLevel == RPC_C_AUTHN_LEVEL_CALL ) { return(RPC_C_AUTHN_LEVEL_PKT); } return(AuthenticationLevel); } RPC_STATUS OSF_BINDING_HANDLE::ResolveBinding ( IN PRPC_CLIENT_INTERFACE RpcClientInterface ) /*++ Routine Description: We need to try and resolve the endpoint for this binding handle if necessary (the binding handle is partially-bound). We check to see if an association has been obtained for this binding handle; if so, we need to do nothing since the binding handle is fully-bound, otherwise, we try and resolve an endpoint for it. Arguments: RpcClientInterface - Supplies interface information to be used in resolving the endpoint. Return Value: RPC_S_OK - The binding handle is now fully-bound. RPC_S_NO_ENDPOINT_FOUND - We were unable to resolve the endpoint for this particular combination of binding handle (network address) and interface. RPC_S_OUT_OF_MEMORY - Insufficient memory is available to resolve the endpoint. --*/ { RPC_STATUS Status; BindingMutex.Request(); if ( Association == 0 ) { Status = DceBinding->ResolveEndpointIfNecessary( RpcClientInterface, InqPointerAtObjectUuid(), InquireEpLookupHandle(), FALSE, InqComTimeout(), DEFAULT_EPMAP_CALL_TIMEOUT, // CallTimeout &ClientAuthInfo ); } else { Status = RPC_S_OK; } BindingMutex.Clear(); return(Status); } RPC_STATUS OSF_BINDING_HANDLE::AddRecursiveEntry ( IN OSF_CCALL * CCall, IN PRPC_CLIENT_INTERFACE RpcInterfaceInformation ) /*++ Routine Description: When a callback occurs, we need to add an entry for the thread and interface being using for the callback to the binding handle. This is so that we can later turn original calls into callbacks if they are from the same thread (as the original call) and to the same interface (as the original call). Arguments: CCall - Supplies the Call on which the original call was sent. RpcInterfaceInformation - Supplies the interface used by the original call. Return Value: RPC_S_OK - An recursive entry has been added to the binding handle for the supplied Call and interface. RPC_S_OUT_OF_MEMORY - Insufficient memory is available to perform the operation. --*/ { OSF_RECURSIVE_ENTRY * RecursiveEntry; BindingMutex.Request(); RecursiveEntry = new OSF_RECURSIVE_ENTRY(GetThreadIdentifier(), RpcInterfaceInformation, CCall); if ( RecursiveEntry == 0 ) { BindingMutex.Clear(); return(RPC_S_OUT_OF_MEMORY); } CCall->RecursiveCallsKey = RecursiveCalls.Insert( RecursiveEntry); if ( CCall->RecursiveCallsKey == -1 ) { BindingMutex.Clear(); delete RecursiveEntry; return(RPC_S_OUT_OF_MEMORY); } ReferenceCount += 1; BindingMutex.Clear(); return(RPC_S_OK); } void OSF_BINDING_HANDLE::RemoveRecursiveCall ( IN OSF_CCALL * CCall ) /*++ Routine Description: The specified Call is removed from the dictionary of active Calls for this binding handle. Arguments: CCall - Supplies the Call to be removed from the dictionary of active Calls. --*/ { OSF_RECURSIVE_ENTRY * RecursiveEntry; BindingMutex.Request(); RecursiveEntry = RecursiveCalls.Delete(CCall->RecursiveCallsKey); if ( RecursiveEntry != 0 ) { delete RecursiveEntry; } CCall->RecursiveCallsKey = -1; ReferenceCount -= 1; BindingMutex.Clear(); } OSF_CASSOCIATION * OSF_BINDING_HANDLE::FindOrCreateAssociation ( IN DCE_BINDING * DceBinding, IN TRANS_INFO *TransInfo, IN RPC_CLIENT_INTERFACE *InterfaceInfo ) /*++ Function Name:FindOrCreateAssociation Parameters: DceBinding - Supplies binding information; ownership of this object passes to this routine. TransportInterface - Supplies a pointer to the data structure which describes a loadable transport. InterfaceInfo - Supplied the interface information for this call. Used to make quick resolution on new binding handles if existing bindings for this interface exist. If supplied, it takes precedence over endpoint matching for selecting an association, and no new association will be created - only existing ones will be found! Description: This routine finds an existing association supporting the requested DCE binding, or create a new association which supports the requested DCE binding. Ownership of the passed DceBinding pass to this routine. Returns: An association which supports the requested binding will be returned; Otherwise, zero will be returned, indicating insufficient memory. --*/ { OSF_CASSOCIATION * CAssociation; RPC_STATUS Status = RPC_S_OK; DictionaryCursor cursor; ULONG_PTR fUnique; BOOL fOnlyEndpointDifferent; int Result; Status = OSF_BINDING_HANDLE::InqTransportOption(RPC_C_OPT_UNIQUE_BINDING, &fUnique); ASSERT(Status == RPC_S_OK); // // We start be looking in the dictionary of existing associations // to see if there is one supporting the binding information specified. // AssocDictMutex->Request(); if (fUnique == 0) { AssociationDict->Reset(cursor); while ( (CAssociation = AssociationDict->Next(cursor)) != 0 ) { if (CAssociation->IsValid()) { // if we're doing shortcut endpoint resolution, and the binding // handle wants to mutually authenticate the server using the // endpoint mapper (endpoint mapper namespace partitioning) but // the association hasn't used that capability, don't do shortcut // endpoint resolution if (InterfaceInfo && (ClientAuthInfo.Capabilities & RPC_C_QOS_CAPABILITIES_LOCAL_MA_HINT) && !CAssociation->LocalMASet ) { AssocDictMutex->Clear(); return NULL; } Result = CAssociation->CompareWithDceBinding(DceBinding, &fOnlyEndpointDifferent); // if the DceBindings are the same, or they differ only // by the endpoint, and it is a NULL endpoint, and there // is InterfaceInfo specified, and this association // supports at least one binding for this interface, and either // this is not the management UUID or the association has a non- // NULL object uuid, then choose the association if (!Result || ( fOnlyEndpointDifferent && DceBinding->IsNullEndpoint() && InterfaceInfo && CAssociation->DoesBindingForInterfaceExist(InterfaceInfo) && ( (RpcpMemoryCompare(&InterfaceInfo->InterfaceId.SyntaxGUID, &MgmtIf, sizeof(UUID)) != 0) || (CAssociation->DceBinding->IsNullObjectUuid() == FALSE) ) ) ) { if (CAssociation->Linger.fAssociationLingered == TRUE) { #if defined (RPC_GC_AUDIT) DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) OSF lingering association resurrected %X %S %S %S\n", GetCurrentProcessId(), GetCurrentProcessId(), CAssociation, CAssociation->DceBinding->InqRpcProtocolSequence(), CAssociation->DceBinding->InqNetworkAddress(), CAssociation->DceBinding->InqEndpoint()); #endif OsfLingeredAssociations --; ASSERT(OsfLingeredAssociations >= 0); CAssociation->Linger.fAssociationLingered = FALSE; } if ((ClientAuthInfo.Capabilities & RPC_C_QOS_CAPABILITIES_LOCAL_MA_HINT) && !CAssociation->LocalMASet) { CAssociation->LocalMASet = TRUE; } CAssociation->IncrementCount(); AssocDictMutex->Clear(); delete DceBinding; return(CAssociation); } } } } // if asked to do short endpoint resolution, don't create new association if (InterfaceInfo) { AssocDictMutex->Clear(); return NULL; } #if defined (RPC_GC_AUDIT) DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) Creating association to: %S, %S, %S\n", GetCurrentProcessId(), GetCurrentProcessId(), DceBinding->InqRpcProtocolSequence(), DceBinding->InqNetworkAddress(), DceBinding->InqEndpoint()); #endif RPC_CONNECTION_TRANSPORT *ClientInfo = (RPC_CONNECTION_TRANSPORT *) TransInfo->InqTransInfo(); CAssociation = new (ClientInfo->ResolverHintSize) OSF_CASSOCIATION(DceBinding, TransInfo, &Status); if ( (Status != RPC_S_OK) && (CAssociation != NULL) ) { CAssociation->DceBinding = 0; delete CAssociation; CAssociation = 0; } if (CAssociation != 0) { CAssociation->Key = AssociationDict->Insert(CAssociation); if (CAssociation->Key == -1) { CAssociation->DceBinding = 0; delete CAssociation; CAssociation = 0; } } AssocDictMutex->Clear(); return(CAssociation); } RPC_STATUS OSF_BINDING_HANDLE::SetTransportOption( IN unsigned long option, IN ULONG_PTR optionValue ) { if (option == RPC_C_OPT_DONT_LINGER) { if (Association == NULL) return RPC_S_WRONG_KIND_OF_BINDING; if (Association->GetDontLingerState()) return RPC_S_OK; Association->SetDontLingerState((BOOL)optionValue); return RPC_S_OK; } else { return BINDING_HANDLE::SetTransportOption(option, optionValue); } } RPC_STATUS OSF_BINDING_HANDLE::InqTransportOption( IN unsigned long option, OUT ULONG_PTR * pOptionValue ) { if (option == RPC_C_OPT_DONT_LINGER) { if (Association == NULL) return RPC_S_WRONG_KIND_OF_BINDING; *pOptionValue = Association->GetDontLingerState(); return RPC_S_OK; } else { return BINDING_HANDLE::InqTransportOption(option, pOptionValue); } } #pragma optimize ("t", on) RPC_STATUS OSF_CASSOCIATION::LookForExistingConnection ( IN OSF_BINDING_HANDLE *BindingHandle, IN BOOL fExclusive, IN CLIENT_AUTH_INFO *ClientAuthInfo, IN int *PresentationContexts, IN int NumberOfPresentationContexts, OUT OSF_CCONNECTION **NewConnection, OUT int *PresentationContextSupported, OUT OSF_CCALL_STATE *InitialCallState, IN BOOL fUseSeparateConnection ) /*++ Function Name:LookForExistingConnection Parameters: BindingHandle - the binding handle through which the call is made fExclusive - non-zero if we are looking for an exclusive connection zero otherwise ClientAuthInfo - a connection must support this specified auth info PresentationContexts - array of presentation contexts, any of which is acceptable to our callers NumberOfPresentationContexts - the size of the PresentationContexts array PreferredPresentationContext - the preferred presentation context for this connection. -1 if no preferences. Note that this is taken from a previous binding to the server - this is not the client preference. NewConnection - on output, the new connection or NULL. Undefined on failure PresentationContextSupported - the presentation context that the chosen connection supports. This is useful only if multiple presentation contexts were given. Also, if the connection supports multiple pcontexts and multiple pcontexts were given this would be any of the pcontexts. This is an index into the NumberOfPresentationContexts array. If the connection supports none of the suggested presentation contexts, this is set to -1. In this case, alter context is required InitialCallState - the initial state that the call should have is returned in this parameter fUseSeparateconnection - if non-zero, a separate connection is requested Description: Returns: RPC_S_OK or RPC_S_* error --*/ { OSF_CCONNECTION *CConnection, *FirstMatch = 0; DictionaryCursor cursor; RPC_STATUS Status; BOOL Supported; ASSERT(ClientAuthInfo); AssociationMutex.VerifyOwned(); *PresentationContextSupported = -1; ActiveConnections.Reset(cursor); if (fExclusive || fUseSeparateConnection) { INT cConnectionFree, cConnectionBusy; if (fExclusive) { cConnectionFree = SYNC_CONN_FREE; cConnectionBusy = SYNC_CONN_BUSY; } else { cConnectionFree = ASYNC_CONN_FREE; cConnectionBusy = ASYNC_CONN_BUSY; } while ((CConnection = ActiveConnections.Next(cursor)) != 0) { if (cConnectionFree == (INT) CConnection->ThreadId) { Status = CConnection->SupportedAuthInfo(ClientAuthInfo, BindingHandle->NPType, fExclusive, &Supported); if (Status != RPC_S_OK) return Status; if (Supported) { if (CConnection->SupportedPContext(PresentationContexts, NumberOfPresentationContexts, PresentationContextSupported) !=0) { CConnection->ThreadId = cConnectionBusy; *InitialCallState = SendingFirstBuffer; break; } else { // // We found a connection that will require an alt-context // before we can use it. // FirstMatch = CConnection; } // if-else } // if Supported } // if ThreadId } // while if (0 == CConnection && FirstMatch) { CConnection = FirstMatch ; CConnection->ThreadId = cConnectionBusy; *InitialCallState = NeedAlterContext; } } else { DWORD ThreadId = GetCurrentThreadId(); while ((CConnection = ActiveConnections.Next(cursor)) != 0) { Status = CConnection->SupportedAuthInfo(ClientAuthInfo, BindingHandle->NPType, fExclusive, &Supported); if (Status != RPC_S_OK) return Status; if (Supported) { if (CConnection->SupportedPContext(PresentationContexts, NumberOfPresentationContexts, PresentationContextSupported) !=0) { if (ThreadId == CConnection->ThreadId) { // // We found a connection where everything matches, // including the thread id. Go ahead and use it. // *InitialCallState = SendingFirstBuffer; break; } } else { if (ThreadId == CConnection->ThreadId) { // // We found a connection where the thread id matches, but // it will need an alt-context, before it can be used. Mark it as // our first choice. // FirstMatch = CConnection; } } // if-else } // if } // while if (0 == CConnection && FirstMatch) { // // Matching thread-id, but will need an alt-context, before // it can be used. The alt-context will be sent when the call // actually gets scheduled. // CConnection = FirstMatch; *InitialCallState = NeedAlterContext; } } // if-else if (CConnection) { if (fExclusive) { CConnection->fExclusive = 1; } else { CConnection->fExclusive = 0; } // CCONN++ CConnection->AddReference(); // mark this connection as just retrieved from the cache // see the comments to the FreshFromCache flag CConnection->SetFreshFromCacheFlag(); } *NewConnection = CConnection; return RPC_S_OK; } #pragma optimize("", on) /* Mechanism: Multiple transfer syntax negotiation Purpose: Negotiate a transfer syntax supported by both the client and the server and optimal for the server (if there is a choice). It should allow for fail-over of the server to a downlevel node in the case of mixed clusters, but it is allowed to fail the first one or more calls after the failover while it adjusts (this restricted implementation was approved by MarioGo on RPC team meeting on Apr 10th, 2000). Implementation: Here's the matrix for the scenarios. The only current difference b/n sync and async is the initial conn establishment. The matrix describes only the case where we support both (the others are trivial) Sync Calls: Conn Av. Preference Action ------------- ------------ ------------- No conn. Doesn't matter Offer both. Don't fix choice for call Conn NDR20 Not set Alter context to both. This cannot fail with invalid xfer syntax. Conn NDR20 NDR20 Use the conn. Conn NDR20 NDR64 The connection is stale. Use the connection anyway. We know it will blow and we'll open a new one. Conn NDR64 Not set Alter context to both. This cannot fail with invalid xfer syntax. Conn NDR64 NDR20 The connection is stale. Use the connection anyway. We know it will blow and we'll open a new one. Conn NDR64 NDR64 Use the conn Conn both Any Use preference. Non-sync Calls: Conn Av. Preference Action ------------- ------------ ------------- No conn. Not set Offer both. If NDR64 is negotiated, negotiate once more (alter context) for NDR20, so that we can send the first call, which was marshalled NDR20. Conn NDR20 Not set Alter context to both. Choose NDR20. Conn NDR20 NDR20 Use the conn. Conn NDR20 NDR64 The connection is stale. Use the connection anyway. We know it will blow and we'll open a new one. Conn NDR64 Not set Alter context to both. Choose NDR20. If NDR64 is chosen, alter context to NDR20. If NDR20 is chosen, use it. Conn NDR64 NDR20 The connection is stale. Use the connection anyway. We know it will blow and we'll open a new one. Conn NDR64 NDR64 Use the conn Conn both All Use preference. */ const int AUTO_ENABLE_IDLE_CLEANUP = 70; RPC_STATUS OSF_CASSOCIATION::AllocateCCall ( IN OSF_BINDING_HANDLE *BindingHandle, IN PRPC_MESSAGE Message, IN CLIENT_AUTH_INFO * ClientAuthInfo, OUT OSF_CCALL ** pCCall, OUT BOOL *fBindingHandleReferenceRemoved ) /*++ Function Name:AllocateCCall Parameters: CCall - Returns the allocated call. ClientAuthInfo - Supplies the authentication and authorization information required for the connection. Description: In this method, we allocate a connection supporting the requested interface information. This means that first we need to find the presentation context corresponding to the requested interface interface. Then we search for an existing connection supporting the presentation context, and then we try and create a new connection. We then ask the Connection object to create a Call for us. Returns: RPC_S_OK - The operation completed successfully. RPC_S_OUT_OF_MEMORY - Insufficient memory is available to create objects necessary to allocate a connection. --*/ { ULONG CallIdToUse; RPC_STATUS Status; OSF_BINDING *BindingsForThisInterface[MaximumNumberOfTransferSyntaxes]; int NumberOfBindingsAvailable; int NumberOfBindingsToUse; int PresentationContextsAvailable[MaximumNumberOfTransferSyntaxes]; int *PresentationContextsToUse; int PresentationContextSupported; int NDR20PresentationContext; int i; OSF_CCALL_STATE InitialCallState; OSF_CCONNECTION *CConnection = 0; BOOL fExclusive = !NONSYNC(Message); ULONG_PTR fUseSeparateConnection = (PARTIAL(Message) != 0); RPC_CLIENT_INTERFACE *RpcInterfaceInformation = (RPC_CLIENT_INTERFACE *) Message->RpcInterfaceInformation; OSF_BINDING *BindingsList; OSF_BINDING *BindingToUse; RPC_DISPATCH_TABLE *DispatchTableToUse; *fBindingHandleReferenceRemoved = FALSE; // // To begin with, we need to obtain the presentation context // corresponding to the specified interface information. // Status = AssociationMutex.RequestSafe(); if (Status) return Status; Status = FindOrCreateOsfBinding(RpcInterfaceInformation, Message, &NumberOfBindingsAvailable, BindingsForThisInterface); if ( Status != RPC_S_OK ) { AssociationMutex.Clear(); return(Status); } CallIdToUse = CallIdCounter++; if (fExclusive == 0 && fUseSeparateConnection == 0) { Status = BindingHandle->InqTransportOption( RPC_C_OPT_BINDING_NONCAUSAL, &fUseSeparateConnection); ASSERT(Status == RPC_S_OK); } // // Ok, now we search for an available connection supporting the // requested presentation context. // // construct the array of presentation contexts any of which will // do the job #ifdef DEBUGRPC BindingsList = 0; NDR20PresentationContext = -1; #endif NumberOfBindingsToUse = NumberOfBindingsAvailable; PresentationContextsToUse = PresentationContextsAvailable; for (i = 0; i < NumberOfBindingsAvailable; i ++) { PresentationContextsAvailable[i] = BindingsForThisInterface[i]->GetPresentationContext(); if (BindingsForThisInterface[i]->IsTransferSyntaxListStart()) { // make sure only one binding is the list start ASSERT(BindingsList == 0); BindingsList = BindingsForThisInterface[i]; } if (BindingsForThisInterface[i]->IsTransferSyntaxServerPreferred()) { // one of the transfer syntaxes is marked as preferred - // try to use it. // Note that this doesn't break the mixed cluster scenario, // because when binding on new connection, we always offer both, regardless of // preferences. We hit this path only when we choose from // existing. If we moved the association to a different node // of the cluster, all the old connections will be blown // away, and it doesn't matter what presentation context we // choose for them. A successful bind to the new node will // reset the preferences, so we're fine NumberOfBindingsToUse = 1; PresentationContextsToUse = &PresentationContextsAvailable[i]; } if (IsNonsyncMessage(Message)) { // the call is non sync and there may be no preference. For non sync, // we start with NDR20, because the client may be downlevel. When // the first bind completes, it will set the preference if (BindingsForThisInterface[i]->CompareWithTransferSyntax(NDR20TransferSyntax) == 0) { NDR20PresentationContext = i; } } // we have obtained our bindings. Add a refcount to them while we hold the association mutex BindingsForThisInterface[i]->AddReference(); } // at least one binding must be the start of the list ASSERT(BindingsList != 0); Status = LookForExistingConnection ( BindingHandle, fExclusive, ClientAuthInfo, PresentationContextsToUse, NumberOfBindingsToUse, &CConnection, &PresentationContextSupported, &InitialCallState, BOOL(fUseSeparateConnection)) ; AssociationMutex.Clear(); if (Status != RPC_S_OK) { ReleaseBindingList(BindingsList); return Status; } if (CConnection == 0) { // // Allocate a new connection // RPC_CONNECTION_TRANSPORT *ClientInfo = (RPC_CONNECTION_TRANSPORT *) TransInfo->InqTransInfo(); Status = RPC_S_OK; CConnection = new(ClientInfo->ClientConnectionSize + ClientInfo->SendContextSize + sizeof(PVOID)) OSF_CCONNECTION( this, ClientInfo, BindingHandle->InqComTimeout(), ClientAuthInfo, fExclusive, BOOL(fUseSeparateConnection), &Status); if (CConnection == 0) { Status = RPC_S_OUT_OF_MEMORY; } if (Status != RPC_S_OK) { delete CConnection; ReleaseBindingList(BindingsList); return Status; } Status = AssociationMutex.RequestSafe(); if (Status) { delete CConnection; ReleaseBindingList(BindingsList); return Status; } if (!fExclusive) { Status = TransInfo->StartServerIfNecessary(); if (Status != RPC_S_OK) { AssociationMutex.Clear(); delete CConnection; ReleaseBindingList(BindingsList); return Status; } } CConnection->ConnectionKey = ActiveConnections.Insert(CConnection); if (CConnection->ConnectionKey == -1) { AssociationMutex.Clear(); delete CConnection; ReleaseBindingList(BindingsList); return RPC_S_OUT_OF_MEMORY; } if (IsValid() == FALSE) { ActiveConnections.Delete(CConnection->ConnectionKey); CConnection->ConnectionKey = -1; AssociationMutex.Clear(); delete CConnection; ReleaseBindingList(BindingsList); return AssociationShutdownError; } if (ActiveConnections.Size() > AUTO_ENABLE_IDLE_CLEANUP) { // EnableIdleConnectionCleanup doesn't fail (void) EnableIdleConnectionCleanup(); } InitialCallState = NeedOpenAndBind; // // Since this is the first call on the connection // we might as well use the cached CCall // we are deliberately not taking the connection mutex // The cached call is already marked as not available in // the constructor of the connection // *pCCall = CConnection->CachedCCall; if (fEnableIdleConnectionCleanup && (fIdleConnectionCleanupNeeded == FALSE)) { fIdleConnectionCleanupNeeded = TRUE; // // Finally, we need to notify the protocol independent layer that // the code to delete idle connections should be executed periodically. // We divide by two to reduce the amount of extra time an idle // connection lives beyond the minimum. // GarbageCollectionNeeded(FALSE, CLIENT_DISCONNECT_TIME1 / 2); } AssociationMutex.Clear(); if (NumberOfBindingsAvailable == 1) { // we support only one binding - just use it BindingToUse = BindingsForThisInterface[0]; DispatchTableToUse = BindingToUse->GetDispatchTable(); ReleaseBindingListWithException(BindingToUse, BindingsList); BindingsList = 0; } else if (IsNonsyncMessage(Message)) { // if there is still more than one available binding left // and the call is non-sync (this can happen while the server // preferences are not yet recorded), artifically limit the connection // lookup to NDR20 to avoid downward level server compatibility problems ASSERT (NDR20PresentationContext != -1); // we may overwrite the choices made up when we iterated over the // available bindings. That's ok - we always want to advertise both // for new connections NumberOfBindingsToUse = 1; PresentationContextsToUse = &PresentationContextsAvailable[NDR20PresentationContext]; i = (int)(PresentationContextsToUse - PresentationContextsAvailable); BindingToUse = BindingsForThisInterface[i]; DispatchTableToUse = BindingToUse->GetDispatchTable(); } else { // even if server preference is set, we should still suggest both // to support the mixed cluster scenario BindingToUse = 0; DispatchTableToUse = 0; } // The refcounts on BindingToUse/BindingsList pass to the call. // If the call fails, we will release these refcounts below. Status = (*pCCall)->ActivateCall( BindingHandle, BindingToUse, BindingsList, CallIdToUse, InitialCallState, DispatchTableToUse, CConnection); if (Status != RPC_S_OK) { if ((*pCCall)->Bindings.AvailableBindingsList) ReleaseBindingList((*pCCall)->Bindings.AvailableBindingsList); else (*pCCall)->GetSelectedBinding()->RemoveReference(); ConnectionAborted(CConnection); delete CConnection; return Status ; } if (PARTIAL(Message)) (*pCCall)->SetIsPipeCallFlag(); if (!fExclusive) { BindingHandle->OSF_BINDING_HANDLE::AddReference(); // add one more reference to the connection in case the sync // path fails with out of memory and starts cleaning up // This extra reference will make sure that the connection // and cached call do not go away underneath the async path // CCONN++ CConnection->OSF_CCONNECTION::AddReference(); (*pCCall)->OSF_CCALL::AddReference(); Status = CConnection->TransPostEvent(*pCCall); if (Status != RPC_S_OK) { BindingHandle->OSF_BINDING_HANDLE::BindingFree(); ConnectionAborted(CConnection); (*pCCall)->OSF_CCALL::RemoveReference(); delete CConnection; return Status; } } } else { // there is a connection found. If the connection supports both // transfer syntaxes, then a server preferred syntax must have // been established. If there is no server preferred syntax, // the chosen connection supports at most one syntax currently // If there is only one binding to use (either because we support // only one, or because there is server preference set), we use it // if the connection supports it. Otherwise, we need to alter // context the connection. // If there are more bindings, this means there are no server // preferences, and it gets more complicated. First, we need to // alter context both to find out the server preference. The server // may choose the same syntax that we already support, or it may // choose a different syntax. In the async case, we choose whatever // is currently supported, but we try to alter context both to give // the server a chance to indicate its preferences. if (NumberOfBindingsToUse == 1) { // only one binding. Do we support it? if (PresentationContextSupported >= 0) // faster version of != -1 { // yes - easy choice. Just use it. // calculate the offset of the chosen presentation context in the original // presentation contexts array (PresentationContextsAvailable). i = (int)((PresentationContextsToUse - PresentationContextsAvailable) + PresentationContextSupported); } else { // if we are here, the connection does not support the transfer // syntax we need. We have only one that we support, so we // stick with it and fail the call if we cannot // negotiate to it (just a shortcut version of the first case). // Note that the LookForExistingConnection has set the state of // the call to NeedAlterContext if this is the case, so the // bind function will do the right thing - we don't need to worry // about it. i = (int)(PresentationContextsToUse - PresentationContextsAvailable); } // this is the same offset as the offset in the BindingsForThisInterface array, since the // two arrays are parallel BindingToUse = BindingsForThisInterface[i]; DispatchTableToUse = BindingToUse->GetDispatchTable(); ReleaseBindingListWithException(BindingToUse, BindingsList); BindingsList = 0; } else { // here NumberOfBindingsToUse is more than one. This means we support // more than one xfer syntax, and the server preferences are not // set. InitialCallState = NeedAlterContext; // We offered both. At least one must be supported - otherwise // the connection should have been gone. if (PresentationContextSupported >= 0) { // this should never happen yet. It can only happen // in the multiple client stubs with differen xfer syntax // support scenario, but we don't support it yet. ASSERT(0); if (IsNonsyncMessage(Message)) { i = (int)((PresentationContextsToUse - PresentationContextsAvailable) + PresentationContextSupported); BindingToUse = BindingsForThisInterface[i]; DispatchTableToUse = BindingToUse->GetDispatchTable(); // Don't whack out the list - this allows the client to offer both // BindingsList = 0; } else { BindingToUse = 0; DispatchTableToUse = 0; } } else { if (IsNonsyncMessage(Message)) { // if there is still more than one available binding left // and the call is non-sync (this can happen while the server // preferences are not yet recorded), artifically limit the connection // lookup to NDR20 to avoid downward level server compatibility problems ASSERT (NDR20PresentationContext != -1); // we may overwrite the choices made up when we iterated over the // available bindings. That's ok - we always want to advertise both // in this case NumberOfBindingsToUse = 1; PresentationContextsToUse = &PresentationContextsAvailable[NDR20PresentationContext]; i = (int)(PresentationContextsToUse - PresentationContextsAvailable); BindingToUse = BindingsForThisInterface[i]; DispatchTableToUse = BindingToUse->GetDispatchTable(); // Don't whack out the list - this allows the client to offer both // BindingsList = 0; } else { // even if server preference is set, we should still suggest both // to support the mixed cluster scenario BindingToUse = 0; DispatchTableToUse = 0; } } } // // This is not the first call on the connection. We will ask it to allocate // a call for us // Status = CConnection->AllocateCCall(pCCall); if (Status == RPC_S_OK) { // The refcounts on BindingToUse/BindingsList pass to the call. // If the call fails, we will release these refcounts in FreeCCall. Status = (*pCCall)->ActivateCall( BindingHandle, BindingToUse, BindingsList, CallIdToUse, InitialCallState, DispatchTableToUse, CConnection); if (Status != RPC_S_OK) { // // Call has not yet started, ok to directly // free the call. // (*pCCall)->FreeCCall(RPC_S_CALL_FAILED_DNE); *fBindingHandleReferenceRemoved = TRUE; return Status; } if (PARTIAL(Message)) (*pCCall)->SetIsPipeCallFlag(); Status = (*pCCall)->ReserveSpaceForSecurityIfNecessary(); if (Status != RPC_S_OK) { // // Call has not yet started, ok to directly // free the call. // (*pCCall)->FreeCCall(RPC_S_CALL_FAILED_DNE); *fBindingHandleReferenceRemoved = TRUE; return Status; } } else { ReleaseBindingList(BindingsList); } } return Status; } BOOL OSF_CASSOCIATION::ConnectionAborted ( IN OSF_CCONNECTION *Connection ) /*++ Function Name:ConnectionAborted Parameters: Description: Returns: --*/ { BOOL fDontKill = FALSE; AssociationMutex.Request(); if (Connection->ConnectionKey != -1) { LogEvent(SU_CCONN, EV_STOP, Connection, this, Connection->ConnectionKey, 1, 0); ActiveConnections.Delete(Connection->ConnectionKey); Connection->ConnectionKey = -1; } if (Connection->fConnectionAborted == 0) { NotifyConnectionClosed(); Connection->fConnectionAborted = 1; } else { fDontKill = TRUE; } AssociationMutex.Clear(); return fDontKill; } RPC_STATUS OSF_CASSOCIATION::FindOrCreateToken ( IN HANDLE hToken, IN LUID *pModifiedId, OUT RPC_TOKEN **ppToken, OUT BOOL *pfTokenFound ) /*++ Function Name:FindOrCreateToken Parameters: Description: Returns: --*/ { DictionaryCursor cursor; RPC_TOKEN *Token; RPC_STATUS Status; Status = AssociationMutex.RequestSafe(); if (Status) return Status; TokenDict.Reset(cursor); while ((Token = TokenDict.Next(cursor)) != 0) { if (FastCompareLUIDAligned(&Token->ModifiedId, pModifiedId)) { *pfTokenFound = TRUE; Token->RefCount++; // Token++; LogEvent(SU_REFOBJ, EV_INC, Token, 0, Token->RefCount, 1, 1); *ppToken = Token; Status = RPC_S_OK; goto Cleanup; } } *pfTokenFound = FALSE; *ppToken = new RPC_TOKEN(hToken, pModifiedId); // constructor cannot fail if (*ppToken == 0) { CloseHandle(hToken); Status = RPC_S_OUT_OF_MEMORY; goto Cleanup; } if (((*ppToken)->Key = TokenDict.Insert(*ppToken)) == -1) { Status = RPC_S_OUT_OF_MEMORY; delete *ppToken; goto Cleanup; } Status = RPC_S_OK; Cleanup: AssociationMutex.Clear(); return Status; } void OSF_CASSOCIATION::ReferenceToken( IN RPC_TOKEN *pToken ) /*++ Function Name:ReferenceToken Parameters: Description: Returns: --*/ { AssociationMutex.Request(); ASSERT(pToken->RefCount); pToken->RefCount++; // Token++ LogEvent(SU_REFOBJ, EV_INC, pToken, 0, pToken->RefCount, 1, 1); AssociationMutex.Clear(); } void OSF_CASSOCIATION::DereferenceToken( IN RPC_TOKEN *pToken ) /*++ Function Name:DereferenceToken Parameters: Description: Returns: --*/ { AssociationMutex.Request(); LogEvent(SU_REFOBJ, EV_DEC, pToken, 0, pToken->RefCount, 1, 1); pToken->RefCount--; // Token-- if (pToken->RefCount == 0) { TokenDict.Delete(pToken->Key); CleanupConnectionList(pToken); delete pToken; } AssociationMutex.Clear(); } void OSF_CASSOCIATION::CleanupConnectionList( IN RPC_TOKEN *pToken ) /*++ Function Name:CleanupConnectionList Parameters: Description: Returns: --*/ { DictionaryCursor cursor; OSF_CCONNECTION *CConnection; AssociationMutex.VerifyOwned(); if ( MaintainContext != 0 && ActiveConnections.Size() <= 1) return; ActiveConnections.Reset(cursor); while ( (CConnection = ActiveConnections.Next(cursor)) != 0 ) { if (CConnection->ThreadId == SYNC_CONN_FREE || CConnection->ThreadId == ASYNC_CONN_FREE) { if (CConnection->MatchModifiedId(&(pToken->ModifiedId)) == TRUE) { CConnection->AddReference(); //CCONN++ ConnectionAborted(CConnection); CConnection->DeleteConnection(); // // I don't if the add/remove reference is really needed // I am only doing it to preserve existing semantics // CConnection->RemoveReference(); // CCONN-- } } } } void ConstructPContextList ( OUT p_cont_list_t *pCon, // Place the list here. IN OSF_BINDING *AvailableBindings, IN int NumberOfBindings ) /*++ Function Name:ConstructPContextList Parameters: Description: Construct the presentation context list in the rpc_bind packet (and implicitly rpc_alter_context) packet. Returns: --*/ { int i; OSF_BINDING *CurrentBinding; pCon->n_context_elem = (unsigned char)NumberOfBindings; pCon->reserved = 0; pCon->reserved2 = 0; CurrentBinding = AvailableBindings; for (i = 0; i < NumberOfBindings; i ++, CurrentBinding = CurrentBinding->GetNextBinding()) { pCon->p_cont_elem[i].p_cont_id = CurrentBinding->GetOnTheWirePresentationContext(); pCon->p_cont_elem[i].n_transfer_syn = (unsigned char) 1; pCon->p_cont_elem[i].reserved = 0; RpcpMemoryCopy(&pCon->p_cont_elem[i].abstract_syntax, CurrentBinding->GetInterfaceId(), sizeof(RPC_SYNTAX_IDENTIFIER)); RpcpMemoryCopy(pCon->p_cont_elem[i].transfer_syntaxes, CurrentBinding->GetTransferSyntaxId(), sizeof(RPC_SYNTAX_IDENTIFIER)); } } OSF_CCONNECTION::OSF_CCONNECTION ( IN OSF_CASSOCIATION *MyAssociation, IN RPC_CONNECTION_TRANSPORT * RpcClientInfo, IN UINT Timeout, IN CLIENT_AUTH_INFO * ClientAuthInfo, IN BOOL fExclusive, IN BOOL fSeparateConnection, OUT RPC_STATUS * pStatus ) : ConnMutex(pStatus), ClientSecurityContext(ClientAuthInfo, 0, FALSE, pStatus) /*++ Function Name:OSF_CCONNECTION Parameters: Description: Constructor for the connection object Returns: --*/ { LogEvent(SU_CCONN, EV_CREATE, this); Flags.ClearAll(); InitConnectionSupportHeaderSign(); Association = MyAssociation; // CASSOC++ Association->AddReference(); ObjectType = OSF_CCONNECTION_TYPE; ClientInfo = RpcClientInfo; State = ConnUninitialized; ComTimeout = Timeout ; u.ConnSendContext = (char *) TransConnection() + ClientInfo->ClientConnectionSize + sizeof(PVOID); *((PVOID *) ((char *) u.ConnSendContext - sizeof(PVOID))) = (PVOID) this; MaxFrag = 512; ConnectionKey = -1; AdditionalLegNeeded = 0; LastTimeUsed = 0; SavedHeader = 0; SavedHeaderSize = 0; MaxSavedHeaderSize = 0; BufferToFree = 0; fIdle = 0; this->fExclusive = fExclusive; this->fSeparateConnection = fSeparateConnection; InitializeWireAuthId(ClientAuthInfo); if (fExclusive) { AdditionalSpaceForSecurity = 0; ThreadId = SYNC_CONN_BUSY; } else { // // If it turns out that needed size is actually bigger // we will really the buffers // AdditionalSpaceForSecurity = 0x140; if (fSeparateConnection) { ThreadId = ASYNC_CONN_BUSY; } else { ThreadId = GetCurrentThreadId(); } } fConnectionAborted = 1; // // We need two references on the connection. One for itself, and // one for the cached ccall, which is implicitly getting allocated. // SetReferenceCount(2); DceSecurityInfo.SendSequenceNumber = 0; DceSecurityInfo.ReceiveSequenceNumber = 0; if (*pStatus == RPC_S_OK) { *pStatus = TransInitialize( Association->DceBinding->InqNetworkAddress(), Association->DceBinding->InqNetworkOptions()); SetTransInitializedFlag(); } if (*pStatus == RPC_S_OK) { // Create a CCALL object. // If the app verifier is enabled, we will create an object which // supports the verifier checks. if (gfRPCVerifierEnabled) { CachedCCall = new (ClientInfo->SendContextSize+sizeof(PVOID)) OSF_CCALL_AVRF(pStatus); } else { CachedCCall = new (ClientInfo->SendContextSize+sizeof(PVOID)) OSF_CCALL(pStatus); } if (CachedCCall == 0) { *pStatus = RPC_S_OUT_OF_MEMORY; } } else { CachedCCall = NULL; } CachedCCallAvailable = 0; CurrentCall = CachedCCall; ConnectionReady = 0; } OSF_CCONNECTION::~OSF_CCONNECTION ( ) { LogEvent(SU_CCONN, EV_DELETE, this); RPC_STATUS Status; if (CachedCCall) { delete CachedCCall; } if (GetTransInitializedFlag()) TransInitComplete(); TransClose(); Association->ConnectionAborted(this); if (SavedHeader != 0) { ASSERT(SavedHeaderSize != 0); RpcpFarFree(SavedHeader); } // CASSOC-- Association->RemoveReference(); } RPC_STATUS OSF_CCONNECTION::ValidateHeader( rpcconn_common * Buffer, unsigned long BufferLength ) { if (ClientSecurityContext.AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE) { unsigned CopyLength; if (Buffer->PTYPE == rpc_bind_ack || Buffer->PTYPE == rpc_alter_context_resp) { CopyLength = BufferLength; } else { CopyLength = sizeof(rpcconn_response); } if (MaxSavedHeaderSize < CopyLength) { if (SavedHeader != 0) { ASSERT(MaxSavedHeaderSize != 0); RpcpFarFree(SavedHeader); } SavedHeader = RpcpFarAllocate(CopyLength); if (SavedHeader == 0) { MaxSavedHeaderSize = 0; return(RPC_S_OUT_OF_MEMORY); } MaxSavedHeaderSize = CopyLength; RpcpMemoryCopy(SavedHeader, Buffer, CopyLength); } else { RpcpMemoryCopy(SavedHeader, Buffer, CopyLength); } SavedHeaderSize = CopyLength; } RPC_STATUS Status = ValidatePacket(Buffer, BufferLength); if ( Status != RPC_S_OK ) { ASSERT( Status == RPC_S_PROTOCOL_ERROR ); return Status; } return RPC_S_OK; } RPC_STATUS OSF_CCONNECTION::TransReceive ( OUT PVOID * Buffer, OUT UINT * BufferLength, IN ULONG Timeout ) /*++ Routine Description: Arguments: Buffer - Returns a packet received from the transport. BufferLength - Returns the length of the buffer. Return Value: RPC_S_OK - We successfully received a packet from the server. RPC_S_* - an error has occurred. See the validate clause at the end --*/ { RPC_STATUS Status; if ( State != ConnOpen ) { return(RPC_P_CONNECTION_CLOSED); } ASSERT(CurrentCall); Status = ClientInfo->SyncRecv( TransConnection(), (BUFFER *) Buffer, BufferLength, Timeout); if ( (Status == RPC_P_RECEIVE_FAILED) || (Status == RPC_P_CONNECTION_SHUTDOWN) || (Status == RPC_P_TIMEOUT)) { State = ConnAborted; } VALIDATE(Status) { RPC_S_OK, RPC_S_OUT_OF_MEMORY, RPC_S_OUT_OF_RESOURCES, RPC_S_CALL_CANCELLED, RPC_P_RECEIVE_ALERTED, RPC_P_TIMEOUT, RPC_P_RECEIVE_FAILED, RPC_P_CONNECTION_SHUTDOWN } CORRUPTION_VALIDATE { RPC_S_PROTOCOL_ERROR } CORRUPTION_END_VALIDATE; return(Status); } RPC_STATUS OSF_CCONNECTION::TransOpen ( IN OSF_BINDING_HANDLE *BindingHandle, IN RPC_CHAR *RpcProtocolSequence, IN RPC_CHAR *NetworkAddress, IN RPC_CHAR *Endpoint, IN RPC_CHAR *NetworkOptions, IN void *ResolverHint, IN BOOL fHintInitialized, IN ULONG CallTimeout ) /*++ Function Name:TransOpen Parameters: CallTimeout - call timeout in milliseconds Description: Returns: --*/ { RPC_STATUS Status ; BOOL fTokenSwapped ; HANDLE OldToken = 0; CLIENT_AUTH_INFO *ClientAuthInfo; fTokenSwapped = BindingHandle->SwapToken(&OldToken); ClientAuthInfo = BindingHandle->InquireAuthInformation(); if (ClientAuthInfo->AdditionalTransportCredentialsType == RPC_C_AUTHN_INFO_TYPE_HTTP) { ASSERT(ClientSecurityContext.AdditionalTransportCredentialsType == RPC_C_AUTHN_INFO_TYPE_HTTP); ASSERT(CompareHttpTransportCredentials( (const IN RPC_HTTP_TRANSPORT_CREDENTIALS_W *)ClientAuthInfo->AdditionalCredentials, (const IN RPC_HTTP_TRANSPORT_CREDENTIALS_W *)ClientSecurityContext.AdditionalCredentials) == 0); } Status = ClientInfo->Open(TransConnection(), RpcProtocolSequence, NetworkAddress, Endpoint, NetworkOptions, ComTimeout, 0, 0, ResolverHint, fHintInitialized, CallTimeout, ClientSecurityContext.AdditionalTransportCredentialsType, ClientSecurityContext.AdditionalCredentials ); RestoreTokenIfNecessary (fTokenSwapped, OldToken); // // If an error occurs in opening the connection, we go ahead and // delete the memory for the connection, and return zero (setting // this to zero does that). // VALIDATE (Status) { RPC_S_OK, RPC_S_PROTSEQ_NOT_SUPPORTED, RPC_S_SERVER_UNAVAILABLE, RPC_S_OUT_OF_MEMORY, RPC_S_OUT_OF_RESOURCES, RPC_S_SERVER_TOO_BUSY, RPC_S_INVALID_NETWORK_OPTIONS, RPC_S_INVALID_ENDPOINT_FORMAT, RPC_S_INVALID_NET_ADDR, RPC_S_ACCESS_DENIED, RPC_S_INTERNAL_ERROR, RPC_S_SERVER_OUT_OF_MEMORY, RPC_S_CALL_CANCELLED } END_VALIDATE; if ( Status == RPC_S_OK ) { State = ConnOpen; } return Status ; } void OSF_CCONNECTION::TransClose ( ) { RPC_STATUS Status; if (State != ConnUninitialized) { __try { Status = ClientInfo->Close(TransConnection(), 0); ASSERT( Status == RPC_S_OK ); } __except( EXCEPTION_EXECUTE_HANDLER ) { #if DBG PrintToDebugger("RPC: exception in Close\n") ; #endif Status = RPC_S_OUT_OF_MEMORY ; } State = ConnUninitialized; } } RPC_STATUS OSF_CCONNECTION::TransAsyncSend ( IN OSF_BINDING_HANDLE * BindingHandle, IN void * Buffer, IN UINT BufferLength, IN void *SendContext ) /*++ Function Name:TransAsyncSend Parameters: BindingHandle - the binding handle on whose identity we are doing the call Description: Returns: --*/ { RPC_STATUS Status; BOOL fTokenSwapped; HANDLE OldToken = 0; { rpcconn_common * pkt = (rpcconn_common *) Buffer; LogEvent(SU_CCONN, EV_PKT_OUT, this, pkt, (pkt->PTYPE << 16) | pkt->frag_length); } // // When this function is called, there is should be not outstanding send // if ( State != ConnOpen ) { return(RPC_P_CONNECTION_CLOSED); } fTokenSwapped = SwapTokenIfNecessary (BindingHandle, &OldToken); DceSecurityInfo.SendSequenceNumber += 1; Status = ClientInfo->Send(TransConnection(), BufferLength, (BUFFER) Buffer, SendContext); RestoreTokenIfNecessary (fTokenSwapped, OldToken); if ( Status != RPC_S_OK ) { State = ConnAborted; } VALIDATE(Status) { RPC_S_OK, RPC_S_OUT_OF_MEMORY, RPC_S_OUT_OF_RESOURCES, RPC_P_SEND_FAILED } END_VALIDATE; return(Status); } RPC_STATUS OSF_CCONNECTION::TransAsyncReceive ( ) /*++ Function Name:TransAsyncReceive Parameters: Description: Returns: --*/ { RPC_STATUS Status; if (State != ConnOpen || fConnectionAborted) { // If the connection has been aborted, then we // need to clean up the calls. if (fConnectionAborted) { // Two threads may call ConnectionAborted, but it is // idempotent. ConnectionAborted(RPC_S_CALL_FAILED); } return(RPC_P_CONNECTION_CLOSED); } // // If the call to Recv succeeds, this reference is removed // in ProcessIOEvents after the call to ProcessReceiveComplete // // CCONN++ AddReference(); Status = ClientInfo->Recv(TransConnection()); VALIDATE(Status) { RPC_S_OK, RPC_S_OUT_OF_MEMORY, RPC_S_OUT_OF_RESOURCES, RPC_P_RECEIVE_ALERTED, RPC_P_RECEIVE_FAILED, RPC_P_CONNECTION_SHUTDOWN } END_VALIDATE; if (Status != RPC_S_OK) { if (Status == RPC_P_RECEIVE_ALERTED || Status == RPC_P_RECEIVE_FAILED || Status == RPC_P_CONNECTION_SHUTDOWN) { ConnectionAborted(RPC_S_CALL_FAILED); } else { ConnectionAborted(Status); } // CCONN-- RemoveReference(); } return(Status); } void OsfBindToServer( PVOID Context ) { ((OSF_CCALL *) Context)->BindToServer( TRUE // this is an async bind - slightly different // refcounting is used ); } RPC_STATUS OSF_CCONNECTION::TransPostEvent ( IN PVOID Context ) /*++ Function Name:TransPostEvent Parameters: Description: Returns: --*/ { LogEvent(SU_CCONN, EV_NOTIFY, this, Context, 0, 1); return ClientInfo->PostEvent( CO_EVENT_BIND_TO_SERVER, Context) ; } RPC_STATUS OSF_CCONNECTION::TransSend ( IN OSF_BINDING_HANDLE * BindingHandle, IN void * Buffer, IN UINT BufferLength, IN BOOL fDisableShutdownCheck, IN BOOL fDisableCancelCheck, IN ULONG Timeout ) /*++ Routine Description: Arguments: BindingHandle - the binding handle on whose identity we are doing the call Buffer - Supplies a packet to be sent to the server. BufferLength - Supplies the length of the buffer in bytes. Return Value: RPC_S_OK - The packet was successfully sent to the server. RPC_S_* - an error occurred - see the validate clause at the end --*/ { RPC_STATUS Status; BOOL fTokenSwapped; HANDLE OldToken = 0; { rpcconn_common * pkt = (rpcconn_common *) Buffer; LogEvent(SU_CCONN, EV_PKT_OUT, this, 0, (pkt->PTYPE << 16) | pkt->frag_length); } if ( State != ConnOpen ) { return(RPC_P_CONNECTION_CLOSED); } if (fDisableCancelCheck == 0 && CurrentCall->fCallCancelled) { return(RPC_S_CALL_CANCELLED); } fTokenSwapped = SwapTokenIfNecessary (BindingHandle, &OldToken); DceSecurityInfo.SendSequenceNumber += 1; Status = ClientInfo->SyncSend(TransConnection(), BufferLength, (BUFFER) Buffer, fDisableShutdownCheck, fDisableCancelCheck, INFINITE); // Timeout RestoreTokenIfNecessary (fTokenSwapped, OldToken); if ( Status == RPC_P_SEND_FAILED ) { State = ConnAborted; } VALIDATE(Status) { RPC_S_OK, RPC_S_OUT_OF_MEMORY, RPC_S_OUT_OF_RESOURCES, RPC_P_SEND_FAILED, RPC_S_CALL_CANCELLED, RPC_P_RECEIVE_COMPLETE, RPC_P_TIMEOUT } CORRUPTION_VALIDATE { RPC_S_PROTOCOL_ERROR } CORRUPTION_END_VALIDATE; return(Status); } void OSF_CCONNECTION::TransAbortConnection ( ) { ClientInfo->Abort(TransConnection()); } RPC_STATUS OSF_CCONNECTION::TransSendReceive ( IN OSF_BINDING_HANDLE * BindingHandle, IN void * SendBuffer, IN UINT SendBufferLength, OUT void * * ReceiveBuffer, OUT UINT * ReceiveBufferLength, IN ULONG Timeout ) /*++ Routine Description: Arguments: BindingHandle - the binding handle on whose identity we are doing the call SendBuffer - Supplies a packet to be sent to the server. SendBufferLength - Supplies the length of the send buffer in bytes. ReceiveBuffer - Returns a packet received from the transport. ReceiveBufferLength - Returns the length of the receive buffer in bytes. dwTimeout - the timeout to wait for the receive. -1 if infinite. Return Value: RPC_S_OK - The packet was successfully sent to the server, and we successfully received one from the server. RPC_S_* - an error occurred - see the validate clause at the end --*/ { RPC_STATUS Status; BOOL fTokenSwapped; HANDLE OldToken = 0; { rpcconn_common * pkt = (rpcconn_common *) SendBuffer; if (pkt->PTYPE != rpc_request) { LogEvent(SU_CCONN, EV_PKT_OUT, this, ULongToPtr(pkt->call_id), (pkt->PTYPE << 16) | pkt->frag_length); } else { LogEvent(SU_CCONN, EV_PKT_OUT, this, ULongToPtr(pkt->call_id), (((rpcconn_request *)pkt)->opnum << 24) | (pkt->PTYPE << 16) | pkt->frag_length); } } if ( State != ConnOpen ) { return(RPC_P_CONNECTION_CLOSED); } if (CurrentCall->fCallCancelled) { return(RPC_S_CALL_CANCELLED); } fTokenSwapped = SwapTokenIfNecessary (BindingHandle, &OldToken); DceSecurityInfo.SendSequenceNumber += 1; if ( ClientInfo->SyncSendRecv != 0 && (CurrentCall->CancelState != CANCEL_NOTINFINITE) && (Timeout == INFINITE)) { Status = ClientInfo->SyncSendRecv(TransConnection(), SendBufferLength, (BUFFER) SendBuffer, ReceiveBufferLength, (BUFFER *) ReceiveBuffer); if (!Status) { rpcconn_common * pkt = (rpcconn_common *) *ReceiveBuffer; LogEvent(SU_CCONN, EV_PKT_IN, this, 0, (pkt->PTYPE << 16) | pkt->frag_length); } } else { Status = ClientInfo->SyncSend (TransConnection(), SendBufferLength, (BUFFER) SendBuffer, FALSE, FALSE, Timeout); // Timeout if ( Status == RPC_S_OK || Status == RPC_P_RECEIVE_COMPLETE ) { Status = ClientInfo->SyncRecv(TransConnection(), (BUFFER *) ReceiveBuffer, ReceiveBufferLength, Timeout); if (!Status) { rpcconn_common * pkt = (rpcconn_common *) *ReceiveBuffer; LogEvent(SU_CCONN, EV_PKT_IN, this, 0, (pkt->PTYPE << 16) | pkt->frag_length); } } } RestoreTokenIfNecessary (fTokenSwapped, OldToken); if ((Status == RPC_P_SEND_FAILED) || (Status == RPC_P_RECEIVE_FAILED) || (Status == RPC_P_CONNECTION_SHUTDOWN) || (Status == RPC_P_TIMEOUT)) { State = ConnAborted; } VALIDATE(Status) { RPC_S_OK, RPC_S_OUT_OF_MEMORY, RPC_S_OUT_OF_RESOURCES, RPC_P_RECEIVE_FAILED, RPC_S_CALL_CANCELLED, RPC_P_SEND_FAILED, RPC_P_CONNECTION_SHUTDOWN, RPC_P_TIMEOUT } CORRUPTION_VALIDATE { RPC_S_PROTOCOL_ERROR } CORRUPTION_END_VALIDATE; return(Status); } UINT OSF_CCONNECTION::TransMaximumSend ( ) /*++ Return Value: The maximum packet size which can be sent on this transport is returned. --*/ { return(ClientInfo->MaximumFragmentSize); } void OSF_CCONNECTION::ConnectionAborted ( IN RPC_STATUS Status, IN BOOL fShutdownAssoc ) /*++ Function Name:AbortConnection Parameters: Description: Returns: --*/ { OSF_CCALL *CCall; unsigned int Size; DictionaryCursor cursor; BOOL fFreeLastBuffer; // the failing of the call may take a reference from underneath us // bump up the reference count while we have a reference on the // object. We'll remove it by the end of the function // CCONN++ ASSERT(fExclusive == 0); // CCONN++ AddReference(); // make sure the connection gets removed from the dictionary Association->ConnectionAborted(this); // // If the caller has asked us to shutdown the association and // the connection has not been flagged to protect the association // then we will shut it down. // if ( fShutdownAssoc && (!Flags.GetFlag(NoAssociationShutdown)) ) { Association->ShutdownRequested(Status, NULL); } ConnMutex.Request(); ActiveCalls.Reset(cursor); while (CCall = ActiveCalls.Next(cursor)) { if (CCall->CALL::GetCallStatus() == RPC_S_CALL_CANCELLED) { CCall->CallFailed(RPC_S_CALL_CANCELLED); } else { fFreeLastBuffer = FALSE; if (CCall->fLastSendComplete) { if (CurrentCall != CCall) { if ((CCall->CurrentState == NeedOpenAndBind) || (CCall->CurrentState == NeedAlterContext)) { CCall->SetFreeLastBufferFlag(); } } else if ((CCall->CurrentState == NeedOpenAndBind) || (CCall->CurrentState == NeedAlterContext) || (CCall->CurrentState == WaitingForAlterContext) || (CCall->CurrentState == SendingFirstBuffer) ) { CCall->SetFreeLastBufferFlag(); } } // // If the call is an async pipe call then we are dealing with an // asyncronous failure from a receive or a send posted by the runtime. // The user may do a pull, which will detect that the call has failed // and will clean it up. If we issue a call-complete notification // during a pull that cleans up the call, then querying the call status // upon receiving the notification will AV. // Hence, we should not issue a notification for async pipe CCALLs if // we know that a receive will be called. // CCall->CallMutex.Request(); if (!CCall->IsAsyncPipeCallBeforePull()) { CCall->CallFailed(Status); } else { // // Should not transition from Complete to Aborted // if (CCall->CurrentState != Complete && CCall->CurrentState != Aborted) { // // Notify the client that the call is aborted. When the stub calls // I_RpcReceive, we can cleanup the call and return a failure. // CCall->AsyncStatus = Status; CCall->CurrentState = Aborted; } } CCall->CallMutex.Clear(); } } // // Remove the send references on all the calls currently in the queue // while (CCall = (OSF_CCALL *) CallQueue.TakeOffQueue(&Size)) { // // Remove the send reference, CCALL-- // CCall->RemoveReference(); } ConnMutex.Clear(); // // Make sure we remove this connection from the dictionary // before deleting it. We don't want another thread to pick it up // TransAbortConnection(); Delete(); // // This routine will always be called with a reference held // ASSERT(RefCount.GetInteger()); State = ConnAborted; // CCONN-- RemoveReference(); } void OSF_CCONNECTION::AdvanceToNextCall( ) /*++ Function Name:AdvanceToNextCall Parameters: Description: Returns: --*/ { UINT Size; RPC_STATUS Status; ConnMutex.Request(); CurrentCall = (OSF_CCALL *) CallQueue.TakeOffQueue(&Size); if (CurrentCall == 0) { MakeConnectionIdle(); ConnMutex.Clear(); } else { ConnMutex.Clear(); Status = CurrentCall->SendData(0); if (Status != RPC_S_OK) { ConnectionAborted(Status); // // The connection cannot die. // // // Remove the send reference for this call. CCALL-- // CurrentCall->RemoveReference(); } } } inline RPC_STATUS OSF_CCONNECTION::TransGetBuffer ( OUT void * * Buffer, IN UINT BufferLength ) /*++ Routine Description: We need a buffer to receive data into or to put data into to be sent. This should be really simple, but we need to make sure that buffer we return is aligned on an 8 byte boundary. The stubs make this requirement. Arguments: Buffer - Returns a pointer to the buffer. BufferLength - Supplies the required length of the buffer in bytes. Return Value: RPC_S_OK - We successfully allocated a buffer of at least the required size. RPC_S_OUT_OF_MEMORY - There is insufficient memory available to allocate the required buffer. --*/ { int * Memory; // // Our memory allocator returns memory which is aligned by at least // 8, so we dont need to worry about aligning it. // Memory = (int *) CoAllocateBuffer(BufferLength); if ( Memory == 0 ) { return RPC_S_OUT_OF_MEMORY; } ASSERT(IsBufferAligned(Memory)); *Buffer = Memory; ASSERT(PadPtr8(*Buffer) == 0); return(RPC_S_OK); } inline void OSF_CCONNECTION::TransFreeBuffer ( IN void * Buffer ) /*++ Routine Description: We need to free a buffer which was allocated via TransGetBuffer. The only tricky part is remembering to remove the padding before actually freeing the memory. --*/ { CoFreeBuffer(Buffer); } RPC_STATUS OSF_CCONNECTION::TransReallocBuffer ( IN OUT void * * Buffer, IN UINT OldSize, IN UINT NewSize ) /*++ Function Name:TransReallocBuffer Parameters: Description: Reallocates a give buffer to the new size. Returns: RPC_S_OK: the buffer is successfully reallocated RPC_S_OUT_OF_MEMORY: the realloc failed, the old buffer is still valid. --*/ { BUFFER NewBuffer; RPC_STATUS Status; Status = TransGetBuffer( (PVOID *) &NewBuffer, NewSize); if (Status != RPC_S_OK) { return RPC_S_OUT_OF_MEMORY; } if (OldSize) { RpcpMemoryCopy(NewBuffer, *Buffer, OldSize); TransFreeBuffer(*Buffer); } *Buffer = NewBuffer; return RPC_S_OK; } inline RPC_STATUS OSF_CCONNECTION::AllocateCCall ( OUT OSF_CCALL **CCall ) /*++ Function Name:AllocateCCall Parameters: Description: Returns: --*/ { RPC_STATUS Status; if (fExclusive) { ASSERT(CachedCCallAvailable == 1); CachedCCallAvailable = 0; *CCall = CachedCCall; } else { if (InterlockedCompareExchange( (PLONG)&CachedCCallAvailable, 0, 1)) { *CCall = CachedCCall; } else { Status = RPC_S_OK; // Create a CCALL object. // If the app verifier is enabled, we will create an object which // supports the verifier checks. if (gfRPCVerifierEnabled) { *CCall = new (ClientInfo->SendContextSize+sizeof(PVOID)) OSF_CCALL_AVRF(&Status); } else { *CCall = new (ClientInfo->SendContextSize+sizeof(PVOID)) OSF_CCALL(&Status); } if (*CCall == 0) { Status = RPC_S_OUT_OF_MEMORY; } if (Status != RPC_S_OK) { delete *CCall; return Status; } } } LogEvent(SU_CCALL, EV_START, *CCall); return RPC_S_OK; } RPC_STATUS OSF_CCONNECTION::AddCall ( IN OSF_CCALL *CCall ) /*++ Function Name: AddCall Parameters: Description: Adds a call to the CallQueue unless this is a CurrentCall. Returns: RPC_S_OK - The call has been added to the queue. RPC_S_* - The call has failed or could not be added to the queue. --*/ { RPC_STATUS Status; // // Think of a better way of doing this. This condition is true the first // time a connection is created, and when we are talking to legacy // servers over non-exclusive connections // if (CurrentCall == CCall) { return RPC_S_OK; } ConnMutex.Request(); if (CurrentCall == 0) { CurrentCall = CCall; } else { if ((State == ConnAborted) || ((Association->IsAssociationReset()) && (State != ConnUninitialized) ) ) { ConnMutex.Clear(); return RPC_S_CALL_FAILED; } // If PutOnQueue suceeds, the call migrates to the CallQueue. // It will be removed from the CallQueue when: // - We advance to the call in the normal course of processing. // - The connection is aborted and the sends fail for all queued calls. // - The call is freed before the send had a chance to complete, // for example due to an abortive cancel. Status = CallQueue.PutOnQueue(CCall, 0); if (Status != 0) { ConnMutex.Clear(); return RPC_S_OUT_OF_MEMORY; } } ConnMutex.Clear(); return RPC_S_OK; } void OSF_CCONNECTION::FreeCCall ( IN OSF_CCALL *CCall, IN RPC_STATUS Status, IN ULONG ComTimeout ) /*++ Function Name:FreeCCall Parameters: CCall - the call that is being freed Status - the status with which the call completed ComTimeout - the communication timeout for this call Description: Free the call, remove reference on the connection. If the free is abortive, we need to cleanup the connection and inform the association about it. Returns: --*/ { LogEvent(SU_CCALL, EV_STOP, CCall); ConnMutex.Request(); // The call may still be sitting on the CallQueue if the // send did not have a chance to complete before we abortively // canceled it. We need to check for this and remove the call // from the queue if necessary. CallQueue.FindAndTakeOffQueue(CCall); if (CCall == CachedCCall) { CachedCCallAvailable = 1; } else { delete CCall; } switch (Status) { case RPC_S_OUT_OF_MEMORY: case RPC_S_OUT_OF_RESOURCES: case RPC_S_ACCESS_DENIED: case RPC_S_PROTOCOL_ERROR: case RPC_S_CALL_FAILED: case RPC_S_CALL_FAILED_DNE: case RPC_S_CALL_CANCELLED: case RPC_S_SEC_PKG_ERROR: case RPC_S_INVALID_ARG: case RPC_S_SERVER_UNAVAILABLE: case RPC_P_CONNECTION_SHUTDOWN: case RPC_P_CONNECTION_CLOSED: // // Need to release the connection mutex, so we won't deadlock // ConnMutex.Clear(); Association->ConnectionAborted(this); ConnMutex.Request(); if (fExclusive) { Delete(); } else { TransAbortConnection(); } break; default: // RPC_S_UNKNOWN_IF & others // Put error codes here only if you are absolutely // sure you can recover. If in doubt, put them // above if (ThreadId == SYNC_CONN_BUSY) { ThreadId = SYNC_CONN_FREE; } else if (ThreadId == ASYNC_CONN_BUSY) { ThreadId = ASYNC_CONN_FREE; ASSERT(fExclusive == FALSE); if (ComTimeout != RPC_C_BINDING_INFINITE_TIMEOUT) { TurnOnOffKeepAlives (FALSE, 0); } } SetLastTimeUsedToNow(); break; } ConnMutex.Clear(); // // Remove the reference held by the call, CCONN-- // RemoveReference(); } void OSF_CCONNECTION::ProcessSendComplete ( IN RPC_STATUS EventStatus, IN BUFFER Buffer ) /*++ Function Name:ProcessSendComplete Parameters: Description: Returns: --*/ { rpcconn_common *Packet = (rpcconn_common *) Buffer; OSF_CCALL *OldCall; switch (Packet->PTYPE) { case rpc_request: case rpc_response: TransFreeBuffer(BufferToFree); if (EventStatus == RPC_S_OK) { // This path may also race with RpcAsyncComplete call. // We haven't seen it in stress or in any tests, so it may be extremely // unlikely. The bug, if it is present, is very hard. OldCall = CurrentCall; ASSERT(OldCall); if (Association->fMultiplex == mpx_yes) { // // We have no more data to send on this // call. Remove ourselves from the call queue // AdvanceToNextCall(); } else { if (OldCall->fOkToAdvanceCall()) { AdvanceToNextCall(); } } // // Remove the send reference on the call, CCALL-- // OldCall->RemoveReference(); return; } break; default: ASSERT(ConnectionReady == 0); TransFreeBuffer(Buffer); ConnectionReady = 1; break; } if (EventStatus != RPC_S_OK) { VALIDATE(EventStatus) { RPC_P_SEND_FAILED, RPC_P_CONNECTION_CLOSED, RPC_P_CONNECTION_SHUTDOWN } END_VALIDATE; ConnectionAborted(RPC_S_CALL_FAILED_DNE); // This path races with RpcAsyncCompleteCall path. We need to make sure that // the call does not get deleted from under our feet. ConnMutex.Request(); OldCall = CurrentCall; if (OldCall) { // // The current I/O failed. // Remove the send reference on the call, CCALL-- // OldCall->RemoveReference(); } ConnMutex.Clear(); } } void OSF_CCONNECTION::ProcessReceiveComplete ( IN RPC_STATUS EventStatus, IN BUFFER Buffer, IN UINT BufferLength ) /*++ Function Name:ProcessReceiveComplete Parameters: Description: Returns: --*/ { RPC_STATUS Status; OSF_CCALL *CCall; BOOL fSubmitReceive; rpcconn_common *Packet = (rpcconn_common *) Buffer; if (EventStatus) { LogEvent(SU_CCONN, EV_PKT_IN, this, LongToPtr(EventStatus)); } else { rpcconn_common * pkt = (rpcconn_common *) Buffer; LogEvent(SU_CCONN, EV_PKT_IN, this, 0, (pkt->PTYPE << 16) | pkt->frag_length); } if (EventStatus != RPC_S_OK) { VALIDATE(EventStatus) { RPC_P_CONNECTION_CLOSED, RPC_P_RECEIVE_FAILED, RPC_P_CONNECTION_SHUTDOWN } END_VALIDATE; ASSERT(Buffer == 0); // second parameter is shutdown association. Cause association shutdown only // if the connection is not an idle connection that is getting garbage collected ConnectionAborted(RPC_S_CALL_FAILED, ThreadId != ASYNC_CONN_FREE); return; } ASSERT(Buffer); // // A packet's size can't exceed MaxFrag bytes with the // possible exception of the bind packet or an alter context packets. // if (Packet->PTYPE != rpc_bind_ack && Packet->PTYPE != rpc_alter_context_resp && ((DataConvertEndian(Packet->drep) == 0 && Packet->frag_length > MaxFrag) || (DataConvertEndian(Packet->drep) != 0 && RpcpByteSwapShort(Packet->frag_length) > MaxFrag) ) ) { CORRUPTION_ASSERT(0 && "Packet->frag_length is too large"); fSubmitReceive = 0; TransAbortConnection(); } unsigned long CallId = Packet->call_id; if (DataConvertEndian(Packet->drep) != 0) { CallId = RpcpByteSwapLong(CallId); } ConnMutex.Request(); CCall = ActiveCalls.Find(IntToPtr(CallId)); if (CCall) { if (CCall->CurrentState == Aborted) { ConnMutex.Clear(); TransAbortConnection(); return; } // We have found the call in the active calls dictionary. We must assume // that if it is in the dictionary, it has a reference count. Under the protection // of the ConnMutex (which must be held when manipulating the ActiveCalls) // we give this call another reference count for the duration of our processing CCall->AddReference(); ConnMutex.Clear(); // // We try to create a thread. If it doesn't work, // well too bad !, we'll go ahead and process this // PDU any way // Status = Association->TransInfo->CreateThread(); VALIDATE(Status) { RPC_S_OK, RPC_S_OUT_OF_MEMORY, RPC_S_OUT_OF_THREADS } END_VALIDATE; // // if fSubmitReceive is 1, we need to post a receive, // otherwise, the receive will be posted by someone else // fSubmitReceive = CCall->ProcessReceivedPDU( (rpcconn_common *) Buffer, BufferLength); CCall->RemoveReference(); // After this point we can no longer touch the call since it may have been freed. } else { ConnMutex.Clear(); fSubmitReceive = 0; TransAbortConnection(); } if (fSubmitReceive) { // // TransAsyncReceive will retry several times // before giving up. // We will clean up the connection on failure. // TransAsyncReceive (); } // // If we have not called TransAsyncReceive, then // we need to probe if the connection has been aborted // and needs to be cleaned up. // Submiting the next receive after processing a receive-complete // is our only chance to check whether a connection has been // aborted when the abort has been a result of association shutdown. // This avoids leaks in async pipe scenarios. // else { if (State != ConnOpen || fConnectionAborted) { ConnectionAborted(RPC_S_CALL_FAILED); } } } RPC_STATUS OSF_CCONNECTION::OpenConnectionAndBind ( IN OSF_BINDING_HANDLE *BindingHandle, IN ULONG Timeout, IN BOOL fAlwaysNegotiateNDR20, OUT FAILURE_COUNT_STATE *fFailureCountExceeded OPTIONAL ) /*++ Function Name: OpenConnectionAndBind Parameters: BindingHandle - the binding handle on which we are doing the call Timeout - the timeout for the bind (if any) fAlwaysNegotiateNDR20 - TRUE if NDR20 should always be negotiated. If the server chooses NDR64, we will explicitly alter-context to NDR20 if this flag is set. fFailureCountExceeded - if supplied, must be FailureCountUnknown. If supplied, and we got bind failure with reason not specified, and we haven't exceeded the failure count, this function will keep retrying. If supplied, and we received bind failure with reason not specified and the failure count is exceeded, it will be set to FailureCountExceeded. Description: Returns: --*/ { RPC_STATUS Status; BOOL fMutexHeld; ULONG MyAssocGroupId; MPX_TYPES myfMpx = Association->fMultiplex; ULONG MyfInitialized; void *MyHint = NULL; OSF_BINDING *BindingNegotiated; OSF_BINDING *IgnoredBinding; BOOL fPossibleAssociationReset; if (ARGUMENT_PRESENT(fFailureCountExceeded)) { ASSERT(*fFailureCountExceeded == FailureCountUnknown); } if (!fExclusive) { // // First thing we do is kick off a thread to go and listen // this stuff is going to take very long // Status = Association->TransInfo->CreateThread(); if (Status != RPC_S_OK) { // // Can't do anything right now, lets just go back and listen // return Status; } } while (1) { if (Association->IsResolverHintSynchronizationNeeded()) { Association->AssociationMutex.Request(); fMutexHeld = TRUE; } else fMutexHeld = FALSE; MyfInitialized = Association->AssocGroupId; if (MyfInitialized == 0) { // make sure the hint is allocated only once on the stack // otherwise, some of the retry paths will loop through here // and may contribute to a stack overflow if (MyHint == NULL) { MyHint = alloca(ClientInfo->ResolverHintSize); ASSERT((ClientInfo->ResolverHintSize == 0) || MyHint); } } else { MyHint = Association->InqResolverHint(); } while (TRUE) { RpcpPurgeEEInfo(); Status = TransOpen ( BindingHandle, Association->DceBinding->InqRpcProtocolSequence(), Association->DceBinding->InqNetworkAddress(), Association->DceBinding->InqEndpoint(), Association->DceBinding->InqNetworkOptions(), MyHint, MyfInitialized, Timeout); if (Status != RPC_S_OK) { if (ComTimeout == RPC_C_BINDING_INFINITE_TIMEOUT && (Status == RPC_S_SERVER_UNAVAILABLE || Status == RPC_S_SERVER_TOO_BUSY)) { continue; } if (fMutexHeld) { Association->AssociationMutex.Clear(); fMutexHeld = FALSE; } if (Status == RPC_S_SERVER_UNAVAILABLE) { Association->ShutdownRequested(Status, NULL); } return Status; } if (fMutexHeld == FALSE) { Association->AssociationMutex.Request(); fMutexHeld = TRUE; } MyAssocGroupId = Association->AssocGroupId; if (MyAssocGroupId != 0) { if (MyfInitialized == 0 && ClientInfo->ResolverHintSize) { // // We lost the race, we need to check if the address // we picked up is the same as the one the winner picked up // if it is not, we need to loop back // if (Association->CompareResolverHint(MyHint)) { if (Association->IsResolverHintSynchronizationNeeded() == FALSE) { // if the resolver does not require synchronization, loop // around without the mutex Association->AssociationMutex.Clear(); fMutexHeld = FALSE; } if (MyHint != Association->InqResolverHint()) Association->FreeResolverHint(MyHint); MyfInitialized = 1; MyHint = Association->InqResolverHint(); TransClose(); continue; } } Association->AssociationMutex.Clear(); fMutexHeld = FALSE; if (MyHint != Association->InqResolverHint()) Association->FreeResolverHint(MyHint); } else { // // We won the race, we need to store the resolved address in // the association // if (ClientInfo->ResolverHintSize) { Association->SetResolverHint(MyHint); Association->ResolverHintInitialized = TRUE; } } break; } // while (1) if (GetTransInitializedFlag()) TransInitComplete(); // // Send a bind packet and wait for response // Status = ActuallyDoBinding ( CurrentCall, MyAssocGroupId, TRUE, // fNewConnection Timeout, &BindingNegotiated, &fPossibleAssociationReset, fFailureCountExceeded); if (Status != RPC_S_OK) { if (fMutexHeld) { Association->AssociationMutex.Clear(); fMutexHeld = FALSE; } LogEvent(SU_CCONN, EV_STATE, ULongToPtr(MyAssocGroupId), ULongToPtr(Association->AssocGroupId), Status, 1, 0); if ((Status == RPC_P_CONNECTION_SHUTDOWN) && ( fPossibleAssociationReset || ( ARGUMENT_PRESENT(fFailureCountExceeded) && (*fFailureCountExceeded == FailureCountNotExceeded) ) ) && (Association->IsValid()) ) { // // Either: // 1. We have hit a race condition where the // AssocGroupId is renegotiated because the // close for the last connection came ahead // of the bind for the next connection. In this // case server returns BindNak with // reason_not_specified, which gets translated // to RPC_P_CONNECTION_SHUTDOWN. Retry again. // or // 2. We got bind_nak with reason not specified // and the failure count was not exceeded // TransClose(); ASSERT(fMutexHeld == FALSE); Association->AssociationMutex.Request(); if (fConnectionAborted == 0) { ASSERT(Association); Association->NotifyConnectionClosed(); fConnectionAborted = 1; } if (fPossibleAssociationReset) Association->FailureCount = 0; InitializeWireAuthId(&ClientSecurityContext); if (ClientSecurityContext.AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE) { // DeleteSecurityContext checks and deletes // the security context only if necessary ClientSecurityContext.DeleteSecurityContext(); } Association->AssociationMutex.Clear(); if (ARGUMENT_PRESENT(fFailureCountExceeded)) { *fFailureCountExceeded = FailureCountUnknown; } continue; } if (fExclusive == 0 && Status == RPC_S_PROTOCOL_ERROR && myfMpx == mpx_unknown) { Association->fMultiplex = mpx_no; //Association->MinorVersion = 0; // // The server seems to be a legacy server, // close the connection and start over, // this time, don't set the PFC_CONC_MPX bit // TransClose(); ASSERT(fMutexHeld == FALSE); Association->AssociationMutex.Request(); if (fConnectionAborted == 0) { ASSERT(Association); Association->NotifyConnectionClosed(); fConnectionAborted = 1; } InitializeWireAuthId(&ClientSecurityContext); if (ClientSecurityContext.AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE) { // DeleteSecurityContext checks and deletes // the security context only if necessary ClientSecurityContext.DeleteSecurityContext(); } Association->AssociationMutex.Clear(); continue; } return Status; } break; } // if we negotiated NDR64, but we were asked to negotiate NDR20, // alter context to get the right context for this call if (fAlwaysNegotiateNDR20 && (BindingNegotiated->CompareWithTransferSyntax(NDR64TransferSyntax) == 0)) { // limit the choice to NDR20 only // We do this by whacking the list of available bindings. Since the chosen binding // is NDR20, this will force the bind to negotiate NDR20. We also change the state // to WaitingForAlterContext ASSERT(CurrentCall->Bindings.SelectedBinding->CompareWithTransferSyntax(NDR20TransferSyntax) == 0); ReleaseBindingListWithException(CurrentCall->Bindings.SelectedBinding, CurrentCall->Bindings.AvailableBindingsList ); CurrentCall->Bindings.AvailableBindingsList = NULL; CurrentCall->CurrentState = NeedAlterContext; Status = ActuallyDoBinding ( CurrentCall, MyAssocGroupId, FALSE, // fNewConnection Timeout, &IgnoredBinding, &fPossibleAssociationReset, // never actually used here NULL // fFailureCountExceeded ); if (Status) { if (fMutexHeld) { Association->AssociationMutex.Clear(); } return Status; } } if (fMutexHeld) { Association->AssociationMutex.Clear(); } ASSERT((CurrentCall->CurrentState == NeedOpenAndBind) || (CurrentCall->CurrentState == NeedAlterContext) || (CurrentCall->CurrentState == Aborted)); if ((CurrentCall == NULL) || (CurrentCall->CurrentState == Aborted)) { TransAbortConnection(); if ((CurrentCall != NULL) && (CurrentCall->GetCallStatus() == RPC_S_CALL_CANCELLED)) { return RPC_S_CALL_CANCELLED; } else { return RPC_S_CALL_FAILED_DNE; } } CurrentCall->CurrentState = SendingFirstBuffer; if (!fExclusive) { Status = TransAsyncReceive(); if (Status != RPC_S_OK) { return Status; } CurrentCall->CallMutex.Request(); if (CurrentCall->CurrentBuffer == 0) { MakeConnectionIdle(); CurrentCall->CallMutex.Clear(); } else { ASSERT(IsIdle() == 0); CurrentCall->CallMutex.Clear(); Status = CurrentCall->SendNextFragment(); } } return Status ; } RPC_STATUS OSF_CCONNECTION::ActuallyDoBinding ( IN OSF_CCALL *CCall, IN ULONG MyAssocGroupId, IN BOOL fNewConnection, IN ULONG Timeout, OUT OSF_BINDING **BindingNegotiated, OUT BOOL *fPossibleAssociationReset, OUT FAILURE_COUNT_STATE *fFailureCountExceeded ) /*++ Function Name:ActuallyDoBinding Parameters: fFailureCountExceeded - if supplied, must be FailureCountUnknown. If we got bind failure with reason not specified, and we haven't exceeded the failure count, it will be set to FailureCountNotExceeded. If we received bind failure with reason not specified and the failure count is exceeded, it will be set to FailureCountExceeded. Description: Returns: --*/ { RPC_STATUS Status; rpcconn_common * Buffer = 0; UINT BufferLength = 0; OSF_BINDING *Binding; BOOL ServerSupportsHeaderSigning = FALSE; if (ARGUMENT_PRESENT(fFailureCountExceeded)) { ASSERT(*fFailureCountExceeded == FailureCountUnknown); } *fPossibleAssociationReset = FALSE; if ( fNewConnection != 0) { ASSERT(fConnectionAborted == 1); Association->AssociationMutex.Request(); if ((MyAssocGroupId != 0) && (Association->AssocGroupId != MyAssocGroupId)) { // if we are already reset, then the server connection may // be killed also. Just back out and retry LogEvent(SU_CASSOC, EV_STATE, (PVOID)55, (PVOID)55, 66, 1, 0); *fPossibleAssociationReset = TRUE; Association->FailureCount = 0; Association->AssociationMutex.Clear(); return (RPC_P_CONNECTION_SHUTDOWN); } Association->NotifyConnectionBindInProgress(); Association->AssociationMutex.Clear(); } Status = SendBindPacket( CCall->BindingHandle, TRUE, CCall, MyAssocGroupId, (fNewConnection ? rpc_bind : rpc_alter_context), Timeout, FALSE, // synchronous &Buffer, &BufferLength, 0, // no input buffer 0 // no input buffer ); // // Now mark this connection as a part of the pool // if ( fNewConnection != 0) { Association->AssociationMutex.Request(); if (Association->fPossibleServerReset) { LogEvent(SU_CASSOC, EV_STATE, (PVOID)77, (PVOID)77, 88, 1, 0); *fPossibleAssociationReset = TRUE; } // // Did we get aborted while we were trying to bind ? // if (ConnectionKey == -1) { Association->NotifyConnectionBindCompleted(); TransAbortConnection(); if (Status == RPC_S_OK) { TransFreeBuffer(Buffer); } Status = RPC_P_CONNECTION_SHUTDOWN; } else { if ((Status != RPC_S_OK) || ( Buffer->PTYPE != rpc_bind_nak )) { Association->NotifyConnectionOpen(); fConnectionAborted = 0; } Association->NotifyConnectionBindCompleted(); } Association->AssociationMutex.Clear(); } if ( Status != RPC_S_OK ) { VALIDATE(Status) { RPC_S_OUT_OF_MEMORY, RPC_S_OUT_OF_RESOURCES, RPC_P_CONNECTION_SHUTDOWN, RPC_P_CONNECTION_CLOSED, RPC_S_UUID_NO_ADDRESS, RPC_S_ACCESS_DENIED, RPC_S_SEC_PKG_ERROR, RPC_S_CALL_CANCELLED, RPC_P_TIMEOUT, ERROR_SHUTDOWN_IN_PROGRESS } CORRUPTION_VALIDATE { RPC_S_PROTOCOL_ERROR } CORRUPTION_END_VALIDATE; // // We'll let the call decide whether to nuke the connection // return(Status); } // We loop around ignoring shutdown packets until we get a response. for (;;) { if ( Buffer->PTYPE == rpc_shutdown ) { Association->ShutdownRequested(RPC_S_CALL_FAILED_DNE, NULL); TransFreeBuffer(Buffer); Status = TransReceive((void **) &Buffer, &BufferLength, Timeout); if ( Status != RPC_S_OK ) { VALIDATE(Status) { RPC_S_OUT_OF_MEMORY, RPC_S_OUT_OF_RESOURCES, RPC_P_RECEIVE_FAILED, RPC_P_CONNECTION_CLOSED, RPC_S_CALL_CANCELLED, RPC_P_TIMEOUT } END_VALIDATE; if ( Status == RPC_P_RECEIVE_FAILED ) { return RPC_P_CONNECTION_CLOSED; } return Status; } // If there is security, we need to save the packet header; // byte-swapping the header will mess up decryption. Status = ValidateHeader(Buffer, BufferLength); if ( Status != RPC_S_OK ) { TransFreeBuffer(Buffer); return Status; } continue; } else if ( fNewConnection ) { // Since this is a new connection, the packet we receive // must be either a bind_ack or a bind_nak; anything else // is an error. if (Buffer->PTYPE == rpc_bind_ack || Buffer->PTYPE == rpc_bind_nak) { break; } else { TransFreeBuffer(Buffer); return RPC_S_PROTOCOL_ERROR; } } else { // This is a preexisting connection. // We allow only an alter_context_response. if ( Buffer->PTYPE == rpc_alter_context_resp ) { break; } else { TransFreeBuffer(Buffer); return RPC_S_PROTOCOL_ERROR; } } } ULONG NewGroupId; // // We subtract from BufferLength the length of the authentication // information; that way ProcessBindAckOrNak can check the length // correctly, whether or not there is security information. // if (MyAssocGroupId == 0) { Association->AssociationMutex.VerifyOwned(); Status = Association->ProcessBindAckOrNak( Buffer, BufferLength - Buffer->auth_length, this, CCall, &NewGroupId, BindingNegotiated, fFailureCountExceeded); } else { Status = Association->AssociationMutex.RequestSafe(); if (Status == RPC_S_OK) { Status = Association->ProcessBindAckOrNak( Buffer, BufferLength - Buffer->auth_length, this, CCall, &NewGroupId, BindingNegotiated, fFailureCountExceeded); Association->AssociationMutex.Clear(); } } LogEvent(SU_CCONN, EV_STATE, ULongToPtr(MyAssocGroupId), ULongToPtr(Association->AssocGroupId), Status, 1, 0); if (fExclusive == 0 && Association->fMultiplex == mpx_unknown) { if (((rpcconn_common *) Buffer)->pfc_flags & PFC_CONC_MPX) { Association->fMultiplex = mpx_yes; } else { Association->fMultiplex = mpx_no; } } if ( Status == RPC_S_OK ) { if (((rpcconn_common *)Buffer)->pfc_flags & PFC_SUPPORT_HEADER_SIGN) ServerSupportsHeaderSigning = TRUE; Status = FinishSecurityContextSetup( CCall, MyAssocGroupId, &Buffer, &BufferLength, Timeout ); } else { TransFreeBuffer(Buffer); } if ( Status == RPC_S_OK ) { if (IsVerificationTrailerNecessary()) { if (ServerSupportsHeaderSigning) { // bind should happen only initially on the connection ASSERT(GetConnectionSupportHeaderSign() == cshsDontKnow); SetConnectionSupportHeaderSignUnsafe (cshsYes); } else if (GetConnectionSupportHeaderSign() == cshsDontKnow) { // if the server said it didn't support header singing, // assume no for now, but verify on first response SetConnectionSupportHeaderSignUnsafe (cshsUnconfirmedNo); } } Binding = CCall->GetSelectedBinding(); if (MyAssocGroupId == 0) { Association->AssociationMutex.VerifyOwned(); if (AddPContext(Binding->GetPresentationContext()) != 0) { Status = RPC_S_OUT_OF_RESOURCES; } else { // // Once we reach here, we know that the binding has been accepted, // so we can go ahead and set the association group id. // warning: as soon as the AssocGroupId is set, threads // will start sending the bind without acquiring the mutex // LogEvent(SU_CASSOC, EV_NOTIFY, Association, this, NewGroupId, 1, 0); Association->AssocGroupId = NewGroupId; } } else { Status = Association->AssociationMutex.RequestSafe(); if (Status == RPC_S_OK) { if (AddPContext(Binding->GetPresentationContext()) != 0) { Status = RPC_S_OUT_OF_RESOURCES; } Association->AssociationMutex.Clear(); } } if (fNewConnection) { Status = CCall->ReserveSpaceForSecurityIfNecessary(); } } else { if (fNewConnection != 0) { // // If Status == DNE, it means that we probably got a B-NAK // [Also note this is a new connection] // If we were using security, [Auth Level != NONE] // delete this connection, and return RPC_P_CONNECTION_SHUTDOWN // which will cause BH->GetBuffer code to retry 2 more times // if (Status == RPC_S_CALL_FAILED_DNE) { // // Retry failures over non-authenticated // binds also.. the ones we retry over are bind naks with // unspecifed reason .. one day we can get OSF to send // bind_nk with reason assoc_group_shutdown.. // && (CConnection->AuthInfo.AuthenticationLevel // != RPC_C_AUTHN_LEVEL_NONE)) // Status = RPC_P_CONNECTION_SHUTDOWN; } } } return(Status); } RPC_STATUS OSF_CCONNECTION::FinishSecurityContextSetup ( IN OSF_CCALL *Call, IN unsigned long AssocGroup, IN OUT rpcconn_common * * Buffer, IN OUT unsigned int * BufferLength, IN ULONG Timeout ) { RPC_STATUS Status = RPC_S_OK; if (ClientSecurityContext.AuthenticationService == RPC_C_AUTHN_NONE || ClientSecurityContext.AuthenticationLevel == RPC_C_AUTHN_LEVEL_NONE) { TransFreeBuffer(*Buffer); return RPC_S_OK; } if ( !ClientSecurityContext.FullyConstructed() ) { // // Some packages need more than one round trip; we keep sending secure // alter-context packets until the security context is fully set up. // do { rpcconn_common * InputBuffer = *Buffer; *Buffer = 0; Status = SendBindPacket( Call->BindingHandle, FALSE, Call, AssocGroup, rpc_alter_context, Timeout, FALSE, // synchronous Buffer, BufferLength, InputBuffer, *BufferLength ); TransFreeBuffer(InputBuffer); } while (Status == RPC_S_OK && !ClientSecurityContext.FullyConstructed() ); if (Status == RPC_S_OK && *Buffer) { TransFreeBuffer(*Buffer); } } else { TransFreeBuffer(*Buffer); } if (RPC_S_OK == Status) { // We need to figure out how much space to reserve for security // information at the end of request and response packets. // In addition to saving space for the signature or header, // we need space to pad the packet to a multiple of the maximum // security block size as well as for the security trailer. switch ( ClientSecurityContext.AuthenticationLevel ) { case RPC_C_AUTHN_LEVEL_CONNECT: // we know all MS providers provide non-zero token on connect, // but the token can be safely omitted. // The problem is that some legacy RPC servers may expect the token to // be present and will check for the buffer. case RPC_C_AUTHN_LEVEL_PKT: case RPC_C_AUTHN_LEVEL_PKT_INTEGRITY: AdditionalSpaceForSecurity = MAXIMUM_SECURITY_BLOCK_SIZE + ClientSecurityContext.MaximumSignatureLength() + sizeof(sec_trailer); break; case RPC_C_AUTHN_LEVEL_PKT_PRIVACY: AdditionalSpaceForSecurity = MAXIMUM_SECURITY_BLOCK_SIZE + ClientSecurityContext.MaximumHeaderLength() + sizeof(sec_trailer); break; default: ASSERT(!"Unknown Security Level\n"); } } return Status; } RPC_STATUS OSF_CCONNECTION::DealWithAlterContextResp ( IN OSF_CCALL *CCall, IN rpcconn_common *Packet, IN int PacketLength, IN OUT BOOL *AlterContextToNDR20IfNDR64Negotiated ) /*++ Function Name:DealWithAlterContextResp Parameters: Description: Returns: --*/ { RPC_STATUS Status; ULONG NewGroupId; BOOL fContextAddingFailed; OSF_BINDING *Binding; OSF_BINDING *NegotiatedBinding; Status = Association->AssociationMutex.RequestSafe(); if (Status) return Status; Status = Association->ProcessBindAckOrNak( Packet, PacketLength - Packet->auth_length, this, CCall, &NewGroupId, &NegotiatedBinding, NULL // fFailureCountExceeded ); if ( Status != RPC_S_OK ) { Association->AssociationMutex.Clear(); } else { // the binding must have been fixed on the call by now Binding = CCall->GetSelectedBinding(); ASSERT(Binding); fContextAddingFailed = AddPContext(Binding->GetPresentationContext()); Association->AssociationMutex.Clear(); if (fContextAddingFailed) return RPC_S_OUT_OF_MEMORY; if (*AlterContextToNDR20IfNDR64Negotiated) { if (NegotiatedBinding->CompareWithTransferSyntax(NDR64TransferSyntax) == 0) { // // Wait for the send to complete // WaitForSend(); CCall->SendAlterContextPDU(); } else { *AlterContextToNDR20IfNDR64Negotiated = FALSE; } } } return Status; } RPC_STATUS OSF_CCONNECTION::SendBindPacket ( IN OSF_BINDING_HANDLE * BindingHandle, OPTIONAL IN BOOL fInitialPass, IN OSF_CCALL *Call, IN ULONG AssocGroup, IN unsigned char PacketType, IN ULONG Timeout, IN BOOL fAsync, OUT rpcconn_common * * Buffer, OUT UINT * BufferLength, IN rpcconn_common * InputPacket, IN unsigned int InputPacketLength ) /*++ Routine Description: This routine is used to send a bind or alter context packet. It will allocate a buffer, fill in the packet, and then send it and receive a reply. The reply buffer is just returned to the caller. Arguments: BindingHandle - the binding handle on whose identity we are doing the call fInitialPass - true if this is the first bind packet sent for this connection Call - the call whose binding information we need to use in order to bind. AssocGroup - Supplies the association group id for the association group of which this connection is a new member. PacketType - Supplies the packet type which must be one of rpc_bind or rpc_alter_context. fAsync - the binding is async Buffer - Returns the reply buffer. BufferLength - Returns the length of the reply buffer. InputPacket - the packet received from a peer, if this is not the first leg of a security negotiation InputPacketLength - the length of the input packet Return Value: RPC_S_OK - The operation completed successfully. RPC_S_OUT_OF_MEMORY - Insufficient memory is available to complete the operation. RPC_S_OUT_OF_RESOURCES - Insufficient resources are available to complete the operation. RPC_S_ACCESS_DENIED - The security package won't allow this. RPC_P_CONNECTION_CLOSED - The connection has been closed and the receive operation failed. The send operation may or may not have succeeded. --*/ { rpcconn_bind * BindPacket = 0; UINT BindPacketLength, SecurityTokenLength; RPC_STATUS Status; sec_trailer * SecurityTrailer; SECURITY_BUFFER_DESCRIPTOR BufferDescriptor; SECURITY_BUFFER SecurityBuffers[4]; DCE_INIT_SECURITY_INFO InitSecurityInfo; UINT CompleteNeeded = 0; OSF_CCALL *CallToBindFor = Call; OSF_BINDING *BindingsAvailable; OSF_BINDING *CurrentBinding; BOOL fMultipleBindingsAvailable; int AvailableBindingsCount; ASSERT(CallToBindFor != 0); BindingsAvailable = CallToBindFor->GetListOfAvaialbleBindings(&fMultipleBindingsAvailable); if (fMultipleBindingsAvailable) { AvailableBindingsCount = 0; CurrentBinding = BindingsAvailable; do { AvailableBindingsCount ++; CurrentBinding = CurrentBinding->GetNextBinding(); } while (CurrentBinding != 0); } else { AvailableBindingsCount = 1; } BindPacketLength = sizeof(rpcconn_bind) + sizeof(p_cont_list_t) + (AvailableBindingsCount - 1) * sizeof(p_cont_elem_t); // The packet length should always be 4-byte aligned: // BindPacketLength = 0x18 + 0x30 + x*0x2c = 0 (mod 4) ASSERT(Pad4(BindPacketLength) == 0) ; // // If we need to send authentication information in the packet, we // need to save space for it. This method prepares and sends both // rpc_bind and rpc_alter_context packets; we will only send // authentication information in rpc_bind packets. This is due to // a design decision that each connection supports only a single // security context, which is determined when the connection is // created. // if (ClientSecurityContext.AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE && !ClientSecurityContext.FullyConstructed()) { VALIDATE(ClientSecurityContext.AuthenticationLevel) { RPC_C_AUTHN_LEVEL_CONNECT, RPC_C_AUTHN_LEVEL_PKT, RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_PKT_PRIVACY } END_VALIDATE; if (fInitialPass) { Status = UuidCreateSequential(&(DceSecurityInfo.AssociationUuid)); if ((Status != RPC_S_OK ) && (Status != RPC_S_UUID_LOCAL_ONLY)) { return(Status); } } // // The packet length is always aligned to a four byte boundary. // Save space for the token and the sec_trailer. We also need // to save the length of the token because we will need it later // if we do third leg authentication. // TokenLength = ClientSecurityContext.Credentials->MaximumTokenLength(); BindPacketLength += TokenLength + sizeof(sec_trailer); } // BindPacketLength may be larger then TransMaximumSend. There // is nothing that we can do about it - the token can get arbitrarily // large. Status = TransGetBuffer((void * *) &BindPacket, BindPacketLength); if ( Status != RPC_S_OK ) { ASSERT( Status == RPC_S_OUT_OF_MEMORY ); TransFreeBuffer(BindPacket); return(RPC_S_OUT_OF_MEMORY); } ConstructPacket((rpcconn_common *) BindPacket, PacketType, BindPacketLength); // // A three-leg protocol will be sending an RPC_AUTH_3 instead of a BIND or ALTER_CONTEXT. // DCE Kerberos is the only package that uses the read-only output buffers. // BindPacket->max_xmit_frag = BindPacket->max_recv_frag = (unsigned short) TransMaximumSend(); BindPacket->assoc_group_id = AssocGroup; BindPacket->common.call_id = CallToBindFor->CallId; BindPacket->common.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG; // we send this on first bind only if ((PacketType == rpc_bind) && IsVerificationTrailerNecessary()) { BindPacket->common.pfc_flags |= PFC_SUPPORT_HEADER_SIGN; } if (fSeparateConnection == 0 && fExclusive == 0 && Association->fMultiplex != mpx_no) { // // We don't want to set PFC_CONC_MPX for all the requests // because the legacy NT server will send a protocol error fault // and nuke the connection // BindPacket->common.pfc_flags |= PFC_CONC_MPX; } ConstructPContextList((p_cont_list_t *) (BindPacket + 1), BindingsAvailable, AvailableBindingsCount); // // If this connection is using security, we need to stick the // authentication information into the packet. // if ( ClientSecurityContext.AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE && !ClientSecurityContext.FullyConstructed() ) { InitSecurityInfo.DceSecurityInfo = DceSecurityInfo; InitSecurityInfo.AuthorizationService = ClientSecurityContext.AuthorizationService; InitSecurityInfo.PacketType = PacketType; BufferDescriptor.ulVersion = 0; BufferDescriptor.cBuffers = 4; BufferDescriptor.pBuffers = SecurityBuffers; SecurityBuffers[3].BufferType = SECBUFFER_PKG_PARAMS | SECBUFFER_READONLY; SecurityBuffers[3].pvBuffer = &InitSecurityInfo; SecurityBuffers[3].cbBuffer = sizeof(DCE_INIT_SECURITY_INFO); if (fInitialPass) { AdditionalLegNeeded = 0; SecurityBuffers[0].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY; SecurityBuffers[0].pvBuffer = BindPacket; SecurityBuffers[0].cbBuffer = sizeof(rpcconn_bind); SecurityBuffers[1].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY; SecurityBuffers[1].pvBuffer = ((unsigned char *) BindPacket) + sizeof(rpcconn_bind); SecurityBuffers[1].cbBuffer = BindPacketLength - sizeof(rpcconn_bind) - ClientSecurityContext.Credentials->MaximumTokenLength(); SecurityBuffers[2].BufferType = SECBUFFER_TOKEN; SecurityBuffers[2].pvBuffer = ((unsigned char *) BindPacket) + BindPacketLength - ClientSecurityContext.Credentials->MaximumTokenLength(); SecurityBuffers[2].cbBuffer = ClientSecurityContext.Credentials->MaximumTokenLength(); Status = ClientSecurityContext.InitializeFirstTime( ClientSecurityContext.Credentials, ClientSecurityContext.ServerPrincipalName, ClientSecurityContext.AuthenticationLevel, &BufferDescriptor, &WireAuthId); LogEvent(SU_CCONN, EV_SEC_INIT1, this, LongToPtr(Status), SecurityBuffers[2].cbBuffer); } else { if (ClientSecurityContext.Legs == ThreeLegs) { BindPacket->common.PTYPE = rpc_auth_3; SecurityBuffers[0].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY; SecurityBuffers[0].pvBuffer = BindPacket; SecurityBuffers[0].cbBuffer = sizeof(rpcconn_auth3); SecurityBuffers[1].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY; SecurityBuffers[1].pvBuffer = ((unsigned char *) BindPacket) + sizeof(rpcconn_auth3); SecurityBuffers[1].cbBuffer = sizeof(sec_trailer); SecurityBuffers[2].BufferType = SECBUFFER_TOKEN; SecurityBuffers[2].pvBuffer = ((unsigned char *) BindPacket) + sizeof(rpcconn_auth3) + sizeof(sec_trailer); SecurityBuffers[2].cbBuffer = TokenLength; // // These structures are already 4-aligned, so no padding is needed. // BindPacketLength = sizeof(rpcconn_auth3) + sizeof(sec_trailer) + TokenLength; } else { SecurityBuffers[0].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY; SecurityBuffers[0].pvBuffer = BindPacket; SecurityBuffers[0].cbBuffer = sizeof(rpcconn_bind); SecurityBuffers[1].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY; SecurityBuffers[1].pvBuffer = ((unsigned char *) BindPacket) + sizeof(rpcconn_bind); SecurityBuffers[1].cbBuffer = BindPacketLength - sizeof(rpcconn_bind) - TokenLength; SecurityBuffers[2].BufferType = SECBUFFER_TOKEN; SecurityBuffers[2].pvBuffer = ((unsigned char *) BindPacket) + BindPacketLength - TokenLength; SecurityBuffers[2].cbBuffer = TokenLength; } // // a third leg auth may not be needed with some packages // on an alter context [where only pcon is changed as opposed // to an alternative client principal] // AdditionalLegNeeded = 0; if (InputPacket) { SECURITY_BUFFER_DESCRIPTOR InputBufferDescriptor; SECURITY_BUFFER InputBuffers[4]; DCE_INIT_SECURITY_INFO InputSecurityInfo; InputSecurityInfo.DceSecurityInfo = DceSecurityInfo; InputSecurityInfo.AuthorizationService = ClientSecurityContext.AuthorizationService; InputSecurityInfo.PacketType = InputPacket->PTYPE; InputBufferDescriptor.ulVersion = 0; InputBufferDescriptor.cBuffers = 4; InputBufferDescriptor.pBuffers = InputBuffers; ASSERT((SavedHeader != 0) && (SavedHeaderSize != 0)); InputBuffers[0].cbBuffer = sizeof(rpcconn_bind_ack) - sizeof(unsigned short); InputBuffers[0].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY; InputBuffers[0].pvBuffer = SavedHeader; InputBuffers[1].cbBuffer = InputPacketLength - (sizeof(rpcconn_bind_ack) - sizeof(unsigned short)) - InputPacket->auth_length; InputBuffers[1].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY; InputBuffers[1].pvBuffer = ((unsigned char *) SavedHeader) + sizeof(rpcconn_bind_ack) - sizeof(unsigned short); InputBuffers[2].cbBuffer = InputPacket->auth_length; InputBuffers[2].BufferType = SECBUFFER_TOKEN; InputBuffers[2].pvBuffer = ((unsigned char *) InputPacket) + InputPacketLength - InputPacket->auth_length; InputBuffers[3].cbBuffer = sizeof(DCE_INIT_SECURITY_INFO); InputBuffers[3].BufferType = SECBUFFER_PKG_PARAMS | SECBUFFER_READONLY; InputBuffers[3].pvBuffer = &InputSecurityInfo; Status = ClientSecurityContext.InitializeThirdLeg( ClientSecurityContext.Credentials, *((unsigned long *) &(BindPacket->common.drep[0])), &InputBufferDescriptor, &BufferDescriptor ); } else { Status = ClientSecurityContext.InitializeThirdLeg( ClientSecurityContext.Credentials, *((unsigned long *) &(BindPacket->common.drep[0])), 0, &BufferDescriptor ); } LogEvent(SU_CCONN, EV_SEC_INIT3, this, LongToPtr(Status), SecurityBuffers[2].cbBuffer); } // // The security package has encrypted or signed the data. // if ( Status == RPC_P_CONTINUE_NEEDED ) { // // Remember the fact that the security package requested that // it be called again. This will be important later: see // OSF_CASSOCIATION::ActuallyDoBinding. // AdditionalLegNeeded = 1; } else if ( Status == RPC_P_COMPLETE_NEEDED ) { CompleteNeeded = 1; } else if ( Status == RPC_P_COMPLETE_AND_CONTINUE ) { AdditionalLegNeeded = 1; CompleteNeeded = 1; } else if ( Status != RPC_S_OK ) { VALIDATE(Status) { RPC_S_OUT_OF_MEMORY, RPC_S_ACCESS_DENIED, RPC_S_SEC_PKG_ERROR, RPC_S_UNKNOWN_AUTHN_SERVICE, RPC_S_INVALID_ARG, ERROR_SHUTDOWN_IN_PROGRESS } END_VALIDATE; TransFreeBuffer(BindPacket); return(Status); } // // The Snego package can behave either as a 3- or 4-leg protocol depending // upon the server. It knows which way to go after the first call to // InitializeSecurityContext(). // if (fInitialPass && AdditionalLegNeeded) { ClientSecurityContext.Legs = GetPackageLegCount( WireAuthId ); if (ClientSecurityContext.Legs == LegsUnknown) { TransFreeBuffer(BindPacket); return RPC_S_OUT_OF_MEMORY; } } // // In NT 4.0 and before, the length was considered a read-only field. // SecurityTokenLength = (UINT) SecurityBuffers[2].cbBuffer; if (!AdditionalLegNeeded && 0 == SecurityTokenLength) { // // No more packets to send. // TransFreeBuffer(BindPacket); return RPC_S_OK; } // // We need to fill in the fields of the security trailer. // // The size allocated above included space for MaximumTokenLength. SecurityTrailer = (sec_trailer *) (((unsigned char *) BindPacket) + BindPacketLength - ClientSecurityContext.Credentials->MaximumTokenLength() - sizeof(sec_trailer)); SecurityTrailer->auth_type = WireAuthId; SecurityTrailer->auth_level = (unsigned char) ClientSecurityContext.AuthenticationLevel; SecurityTrailer->auth_pad_length = 0; SecurityTrailer->auth_reserved = 0; // we used to pass a pointer to the connection, which is information // disclosure. If we ever get to the point of supporting more than // one security context per connection for this wire protocol, we should // use a dictionary index to indicate the security context for a given // connection. As long as pass non-zero here, we're fine. SecurityTrailer->auth_context_id = SecurityContextIdWireRep; // // Ok, finally, we need to adjust the length of the packet, // and set the length of the authentication information. // BindPacket->common.auth_length = (unsigned short) SecurityTokenLength; BindPacketLength = BindPacketLength - ClientSecurityContext.Credentials->MaximumTokenLength() + SecurityTokenLength; BindPacket->common.frag_length = (unsigned short) BindPacketLength; if ( CompleteNeeded != 0 ) { Status = ClientSecurityContext.CompleteSecurityToken( &BufferDescriptor); if (Status != 0) { TransFreeBuffer(BindPacket); return(Status); } } } ASSERT(BindPacket->common.frag_length <= MAX_SUPPORTED_FRAG_LENGTH); if (fAsync) { Status = TransAsyncSend(BindingHandle, BindPacket, BindPacketLength, u.ConnSendContext); } else if (BindPacket->common.PTYPE == rpc_auth_3) { Status = TransSend(BindingHandle, BindPacket, BindPacketLength, TRUE, // fDisableShutdownCheck FALSE, // fDisableCancelCheck Timeout ); // // Null out the reply buffer, because there is none ! // *Buffer = NULL; } else { Status = TransSendReceive(BindingHandle, BindPacket, BindPacketLength, (void * *) Buffer, BufferLength, Timeout); } if ( Status != RPC_S_OK ) { VALIDATE(Status) { RPC_S_OUT_OF_MEMORY, RPC_S_ACCESS_DENIED, RPC_S_OUT_OF_RESOURCES, RPC_P_CONNECTION_CLOSED, RPC_P_RECEIVE_FAILED, RPC_P_SEND_FAILED, RPC_P_CONNECTION_SHUTDOWN, RPC_S_CALL_CANCELLED, RPC_P_TIMEOUT } CORRUPTION_VALIDATE { RPC_S_PROTOCOL_ERROR } CORRUPTION_END_VALIDATE; TransFreeBuffer(BindPacket); if ((Status == RPC_P_RECEIVE_FAILED) || (Status == RPC_P_SEND_FAILED)) { return(RPC_P_CONNECTION_CLOSED); } return(Status); } else { ClearFreshFromCacheFlag(); if (fAsync == 0) { switch (BindPacket->common.PTYPE) { case rpc_auth_3: // don't have a new packet break; case rpc_fault: Status = ValidatePacket(*Buffer, *BufferLength); if (Status == RPC_S_OK) { Status = ((rpcconn_fault *) *Buffer)->status; } break; default: Status = ValidateHeader(*Buffer, *BufferLength); } TransFreeBuffer(BindPacket); } } return Status; } void OSF_CCONNECTION::SetMaxFrag ( IN unsigned short max_xmit_frag, IN unsigned short max_recv_frag ) { UNUSED(max_recv_frag); unsigned TranMax = TransMaximumSend(); MaxFrag = max_xmit_frag; if (MaxFrag > TranMax || MaxFrag == 0) { MaxFrag = (unsigned short) TranMax; } CORRUPTION_ASSERT( MaxFrag >= MUST_RECV_FRAG_SIZE ); } RPC_STATUS OSF_CCONNECTION::SendFragment( IN rpcconn_common *pFragment, IN OSF_CCALL *CCall, IN UINT LastFragmentFlag, IN UINT HeaderSize, IN UINT MaxSecuritySize, IN UINT DataLength, IN UINT MaxFragmentLength, IN unsigned char *ReservedForSecurity, IN BOOL fAsync, IN void *SendContext, IN ULONG Timeout, OUT void **ReceiveBuffer, OUT UINT *ReceiveBufferLength ) /*++ Routine Description: Sends on fragment --*/ { sec_trailer * SecurityTrailer; UINT SecurityLength; UINT AuthPadLength; SECURITY_BUFFER_DESCRIPTOR BufferDescriptor; SECURITY_BUFFER SecurityBuffers[5]; DCE_MSG_SECURITY_INFO MsgSecurityInfo; RPC_STATUS Status; ULONG ReadOnlyFlag; long AuthLengthChange; if (ClientSecurityContext.AuthenticationLevel == RPC_C_AUTHN_LEVEL_NONE || (ClientSecurityContext.AuthenticationLevel == RPC_C_AUTHN_LEVEL_CONNECT && MaxSecuritySize == 0)) { SecurityLength = 0; if (LastFragmentFlag != 0) { pFragment->pfc_flags |= PFC_LAST_FRAG; } } else { VALIDATE(ClientSecurityContext.AuthenticationLevel) { RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_AUTHN_LEVEL_PKT, RPC_C_AUTHN_LEVEL_CONNECT } END_VALIDATE; if (GetConnectionSupportHeaderSign() == cshsYes) ReadOnlyFlag = SECBUFFER_READONLY_WITH_CHECKSUM; else ReadOnlyFlag = SECBUFFER_READONLY; if ( LastFragmentFlag == 0 ) { SecurityTrailer = (sec_trailer *) (((unsigned char *) pFragment) + MaxFragmentLength - MaxSecuritySize); // // It is not the last fragment, so we need to save away the // part of the buffer which could get overwritten with // authentication information. We can not use memcpy, // because the source and destination regions may overlap. // RpcpMemoryMove(ReservedForSecurity, SecurityTrailer, MaxSecuritySize); AuthPadLength = 0; } else { ASSERT( MAXIMUM_SECURITY_BLOCK_SIZE == 16 ); AuthPadLength = Pad16(HeaderSize+DataLength+sizeof(sec_trailer)); // Zero-out un-initialized pad. if (AuthPadLength != 0) { RpcpMemorySet(((unsigned char *) pFragment) + HeaderSize + DataLength, 0, AuthPadLength); } DataLength += AuthPadLength; ASSERT( ((HeaderSize+DataLength+sizeof(sec_trailer)) % MAXIMUM_SECURITY_BLOCK_SIZE) == 0 ); SecurityTrailer = (sec_trailer *) (((unsigned char *) pFragment) + DataLength + HeaderSize); pFragment->pfc_flags |= PFC_LAST_FRAG; } SecurityTrailer->auth_type = (unsigned char) WireAuthId; SecurityTrailer->auth_level = (unsigned char) ClientSecurityContext.AuthenticationLevel; SecurityTrailer->auth_pad_length = (unsigned char) AuthPadLength; SecurityTrailer->auth_reserved = 0; SecurityTrailer->auth_context_id = SecurityContextIdWireRep; BufferDescriptor.ulVersion = 0; BufferDescriptor.cBuffers = 5; BufferDescriptor.pBuffers = SecurityBuffers; SecurityBuffers[0].cbBuffer = HeaderSize; SecurityBuffers[0].BufferType = SECBUFFER_DATA | ReadOnlyFlag; SecurityBuffers[0].pvBuffer = ((unsigned char *) pFragment); SecurityBuffers[1].cbBuffer = (LastFragmentFlag != 0 ? (DataLength) : (MaxFragmentLength - HeaderSize - MaxSecuritySize) ); SecurityBuffers[1].BufferType = SECBUFFER_DATA; SecurityBuffers[1].pvBuffer = ((unsigned char *) pFragment) + HeaderSize; SecurityBuffers[2].BufferType = SECBUFFER_DATA | ReadOnlyFlag; SecurityBuffers[2].cbBuffer = sizeof(sec_trailer); SecurityBuffers[2].pvBuffer = SecurityTrailer; SecurityBuffers[3].cbBuffer = MaxSecuritySize - sizeof(sec_trailer); SecurityBuffers[3].BufferType = SECBUFFER_TOKEN; SecurityBuffers[3].pvBuffer = SecurityTrailer + 1; SecurityBuffers[4].cbBuffer = sizeof(DCE_MSG_SECURITY_INFO); SecurityBuffers[4].BufferType = (SECBUFFER_PKG_PARAMS | SECBUFFER_READONLY); SecurityBuffers[4].pvBuffer = &MsgSecurityInfo; MsgSecurityInfo.SendSequenceNumber = DceSecurityInfo.SendSequenceNumber; MsgSecurityInfo.ReceiveSequenceNumber = InquireReceiveSequenceNumber(); MsgSecurityInfo.PacketType = pFragment->PTYPE; // // DCE computes check sums for Header also // Make sure Header remains intact // Infact may need to extend security interface if // some packages return dynamic size seals/signatures // pFragment->auth_length = SecurityLength = (unsigned short) SecurityBuffers[3].cbBuffer; SecurityLength += sizeof(sec_trailer); if ( LastFragmentFlag != 0) { pFragment->pfc_flags |= PFC_LAST_FRAG; pFragment->frag_length = HeaderSize + DataLength + SecurityLength; } else { pFragment->frag_length += SecurityLength - MaxSecuritySize; } Status = ClientSecurityContext.SignOrSeal( MsgSecurityInfo.SendSequenceNumber, ClientSecurityContext.AuthenticationLevel != RPC_C_AUTHN_LEVEL_PKT_PRIVACY, &BufferDescriptor); // // The fragment may have been checksumed. Do not touch the signed portions // of the fragment after this (including the header), if you do it will // cause a checksum error // AuthLengthChange = pFragment->auth_length - SecurityBuffers[3].cbBuffer; ASSERT( AuthLengthChange >= 0); // if the token ended up shorter than the buffer we supplied, // sanitize the rest of the token buffer to avoid leaking // process data on the wire. if (AuthLengthChange > 0) { RpcpMemorySet(((unsigned char *)(SecurityBuffers[3].pvBuffer)) + SecurityBuffers[3].cbBuffer, 0, AuthLengthChange ); } if (Status != RPC_S_OK) { if ( LastFragmentFlag == 0 ) { RpcpMemoryCopy(SecurityTrailer, ReservedForSecurity, MaxSecuritySize); } if (Status == ERROR_SHUTDOWN_IN_PROGRESS) { return Status; } if ((Status == SEC_E_CONTEXT_EXPIRED) || (Status == SEC_E_QOP_NOT_SUPPORTED)) { return (RPC_S_SEC_PKG_ERROR); } return (RPC_S_ACCESS_DENIED); } } ASSERT(pFragment->frag_length <= MaxFrag); if (LastFragmentFlag != 0) { ASSERT(!RpcpCheckHeap()); if (fAsync) { Status = TransAsyncSend(CCall->BindingHandle, pFragment, pFragment->frag_length, SendContext); } else { if (ReceiveBuffer) { Status = TransSendReceive(CCall->BindingHandle, pFragment, pFragment->frag_length, ReceiveBuffer, ReceiveBufferLength, Timeout); } else { Status = TransSend(CCall->BindingHandle, pFragment, pFragment->frag_length, FALSE, // fDisableShutdownCheck FALSE, // fDisableCancelCheck Timeout); } } if (Status != RPC_S_OK) { if ((Status == RPC_P_CONNECTION_CLOSED) || (Status == RPC_P_SEND_FAILED)) { RpcpErrorAddRecord(EEInfoGCRuntime, RPC_S_CALL_FAILED_DNE, EEInfoDLOSF_CCONNECTION__SendFragment10, (ULONG)Status); return(RPC_S_CALL_FAILED_DNE); } if (Status == RPC_P_RECEIVE_FAILED) { RpcpErrorAddRecord(EEInfoGCRuntime, RPC_S_CALL_FAILED, EEInfoDLOSF_CCONNECTION__SendFragment10, (ULONG)Status); return(RPC_S_CALL_FAILED); } if (Status == RPC_P_TIMEOUT) { RpcpErrorAddRecord(EEInfoGCRuntime, RPC_S_CALL_CANCELLED, EEInfoDLOSF_CCONNECTION__SendFragment10, (ULONG)Status, (ULONG)Timeout); return(RPC_S_CALL_CANCELLED); } VALIDATE(Status) { RPC_S_OUT_OF_MEMORY, RPC_S_OUT_OF_RESOURCES, RPC_P_CONNECTION_SHUTDOWN, RPC_S_CALL_CANCELLED } CORRUPTION_VALIDATE { RPC_S_PROTOCOL_ERROR } CORRUPTION_END_VALIDATE; return(Status); } ClearFreshFromCacheFlag(); return(RPC_S_OK); } if (fAsync) { Status = TransAsyncSend(CCall->BindingHandle, pFragment, pFragment->frag_length, CCall->CallSendContext); } else { Status = TransSend(CCall->BindingHandle, pFragment, pFragment->frag_length, FALSE, // fDisableShutdownCheck FALSE, // fDisableCancelCheck Timeout); // // We need to restore the part of the buffer which we overwrote // with authentication information. // if ((ClientSecurityContext.AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE) && (ClientSecurityContext.AuthenticationLevel != RPC_C_AUTHN_LEVEL_CONNECT || (MaxSecuritySize != 0))) { // // if MaxSecuritySize == 0, there will be no copying, // so its OK to not check for it. // VALIDATE(ClientSecurityContext.AuthenticationLevel) { RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_AUTHN_LEVEL_PKT, RPC_C_AUTHN_LEVEL_CONNECT } END_VALIDATE; RpcpMemoryCopy(SecurityTrailer, ReservedForSecurity, MaxSecuritySize); } if (ReceiveBuffer && Status == RPC_P_RECEIVE_COMPLETE) { // we're going to do a receive - whack any eeinfo // on the thread that the WS_CheckForShutdowns has // added RpcpPurgeEEInfo(); Status = TransReceive(ReceiveBuffer, ReceiveBufferLength, Timeout); } } if ( Status != RPC_S_OK ) { if ((Status == RPC_P_CONNECTION_CLOSED) || (Status == RPC_P_SEND_FAILED)) { RpcpErrorAddRecord(EEInfoGCRuntime, RPC_S_CALL_FAILED_DNE, EEInfoDLOSF_CCONNECTION__SendFragment20, (ULONG)Status); return(RPC_S_CALL_FAILED_DNE); } if (Status == RPC_P_TIMEOUT) { RpcpErrorAddRecord(EEInfoGCRuntime, RPC_S_CALL_CANCELLED, EEInfoDLOSF_CCONNECTION__SendFragment20, (ULONG)Status, (ULONG)Timeout); return RPC_S_CALL_CANCELLED; } VALIDATE(Status) { RPC_S_OUT_OF_MEMORY, RPC_S_OUT_OF_RESOURCES, RPC_P_CONNECTION_SHUTDOWN, RPC_S_CALL_CANCELLED, RPC_P_RECEIVE_FAILED } CORRUPTION_VALIDATE { RPC_S_PROTOCOL_ERROR } CORRUPTION_END_VALIDATE; return(Status); } ClearFreshFromCacheFlag(); return Status; } RPC_STATUS OSF_CCONNECTION::SupportedAuthInfo ( IN CLIENT_AUTH_INFO * ClientAuthInfo, IN NamedPipeType NPType, IN BOOL IsExclusiveConnection, OUT BOOL *Supported ) /*++ Arguments: ClientAuthInfo - Supplies the authentication and authorization information required of this connection. A value of zero (the pointer is zero) indicates that we want an unauthenticated connection. NPType - the type of transport w.r.t. to named pipes. See the definition of NamedPipeType for description of the bitmasks IsExclusiveConnection - if NPType is not named pipe, this parameter is ignored. Otherwise non-zero means the connection is exclusive. Zero means the connection is non-exclusive. Supported - on output, non-zero if the auth info is supported, zero otherwise. Undefined on failure. Return Value: RPC_S_OK or RPC_S_* error --*/ { return (ClientSecurityContext.IsSupportedAuthInfo(ClientAuthInfo, NPType, IsExclusiveConnection, Supported)); } RPC_STATUS OSF_CCONNECTION::AddActiveCall ( IN ULONG CallId, IN OSF_CCALL *CCall ) /*++ Function Name:AddActiveCall Parameters: Description: Returns: --*/ { int retval; ConnMutex.Request(); if (State == ConnAborted) { ConnMutex.Clear(); return RPC_S_CALL_FAILED; } retval = ActiveCalls.Insert(IntToPtr(CallId), CCall); ConnMutex.Clear(); if (retval == -1) { return RPC_S_OUT_OF_MEMORY; } return RPC_S_OK; } RPC_STATUS OSF_CCONNECTION::CallCancelled ( OUT PDWORD Timeout ) /*++ Function Name:CallCancelled Parameters: Description: This function is called by the transport interface when it notices that it has received an altert. This routine is called via I_RpcIOAlerted Returns: --*/ { if (fExclusive == 0 || CurrentCall == 0) { return RPC_S_NO_CALL_ACTIVE; } ASSERT(fExclusive && CurrentCall); // // Even if we get alerted in the bind path, we already have a call object. // That makes it easy for us to track cancels // return CurrentCall->CallCancelled(Timeout); } MTSyntaxBinding *CreateOsfBinding( IN RPC_SYNTAX_IDENTIFIER *InterfaceId, IN TRANSFER_SYNTAX_STUB_INFO *TransferSyntaxInfo, IN int CapabilitiesBitmap ) { return new OSF_BINDING(InterfaceId, TransferSyntaxInfo, CapabilitiesBitmap); } BOOL CheckOsfBindingForDestruction( IN MTSyntaxBinding *Binding, IN void *CallbackContext ) { OSF_CASSOCIATION *Association; OSF_BINDING *OsfBinding; OsfBinding = (OSF_BINDING *)Binding; // is this binding ready for cleanup if (OsfBinding->IsRefCountZero()) { Association = (OSF_CASSOCIATION *)CallbackContext; Association->DestroyBinding(OsfBinding); return TRUE; } else return FALSE; } extern "C" RPC_IF_HANDLE _mgmt_ClientIfHandle; OSF_CCALL::OSF_CCALL ( RPC_STATUS __RPC_FAR * pStatus ) : CallMutex(pStatus), SyncEvent(pStatus, 0), fAdvanceCallCount(0) { LogEvent(SU_CCALL, EV_CREATE, this); ObjectType = OSF_CCALL_TYPE; ReservedForSecurity = 0; SecBufferLength = 0; SavedHeaderSize = 0; SavedHeader = 0; InReply = 0; EEInfo = NULL; CachedAPCInfoAvailable = 1; CallbackLevel = 0; CallSendContext = (char *) this+sizeof(OSF_CCALL)+sizeof(PVOID); *((PVOID *) ((char *) CallSendContext - sizeof(PVOID))) = (PVOID) this; } OSF_CCALL::~OSF_CCALL ( ) { LogEvent(SU_CCALL, EV_DELETE, this); if (CachedAPCInfoAvailable == 0) { ASSERT(!"Can't destroy call with queued APCs on it"); } if (ReservedForSecurity) { RpcpFarFree(ReservedForSecurity); } if (SavedHeader) { RpcpFarFree(SavedHeader); } Connection->NotifyCallDeleted(); } RPC_STATUS OSF_CCALL::ReserveSpaceForSecurityIfNecessary (void) { SECURITY_CONTEXT *ClientSecurityContext ; ClientSecurityContext = &(Connection->ClientSecurityContext); // // We need to figure out about security: do we need to put authentication // information into each packet, and if so, how much space should we // reserve. So that we have space to save the contents of the buffer // which will be overwritten with authentication information (for all but // the last fragment). // if (ClientSecurityContext->AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE) { VALIDATE(ClientSecurityContext->AuthenticationLevel) { RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_AUTHN_LEVEL_CONNECT, RPC_C_AUTHN_LEVEL_PKT } END_VALIDATE; MaxSecuritySize = Connection->AdditionalSpaceForSecurity - MAXIMUM_SECURITY_BLOCK_SIZE; if (MaxSecuritySize < sizeof(sec_trailer)) { ASSERT(MaxSecuritySize >= sizeof(sec_trailer)); return(RPC_S_INTERNAL_ERROR); } if (MaxSecuritySize == sizeof(sec_trailer)) { if (ClientSecurityContext->AuthenticationLevel != RPC_C_AUTHN_LEVEL_CONNECT) { ASSERT(0); return(RPC_S_INTERNAL_ERROR); } MaxSecuritySize = 0; } else { if (SecBufferLength < Connection->AdditionalSpaceForSecurity) { if (ReservedForSecurity) { RpcpFarFree(ReservedForSecurity); } ReservedForSecurity = (unsigned char *) RpcpFarAllocate(Connection->AdditionalSpaceForSecurity); if (ReservedForSecurity == 0) { SecBufferLength = 0; return RPC_S_OUT_OF_MEMORY; } SecBufferLength = Connection->AdditionalSpaceForSecurity; } } SetMaxSecuritySizeUpdatedFlag(); } // if the header size has already been determined, update the frag length // if not, we'll let the GetBuffer thread do it. Also, there is a small // race condition where we may update it twice, but since this is perf // only, we don't care - in the common case the gains are huge when we // don't have to update if (HeaderSize != 0) UpdateMaxFragLength(ClientSecurityContext->AuthenticationLevel); fDataLengthNegotiated = TRUE; return RPC_S_OK; } void OSF_CCALL::UpdateObjectUUIDInfo (IN UUID *ObjectUuid) { UUID *ObjectUuidToUse; ULONG AuthnLevel; // // Do the initial setup // if (ObjectUuid) { ObjectUuidToUse = ObjectUuid; } else if (BindingHandle->InqIfNullObjectUuid() == 0) { ObjectUuidToUse = BindingHandle->InqPointerAtObjectUuid(); } else { ObjectUuidToUse = 0; UuidSpecified = 0; HeaderSize = sizeof(rpcconn_request); } if (ObjectUuidToUse) { UuidSpecified = 1; HeaderSize = sizeof(rpcconn_request) + sizeof(UUID); RpcpMemoryCopy(&this->ObjectUuid, ObjectUuidToUse, sizeof(UUID)); } AuthnLevel = Connection->ClientSecurityContext.AuthenticationLevel; // recalc if either there is no security size or if it is ready (i.e. updated) if (GetMaxSecuritySizeUpdatedFlag() || (AuthnLevel == RPC_C_AUTHN_LEVEL_NONE)) { UpdateMaxFragLength(AuthnLevel); } } void OSF_CCALL::UpdateMaxFragLength (ULONG AuthnLevel) { BOOL fExclusive = Connection->fExclusive; // if the connection is exclusive, this all happens on the same thread - // no need for a mutex if (!fExclusive) CallMutex.Request(); MaximumFragmentLength = Connection->MaxFrag; if (AuthnLevel != RPC_C_AUTHN_LEVEL_NONE) { MaximumFragmentLength -= ((MaximumFragmentLength - HeaderSize - MaxSecuritySize) % MAXIMUM_SECURITY_BLOCK_SIZE); } MaxDataLength = MaximumFragmentLength - HeaderSize - MaxSecuritySize; if (!fExclusive) CallMutex.Clear(); } BOOL OSF_CCALL::IssueNotification ( IN RPC_ASYNC_EVENT Event ) { BOOL fRes; RPC_STATUS Status; if (pAsync == 0) { if (Connection->fExclusive == 0) { SyncEvent.Raise(); } return 0; } // we must have bailed out by now if this is sync ASSERT (pAsync); fRes = CCALL::IssueNotificationEntry(Event); if (!fRes) return 0; if (AsyncStatus == RPC_S_OK) { RPC_SECURITY_CALLBACK_FN *SecurityCallback = NULL; Status = BindingHandle->InqTransportOption( RPC_C_OPT_SECURITY_CALLBACK, (ULONG_PTR *) &SecurityCallback); ASSERT(Status == RPC_S_OK); if (SecurityCallback) { (*SecurityCallback) (this); } } return CCALL::IssueNotificationMain(Event); } const int MAX_ASYNC_RETRIES = 3; RPC_STATUS OSF_CCALL::BindToServer ( BOOL fAsyncBind ) /*++ Function Name:BindToServer Parameters: Description: Returns: --*/ { RPC_STATUS Status; OSF_BINDING_HANDLE *pLocalBindingHandle = BindingHandle; OSF_CCONNECTION *pLocalConnection; ULONG Timeout; BOOL fBindingHandleTimeoutUsed = TRUE; BOOL fAlwaysNegotiateNDR20 = FALSE; OSF_BINDING *IgnoredBinding; ULONG LocalAssociationGroupId; BOOL Ignored; FAILURE_COUNT_STATE fFailureCountExceeded = FailureCountUnknown; int AsyncRetries = 0; if (EEInfo) { FreeEEInfoChain(EEInfo); EEInfo = NULL; } if (fAsyncBind == FALSE) { // // We party on the call even after the connection is aborted. // We need to keep a reference on the call // CCALL++ // We do this only in the sync case, as in the async, // the reference has already been added for us by the caller // AddReference(); } else { ASSERT(Connection->fExclusive == FALSE); // if this is a new, non-exclusive conn if (pLocalBindingHandle && (CurrentState == NeedOpenAndBind)) { // if we have had no preferences at the time of // establishment (which is signified by the fact that both // SelectedBinding and AvalableBindingsList is set) if (Bindings.SelectedBinding && Bindings.AvailableBindingsList) { fAlwaysNegotiateNDR20 = TRUE; } } } Connection->CurrentCall = this; if (pLocalBindingHandle) { Timeout = GetEffectiveTimeoutForBind(pLocalBindingHandle, &fBindingHandleTimeoutUsed); do { switch (CurrentState) { case NeedOpenAndBind: Status = Connection->OpenConnectionAndBind( pLocalBindingHandle, Timeout, fAlwaysNegotiateNDR20, &fFailureCountExceeded); break; case NeedAlterContext: if (Connection->fExclusive) { LocalAssociationGroupId = Connection->Association->AssocGroupId; if (LocalAssociationGroupId) { Status = Connection->ActuallyDoBinding( this, Connection->Association->AssocGroupId, 0, Timeout, &IgnoredBinding, &Ignored, // fPossibleAssociationReset NULL // fFailureCountExceeded ); if (Status == RPC_S_OK) { CurrentState = SendingFirstBuffer; } } else Status = RPC_S_CALL_FAILED_DNE; } else { Status = SendAlterContextPDU(); } break; default: #if DBG PrintToDebugger("RPC: BindToServer was a nop, CurrentState: %d\n", CurrentState); #endif Status = RPC_S_OK; break; } if (fAsyncBind) { ASSERT(fFailureCountExceeded != FailureCountNotExceeded); // if this is a bind_nak with reason not specified, and retry // attempts exhausted, shutdown the association, but without this // connection, and then retry the OpenConnectionAndBind if ( (fFailureCountExceeded == FailureCountExceeded) && (CurrentState == NeedOpenAndBind) ) { Connection->Association->ShutdownRequested(RPC_S_OK, Connection); // do some cleanup work on the current connection to avoid // leaks. Connection->TransClose(); Connection->InitializeWireAuthId(&Connection->ClientSecurityContext); if (Connection->ClientSecurityContext.AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE) { // DeleteSecurityContext checks and deletes // the security context only if necessary Connection->ClientSecurityContext.DeleteSecurityContext(); } fFailureCountExceeded = FailureCountUnknown; AsyncRetries ++; // retry the bind continue; } pLocalBindingHandle->BindingFree(); } // all paths except those with explicit continue exit the loop break; } while (AsyncRetries <= MAX_ASYNC_RETRIES); } else Status = RPC_S_CALL_FAILED_DNE; // save the data member in a local variable pLocalConnection = Connection; if (Status != RPC_S_OK) { if (pLocalBindingHandle) { Status = GetStatusForTimeout( pLocalBindingHandle, Status, fBindingHandleTimeoutUsed); } if (Connection->fExclusive == 0) { if (Status == RPC_P_CONNECTION_CLOSED || Status == RPC_P_CONNECTION_SHUTDOWN || Status == RPC_P_SEND_FAILED) { Status = RPC_S_CALL_FAILED_DNE; } Connection->ConnectionAborted(Status, 0); // // Remove the send reference for this call, CCALL-- // RemoveReference(); } } // // Remove the reference we added above // CCALL-- // RemoveReference(); if (fAsyncBind) pLocalConnection->RemoveReference(); return Status; } RPC_STATUS OSF_CCALL::ActuallyAllocateBuffer ( OUT void * * Buffer, IN UINT BufferLength ) /*++ Routine Description: We need a buffer to receive data into or to put data into to be sent. This should be really simple, but we need to make sure that buffer we return is aligned on an 8 byte boundary. The stubs make this requirement. Arguments: Buffer - Returns a pointer to the buffer. BufferLength - Supplies the required length of the buffer in bytes. Return Value: RPC_S_OK - We successfully allocated a buffer of at least the required size. RPC_S_OUT_OF_MEMORY - There is insufficient memory available to allocate the required buffer. --*/ { return Connection->TransGetBuffer(Buffer, BufferLength); } void OSF_CCALL::ActuallyFreeBuffer ( IN void * Buffer ) /*++ Routine Description: We need to free a buffer which was allocated via TransGetBuffer. The only tricky part is remembering to remove the padding before actually freeing the memory. --*/ { Connection->TransFreeBuffer(Buffer); } RPC_STATUS OSF_CCALL::ActivateCall ( IN OSF_BINDING_HANDLE *BindingHandle, IN OSF_BINDING *Binding, IN OSF_BINDING *AvailableBindingsList, IN ULONG CallIdToUse, IN OSF_CCALL_STATE InitialCallState, IN PRPC_DISPATCH_TABLE DispatchTable, IN OSF_CCONNECTION *CConnection ) /*++ Function Name:ActivateCall Parameters: BindingHandle - the binding handle on which the call is made Binding - the OSF_BINDING to use for this call. Carries a refcount if AvailableBindingList is NULL. AvailableBindingsList - the list of available bindings for this call. If the binding is already selected, it will be NULL. If not, each element from the list carries a refcount. CallIdToUse - what call id to use. InitialCallState - the initial state of this call DispatchTable - the dispatch table to use for this call. Used only for callbacks CConnection - the connection on which the call will be made. Returns: Remarks: In the case of failure, the binding(s) should be released by the caller. --*/ { RPC_STATUS Status = RPC_S_OK; ASSERT(BufferQueue.IsQueueEmpty()); MaxSecuritySize = 0; MaxDataLength = 0; Connection = CConnection; this->BindingHandle = BindingHandle; CurrentBuffer = 0; CurrentOffset = 0; CurrentState = InitialCallState; CallStack = 0; RcvBufferLength = 0; pAsync = 0; NeededLength = 0; MaximumFragmentLength = 0; LastBuffer = NULL; RecursiveCallsKey = -1; fDataLengthNegotiated = FALSE; AllocHint = 0; Flags.ClearAll(); if (Binding) { // we can have both binding and binding list only for non-sync // calls ASSERT((AvailableBindingsList == NULL) || (Connection->fExclusive == FALSE)); } else { // if we don't have a binding, this must be a sync call. // Async calls must have their bindings fixed by now ASSERT(Connection->fExclusive); } Bindings.SelectedBinding = Binding; Bindings.AvailableBindingsList = AvailableBindingsList; this->DispatchTableCallback = DispatchTable; CallId = CallIdToUse; fCallCancelled = FALSE; CancelState = CANCEL_NOTREGISTERED; AdditionalSpaceForSecurity = Connection->AdditionalSpaceForSecurity; fPeerChoked = 0; HeaderSize = 0; if (Connection->fExclusive == 0) { // // 1. The first reference is removed when // all the sends are complete. This is called the send reference // CCALL++ // 2. The second one is removed when the client is done with the call, // ie: when freebuffer is called or when an operation fails. This is called // the call reference. CCALL++ // SetReferenceCount(2); fAdvanceCallCount.SetInteger(0); AsyncStatus = RPC_S_ASYNC_CALL_PENDING ; FirstSend = 1; NotificationIssued = -1; fChoked = 0; fLastSendComplete = 0; CallingThread = RpcpGetThreadPointer(); ASSERT(CallingThread != 0); Status = Connection->AddActiveCall( CallIdToUse, this); } else { CallingThread = 0; } return Status; } RPC_STATUS OSF_CCALL::SendHelper ( IN PRPC_MESSAGE Message, OUT BOOL *fFirstSend ) /*++ Function Name:Send Parameters: Message - Contains information about the request Description: Returns: --*/ { void *NewBuffer; int RemainingLength = 0; RPC_STATUS Status = RPC_S_OK; BOOL fRegisterFailed = 0; ASSERT(HeaderSize != 0); *fFirstSend = 0; if (PARTIAL(Message)) { if (AllocHint == 0) AllocHint = Message->BufferLength; if (fDataLengthNegotiated == 0 || Message->BufferLength < MaxDataLength) { if (pAsync && (pAsync->Flags & RPC_C_NOTIFY_ON_SEND_COMPLETE)) { if (!IssueNotification(RpcSendComplete)) { CallFailed(Status); // this is async only. We don't need to unregister for cancels return Status; } } return (RPC_S_SEND_INCOMPLETE); } ASSERT(MaxDataLength); RemainingLength = Message->BufferLength % MaxDataLength; if (RemainingLength) { Status = GetBufferDo(RemainingLength, &NewBuffer); if (Status != RPC_S_OK) { CallFailed(Status); UnregisterCallForCancels(); return Status; } Message->BufferLength -= RemainingLength; RpcpMemoryCopy(NewBuffer, (char *) Message->Buffer + Message->BufferLength, RemainingLength); } } else { ASSERT(LastBuffer == NULL); LastBuffer = Message->Buffer; AllocHint = Message->BufferLength; } // // Add a reference for this send, CCALL++ // AddReference(); // // You get to actually send only if you are the CurrentCall // and the connection is idle. // Retry: if (AsyncStatus != RPC_S_ASYNC_CALL_PENDING) { Status = AsyncStatus; ASSERT(Status != RPC_S_OK); if (Status == RPC_S_CALL_FAILED_DNE) { Status = RPC_S_CALL_FAILED; } // N.B. The connection that does the bind will have // its send reference removed in case of failure, and // will be the current call. Therefore, we remove // the reference only if we aren't the current call if (Connection->CurrentCall != this) { // // We didn't get a chance to submit a new send // Remove the send reference CCALL-- // OSF_CCALL::RemoveReference(); } goto Cleanup; } CallMutex.Request(); if (CurrentBuffer) { // // If the call had failed, we will find out after the we return // from this routine // if (pAsync == 0 && BufferQueue.Size() >= 4) { fChoked = 1; CallMutex.Clear(); SyncEvent.Wait(); goto Retry; } // // Since CurrentBuffer != 0, the connection could not have been // in a quiscent state. Therefore, we don't need to tickle it. This // also means that the call is currently in the call queue of the connection. // So, we don't need to add ourselves to the call queue. // if (BufferQueue.PutOnQueue(Message->Buffer, Message->BufferLength)) { Status = RPC_S_OUT_OF_MEMORY; if (Connection->CurrentCall != this) { // // We didn't get a chance to submit a new send // Remove the send reference CCALL-- // OSF_CCALL::RemoveReference(); } } CallMutex.Clear(); } else { CurrentOffset = 0; CurrentBuffer = Message->Buffer; CurrentBufferLength = Message->BufferLength; if (FirstSend) { FirstSend = 0; CallStack++; CallMutex.Clear(); Status = RegisterCallForCancels(); if (Status != RPC_S_OK) { fRegisterFailed = 1; if (Connection->CurrentCall != this) { // // We didn't get a chance to submit a new send // Remove the send reference CCALL-- // OSF_CCALL::RemoveReference(); } goto Cleanup; } Status = Connection->AddCall(this); *fFirstSend = 1; if (Status != RPC_S_OK) { if (Connection->CurrentCall != this) { // // We didn't get a chance to submit a new send // Remove the send reference CCALL-- // OSF_CCALL::RemoveReference(); } goto Cleanup; } } else { CallMutex.Clear(); } // // The connection could be in a quiescent state // we need to tickle it // Connection->ConnMutex.Request(); if (CurrentState == Complete) { Connection->ConnMutex.Clear(); // Status should already be RPC_S_OK ASSERT(Status == RPC_S_OK); goto Cleanup; } if (Connection->CurrentCall == this && Connection->IsIdle()) { Connection->MakeConnectionActive(); if ((Connection->fExclusive == FALSE) && (BindingHandle->InqComTimeout() != RPC_C_BINDING_INFINITE_TIMEOUT)) { // this is a best effort - ignore failure (void)Connection->TurnOnOffKeepAlives(TRUE, // turn on BindingHandle->InqComTimeout() ); } Connection->ConnMutex.Clear(); Status = SendData(0); if (Status != RPC_S_OK) { // // We didn't get a chance to submit a new send // Remove the send reference CCALL-- // OSF_CCALL::RemoveReference(); } } else { Connection->ConnMutex.Clear(); } } Cleanup: if (Status) { ASSERT(Status != RPC_S_SEND_INCOMPLETE); if (RemainingLength) { // // NewBuffer should be initialized // FreeBufferDo(NewBuffer); } AsyncStatus = Status; if (!fRegisterFailed) { UnregisterCallForCancels(); } } else { if (RemainingLength) { Message->Buffer = NewBuffer; Message->BufferLength = RemainingLength; ActualBufferLength = RemainingLength; Status = RPC_S_SEND_INCOMPLETE; } else { ActualBufferLength = 0; Message->Buffer = 0; Message->BufferLength = 0; } // // Call reference is removed in FreeBuffer // } // // Remove the reference for this send we added above, CCALL-- // OSF_CCALL::RemoveReference(); return Status; } RPC_STATUS OSF_CCALL::Send ( IN PRPC_MESSAGE Message ) { int i; BOOL fFirstSend; void *TempBuffer; RPC_STATUS Status; PRPC_ASYNC_STATE MypAsync = this->pAsync; OSF_BINDING_HANDLE *MyBindingHandle = this->BindingHandle; AddVerificationTrailer(Message); // // WARNING: Do not use any members of OSF_CCALL beyond this point. // the object could have been deleted. // for (i = 0;;i++) { Status = ((OSF_CCALL *) Message->Handle)->SendHelper(Message, &fFirstSend); if (Status == RPC_S_OK) { if (!PARTIAL(Message) && MypAsync) { // // Last send // ((OSF_CCALL *) Message->Handle)->CallMutex.Request(); ASSERT(((OSF_CCALL *) Message->Handle)->fLastSendComplete == 0); if (((OSF_CCALL *) Message->Handle)->CurrentState == Aborted) { Status = ((OSF_CCALL *) Message->Handle)->GetCallStatus(); ((OSF_CCALL *) Message->Handle)->CallMutex.Clear(); // // Remove the call reference, CCALL-- // ((OSF_CCALL *) Message->Handle)->RemoveReference(); // // No need to free the send buffer, it will be freed when the // send is complete // } else { // // For any future failures, a notification is issued // ((OSF_CCALL *) Message->Handle)->fLastSendComplete = 1; ((OSF_CCALL *) Message->Handle)->CallMutex.Clear(); } } return Status; } if (Status != RPC_S_CALL_FAILED_DNE || i > 0 || fFirstSend == 0) { if (Status != RPC_S_SEND_INCOMPLETE) { ((OSF_CCALL *) Message->Handle)->FreeBufferDo(Message->Buffer); // // Remove the call reference, CCALL-- // ((OSF_CCALL *) Message->Handle)->RemoveReference(); } return Status; } Status = AutoRetryCall(Message, FALSE, // not in SendReceive path MyBindingHandle, Status, MypAsync); if (Status != RPC_S_OK) break; } return Status; } RPC_STATUS OSF_CCALL::AutoRetryCall ( IN OUT RPC_MESSAGE *Message, IN BOOL fSendReceivePath, IN OSF_BINDING_HANDLE *LocalBindingHandle, IN RPC_STATUS CurrentStatus, IN RPC_ASYNC_STATE *AsyncState OPTIONAL ) /*++ Function Name:AutoRetryCall Parameters: Message - Contains information about the request fSendReceivePath - TRUE if this is the send receive path, FALSE if it is the Send path. We need to differentiate, because we do the cleanup of the old call in slightly different ways LocalBindingHandle - a local copy of the binding handle that was used to make the original call AsyncState - a pointer to the async state of the original call (if any) Description: Retries the current call. We first save the old buffer to a new buffer. Returns: --*/ { void *TempBuffer; RPC_STATUS Status; UUID MyUuid; UUID *UuidToUse; BOOL fUuidSpecified; OSF_CCALL *OldCall; RPC_SYNTAX_IDENTIFIER TransferSyntax; BOOL fInterfaceSupportsMultipleTransferSyntaxes; // any failure after this is unrelated RpcpPurgeEEInfo(); OldCall = (OSF_CCALL *) Message->Handle; fInterfaceSupportsMultipleTransferSyntaxes = DoesInterfaceSupportMultipleTransferSyntaxes(Message->RpcInterfaceInformation); ASSERT(OldCall->HeaderSize != 0); fUuidSpecified = OldCall->UuidSpecified; if (fUuidSpecified) { RpcpMemoryCopy(&MyUuid, &(OldCall->ObjectUuid), sizeof(UUID)); } TempBuffer = RpcpFarAllocate(Message->BufferLength); if (TempBuffer != 0) RpcpMemoryCopy(TempBuffer, Message->Buffer, Message->BufferLength); // save the transfer syntax before we nuke the call if (fInterfaceSupportsMultipleTransferSyntaxes) RpcpMemoryCopy(&TransferSyntax, Message->TransferSyntax, sizeof(RPC_SYNTAX_IDENTIFIER)); OldCall->CleanupOldCallOnAutoRetry(Message->Buffer, fSendReceivePath, CurrentStatus); if (TempBuffer == 0) { return RPC_S_OUT_OF_MEMORY; } Message->Handle = LocalBindingHandle; UuidToUse = fUuidSpecified ? &MyUuid : 0; Status = NegotiateTransferSyntaxAndGetBuffer(Message, &TransferSyntax, UuidToUse); if (Status != RPC_S_OK) { // the transfer syntax should not change in the async path for now - this // indicates application error, and it's better to ASSERT here in checked // builds, than leave the app writer bewildered on what has gone wrong if // we just spew the error code back if (!fSendReceivePath) { ASSERT(Status != RPC_P_TRANSFER_SYNTAX_CHANGED); } RpcpFarFree(TempBuffer); return Status; } if (AsyncState) { Status = I_RpcAsyncSetHandle(Message, AsyncState); if (Status != RPC_S_OK) { RpcpFarFree(TempBuffer); return Status; } } RpcpMemoryCopy(Message->Buffer, TempBuffer, Message->BufferLength); RpcpFarFree(TempBuffer); return RPC_S_OK; } RPC_STATUS OSF_CCALL::AsyncSend ( IN PRPC_MESSAGE Message ) /*++ Function Name:AsyncSend Parameters: Message - Contains information about the request. Description: This function is used in conjunction with Async RPC. This function may be used to send regular requests as well as pipe requests. Returns: --*/ { return Send(Message); } RPC_STATUS OSF_CCALL::Receive ( IN OUT PRPC_MESSAGE Message, IN UINT Size ) /*++ Function Name:Receive Parameters: Description: This function is used in conjunction with synchronous pipes. It is used to receive partial pipe data. If the RPC_BUFFER_EXTRA flag is set, the pipe data is appended to the end of the current buffer. If it is not set, the pipe data is copied from starting from the beginning of the buffer. Returns: --*/ { RPC_STATUS Status = RPC_S_OK; unsigned long OldFlags = IsExtraMessage(Message); ASSERT(pAsync == 0); if (!EXTRA(Message) && Message->Buffer) { ActuallyFreeBuffer((char *)Message->Buffer-sizeof(rpcconn_request)); Message->Buffer = 0; } while (TRUE) { switch (CurrentState) { case Complete: Status = GetCoalescedBuffer(Message); break; case Aborted: ASSERT(AsyncStatus != RPC_S_OK); Status = AsyncStatus; break; default: if (RcvBufferLength >= Connection->MaxFrag) { Status = GetCoalescedBuffer(Message); if (Status != RPC_S_OK) { break; } if (PARTIAL(Message) && Message->BufferLength >= Size) { break; } Message->RpcFlags |= RPC_BUFFER_EXTRA; } else { // // the call is not yet complete, wait for it. // SyncEvent.Wait(); } continue; } break; } Message->DataRepresentation = Connection->Association->SavedDrep; Message->RpcFlags &= ~(RPC_BUFFER_EXTRA); Message->RpcFlags |= OldFlags; if (Status != RPC_S_OK) { UnregisterCallForCancels(); AsyncStatus = Status; // Remove the call reference, CCALL-- OSF_CCALL::RemoveReference(); } return Status; } RPC_STATUS OSF_CCALL::GetCoalescedBuffer ( IN PRPC_MESSAGE Message ) /*++ Function Name:GetCoalescedBuffer Parameters: Message - the message structure that will receive the params Description: This routine will coalesce the buffers in the buffer queue into a single buffer and return it in the Message structure. If the RPC_BUFFER_EXTRA flag is set, the data is appended to the existing buffer in Message->Buffer. Returns: RPC_S_OK - the function was successful in doing its job RPC_S_OUT_OF_MEMORY - ran out of memory. --*/ { char *Current; PVOID NewBuffer, Buffer; UINT bufferlength; UINT TotalLength; int Extra = IsExtraMessage(Message); RPC_STATUS Status; BOOL fSubmitReceive = 0; CallMutex.Request(); if (RcvBufferLength == 0) { CallMutex.Clear(); if (!Extra) { ASSERT(CurrentState == Complete); Message->Buffer = BufferQueue.TakeOffQueue(&bufferlength); ASSERT(Message->Buffer); ASSERT(bufferlength == 0); Message->BufferLength = 0; Message->RpcFlags |= RPC_BUFFER_COMPLETE; CallStack--; } return RPC_S_OK; } if (Extra) { TotalLength = RcvBufferLength + Message->BufferLength; } else { TotalLength = RcvBufferLength; } Status = Connection->TransGetBuffer ( &NewBuffer, TotalLength+sizeof(rpcconn_request)); if (Status != RPC_S_OK) { CallMutex.Clear(); return RPC_S_OUT_OF_MEMORY; } NewBuffer = (char *) NewBuffer+sizeof(rpcconn_request); if (Extra && Message->Buffer) { RpcpMemoryCopy(NewBuffer, Message->Buffer, Message->BufferLength); Current = (char *) NewBuffer + Message->BufferLength; Connection->TransFreeBuffer( (char *) Message->Buffer -sizeof(rpcconn_request)); } else { Current = (char *) NewBuffer; } while ((Buffer = BufferQueue.TakeOffQueue(&bufferlength)) != 0) { RpcpMemoryCopy(Current, Buffer, bufferlength); Current += bufferlength; Connection->TransFreeBuffer( (char *) Buffer -sizeof(rpcconn_request)); } if (fPeerChoked) { fSubmitReceive = 1; fPeerChoked = 0; } Message->Buffer = NewBuffer; Message->BufferLength = TotalLength; if (CurrentState == Complete) { Message->RpcFlags |= RPC_BUFFER_COMPLETE; CallStack--; } RcvBufferLength = 0; CallMutex.Clear(); if (fSubmitReceive) { Connection->TransAsyncReceive(); } return RPC_S_OK; } RPC_STATUS OSF_CCALL::AsyncReceive ( IN OUT PRPC_MESSAGE Message, IN UINT Size ) /*++ Function Name:AsyncReceive Parameters: Message - Contains information about the call Size - This field is ignored on the client. Description: This is API is used to receive the non-pipe data in an async call. It this function is called before the call is actually complete, an RPC_S_ASYNC_CALL_PENDING is returned. Returns: --*/ { RPC_STATUS Status; if (!EXTRA(Message) && Message->Buffer) { ActuallyFreeBuffer((char *)Message->Buffer-sizeof(rpcconn_request)); Message->Buffer = 0; Message->BufferLength = 0; } while (TRUE) { switch (CurrentState) { case Complete: Status = GetCoalescedBuffer(Message); break; case Aborted: Status = AsyncStatus; break; default: if (PARTIAL(Message)) { CallMutex.Request(); // We should try to satisfy the size requirement only // if the call has not yet been completed or aborted. // If the call has been completed, we will just give back all we have. // If the call has been aborted, we will clean up. if (RcvBufferLength < Size && CurrentState != Complete && CurrentState != Aborted) { if (NOTIFY(Message)) { NeededLength = Size; } CallMutex.Clear(); return RPC_S_ASYNC_CALL_PENDING; } else if (CurrentState != Aborted) { CallMutex.Clear(); // We need to release CallMutex before calling GetCoalescedBuffer // since it may call OSF_CCONNECTION::TransAsyncReceive which // may get ConnectionMutex to clean up the connection. // Other parts of the code take ConnectionMutex and then CallMutex, so // this may cause a deadlock. This call does not need to be guarded. Status = GetCoalescedBuffer(Message); } else { CallMutex.Clear(); // The call has been aborted. ASSERT(AsyncStatus != RPC_S_OK && AsyncStatus != RPC_S_ASYNC_CALL_PENDING); Status = AsyncStatus; } } else { return RPC_S_ASYNC_CALL_PENDING; } break; } break; } Message->DataRepresentation = Connection->Association->SavedDrep; if (Status != RPC_S_OK) { if (EEInfo) { // Try to move the EEInfo to the thread. // It is possible that the call is made on a thread that has never had RPC TLS initialized. // We can only move the EEInfo to the thread if it has RPC TLS. // If the THREAD object does not exist and could not be created, we have // to discard the EEInfo. if (ThreadSelf() != NULL) { RpcpPurgeEEInfo(); RpcpSetEEInfo(EEInfo); } else { FreeEEInfoChain(EEInfo); } EEInfo = NULL; } // It is possible that this path is executed after call-complete // notification has been issued for an aborted call in async pipes. // Consider this scenario: // // - A call C is created. No pulls have been done yet. // - A connection for the call is aborted. A call-complete notification // is issued for the call. // - If a pull is called in a race, cleaning up the call here may cause // an AV when the user queries call status after getting call-complete. // // Therefore we should not clean up if notification has been issued for an // async pipe call. We will cleanup when the call is completed. // // We will also need to fake success, since the app assumes that the call // is done and cleaned up once an async pipe pull returned a failure. if (!GetIsAsyncPipeCallFlag() || NotificationIssued == -1) { // // FreeBuffer is not going to be called. Cleanup now.. // AsyncStatus = Status; // remove the call reference, CCALL-- OSF_CCALL::RemoveReference(); } else { // Let's fake success by returning call-pending. This way there is no data // transfered to the user and she can calmly wait for the failure to come // asyncronously. Status = RPC_S_ASYNC_CALL_PENDING; } } return Status; } RPC_STATUS OSF_CCALL::SetAsyncHandle ( IN PRPC_ASYNC_STATE pAsync ) /*++ Function Name:SetAsyncHandle Parameters: Description: Returns: --*/ { if (DoesAsyncCallHavePipe((char *)pAsync)) { SetIsAsyncPipeCallFlag(); } return CALL::SetAsyncHandle(pAsync); } RPC_STATUS OSF_CCALL::ReceiveReply ( IN rpcconn_request *Request, IN PRPC_MESSAGE Message ) /*++ Function Name:ReceiveReply Parameters: Description: Helper function to receive the complete reply. The reply may either be a callback, or a response. Returns: --*/ { int BytesRead; PVOID NewBuffer; RPC_STATUS Status; UINT BytesRemaining; RPC_MESSAGE NewMessage; UINT NewBufferLength; ULONG AllocHint = Request->alloc_hint; ULONG Timeout; // // Allocate a buffer, big enough to hold the non pipe data. // All the non pipe data will go into the first buffer, all other fragments // will go as separate buffers in the received buffer queue // if (AllocHint) { // NDR doesn't handle above 2G. Make sure we're not handed more than that if (AllocHint & 0x80000000UL) { Connection->TransFreeBuffer(Request); return RPC_S_PROTOCOL_ERROR; } Status = Connection->TransGetBuffer( &NewBuffer, AllocHint+sizeof(rpcconn_request)); if (Status != RPC_S_OK) { Connection->TransFreeBuffer(Request); return Status; } NewBuffer = (char *) NewBuffer+sizeof(rpcconn_request); RpcpMemoryCopy(NewBuffer, Message->Buffer, Message->BufferLength); Connection->TransFreeBuffer(Request); Message->Buffer = NewBuffer; BytesRemaining = AllocHint - Message->BufferLength; } else { BytesRemaining = 0; } BytesRead = Message->BufferLength; NewMessage.RpcFlags = Message->RpcFlags; Timeout = GetBindingHandleTimeout(BindingHandle); // // Receive the complete data // while (!COMPLETE(&NewMessage)) { Status = Connection->TransReceive( &NewBuffer, &NewBufferLength, Timeout); if (Status != RPC_S_OK) { Connection->TransFreeBuffer((char *) Message->Buffer-sizeof(rpcconn_request)); if ((Status == RPC_P_RECEIVE_FAILED) || (Status == RPC_P_CONNECTION_CLOSED) || (Status == RPC_P_CONNECTION_SHUTDOWN)) { RpcpErrorAddRecord(EEInfoGCRuntime, RPC_S_CALL_FAILED, EEInfoDLOSF_CCALL__ReceiveReply10, Status); return(RPC_S_CALL_FAILED); } if (Status == RPC_P_TIMEOUT) { RpcpErrorAddRecord(EEInfoGCRuntime, RPC_S_CALL_CANCELLED, EEInfoDLOSF_CCALL__ReceiveReply20, (ULONG)Status, (ULONG)Timeout); return RPC_S_CALL_CANCELLED; } return Status; } // Sanity check the packet. // We expect response packet or a request if we are InCallbackRequest (CallStack > 0). // If alloc_hint was previously specified and we set BytesRemaining the server // better stick to it. unsigned char PTYPE = ((rpcconn_request *)NewBuffer)->common.PTYPE; if (!((PTYPE == rpc_response || (PTYPE == rpc_request && CurrentState == InCallbackRequest && this->CallStack > 0)))) { CORRUPTION_ASSERT(0); Connection->TransFreeBuffer((char *) Message->Buffer-sizeof(rpcconn_request)); return RPC_S_PROTOCOL_ERROR; } Status = ActuallyProcessPDU( (rpcconn_common *) NewBuffer, NewBufferLength, &NewMessage); if (Status != RPC_S_OK) { Connection->TransFreeBuffer((char *) Message->Buffer-sizeof(rpcconn_request)); return Status; } if (BytesRemaining < NewMessage.BufferLength) { // // This code path is taken only in the OSF interop case // Message->Buffer = (char *) Message->Buffer - sizeof(rpcconn_request); Status = Connection->TransReallocBuffer( &Message->Buffer, BytesRead+sizeof(rpcconn_request), BytesRead +NewMessage.BufferLength +sizeof(rpcconn_request)); if (Status != RPC_S_OK) { Connection->TransFreeBuffer(Message->Buffer); return Status; } Message->Buffer = (char *) Message->Buffer + sizeof(rpcconn_request); BytesRemaining = NewMessage.BufferLength; } RpcpMemoryCopy((char *) Message->Buffer+BytesRead, NewMessage.Buffer, NewMessage.BufferLength); ActuallyFreeBuffer(NewBuffer); BytesRead += NewMessage.BufferLength; BytesRemaining -= NewMessage.BufferLength; } CORRUPTION_ASSERT(BytesRemaining == 0); Message->BufferLength = BytesRead; Message->RpcFlags = RPC_BUFFER_COMPLETE; return RPC_S_OK; } RPC_STATUS OSF_CCALL::DealWithCallback ( IN rpcconn_request *Request, IN PRPC_MESSAGE Message ) /*++ Function Name:DealWithCallback Parameters: Description: Returns: --*/ { RPC_STATUS Status, ExceptionCode; void *OriginalBuffer ; if (CallStack == 1) { BindingHandle->AddRecursiveEntry(this, (RPC_CLIENT_INTERFACE *) Message->RpcInterfaceInformation); } EnterCallback(); if (!COMPLETE(Message)) { ASSERT(Request); ASSERT(Request->common.PTYPE == rpc_request); // // Receive the complete callback request. // Status = ReceiveReply(Request, Message); if (Status != RPC_S_OK) { return Status; } } ASSERT(COMPLETE(Message)); CurrentState = SendingFirstBuffer; OriginalBuffer = Message->Buffer; // // Dispatch the callback // Status = DispatchCallback( DispatchTableCallback, Message, &ExceptionCode); ExitCallback(); ActuallyFreeBuffer((char *) OriginalBuffer-sizeof(rpcconn_request)); if ( Status != RPC_S_OK ) { VALIDATE(Status) { RPC_P_EXCEPTION_OCCURED, RPC_S_PROCNUM_OUT_OF_RANGE } END_VALIDATE; if (Status == RPC_S_PROCNUM_OUT_OF_RANGE) { SendFault(RPC_S_PROCNUM_OUT_OF_RANGE, 0); } else { SendFault(ExceptionCode, 0); Status = ExceptionCode; } RpcpPurgeEEInfo(); return Status; } CurrentState = InCallbackReply; CurrentOffset = 0; CurrentBuffer = Message->Buffer; CurrentBufferLength = Message->BufferLength; LastBuffer = Message->Buffer; Status = SendNextFragment(rpc_response); ASSERT(Connection->fExclusive); if (Connection->fExclusive) { if (Status != RPC_S_OK || (CurrentBufferLength == 0)) { goto Cleanup; } while (CurrentBufferLength) { Status = SendNextFragment(rpc_response, FALSE); if (Status != RPC_S_OK) { break; } } Cleanup: // Do not remove this call from the dictionary if it has already // been removed. If it has been removed, RecursiveCallsKey == -1. if (CallStack == 1 && RecursiveCallsKey != -1) { BindingHandle->RemoveRecursiveCall(this); } FreeBufferDo(Message->Buffer); } else { // // Callbacks not allowed in the async or in pipes // Status = RPC_S_CALL_FAILED; } return Status; } RPC_STATUS OSF_CCALL::FastSendReceive ( IN OUT PRPC_MESSAGE Message, OUT BOOL *fRetry ) /*++ Function Name:FastSendReceive Parameters: Description: Returns: --*/ { RPC_STATUS Status; UINT BufferLength; rpcconn_common *Request; DebugClientCallInfo *ClientCallInfo; DebugCallTargetInfo *CallTargetInfo; CellTag ClientCallInfoCellTag; CellTag CallTargetInfoCellTag; THREAD *ThisThread = RpcpGetThreadPointer(); BOOL fDebugInfoSet = FALSE; ULONG Timeout; unsigned char SavedPTYPE; CurrentOffset = 0; CurrentBuffer = Message->Buffer; CurrentBufferLength = Message->BufferLength; Message->RpcFlags = 0; ASSERT(ThisThread); // if either client side debugging is enabled or we are // calling on a thread that has a scall dispatched if (IsClientSideDebugInfoEnabled() || ((ThisThread->Context) && IsServerSideDebugInfoEnabled())) { CStackAnsi AnsiString; RPC_CHAR *Endpoint; RPC_CHAR *NetworkAddress; int EndpointLength; int NetworkAddressLength; if (!IsClientSideDebugInfoEnabled()) { Status = SetDebugClientCallInformation(&ClientCallInfo, &ClientCallInfoCellTag, &CallTargetInfo, &CallTargetInfoCellTag, Message, ThisThread->DebugCell, ThisThread->DebugCellTag); } else { Status = SetDebugClientCallInformation(&ClientCallInfo, &ClientCallInfoCellTag, &CallTargetInfo, &CallTargetInfoCellTag, Message, NULL, NULL); } if (Status != RPC_S_OK) return Status; ClientCallInfo->CallID = CallId; Endpoint = Connection->InqEndpoint(); NetworkAddress = Connection->InqNetworkAddress(); EndpointLength = RpcpStringLength(Endpoint) + 1; NetworkAddressLength = RpcpStringLength(NetworkAddress) + 1; *(AnsiString.GetPAnsiString()) = (char *)_alloca(max(EndpointLength, NetworkAddressLength)); Status = AnsiString.Attach(Endpoint, EndpointLength, EndpointLength * 2); // effectively ignore failure in the conversion if (Status == RPC_S_OK) { strncpy(ClientCallInfo->Endpoint, AnsiString, sizeof(ClientCallInfo->Endpoint)); } CallTargetInfo->ProtocolSequence = Connection->ClientInfo->TransId; Status = AnsiString.Attach(NetworkAddress, NetworkAddressLength, NetworkAddressLength * 2); if (Status == RPC_S_OK) { strncpy(CallTargetInfo->TargetServer, AnsiString, sizeof(CallTargetInfo->TargetServer)); } fDebugInfoSet = TRUE; } Status = SendNextFragment(rpc_request, TRUE, (void **) &Request, &BufferLength) ; if (Status != RPC_S_OK) { goto Cleanup; } *fRetry = FALSE; while (CurrentBufferLength) { Status = SendNextFragment(rpc_request, FALSE, (void **) &Request, &BufferLength); if (Status != RPC_S_OK) { goto Cleanup; } } // // We have sent the complete request. It is time to start // receiving the reply. The reply could either be a response // or a callback // CurrentState = WaitingForReply; RpcpPurgeEEInfo(); while (1) { // // This is the only place where we can receive a callback PDU // // Request will be freed if it were of rpc_alter_context_resp type. // We should not touch it after the call to ActuallyProcessPDU. SavedPTYPE = Request->PTYPE; Status = ActuallyProcessPDU( Request, BufferLength, Message); if (Status != RPC_S_OK) { goto Cleanup; } NO_CORRUPTION_VALIDATE(SavedPTYPE) { rpc_request, rpc_response, rpc_shutdown } NO_CORRUPTION_END_VALIDATE; switch (SavedPTYPE) { case rpc_request: // Check to make sure this procnum can actually receive callbacks and // a valid callback is specified. // On error - ignore the PDU. if (DispatchTableCallback && Message->ProcNum < DispatchTableCallback->DispatchTableCount && DispatchTableCallback->DispatchTable[Message->ProcNum] != NULL && CurrentState == InCallbackRequest) { Status = DealWithCallback( (rpcconn_request *) Request, Message); Message->RpcFlags = 0; } else { CORRUPTION_ASSERT(0); Status = RPC_S_PROTOCOL_ERROR; goto Cleanup; } break; case rpc_response: if (!COMPLETE(Message)) { Status = ReceiveReply( (rpcconn_request *) Request, Message); } goto Cleanup; default: // ignore the pdu break; } Timeout = GetBindingHandleTimeout(BindingHandle); Status = Connection->TransReceive( (PVOID *) &Request, &BufferLength, Timeout); if (Status != RPC_S_OK) { if ((Status == RPC_P_RECEIVE_FAILED ) || ( Status == RPC_P_CONNECTION_CLOSED ) ) { RpcpErrorAddRecord(EEInfoGCRuntime, RPC_S_CALL_FAILED_DNE, EEInfoDLOSF_CCALL__FastSendReceive10, Status); Status = RPC_S_CALL_FAILED; } else if (Status == RPC_P_CONNECTION_SHUTDOWN) { RpcpErrorAddRecord(EEInfoGCRuntime, RPC_S_CALL_FAILED_DNE, EEInfoDLOSF_CCALL__FastSendReceive20, Status); Status = RPC_S_CALL_FAILED_DNE; } else if (Status == RPC_P_TIMEOUT) { Status = RPC_S_CALL_CANCELLED; RpcpErrorAddRecord(EEInfoGCRuntime, Status, EEInfoDLOSF_CCALL__FastSendReceive30, RPC_P_TIMEOUT, Timeout); } goto Cleanup; } } Cleanup: if (fDebugInfoSet) { FreeCell(CallTargetInfo, &CallTargetInfoCellTag); FreeCell(ClientCallInfo, &ClientCallInfoCellTag); } return Status; } void OSF_CCALL::CallFailed ( IN RPC_STATUS Status ) /*++ Function Name: Parameters: Description: Returns: --*/ { ExtendedErrorInfo * pThreadEEInfo; BOOL fIssueNotification; CallMutex.Request(); // // Should not transition from Complete -> Aborted // if (CurrentState != Complete && CurrentState != Aborted) { // // Notify the client that the call is complete. When the stub calls // I_RpcReceive, we can cleanup the call and return a failure // status. // AsyncStatus = Status; // // For async pipes, this path races with OSF_CCALL::ProcessResponse. // If the notification has been issued there, then the user may do a pull. // We do not want to issue a notification iff there will be a thread // attempting a pull and cleaning up the call syncronously. // // If this is not an async pipe call, then we should only issue notification // iff there will be no more sends. // // Therefore the condition for issuing notification is: // (async pipe call -> there will not be a pull) && (!async pipe call -> fLastSendComplete) // fIssueNotification = GetIsAsyncPipeCallFlag() ? !IsAsyncPipeCallBeforePull() : fLastSendComplete; CurrentState = Aborted; // // If the last send is complete, then we need to issue the notification so // that I_RpcReceive is called. If the last send is not complete, we don't need // to issue the notification. // if (pAsync) { if (EEInfo) { FreeEEInfoChain(EEInfo); EEInfo = NULL; } pThreadEEInfo = RpcpGetEEInfo(); if (pThreadEEInfo) { EEInfo = pThreadEEInfo; RpcpClearEEInfo(); } if (fIssueNotification) { IssueNotification(); } } else { SyncEvent.Raise(); } } CallMutex.Clear(); } RPC_STATUS OSF_CCALL::CallCancelled ( OUT PDWORD Timeout ) /*++ Function Name:CallCancelled Parameters: Description: This function is called via the connection whenever the transport interface notices that it has received an alert. This function should only be used in conjuction with sync non pipe calls. Returns: RPC_S_OK: The call was cancelled others - if a failure occured. --*/ { RPC_STATUS Status; if (CurrentState == NeedOpenAndBind) { *Timeout = 0; return RPC_S_OK; } if (fCallCancelled == 0) { return RPC_S_NO_CALL_ACTIVE; } Status = SendCancelPDU(); // // Ignore the return status // *Timeout = (DWORD) ThreadGetRpcCancelTimeout(); return Status; } inline RPC_STATUS OSF_CCALL::SendReceiveHelper ( IN OUT PRPC_MESSAGE Message, OUT BOOL *fRetry ) /*++ Function Name:SendReceive Parameters: Description: Returns: --*/ { RPC_STATUS Status; THREAD *ThreadInfo; CallStack++; ASSERT(!PARTIAL(Message) && !ASYNC(Message)); ThreadInfo = RpcpGetThreadPointer(); ASSERT(ThreadInfo); if (CallStack == 1) { Status = ThreadInfo->RegisterForCancels(this); if (Status != RPC_S_OK) { return Status; } } if (ThreadInfo->CancelTimeout == RPC_C_CANCEL_INFINITE_TIMEOUT) { CancelState = CANCEL_INFINITE; } else { CancelState = CANCEL_NOTINFINITE; } LastBuffer = Message->Buffer; ASSERT (Connection->fExclusive); ASSERT(CurrentState == SendingFirstBuffer); Status = FastSendReceive(Message, fRetry); if (CallStack == 1) { ThreadInfo->UnregisterForCancels(); } CallStack--; if (Status == RPC_S_OK && CallStack == 0) { RPC_SECURITY_CALLBACK_FN *SecurityCallback = NULL; CurrentState = Complete; Status = BindingHandle->OSF_BINDING_HANDLE::InqTransportOption( RPC_C_OPT_SECURITY_CALLBACK, (ULONG_PTR *) &SecurityCallback); ASSERT(Status == RPC_S_OK); if (SecurityCallback) { (*SecurityCallback) (this); } } return Status; } RPC_STATUS OSF_CCALL::SendReceive ( IN OUT PRPC_MESSAGE Message ) /*++ Function Name:SendReceive Parameters: Description: Returns: --*/ { RPC_STATUS Status; void *TempBuffer ; OSF_BINDING_HANDLE *MyBindingHandle; void *OriginalBuffer; BOOL fRetry = TRUE; int RetryAttempts = 0; OSF_CCONNECTION *LocalConnection; AssocDictMutex->VerifyNotOwned(); AddVerificationTrailer(Message); MyBindingHandle = BindingHandle; // // WARNING: Do not use any members of OSF_CCALL beyond this point. // the object could have been deleted. // while (RetryAttempts <= 5) { OriginalBuffer = Message->Buffer; Status = ((OSF_CCALL *) Message->Handle)->SendReceiveHelper(Message, &fRetry); if (Status == RPC_S_OK || ((OSF_CCALL *) Message->Handle)->CallStack > 0) { ((OSF_CCALL *) Message->Handle)->FreeBufferDo(OriginalBuffer); break; } else { ASSERT(Status != RPC_S_SEND_INCOMPLETE); ASSERT(((OSF_CCALL *) Message->Handle)->CallStack == 0); if (Status == RPC_P_CONNECTION_SHUTDOWN) { Status = RPC_S_CALL_FAILED_DNE; } LocalConnection = ((OSF_CCALL *) Message->Handle)->Connection; if (fRetry == FALSE || (Status != RPC_S_CALL_FAILED_DNE) || (LocalConnection->ClientSecurityContext.AuthenticationLevel == RPC_C_AUTHN_LEVEL_PKT_PRIVACY)) { ((OSF_CCALL *) Message->Handle)->FreeBufferDo(OriginalBuffer); ((OSF_CCALL *) Message->Handle)->FreeCCall(Status); LogEvent(SU_CCALL, EV_DELETE, Message->Handle, 0, Status, 1, 1); break; } } if (!LocalConnection->GetFreshFromCacheFlag()) { // count this as a retry attempt only if the // connection was not from the cache RetryAttempts ++; } Status = AutoRetryCall(Message, TRUE, // this is the SendReceive path MyBindingHandle, Status, 0); if (Status != RPC_S_OK) break; if (RetryAttempts > 5) Status = RPC_S_CALL_FAILED_DNE; } return Status; } RPC_STATUS OSF_CCALL::ProcessRequestOrResponse ( IN rpcconn_request *Request, IN UINT PacketLength, IN BOOL fRequest, IN OUT PRPC_MESSAGE Message ) /*++ Function Name:ProcessRequestOrResponse Parameters: fRequest - If true, this is a request. Otherwise, it is a response Description: This function is called by ActuallyProcessPDU Returns: --*/ { RPC_STATUS Status; OSF_BINDING *LocalBinding; if ((Request->common.pfc_flags & PFC_OBJECT_UUID) != 0) { CORRUPTION_ASSERT(0); return RPC_S_PROTOCOL_ERROR; } if ((Request->common.pfc_flags & PFC_FIRST_FRAG)) { InReply = 1; ASSERT(BufferQueue.IsQueueEmpty()); Message->DataRepresentation = Connection->Association->SavedDrep; // // Transition to the next state // if (fRequest) { CurrentState = InCallbackRequest; } else { CurrentState = Receiving; } } else { if (CurrentState == WaitingForReply) { CORRUPTION_ASSERT(0); return RPC_S_PROTOCOL_ERROR; } } if ((Request->common.pfc_flags & PFC_LAST_FRAG) != 0) { Message->RpcFlags |= RPC_BUFFER_COMPLETE; } Status = EatAuthInfoFromPacket( Request, &PacketLength); if (Status != RPC_S_OK) { return Status; } // tell the connection the call came back successfully. If this was not a pipe call if (GetIsPipeCallFlag() == FALSE) { Connection->SuccessfulResponseReceived(); // if the binding is still not verified, count it as verified LocalBinding = GetSelectedBinding(); if (LocalBinding->GetPContextVerifiedFlag() == FALSE) LocalBinding->SetPContextVerifiedFlag(); } Message->BufferLength = PacketLength - sizeof(rpcconn_request); Message->Buffer = (void *) (Request + 1); return RPC_S_OK; } RPC_STATUS OSF_CCALL::ActuallyProcessPDU ( IN rpcconn_common *Packet, IN UINT PacketLength, IN OUT PRPC_MESSAGE Message, IN BOOL fAsync, OUT BOOL *pfSubmitReceive ) /*++ Function Name:ActuallyProcessPDU Parameters: Description: Packet is freed on failure or if it is of type rpc_alter_context_resp. The caller should not touch the packet in these cases after the return. Returns: --*/ { RPC_STATUS Status = RPC_S_OK; ULONG FaultStatus; BOOL AlterContextToNDR20IfNDR64Negotiated; // // If there is security save the rpc header // if (Connection->ClientSecurityContext.AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE ) { CallMutex.Request(); if (SavedHeader == 0) { SavedHeader = RpcpFarAllocate(sizeof(rpcconn_response)); if (SavedHeader == 0) { CallMutex.Clear(); Status = RPC_S_OUT_OF_MEMORY; goto Cleanup; } SavedHeaderSize = sizeof(rpcconn_response); } CallMutex.Clear(); RpcpMemoryCopy( SavedHeader, Packet, sizeof(rpcconn_response)); } Status = ValidatePacket(Packet, PacketLength); if (Status != RPC_S_OK) { goto Cleanup; } switch (Packet->PTYPE) { case rpc_response: Status = ProcessRequestOrResponse( (rpcconn_request *) Packet, PacketLength, 0, Message); if (Status != RPC_S_OK) { goto Cleanup; } if (fAsync) { Status = ProcessResponse((rpcconn_response *) Packet, Message, pfSubmitReceive); } break; case rpc_alter_context_resp: if (CurrentState != WaitingForAlterContext) { CORRUPTION_ASSERT(0); Status = RPC_S_PROTOCOL_ERROR; goto Cleanup; } // if we have chosen NDR20 in the call, we must warn the DealWithAlterContextResp // to alter context once more to NDR20 if NDR64 was chosen by the server if (Bindings.AvailableBindingsList && Bindings.SelectedBinding && (Bindings.SelectedBinding->CompareWithTransferSyntax(NDR20TransferSyntax) == 0)) { AlterContextToNDR20IfNDR64Negotiated = TRUE; } else { AlterContextToNDR20IfNDR64Negotiated = FALSE; } Status = Connection->DealWithAlterContextResp( this, Packet, PacketLength, &AlterContextToNDR20IfNDR64Negotiated); ActuallyFreeBuffer(Packet); if (Status != RPC_S_OK) { return Status; } // if we sent another alter context, return and wait for the response // N.B. We don't need to wait for the send because we're not // going to proceed with another send. When we get the response // and process it, we will wait for the send (when this condition // evaluates to FALSE). if (AlterContextToNDR20IfNDR64Negotiated) return RPC_S_OK; // // Wait for the send to complete // Connection->WaitForSend(); // // We sent the alter-context PDU when it was our turn, // now that we have received a response, we need to get // the ball rolling. // CurrentState = SendingFirstBuffer; ASSERT(Connection->IsIdle() == 0); CallMutex.Request(); if (CurrentBuffer) { CallMutex.Clear(); Status = SendNextFragment(); } else { // // We don't have a buffer to send from this call, we will force // the connection to idle and wait for the this call to give us // its buffer. The send function will notice that the connection is // idle, and send its first data buffer. // Connection->MakeConnectionIdle(); CallMutex.Clear(); ASSERT(Status == RPC_S_OK); } // The Packet has been freed above. return Status; case rpc_request: // // if we are going to reuse this function to handle // sync SendReceive, we need to keep track of this // and puke on the other cases (ie: when using Async // and when using pipes). // if (fAsync) { SendFault(RPC_S_CALL_FAILED, 0); Status = RPC_S_CALL_FAILED; goto Cleanup; } if ( Packet->call_id != CallId ) { CORRUPTION_ASSERT(0); Status = RPC_S_PROTOCOL_ERROR; goto Cleanup; } if (((rpcconn_request *) Packet)->p_cont_id != GetSelectedBinding()->GetOnTheWirePresentationContext() ) { SendFault(RPC_S_UNKNOWN_IF, 0); Status = RPC_S_UNKNOWN_IF; goto Cleanup; } Status = ProcessRequestOrResponse( (rpcconn_request *) Packet, PacketLength, 1, Message); if (Status != RPC_S_OK) { goto Cleanup; } Message->ProcNum = ((rpcconn_request *) Packet)->opnum; break; case rpc_fault: // // Flag the connection to protect a shutdown of the association. // This flag will be checked when we abort the connection to see // if its ok to abort the entire association. When receiving a // fault, we only want to close the connection, not the association // Connection->Flags.SetFlagUnsafe(NoAssociationShutdown); FaultStatus = ((rpcconn_fault *) Packet)->status; if ((FaultStatus == 0) && (Packet->frag_length >= FaultSizeWithoutEEInfo + 4)) { // // DCE 1.0.x style fault status: // Zero status and stub data contains the fault. // FaultStatus = *(ULONG *) ((unsigned char *)Packet + FaultSizeWithoutEEInfo); } if (DataConvertEndian(Packet->drep) != 0) { FaultStatus = RpcpByteSwapLong(FaultStatus); } CORRUPTION_ASSERT(FaultStatus != 0); Status = MapFromNcaStatusCode(FaultStatus); ASSERT(Status != RPC_S_OK); if (((rpcconn_fault *) Packet)->reserved & FaultEEInfoPresent) { size_t EEInfoSize = GetEEInfoSizeFromFaultPacket((rpcconn_fault *) Packet); // Sanity-check the EEInfo length if (EEInfoSize > Packet->frag_length - FaultSizeWithoutEEInfo) { CORRUPTION_ASSERT(0 && "Reported EEInfo size is larger then the packet"); } else { UnpickleEEInfoFromBuffer(((rpcconn_fault *) Packet)->buffer, EEInfoSize); } } // // In 3.5 we didnt Sign/Seal Faults. So .. Unsign/UnSeal doesnt // get called and hence Client side and Server side Seq# are // out of Sync.. So cheat .. // Connection->IncReceiveSequenceNumber(); if (fAsync) { if (Connection->Association->fMultiplex == mpx_no && fOkToAdvanceCall()) { // // In the multiplexed case, the call is advanced // when the send completes // Connection->AdvanceToNextCall(); } } break; case rpc_orphaned : case rpc_cancel : case rpc_shutdown : // // For the first release, we will just ignore these messages. // ASSERT(Status == RPC_S_OK); break; default: // We should never get here for trully invalid packets. In ValidatePacket // we will call MinPacketLength and notice that PTYPE is bogus failing then. // So someone must have sent us a packet with type comprehensible to server only. CORRUPTION_ASSERT(0 && "Invalid packet type"); Status = RPC_S_PROTOCOL_ERROR; break; } Cleanup: if (Status != RPC_S_OK) { ActuallyFreeBuffer(Packet); } return Status; } BOOL OSF_CCALL::ProcessReceivedPDU ( IN void *Buffer, IN int BufferLength ) /*++ Function Name:ProcessReceivedPDU Parameters: Description: Returns: --*/ { RPC_STATUS Status; RPC_MESSAGE Message; rpcconn_common * Packet = (rpcconn_common *) Buffer; BOOL fSubmitReceive = 1; Message.RpcFlags = 0; Status = ActuallyProcessPDU( Packet, BufferLength, &Message, 1, &fSubmitReceive); if (Status != RPC_S_OK) { CallFailed(Status); } return fSubmitReceive; } RPC_STATUS OSF_CCALL::UpdateBufferSize ( IN OUT void **Buffer, IN int CurrentBufferLength ) { RPC_MESSAGE Message; RPC_STATUS Status; Message.RpcFlags = 0; Message.Handle = this; Message.ProcNum = ProcNum; Message.BufferLength = CurrentBufferLength; Status = GetBufferWithoutCleanup(&Message, 0); if (Status != RPC_S_OK) { CallFailed(Status); return Status; } RpcpMemoryCopy(Message.Buffer, *Buffer, CurrentBufferLength); ActuallyFreeBuffer((char *) (*Buffer) - sizeof(rpcconn_request)); return RPC_S_OK; } RPC_STATUS OSF_CCALL::NegotiateTransferSyntaxAndGetBuffer ( IN OUT PRPC_MESSAGE Message, IN RPC_SYNTAX_IDENTIFIER *OldTransferSyntax, IN UUID *ObjectUuid ) { OSF_BINDING_HANDLE *BindingHandle; OSF_CCALL *CCall; RPC_STATUS Status; BOOL fInterfaceSupportsMultipleTransferSyntaxes; BindingHandle = (OSF_BINDING_HANDLE *)Message->Handle; ASSERT(BindingHandle->Type(OSF_BINDING_HANDLE_TYPE)); fInterfaceSupportsMultipleTransferSyntaxes = DoesInterfaceSupportMultipleTransferSyntaxes(Message->RpcInterfaceInformation); Status = BindingHandle->OSF_BINDING_HANDLE::NegotiateTransferSyntax(Message); if (Status != RPC_S_OK) return Status; CCall = (OSF_CCALL *)Message->Handle; if (fInterfaceSupportsMultipleTransferSyntaxes) { if (RpcpMemoryCompare(OldTransferSyntax, Message->TransferSyntax, sizeof(RPC_SYNTAX_IDENTIFIER)) != 0) { // the transfer syntax has changed - possible during auto-reconnect, especially in a // mixed cluster environment // // We cannot free the call, because an async bind may be // in progress. All we should do is remove our ref counts // The async bind path holds its own ref count, so // we don't need to worry about it // CCall->AsyncStatus = RPC_S_CALL_FAILED_DNE; // we need to remove only one reference - the second reference // is removed during a successful bind, and another reference // will be added when a successful send is made - we're not // there yet, so we have only one reference. CCall->OSF_CCALL::RemoveReference(); // When NDR starts supporting remarshalling, we should // return RPC_P_TRANSFER_SYNTAX_CHANGED return RPC_S_CALL_FAILED_DNE; } } Status = CCall->GetBuffer(Message, ObjectUuid); return Status; } RPC_STATUS OSF_CCALL::SendMoreData ( IN BUFFER Buffer ) /*++ Function Name:SendMoreData Parameters: Description: This function can only be called on a send completion Returns: --*/ { RPC_STATUS Status; void * SecurityTrailer; CallMutex.Request(); if (Buffer) { // // If we reach here, it means that this routine was called // as a result of a send complete // ASSERT(HeaderSize != 0); ASSERT(CurrentBuffer); ASSERT(CurrentBuffer != LastBuffer || CurrentBufferLength > MaxDataLength); CurrentOffset += MaxDataLength; CurrentBufferLength -= MaxDataLength; if (CurrentBufferLength == 0) { FreeBufferDo(CurrentBuffer); if (pAsync && (pAsync->Flags & RPC_C_NOTIFY_ON_SEND_COMPLETE)) { if (!IssueNotification(RpcSendComplete)) { CallMutex.Clear(); #if DBG PrintToDebugger("RPC: SendMoreData failed: %d\n", AsyncStatus); #endif return RPC_S_OUT_OF_MEMORY; } } // // We can be in SendingFirstBuffer, if the we had a very small pipe // VALIDATE(CurrentState) { SendingMoreData, SendingFirstBuffer } END_VALIDATE; CurrentOffset = 0; CurrentBuffer = BufferQueue.TakeOffQueue( (unsigned int *) &CurrentBufferLength); if (fChoked == 1 && pAsync == 0 && BufferQueue.Size() <=1) { fChoked = 0; SyncEvent.Raise(); } if (CurrentBuffer) { if ((AdditionalSpaceForSecurity < Connection->AdditionalSpaceForSecurity) && UpdateBufferSize(&CurrentBuffer, CurrentBufferLength) != RPC_S_OK) { CallMutex.Clear(); return RPC_S_OUT_OF_MEMORY; } } else { Connection->MakeConnectionIdle(); ASSERT(CurrentBufferLength == 0); CallMutex.Clear(); return RPC_S_OK; } } else { // // We need to restore the part of the buffer which we overwrote // with authentication information. // if (Connection->ClientSecurityContext.AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE) { VALIDATE(Connection->ClientSecurityContext.AuthenticationLevel) { RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_AUTHN_LEVEL_PKT, RPC_C_AUTHN_LEVEL_CONNECT } END_VALIDATE; ASSERT(HeaderSize != 0); SecurityTrailer = (char *) Buffer + MaximumFragmentLength - MaxSecuritySize; RpcpMemoryCopy(SecurityTrailer, ReservedForSecurity, MaxSecuritySize); } } } else { if (AdditionalSpaceForSecurity < Connection->AdditionalSpaceForSecurity) { if (UpdateBufferSize(&CurrentBuffer, CurrentBufferLength) != RPC_S_OK) { CallMutex.Clear(); return RPC_S_OUT_OF_MEMORY; } } } CallMutex.Clear(); BOOL fFirstSend = (CurrentState == SendingFirstBuffer) && (CurrentOffset == 0); // // When the last fragment is sent // the state changes to WaitingForReply // Status = SendNextFragment(rpc_request, fFirstSend); if (Status != RPC_S_OK) { VALIDATE(CurrentState) { InCallbackReply, SendingMoreData, SendingFirstBuffer, WaitingForReply, Aborted } END_VALIDATE; if (CurrentState == InCallbackReply) { AsyncStatus = Status; SendFault(Status, 0); Status = RPC_S_OK; } } return Status; } RPC_STATUS OSF_CCALL::SendData ( IN BUFFER Buffer ) { RPC_STATUS Status = RPC_S_OK; switch (CurrentState) { case NeedAlterContext: // // need to send an alter context on the call // Status = SendAlterContextPDU(); break; case WaitingForAlterContext: // // We are still waiting for alter-context to complete, // we don't have anything to do at this very point. // We will start sending data once we receive the // response to the alter-context. // break; case SendingMoreData: case InCallbackReply: case SendingFirstBuffer: // // the call is still sending the non pipe data // we need to finish sending this before // we can move on the the next call. // Status = SendMoreData(Buffer); break; case Aborted: // // some failure occured. the call is now in an // aborted state // Status = AsyncStatus; #if DBG PrintToDebugger("RPC: Call in aborted state\n"); #endif ASSERT(Status != RPC_S_OK); break; case Complete: // // the call is complete, the receive complete before the send could // complete, but we should have advanced to the next call when // sending the last fragment. We should never get into this state, // unless we are talking to a legacy server // ASSERT(Status == RPC_S_OK); ASSERT(Connection->Association->fMultiplex == mpx_no); break; case Receiving: // // We should never be in this state unless we are talking to a legacy // server // ASSERT(Connection->Association->fMultiplex == mpx_no); // intentional fall through case WaitingForReply: ASSERT(Status == RPC_S_OK); break; case InCallbackRequest: default: // // we should never be in these states. #if DBG PrintToDebugger("RPC: Bad call state: %d\n", CurrentState); #endif ASSERT(0); Status = RPC_S_INTERNAL_ERROR; break; } return Status; } void OSF_CCALL::ProcessSendComplete ( IN RPC_STATUS EventStatus, IN BUFFER Buffer ) /*++ Function Name:ProcessSendComplete Parameters: Description: Returns: --*/ { if (EventStatus == RPC_S_OK) { EventStatus = SendData(Buffer); } if (EventStatus != RPC_S_OK) { if (EventStatus == RPC_P_CONNECTION_CLOSED || EventStatus == RPC_P_CONNECTION_SHUTDOWN || EventStatus == RPC_P_SEND_FAILED) { EventStatus = RPC_S_CALL_FAILED; } Connection->ConnectionAborted(EventStatus); // // Remove the send reference on the call, CCALL-- // RemoveReference(); } } RPC_STATUS OSF_CCALL::SendNextFragment ( IN unsigned char PacketType, IN BOOL fFirstSend, OUT void **ReceiveBuffer, OUT UINT *ReceivedLength ) /*++ Function Name:SendNextFragment Parameters: Description: Returns: --*/ { int PacketLength; RPC_STATUS Status; BOOL LastFragmentFlag; rpcconn_common *pFragment; void *SendContext = CallSendContext; int MyBufferLength; int MyHeaderSize = HeaderSize; ULONG Timeout; ASSERT(HeaderSize != 0); ASSERT(MaxDataLength); ASSERT(CurrentBuffer); if (UuidSpecified && (CallStack > 1 || PacketType != rpc_request)) { MyHeaderSize -= sizeof(UUID); } // // Prepare the fragment // if (CurrentBuffer == LastBuffer && CurrentBufferLength <= MaxDataLength) { PacketLength = CurrentBufferLength + MyHeaderSize + MaxSecuritySize; LastFragmentFlag = 1; if (CurrentState != InCallbackReply) { ASSERT((CurrentState == SendingFirstBuffer) || (CurrentState == SendingMoreData) || (CurrentState == Aborted)); CurrentState = WaitingForReply; if (Connection->fExclusive == 0) { // // This async send will complete on the connection // and the connection will free the buffer // SendContext = Connection->u.ConnSendContext; Connection->BufferToFree = ActualBuffer(CurrentBuffer); } } } else { PacketLength = MaximumFragmentLength; LastFragmentFlag = 0; if (CurrentBufferLength == MaxDataLength && CurrentState == SendingFirstBuffer) { CurrentState = SendingMoreData; } } pFragment = (rpcconn_common *) ((char *) CurrentBuffer + CurrentOffset - MyHeaderSize); ConstructPacket(pFragment, PacketType, PacketLength); if (fFirstSend) { pFragment->pfc_flags |= PFC_FIRST_FRAG; } if ( PacketType == rpc_request ) { if (UuidSpecified && (pAsync || CallStack == 1)) { pFragment->pfc_flags |= PFC_OBJECT_UUID; RpcpMemoryCopy(((unsigned char *) pFragment) + sizeof(rpcconn_request), &ObjectUuid, sizeof(UUID)); } if (fFirstSend && AllocHint) ((rpcconn_request *) pFragment)->alloc_hint = AllocHint; else ((rpcconn_request *) pFragment)->alloc_hint = CurrentBufferLength; ((rpcconn_request *) pFragment)->p_cont_id = GetSelectedBinding()->GetOnTheWirePresentationContext(); ((rpcconn_request *) pFragment)->opnum = (unsigned short) ProcNum; } else { // we always use CurrentBufferLength for the alloc hint in the non-request // case because the non-request case is for callbacks only, and they // don't use pipes. In the non-pipe case CurrentBufferLength is accurate // as the alloc hint. ((rpcconn_response *) pFragment)->alloc_hint = CurrentBufferLength; ((rpcconn_response *) pFragment)->p_cont_id = GetSelectedBinding()->GetOnTheWirePresentationContext(); ((rpcconn_response *) pFragment)->alert_count = 0; ((rpcconn_response *) pFragment)->reserved = 0; } pFragment->call_id = CallId; MyBufferLength = CurrentBufferLength; if (Connection->fExclusive) { Timeout = GetBindingHandleTimeout(BindingHandle); if (LastFragmentFlag == 0) { CurrentOffset += MaxDataLength; CurrentBufferLength -= MaxDataLength; if (UuidSpecified && (CallStack > 1 || PacketType != rpc_request)) { CurrentOffset += sizeof(UUID); CurrentBufferLength -= sizeof(UUID); } ASSERT(((long)CurrentBufferLength) >= 0); } else { CurrentBufferLength = 0; } } else Timeout = INFINITE; if (ReceiveBuffer) { *ReceiveBuffer = NULL; } #ifdef _BUILD_WITH_DELAYS_ // Useful for catching race conditions where buffer being sent gets freed // in async paths. Sleep(100); #endif Status = Connection->SendFragment ( pFragment, this, LastFragmentFlag, MyHeaderSize, MaxSecuritySize, MyBufferLength, MaximumFragmentLength, ReservedForSecurity, !(Connection->fExclusive), SendContext, Timeout, ReceiveBuffer, ReceivedLength); if (ReceiveBuffer && *ReceiveBuffer) { CurrentBufferLength = 0; } return Status; } RPC_STATUS OSF_CCALL::ProcessResponse ( IN rpcconn_response *Packet, IN PRPC_MESSAGE Message, OUT BOOL *pfSubmitReceive ) /*++ Function Name:ProcessResponse Parameters: Description: Process the response data. The first buffer is placed on the buffer queue only after alloc_hint bytes have been received. Returns: --*/ { RPC_STATUS Status; // // We don't need to look at alloc_hint for the response PDUs // we can simply queue up the buffers. When we get the last one, // we'll coalesce them for for the non pipe case. For the pipe case, // we will progressively give the buffers to the stub. // CallMutex.Request(); if (QueueBuffer(Message->Buffer, Message->BufferLength)) { CallFailed(RPC_S_OUT_OF_MEMORY); CallMutex.Clear(); return RPC_S_OUT_OF_MEMORY; } if (COMPLETE(Message)) { CurrentState = Complete; // If a receive-complete notification has been armed and // there is a buffer that has not yet been given to NDR, // we need to issue a notification before // completing the call. // // We do not want to issue the notification if the received buffer // has zero length since there is nothing to give NDR. if (NeededLength > 0 && RcvBufferLength > 0) { // There is no need to disarm the notification since there // will be no more pipe buffers received. IssueNotification(RpcReceiveComplete); } AsyncStatus = RPC_S_OK; CallMutex.Clear(); if (Connection->Association->fMultiplex == mpx_no && fOkToAdvanceCall()) { // // In the multiplexed case, the call is advanced // when the send completes // Connection->AdvanceToNextCall(); } // For async out-pipe we need to wait for the user to retrieve the data // before comleting the call. // When the pipe is drained, NdrCheckAsyncPipeStatus will return RPC_X_INVALID_PIPE_OBJECT. // // For async in-pipe we need to wait issuing the call-complete notification untill we know // that the client has processed the send-complete notification for the last // non-null push. When the client has done so, it will do a null push and the runtime // will do the last send. if (GetIsAsyncPipeCallFlag()) { long PipeStatus; while (TRUE) { Status = NdrCheckAsyncPipeStatus((char *)pAsync, &PipeStatus); // Issue notification when the last send is done and the pipe has been drained. // We know that NdrCheckAsyncPipeStatus has been called in the past and returned // successfully since IsAsyncPipeCallFlag is set, therefore RPC_X_INVALID_PIPE_OBJECT // should be returned iff NDR is done with pipes. if (fLastSendComplete && Status == RPC_X_INVALID_PIPE_OBJECT) break; PauseExecution(20); } } IssueNotification(); } else { if (pAsync == 0) { if (BufferQueue.Size() >= 4 && pfSubmitReceive) { fPeerChoked = 1; *pfSubmitReceive = 0; } CallMutex.Clear(); SyncEvent.Raise(); } else { // This path races with the issuing of notification in CallFailed. // There, the notification will be issued iff fLastSendComplete. // Therefore we should issue notification here iff the call has not been // aborted or !fLastSendComplete. This way we avoid issuing two notifications. if (CurrentState != Aborted || !fLastSendComplete) { // Issue receive-complete notification if we have enough data // to satisfy the NeededLength requirement. if (NeededLength > 0 && RcvBufferLength >= NeededLength) { // Disarm the notificaiton. NeededLength = 0; IssueNotification(RpcReceiveComplete); } else { if (GetIsAsyncPipeCallFlag() && BufferQueue.Size() >= 4 && pfSubmitReceive) { fPeerChoked = 1; *pfSubmitReceive = 0; } } } CallMutex.Clear(); } } return RPC_S_OK; } RPC_STATUS OSF_CCALL::SendAlterContextPDU ( ) /*++ Function Name:SendAlterContextPDU Parameters: Description: Returns: --*/ { RPC_STATUS Status; ULONG Timeout; BOOL fBindingHandleTimeoutUsed; // // We try to create a thread to go down and listen // Status = BindingHandle->TransInfo->CreateThread(); VALIDATE(Status) { RPC_S_OK, RPC_S_OUT_OF_MEMORY, RPC_S_OUT_OF_THREADS } END_VALIDATE; CurrentState = WaitingForAlterContext; ASSERT(Connection->Association->AssocGroupId); Timeout = GetEffectiveTimeoutForBind( BindingHandle, &fBindingHandleTimeoutUsed); // // Send the alter-context PDU // Status = Connection->SendBindPacket( BindingHandle, FALSE, this, Connection->Association->AssocGroupId, rpc_alter_context, Timeout ); if (Status != RPC_S_OK) { Status = GetStatusForTimeout(BindingHandle, Status, fBindingHandleTimeoutUsed); CallFailed(Status); #if DBG PrintToDebugger("RPC: SendAlterContextPDU failed: %d\n", Status); #endif } return Status; } RPC_STATUS OSF_CCALL::EatAuthInfoFromPacket ( IN rpcconn_request * Request, IN OUT UINT * RequestLength ) /*++ Routine Description: If there is authentication information in the packet, this routine will check it, and perform security as necessary. This may include calls to the security support package. Arguments: Request - Supplies the packet which may contain authentication information. RequestLength - Supplies the length of the packet in bytes, and returns the length of the packet without authentication information. Return Value: RPC_S_OK - Everything went just fine. RPC_S_ACCESS_DENIED - A security failure of some sort occured. RPC_S_PROTOCOL_ERROR - This will occur if no authentication information is in the packet, and some was expected, or visa versa. --*/ { RPC_STATUS Status; sec_trailer * SecurityTrailer; SECURITY_BUFFER SecurityBuffers[5]; DCE_MSG_SECURITY_INFO MsgSecurityInfo; SECURITY_BUFFER_DESCRIPTOR BufferDescriptor; ULONG ReadOnlyFlag; if ( Request->common.auth_length != 0 ) { SecurityTrailer = (sec_trailer *) (((unsigned char *) Request) + Request->common.frag_length - Request->common.auth_length - sizeof(sec_trailer)); if (!IsBufferAlignedOnStructBoundary(SecurityTrailer)) { CORRUPTION_ASSERT(0 && "SecurityTrailer is unaligned"); return(RPC_S_PROTOCOL_ERROR); } CORRUPTION_ASSERT(SecurityTrailer->auth_context_id == SecurityContextIdWireRep); if ((Connection->ClientSecurityContext.AuthenticationLevel == RPC_C_AUTHN_LEVEL_NONE)) { return(RPC_S_PROTOCOL_ERROR); } if (Connection->GetConnectionSupportHeaderSign() == cshsYes) ReadOnlyFlag = SECBUFFER_READONLY_WITH_CHECKSUM; else ReadOnlyFlag = SECBUFFER_READONLY; *RequestLength -= Request->common.auth_length; MsgSecurityInfo.SendSequenceNumber = Connection->InquireSendSequenceNumber(); MsgSecurityInfo.ReceiveSequenceNumber = Connection->InquireReceiveSequenceNumber(); MsgSecurityInfo.PacketType = Request->common.PTYPE; BufferDescriptor.ulVersion = 0; BufferDescriptor.cBuffers = 5; BufferDescriptor.pBuffers = SecurityBuffers; SecurityBuffers[0].cbBuffer = sizeof(rpcconn_request); SecurityBuffers[0].BufferType = SECBUFFER_DATA | ReadOnlyFlag; SecurityBuffers[0].pvBuffer = ((unsigned char *) SavedHeader); // Make sure the data buffer length will not become negative. ASSERT(*RequestLength >= sizeof(rpcconn_request) + sizeof (sec_trailer)); SecurityBuffers[1].cbBuffer = *RequestLength - sizeof(rpcconn_request) - sizeof (sec_trailer); SecurityBuffers[1].BufferType = SECBUFFER_DATA; SecurityBuffers[1].pvBuffer = ((unsigned char *) Request) + sizeof(rpcconn_request); SecurityBuffers[2].cbBuffer = sizeof(sec_trailer); SecurityBuffers[2].BufferType = SECBUFFER_DATA | ReadOnlyFlag; SecurityBuffers[2].pvBuffer = SecurityTrailer; SecurityBuffers[3].cbBuffer = Request->common.auth_length; SecurityBuffers[3].BufferType = SECBUFFER_TOKEN; SecurityBuffers[3].pvBuffer = SecurityTrailer + 1; SecurityBuffers[4].cbBuffer = sizeof(DCE_MSG_SECURITY_INFO); SecurityBuffers[4].BufferType = (SECBUFFER_PKG_PARAMS | SECBUFFER_READONLY); SecurityBuffers[4].pvBuffer = &MsgSecurityInfo; Status = Connection->ClientSecurityContext.VerifyOrUnseal( MsgSecurityInfo.ReceiveSequenceNumber, Connection->ClientSecurityContext.AuthenticationLevel != RPC_C_AUTHN_LEVEL_PKT_PRIVACY, &BufferDescriptor); if ( Status != RPC_S_OK ) { ASSERT( (Status == RPC_S_ACCESS_DENIED) || (Status == ERROR_PASSWORD_MUST_CHANGE) || (Status == ERROR_PASSWORD_EXPIRED) || (Status == ERROR_ACCOUNT_DISABLED) || (Status == ERROR_INVALID_LOGON_HOURS) || (Status == RPC_S_OUT_OF_MEMORY)); return(Status); } *RequestLength -= (sizeof(sec_trailer) + SecurityTrailer->auth_pad_length); } else { if ((Connection->ClientSecurityContext.AuthenticationLevel == RPC_C_AUTHN_LEVEL_PKT_INTEGRITY) || (Connection->ClientSecurityContext.AuthenticationLevel == RPC_C_AUTHN_LEVEL_PKT_PRIVACY)) { return(RPC_S_PROTOCOL_ERROR); } } Connection->IncReceiveSequenceNumber(); return(RPC_S_OK); } void OSF_CCALL::SendFault ( IN RPC_STATUS Status, IN int DidNotExecute ) { rpcconn_fault Fault; memset(&Fault, 0, sizeof(Fault)); ConstructPacket((rpcconn_common *) &Fault,rpc_fault, sizeof(rpcconn_fault)); if (DidNotExecute) Fault.common.pfc_flags |= PFC_DID_NOT_EXECUTE; Fault.common.pfc_flags |= PFC_FIRST_FRAG | PFC_LAST_FRAG; Fault.p_cont_id = GetSelectedBinding()->GetOnTheWirePresentationContext(); Fault.status = MapToNcaStatusCode(Status); Fault.common.call_id = CallId; Connection->TransSend(BindingHandle, &Fault, sizeof(rpcconn_fault), TRUE, // fDisableShutdownCheck TRUE, // fDisableCancelCheck INFINITE ); } RPC_STATUS OSF_CCALL::SendCancelPDU( ) { rpcconn_common CancelPDU; RPC_STATUS Status; ULONG Timeout; ConstructPacket( (rpcconn_common *) &CancelPDU, rpc_cancel, sizeof(rpcconn_common)); CancelPDU.call_id = CallId; CancelPDU.pfc_flags = PFC_LAST_FRAG | PFC_PENDING_CANCEL; Timeout = GetBindingHandleTimeout(BindingHandle); Status = Connection->TransSend(BindingHandle, &CancelPDU, sizeof(rpcconn_common), TRUE, // fDisableShutdownCheck TRUE, // fDisableCancelCheck Timeout ); if (Status == RPC_P_TIMEOUT) { Status = RPC_S_CALL_CANCELLED; } else { ASSERT(Status != RPC_S_CALL_CANCELLED); } return Status; } RPC_STATUS OSF_CCALL::SendOrphanPDU ( ) { rpcconn_common Orphan; RPC_STATUS Status; ULONG Timeout; ConstructPacket( (rpcconn_common *) &Orphan, rpc_orphaned, sizeof(rpcconn_common)); Orphan.call_id = CallId; Orphan.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG; Timeout = GetBindingHandleTimeout(BindingHandle); Status = Connection->TransSend(BindingHandle, &Orphan, sizeof(rpcconn_common), TRUE, // fDisableShutdownCheck TRUE, // fDisableCancelCheck Timeout ); return Status; } RPC_STATUS OSF_CCALL::NegotiateTransferSyntax ( IN OUT PRPC_MESSAGE Message ) { // this can only happen for callbacks ASSERT(IsCallInCallback()); // just return the transfer syntax already negotiated in the binding Message->TransferSyntax = GetSelectedBinding()->GetTransferSyntaxId(); return RPC_S_OK; } RPC_STATUS OSF_CCALL::GetBuffer ( IN OUT PRPC_MESSAGE Message, IN UUID *ObjectUuid ) /*++ Routine Description: Arguments: Message - Supplies a description containing the length of buffer to be allocated, and returns the allocated buffer. ObjectUuid - this parameter is ignored Return Value: RPC_S_OK - A buffer of the requested size has successfully been allocated. RPC_S_OUT_OF_MEMORY - Insufficient memory is available. --*/ { RPC_STATUS Status; AssocDictMutex->VerifyNotOwned(); UpdateObjectUUIDInfo(ObjectUuid); Status = GetBufferWithoutCleanup(Message, ObjectUuid); // do the cleanup to get regular GetBuffer semantics if (Status != RPC_S_OK) { // // We cannot free the call, because an async bind may be // in progress. All we should do is remove our ref counts // The async bind path holds its own ref count, so // we don't need to worry about it // AsyncStatus = RPC_S_CALL_FAILED_DNE; if (Connection->fExclusive == 0) { // async calls have one more reference OSF_CCALL::RemoveReference(); } // We should wait cleaning up the call if we are in a callback. // If we clean it up now, then the object will be gone on the return to // DispatchCallback and we will AV. if (!IsCallInCallback()) { OSF_CCALL::RemoveReference(); } } return(Status); } RPC_STATUS OSF_CCALL::GetBufferWithoutCleanup ( IN OUT PRPC_MESSAGE Message, IN UUID *ObjectUuid ) { RPC_STATUS Status; ULONG MaxFrag; ULONG NewLength; MaxFrag = Connection->MaxFrag; ProcNum = Message->ProcNum; // // In addition to saving space for the request (or response) header // and an object UUID, we want to save space for security information // if necessary. // if ((Message->RpcFlags & RPC_BUFFER_PARTIAL) && (Message->BufferLength < MaxFrag)) { ActualBufferLength = MaxFrag; } else { ActualBufferLength = Message->BufferLength; } NewLength = ActualBufferLength + sizeof(rpcconn_request) + sizeof(UUID) + (2 * Connection->AdditionalSpaceForSecurity) + SizeVerificationTrailer(); Status = ActuallyAllocateBuffer(&Message->Buffer, NewLength); if ( Status != RPC_S_OK ) { ASSERT( Status == RPC_S_OUT_OF_MEMORY ); return Status; } ASSERT(HeaderSize != 0); if (UuidSpecified) { Message->Buffer = (char *) Message->Buffer + sizeof(rpcconn_request) + sizeof(UUID); } else { Message->Buffer = (char *) Message->Buffer + sizeof(rpcconn_request); } return RPC_S_OK; } RPC_STATUS OSF_CCALL::GetBufferDo ( IN UINT culRequiredLength, OUT void * * ppBuffer, IN int fDataValid, IN int DataLength ) /*++ Function Name:GetBufferDo Parameters: Description: Returns: --*/ { RPC_STATUS Status; void *NewBuffer; Status = ActuallyAllocateBuffer(&NewBuffer, culRequiredLength + sizeof(rpcconn_request) + sizeof(UUID)); if (Status) return(RPC_S_OUT_OF_MEMORY); ASSERT(HeaderSize != 0); if (UuidSpecified) { NewBuffer = (((unsigned char *) NewBuffer) + sizeof(rpcconn_request)) + sizeof(UUID); } else { NewBuffer = (((unsigned char *) NewBuffer) + sizeof(rpcconn_request)); } if (fDataValid) { RpcpMemoryCopy(NewBuffer, *ppBuffer, DataLength); ActuallyFreeBuffer(*ppBuffer); } *ppBuffer = NewBuffer; return(RPC_S_OK); } void OSF_CCALL::FreeBufferDo ( IN void *Buffer ) /*++ Function Name:FreeBufferDo Parameters: Description: Returns: --*/ { ASSERT(HeaderSize != 0); if (UuidSpecified) { Buffer = (char *) Buffer - sizeof(rpcconn_request) - sizeof(UUID); } else { Buffer = (char *) Buffer - sizeof(rpcconn_request); } ActuallyFreeBuffer((char *)Buffer); } void OSF_CCALL::FreeBuffer ( IN PRPC_MESSAGE Message ) /*++ Function Name:FreeBuffer Parameters: Description: Returns: --*/ { if (CallStack == 0) { if (Message->Buffer != NULL) { if (CurrentState == Complete) { ActuallyFreeBuffer((char *)Message->Buffer-sizeof(rpcconn_response)); } else { FreeBufferDo(Message->Buffer); } } if (Connection->fExclusive) { FreeCCall(RPC_S_OK); } else { UnregisterCallForCancels(); // Remove the call reference CCALL-- RemoveReference(); } } else { if (Message->Buffer != NULL) { ActuallyFreeBuffer((char *)Message->Buffer-sizeof(rpcconn_response)); CurrentBufferLength = 0; } else { // Wipe out stale EEInfo that may be sitting in TLS. // FreeBuffer may be called as a result of RpcAsyncCompleteCall. // In this case it is possible that the call is made on a thread that // has never had the RPC TLS inited. if (RpcpGetThreadPointer() != NULL) { RpcpPurgeEEInfo (); } FreeCCall(RPC_S_OK); } } } void OSF_CCALL::FreePipeBuffer ( IN PRPC_MESSAGE Message ) /*++ Function Name:FreePipeBuffer Parameters: Description: Returns: --*/ { ASSERT(HeaderSize != 0); if (UuidSpecified) { Message->Buffer = (char *) Message->Buffer - sizeof(rpcconn_request) - sizeof(UUID); } else { Message->Buffer = (char *) Message->Buffer - sizeof(rpcconn_request); } ActuallyFreeBuffer((char *)Message->Buffer); } RPC_STATUS OSF_CCALL::ReallocPipeBuffer ( IN PRPC_MESSAGE Message, IN UINT NewSize ) /*++ Function Name:ReallocPipeBuffer Parameters: Description: Returns: --*/ { void *TempBuffer; RPC_STATUS Status; ULONG SizeToAlloc; ULONG MaxFrag = Connection->MaxFrag; if (NewSize > ActualBufferLength) { SizeToAlloc = (NewSize > MaxFrag) ? NewSize:MaxFrag; Status = ActuallyAllocateBuffer(&TempBuffer, SizeToAlloc + sizeof(rpcconn_request) + sizeof(UUID) + (2 * Connection->AdditionalSpaceForSecurity)); if ( Status != RPC_S_OK ) { ASSERT( Status == RPC_S_OUT_OF_MEMORY ); return(RPC_S_OUT_OF_MEMORY); } ASSERT(HeaderSize != 0); // // N.B. Potentially, if we could return ActualBufferLength // in NewSize, the stubs can take advantage of that and gain // perf. // if (UuidSpecified) { TempBuffer = (char *) TempBuffer + sizeof(rpcconn_request) + sizeof(UUID); } else { TempBuffer = (char *) TempBuffer + sizeof(rpcconn_request); } if (Message->BufferLength > 0) { RpcpMemoryCopy(TempBuffer, Message->Buffer, Message->BufferLength); FreePipeBuffer(Message); } Message->Buffer = TempBuffer; ActualBufferLength = SizeToAlloc; } Message->BufferLength = NewSize; return (RPC_S_OK); } void OSF_CCALL::FreeCCall ( IN RPC_STATUS Status ) /*++ Routine Description: This routine is used to free a call. --*/ { void *Buffer; UINT BufferLength; OSF_BINDING_HANDLE *MyBindingHandle; ULONG Timeout; ASSERT(BindingHandle != 0); // Wait for the IO completion processing if it is being done asyncronously. Connection->ConnMutex.Request(); if (Connection->fExclusive == 0) { // It is possible that we have already removed this call from // the dictionary if we performed an abortive cancel. Since // removal is Idempotent (assuming the CallId doesn't wrap too quickly) // we are fine. Connection->ActiveCalls.Delete(IntToPtr(CallId)); } Connection->ConnMutex.Clear(); // At this point no IO completion processing on the call can start because // the call is gone from the dict and all active processing has completed. LogEvent(SU_CCALL, EV_DELETE, this, NULL, Status, 1, 1); // // Empty the buffer queue and nuke the buffers // while (Buffer = BufferQueue.TakeOffQueue(&BufferLength)) { if (InReply) { ActuallyFreeBuffer((char *) Buffer-sizeof(rpcconn_request)); } else { FreeBufferDo(Buffer); } } // Check whether OSF_CCONNECTION::ConnectionAborted requested the freeing // of LastBuffer. if (GetFreeLastBufferFlag()) { ActuallyFreeBuffer(ActualBuffer(LastBuffer)); LastBuffer = NULL; } if (Bindings.AvailableBindingsList) ReleaseBindingList(Bindings.AvailableBindingsList); else GetSelectedBinding()->RemoveReference(); if (RecursiveCallsKey != -1) { BindingHandle->RemoveRecursiveCall(this); } // // We will not send an Orphan PDU if the call was cancelled // This is because, we are going to close the connection anyway // When a connection close is received while a call is in progress, // it is treated as an orphan // MyBindingHandle = BindingHandle; BindingHandle = 0; // // N.B. If this call failed with a fatal error, we will nuke the connection // and all calls after it. // // // There is a race condition between RpcAsyncCompleteCall and // OSF_CCONNECTION::ProcessSendComplete. It is possible that: // // T1: Picks a send-complete notification to process for call C // T2: Hits a failure on some connection, // T2: shuts down the association, // T2: shuts down C's connection, failing C and issuing call-complete for C. // T3: pick up the notification and RpcAsyncCompletes C, freeing it. // // In MaybeAdvanceToNextCall we will grab the ConnMutex. We will also need to // grab it while processing the notification. // // // If its been this long and we are still the current call, // we need to advance the call // Connection->MaybeAdvanceToNextCall(this); // // release this CCall to the connection // if (MyBindingHandle) Timeout = MyBindingHandle->InqComTimeout(); else Timeout = 0; Connection->FreeCCall(this, Status, Timeout); // // The ref count on the binding handle // needs to be decremented if the binding handle is still there // if (MyBindingHandle) MyBindingHandle->BindingFree(); } #if 1 RPC_STATUS OSF_CCALL::Cancel( void * ThreadHandle ) { fCallCancelled = TRUE; return RPC_S_OK; } #else RPC_STATUS OSF_CCALL::Cancel( void * Tid ) { RPC_STATUS Status; Cancelled = TRUE; Status = I_RpcIOAlerted((OSF_CCONNECTION *)this,(DWORD)Tid); return RPC_S_OK; } #endif RPC_STATUS OSF_CCALL::CancelAsyncCall ( IN BOOL fAbort ) /*++ Function Name:CancelAsyncCall Parameters: fAbort - TRUE: the cancel is abortive, ie, the call completes immediately FALSE: a cancel PDU is sent to the server, the call doesn't complete until the server returns Description: Returns: RPC_S_OK: The call was successfully cancelled others - an error occured during the cancellation process --*/ { RPC_STATUS Status; // The EEInfo that may be sitting on this thread could have // nothing to do with the the async call that we are about to cancel. RpcpPurgeEEInfo(); switch (CurrentState) { case NeedOpenAndBind: case NeedAlterContext: case WaitingForAlterContext: // // The call has not yet started // fail the call right now // CallFailed(RPC_S_CALL_CANCELLED); break; case Aborted: case Complete: // // The call has either failed or has completed // we don't need to do anything // break; default: // // The call is in progress, we need to cancel it. // if (fAbort) { SendOrphanPDU(); Connection->ConnMutex.Request(); // The call is cancled, we will be issuing call complete notification // but first we remove this call from the dictionary. If we // delay this removal till FreeCCalls, then we will break the rule // that all calls in the ActiveCalls dictionary must have reference counts. // Since the call is cancled, we do not need to look it up any more // (for instance in ProcessReceiveComplete Connection->ActiveCalls.Delete(IntToPtr(CallId)); Connection->ConnMutex.Clear(); CallFailed(RPC_S_CALL_CANCELLED); } else { return SendCancelPDU(); } } return RPC_S_OK; } RPC_STATUS OSF_CCALL::BindCompleteNotify ( IN p_result_t *OsfResult, IN int IndexOfPresentationContextAccepted, OUT OSF_BINDING **BindingNegotiated ) /*++ Function Name:BindCompleteNotify Parameters: OsfResult - The one and only result element that contained acceptance IndexOfPresentationContextAccepted - the index of the accepted presentation context. Recall that the server indicates acceptance by position. BindingNegotiated - on success for multiple bindings proposed, the pointer to the OSF binding that the server chose. On failure, or if only one binding is proposed, it is undefined. Description: Examines the accepted context, does a bunch of validity checks, and if necessary, fixes the binding which the call will use. If the binding is already fixed, it won't touch it. Returns: RPC_S_OK: The acceptance is valid, and the call binding was fixed others: error code --*/ { int CurrentBindingIndex; if (Bindings.AvailableBindingsList == NULL) { // only one binding was proposed - it better be accepted if (GetSelectedBinding()->CompareWithTransferSyntax(&OsfResult->transfer_syntax) != 0) { return RPC_S_PROTOCOL_ERROR; } if (IndexOfPresentationContextAccepted > 0) return RPC_S_PROTOCOL_ERROR; } else { OSF_BINDING *CurrentBinding = GetBindingList(); OSF_BINDING *BindingToUse; // multiple bindings were proposed - lookup the binding that // the server chose, fix our binding and record the server // preferences BindingToUse = 0; CurrentBindingIndex = 0; do { if (CurrentBinding->CompareWithTransferSyntax(&OsfResult->transfer_syntax) == 0) { BindingToUse = CurrentBinding; break; } CurrentBinding = CurrentBinding->GetNextBinding(); CurrentBindingIndex ++; } while (CurrentBinding != 0); if (BindingToUse == 0) { CORRUPTION_ASSERT(0); // if the transfer syntax approved is none of the transfer syntaxes we suggested // this is a protocol error return RPC_S_PROTOCOL_ERROR; } if (CurrentBindingIndex != IndexOfPresentationContextAccepted) { CORRUPTION_ASSERT(0); // if server did choose a transfer syntax from a different p_cont_elem_t, // this is a protocol error return RPC_S_PROTOCOL_ERROR; } // we have suggested multiple syntaxes, and the server picked one of them - record // the server preferences. Instead of just setting the preference on the binding // the server chose, we need to walk the list, and reset the preferences on the // other bindings, to handle mixed cluster scenario case, where the server // preferences actually change BindingToUse->TransferSyntaxIsServerPreferred(); CurrentBinding = GetBindingList(); do { if (CurrentBinding != BindingToUse) CurrentBinding->TransferSyntaxIsNotServerPreferred(); CurrentBinding = CurrentBinding->GetNextBinding(); } while (CurrentBinding != 0); if (Bindings.SelectedBinding == NULL) { Bindings.SelectedBinding = BindingToUse; } ReleaseBindingListWithException (Bindings.SelectedBinding, Bindings.AvailableBindingsList); Bindings.AvailableBindingsList = 0; *BindingNegotiated = BindingToUse; DispatchTableCallback = BindingToUse->GetDispatchTable(); } return RPC_S_OK; } int OSF_CCALL::SizeVerificationTrailer ( void ) /*++ Routine Description: Calculates and returns the size of the verification trailer during a sizing pass. Arguments: Return Value: The additional size of the verification trailer to add to the packet size including possible alignment. --*/ { ULONG VTSize = 0; BOOL VTHeaderSized = FALSE; OSF_BINDING *Binding; if (Connection->IsVerificationTrailerNecessary() && (GetIsPipeCallFlag() == FALSE)) { // quick check for cshsDontKnow and cshsUnconfirmedNo if (Connection->GetConnectionSupportHeaderSign() < cshsYes) { // add space for the trailer header and alignment VTSize += VTHeaderSize; VTHeaderSized = TRUE; // add space for the bitmask command VTSize += sizeof(rpc_sec_vt_bitmask); } Binding = GetSelectedBinding(); // if the presentation context hasn't been verified, we need to put it // in the trailer if (Binding->GetPContextVerifiedFlag() == FALSE) { // add space for the trailer header and alignment if necessary if (VTHeaderSized == FALSE) { VTSize += VTHeaderSize; VTHeaderSized = TRUE; } VTSize += sizeof(rpc_sec_vt_pcontext); } if (Connection->IsSChannel()) { // add space for the trailer header and alignment if necessary if (VTHeaderSized == FALSE) { VTSize += VTHeaderSize; VTHeaderSized = TRUE; } VTSize += sizeof(rpc_sec_vt_header2); } } return VTSize; } C_ASSERT(sizeof(rpc_sec_verification_trailer) == sizeof(SecVerificationTrailerSignature)); void OSF_CCALL::AddVerificationTrailer ( IN RPC_MESSAGE *RpcMessage ) /*++ Routine Description: Adds a verification trailer to the NDR buffer for the given RpcMessage. Arguments: RpcMessage - the RPC message we were asked to send. Return Value: --*/ { rpc_sec_vt_bitmask *vt_bitmask; rpc_sec_verification_trailer_command *LastCommand; rpc_sec_vt_pcontext *vt_pcontext; rpc_sec_vt_header2 *vt_header2; unsigned char *CurrentPosition; OSF_BINDING *Binding; BOOL VTHeaderAdded; if (Connection->IsVerificationTrailerNecessary() && (GetIsPipeCallFlag() == FALSE)) { CurrentPosition = (unsigned char *)RpcMessage->Buffer + RpcMessage->BufferLength; VTHeaderAdded = FALSE; LastCommand = NULL; // quick check for cshsDontKnow and cshsUnconfirmedNo if (Connection->GetConnectionSupportHeaderSign() < cshsYes) { // We know that state will never go back. That is, state will never go from cshsYes // or cshsConfirmedNo to cshsDontKnow or cshsUnconfirmedNo. So if we are in states // that add trailer, we must have been in those states during sizing too. Thus we know // the size is there vt_bitmask = (rpc_sec_vt_bitmask *)AddVerificationTrailerHeader (RpcMessage); VTHeaderAdded = TRUE; vt_bitmask->command_common.command = SEC_VT_COMMAND_BITMASK_1; vt_bitmask->command_common.length = sizeof(rpc_sec_vt_bitmask) - FIELD_OFFSET(rpc_sec_vt_bitmask, bits); vt_bitmask->bits = CLIENT_SUPPORT_HEADER_SIGNING; LastCommand = (rpc_sec_verification_trailer_command *)vt_bitmask; CurrentPosition = (unsigned char *)(vt_bitmask + 1); // make sure we are 4 byte aligned after the current position ASSERT(PadPtr4(CurrentPosition) == 0); } Binding = GetSelectedBinding(); // if the presentation context hasn't been verified, we need to put it // in the trailer if (Binding->GetPContextVerifiedFlag() == FALSE) { // We know that state will never go back. That is, state will never go from verified to // unverified. Thus if it is not verified here, it wasn't verified during sizing // either, which means it must have been sized. if (VTHeaderAdded == FALSE) { vt_pcontext = (rpc_sec_vt_pcontext *)AddVerificationTrailerHeader (RpcMessage); VTHeaderAdded = TRUE; } else { vt_pcontext = (rpc_sec_vt_pcontext *)CurrentPosition; } vt_pcontext->command_common.command = SEC_VT_COMMAND_PCONTEXT; vt_pcontext->command_common.length = sizeof(rpc_sec_vt_pcontext) - FIELD_OFFSET(rpc_sec_vt_pcontext, InterfaceId); RpcpMemoryCopy(&vt_pcontext->InterfaceId, Binding->GetInterfaceId(), sizeof(RPC_SYNTAX_IDENTIFIER)); RpcpMemoryCopy(&vt_pcontext->TransferSyntax, Binding->GetTransferSyntaxId(), sizeof(RPC_SYNTAX_IDENTIFIER)); LastCommand = (rpc_sec_verification_trailer_command *)vt_pcontext; CurrentPosition = (unsigned char *)(vt_pcontext + 1); // make sure we are 4 byte aligned after the current position ASSERT(PadPtr4(CurrentPosition) == 0); } if (Connection->IsSChannel()) { if (VTHeaderAdded == FALSE) { vt_header2 = (rpc_sec_vt_header2 *)AddVerificationTrailerHeader (RpcMessage); VTHeaderAdded = TRUE; } else { vt_header2 = (rpc_sec_vt_header2 *)CurrentPosition; } vt_header2->command_common.command = SEC_VT_COMMAND_HEADER2; vt_header2->command_common.length = sizeof(rpc_sec_vt_header2) - FIELD_OFFSET(rpc_sec_vt_header2, PTYPE); vt_header2->call_id = CallId; vt_header2->Reserved1 = 0; vt_header2->Reserved2 = 0; vt_header2->drep[0] = NDR_LOCAL_CHAR_DREP | NDR_LOCAL_INT_DREP; vt_header2->drep[1] = NDR_LOCAL_FP_DREP; vt_header2->drep[2] = 0; vt_header2->drep[3] = 0; vt_header2->opnum = (USHORT)RpcMessage->ProcNum; vt_header2->p_cont_id = Binding->GetOnTheWirePresentationContext(); vt_header2->PTYPE = rpc_request; LastCommand = (rpc_sec_verification_trailer_command *)vt_header2; CurrentPosition = (unsigned char *)(vt_header2 + 1); // make sure we are 4 byte aligned after the current position ASSERT(PadPtr4(CurrentPosition) == 0); } // was there at least one command? if (LastCommand) { // yes. Mark is as last and update the total length LastCommand->command |= SEC_VT_COMMAND_END; RpcMessage->BufferLength = (ULONG)(CurrentPosition - (unsigned char *)RpcMessage->Buffer); } } } rpc_sec_verification_trailer_command *OSF_CCALL::AddVerificationTrailerHeader ( IN RPC_MESSAGE *RpcMessage ) /*++ Routine Description: Adds the header of the verification trailer to the NDR buffer for the given RpcMessage. Arguments: RpcMessage - the RPC message we were asked to send. Return Value: The place to write the first command after the trailer header. --*/ { rpc_sec_verification_trailer *vt; unsigned char *LastPosition; LastPosition = (unsigned char *)RpcMessage->Buffer + RpcMessage->BufferLength; vt = (rpc_sec_verification_trailer *) AlignPtr4(LastPosition); // zero out the alignment gap RpcpMemorySet(LastPosition, 0, ((unsigned char *)vt) - LastPosition); RpcpMemoryCopy(vt, SecVerificationTrailerSignature, sizeof(rpc_sec_verification_trailer)); return (rpc_sec_verification_trailer_command *)(vt + 1); } RPC_STATUS OSF_CCALL_AVRF::GetBuffer ( IN OUT PRPC_MESSAGE Message, IN UUID *ObjectUuid ) /*++ Routine Description: Checks is the call is remote. If it is, checks whether sufficiently strong security is being used and calls the parent's method. --*/ { DCE_BINDING *DceBinding = this->Connection->Association->DceBinding; BOOL fWeakSecurity = false; // // Check to make sure that the security being used is strong enough // when making remote calls. // We will make this check on each call. // ASSERT(DceBinding->InqNetworkAddress() != 0); if (gfRPCVerifierEnabledWithBreaks && DceBinding->InqNetworkAddress()[0] != 0) { // // Check if this is a remote call. // This is a "best-effort" check, since in general it is hard to tell if the call is remote. // Here, we may decide that some calls are remote when they are actually local. // // We first check if the server's name begins with the local machine's name. // If it does, then we may be using it's name, or it's DNS name, or just happen to be using // a very similar server name (which is unlikely). // And finally we try a TCP loopback address. // // The server name is considered to be case-insensitive. // We are going to create copies of the names and convert both to lower case // before comparing. size_t SizeOfComputerName = gLocalComputerNameLength*sizeof(RPC_CHAR); size_t SizeOfNetworkAddress = (RpcpStringLength(DceBinding->InqNetworkAddress())+1)*sizeof(RPC_CHAR); RPC_CHAR *LowerCaseComputerName = (RPC_CHAR *)alloca(SizeOfComputerName); RPC_CHAR *LowerCaseNetworkAddress = (RPC_CHAR *)alloca(SizeOfNetworkAddress); memcpy(LowerCaseComputerName, gLocalComputerName, SizeOfComputerName); memcpy(LowerCaseNetworkAddress, DceBinding->InqNetworkAddress(), SizeOfNetworkAddress); LowerCaseComputerName = RpcpStringToLower(LowerCaseComputerName); LowerCaseNetworkAddress = RpcpStringToLower(LowerCaseNetworkAddress); // See if the server looks like a remote one. if (RpcpStrStr(LowerCaseNetworkAddress, LowerCaseComputerName) != LowerCaseNetworkAddress && RpcpStringCompareInt(DceBinding->InqNetworkAddress(), RPC_CONST_STRING("localhost")) != 0 && RpcpStringCompareInt(DceBinding->InqNetworkAddress(), RPC_CONST_STRING("127.0.0.1")) != 0 && RpcpStringCompareInt(DceBinding->InqNetworkAddress(), RPC_CONST_STRING("\\\\.")) != 0 && RpcpStringCompareInt(DceBinding->InqNetworkAddress(), RPC_CONST_STRING(".")) != 0 ) { UUID *IfUuid = &(((RPC_CLIENT_INTERFACE *)Message->RpcInterfaceInformation)->InterfaceId.SyntaxGUID); // // Check to make sure strong security is being used. // // Check if an unsafe protseq is being used. if (IsProtseqUnsafe(DceBinding->InqRpcProtocolSequence())) { RPC_VERIFIER_WARNING_MSG("Possible security threat: Client is calling server over an unsafe protseq", RPC_VERIFIER_UNSAFE_PROTOCOL); fWeakSecurity = true; } // Make sure that the client uses encryption. if (this->Connection->ClientSecurityContext.AuthenticationLevel != RPC_C_AUTHN_LEVEL_PKT_PRIVACY && !IsInterfaceExempt(IfUuid, ALLOW_UNENCRYPTED_REMOTE_ACCESS)) { RPC_VERIFIER_WARNING_MSG("Possible security threat: Client is calling a remote endpoint without RPC_C_AUTHN_LEVEL_PKT_PRIVACY", RPC_VERIFIER_WEAK_SECURITY_FOR_REMOTE_CALL); fWeakSecurity = true; } // Make sure that the client asks for mutual authentication. if(!(this->Connection->ClientSecurityContext.Capabilities & RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH) && !IsInterfaceExempt(IfUuid, ALLOW_NO_MUTUAL_AUTH_REMOTE_ACCESS)) { RPC_VERIFIER_WARNING_MSG("Possible security threat: Client is calling a remote endpoint without mutual authentication", RPC_VERIFIER_WEAK_SECURITY_FOR_REMOTE_CALL); fWeakSecurity = true; } // If strong security is not being used, print a warning message. if (fWeakSecurity) { DbgPrint("RPC: Protocol: %S NetworkAddress: %S Interface UUID: ", DceBinding->InqRpcProtocolSequence(), DceBinding->InqNetworkAddress()); PrintUUID(IfUuid); DbgPrint("\n"); RPC_VERIFIER_PRINT_OFFENDING_STACK(3, 4); } } } return OSF_CCALL::GetBuffer(Message, ObjectUuid); } OSF_CASSOCIATION::OSF_CASSOCIATION ( IN DCE_BINDING * DceBinding, IN TRANS_INFO *TransInfo, IN OUT RPC_STATUS * Status ) : AssociationMutex(Status), CallIdCounter(1), BindHandleCount(1) /*++ Routine Description: We construct a OSF_CASSOCIATION object in this routine. This consists of initializing some instance variables, and saving the parameters away. Arguments: DceBinding - Supplies the binding information for this association. Ownership of this data passes to this object. RpcClientInfo - Supplies the information necessary to use the loadable transport corresponding to the network interface type used by this association. --*/ { ALLOCATE_THIS(OSF_CASSOCIATION); LogEvent(SU_CASSOC, EV_START, this, 0, 0, 1, 0); ObjectType = OSF_CASSOCIATION_TYPE; AssocGroupId = 0; this->DceBinding = DceBinding; this->TransInfo = TransInfo; SecondaryEndpoint = 0; OpenConnectionCount = 0; ConnectionsDoingBindCount = 0; fPossibleServerReset = 0; ResolverHintInitialized = FALSE; DontLinger = FALSE; MaintainContext = 0; AssociationValid = TRUE; FailureCount = 0; fMultiplex = mpx_unknown; SetReferenceCount(1); SavedDrep = 0; fIdleConnectionCleanupNeeded = FALSE; Linger.fAssociationLingered = FALSE; LocalMASet = FALSE; } OSF_CASSOCIATION::~OSF_CASSOCIATION ( ) { OSF_BINDING * Binding; DictionaryCursor cursor; if (ResolverHintInitialized) { FreeResolverHint(InqResolverHint()); } if (DceBinding != 0) { delete DceBinding; } FreeAllBindings(); if ( SecondaryEndpoint != 0 ) { delete SecondaryEndpoint; } if (fIdleConnectionCleanupNeeded) { if (InterlockedDecrement(&PeriodicGarbageCollectItems) == 0) { #if defined (RPC_GC_AUDIT) DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) PeriodicGarbageCollectItems dropped to 0\n", GetCurrentProcessId(), GetCurrentProcessId()); #endif } } } void OSF_CASSOCIATION::NotifyConnectionOpen ( ) /*++ Function Name:NotifyConnectionOpen Parameters: Description: Returns: --*/ { AssociationMutex.VerifyOwned(); OpenConnectionCount++; LogEvent(SU_CASSOC, EV_INC, this, 0, OpenConnectionCount, 1, 1); } void OSF_CASSOCIATION::NotifyConnectionClosed ( ) /*++ Routine Description: This routine is necessary so that we can know when to set the association group id back to zero. We do this when no more connections owned by this association can possibly be connected with the server. --*/ { AssociationMutex.Request(); ASSERT( OpenConnectionCount > 0 ); OpenConnectionCount -= 1; LogEvent(SU_CASSOC, EV_DEC, this, 0, OpenConnectionCount, 1, 1); if ( OpenConnectionCount == 0 ) { if (ConnectionsDoingBindCount == 0) { LogEvent(SU_CASSOC, EV_NOTIFY, this, ULongToPtr(OpenConnectionCount), 0, 1, 0); if (IsValid()) { // don't reset invalid associations FreeAllBindings(); ResetAssociation(); } } else { if (IsValid()) { // don't signal possible reset on invalid associations - this will // cause more retries fPossibleServerReset = TRUE; } } } AssociationMutex.Clear(); } void OSF_CASSOCIATION::NotifyConnectionBindInProgress ( void ) { AssociationMutex.VerifyOwned(); ConnectionsDoingBindCount ++; LogEvent(SU_CASSOC, EV_INC, this, (PVOID)1, ConnectionsDoingBindCount, 1, 1); } void OSF_CASSOCIATION::NotifyConnectionBindCompleted ( void ) { AssociationMutex.VerifyOwned(); ConnectionsDoingBindCount --; LogEvent(SU_CASSOC, EV_DEC, this, (PVOID)1, ConnectionsDoingBindCount, 1, 1); if (ConnectionsDoingBindCount == 0) { if (OpenConnectionCount == 0) { LogEvent(SU_CASSOC, EV_NOTIFY, this, ULongToPtr(OpenConnectionCount), ConnectionsDoingBindCount, 1, 0); if (IsValid()) { // don't reset invalid associations FreeAllBindings(); ResetAssociation(); } } else { if (IsValid()) { // don't signal possible reset on invalid associations - this will // cause more retries fPossibleServerReset = FALSE; } } } } RPC_STATUS OSF_CASSOCIATION::ProcessBindAckOrNak ( IN rpcconn_common * Buffer, IN UINT BufferLength, IN OSF_CCONNECTION * CConnection, IN OSF_CCALL *CCall, OUT ULONG *NewGroupId, OUT OSF_BINDING **BindingNegotiated, OUT FAILURE_COUNT_STATE *fFailureCountExceeded ) /*++ Routine Description: Arguments: Buffer - Supplies the buffer containing either the bind_ack, bind_nak, or alter_context_resp packet. BufferLength - Supplies the length of the buffer, less the length of the authorization information. CConnection - Supplies the connection from which we received the packet. CCall - the call for which the bind is done. NewGroupId - if the bind was successful the new association group id will be returned. BindingNegotiated - The binding that was negotiated in the case of success and multiple proposed bindings. Undefined if there is failure, or if only one binding was proposed. fFailureCountExceeded - if supplied, must be FailureCountUnknown. If we got bind failure with reason not specified, and we haven't exceeded the failure count, it will be set to FailureCountNotExceeded. If we received bind failure with reason not specified and the failure count is exceeded, it will be set to FailureCountExceeded. Return Value: RPC_S_OK - The client has successfully bound with the server. RPC_S_PROTOCOL_ERROR - The packet received from the server does not follow the protocol. RPC_S_OUT_OF_MEMORY - Insufficient memory is available to make a copy of the secondary endpoint. RPC_S_UNSUPPORTED_TRANS_SYN - The transfer syntax supplied by the client is not supported by the server. RPC_S_UNKNOWN_IF - The interface to which the client wished to bind is not supported by the server. RPC_S_SERVER_TOO_BUSY - The server is too busy to accept the clients bind request. RPC_S_UNKNOWN_AUTHN_TYPE - The server does not support the authentication type specified by the client. --*/ { rpcconn_bind_ack *pBindAck; rpcconn_bind_nak *pBindNak; p_result_list_t *pResults; int port_spec_plus_pad; UINT SecondaryEndpointLength; unsigned char * Pointer; RPC_STATUS Status; int NumberOfResultElements; int i; BOOL fConvertEndian; int PresentationContextAccepted; if (ARGUMENT_PRESENT(fFailureCountExceeded)) { ASSERT(*fFailureCountExceeded == FailureCountUnknown); } AssociationMutex.VerifyOwned(); SavedDrep = (Buffer->drep[0]| (Buffer->drep[1] << 8)); // // The common header of the packet has already been validated and data // converted, if necessary, by whoever called this method. // if ( (Buffer->PTYPE == rpc_bind_ack) || (Buffer->PTYPE == rpc_alter_context_resp)) { FailureCount = 0; // // The bind_ack and alter_context_resp packets are the same. // pBindAck = (rpcconn_bind_ack *) Buffer; // // We need to convert the max_xmit_frag, max_recv_frag, and // assoc_group_id fields of the packet. // if ( DataConvertEndian(Buffer->drep) != 0 ) { pBindAck->max_xmit_frag = RpcpByteSwapShort(pBindAck->max_xmit_frag); pBindAck->max_recv_frag = RpcpByteSwapShort(pBindAck->max_recv_frag); pBindAck->assoc_group_id = RpcpByteSwapLong(pBindAck->assoc_group_id); pBindAck->sec_addr_length = RpcpByteSwapShort(pBindAck->sec_addr_length); } if ( Buffer->PTYPE == rpc_bind_ack ) { if (Buffer->pfc_flags & PFC_CONC_MPX) { fMultiplex = mpx_yes; } CConnection->SetMaxFrag(pBindAck->max_xmit_frag, pBindAck->max_recv_frag); } BufferLength -= sizeof(rpcconn_bind_ack); Pointer = (unsigned char *) (pBindAck + 1); if ( pBindAck->sec_addr_length ) { SecondaryEndpointLength = pBindAck->sec_addr_length; // // The secondary address length is two bytes long. We want // to align the total of the secondary address length itself, // the the secondary address. Hence, the length of the secondary // address and the necessary pad is calculated below. Think // very carefully before changing this piece of code. // port_spec_plus_pad = SecondaryEndpointLength + Pad4(SecondaryEndpointLength + 2); if ( BufferLength < (UINT) port_spec_plus_pad ) { return(RPC_S_PROTOCOL_ERROR); } #if 0 if ( SecondaryEndpoint != 0 ) { delete SecondaryEndpoint; } SecondaryEndpoint = new unsigned char[SecondaryEndpointLength]; if ( SecondaryEndpoint == 0 ) return(RPC_S_OUT_OF_MEMORY); RpcpMemoryCopy(SecondaryEndpoint, Pointer, SecondaryEndpointLength); if ( DataConvertCharacter(Buffer->drep) != 0 ) { ConvertStringEbcdicToAscii(SecondaryEndpoint); } #endif BufferLength -= port_spec_plus_pad; Pointer = Pointer + port_spec_plus_pad; } else { Pointer = Pointer + 2; BufferLength -= 2; } pResults = (p_result_list_t *) Pointer; // the buffer must have at least as much results as it claims to have NumberOfResultElements = pResults->n_results; if (BufferLength < sizeof(p_result_list_t) + sizeof(p_result_t) * (NumberOfResultElements - 1)) { return(RPC_S_PROTOCOL_ERROR); } PresentationContextAccepted = -1; fConvertEndian = DataConvertEndian(Buffer->drep); // we walk through the list of elements, and see which ones are accepted, and which // ones are rejected. If we have at least one accepted, the bind succeeds. If all are // rejected, we arbitrarily pick the first error code and declare it the cause of // the failure. Note that according to DCE spec, it is not a protocol error to // have a bind ack and no presentation contexts accepted for (i = 0; i < NumberOfResultElements; i ++) { if (fConvertEndian) { pResults->p_results[i].result = RpcpByteSwapShort(pResults->p_results[i].result); pResults->p_results[i].reason = RpcpByteSwapShort(pResults->p_results[i].reason); ByteSwapSyntaxId(&(pResults->p_results[i].transfer_syntax)); } if ( pResults->p_results[i].result == acceptance ) { // currently we can handle at most one acceptance. Everything else // is a protocol error. This is fine since we know only we will // propose NDR64, and any third party should accept at most NDR20 // Our servers will always choose exactly one if (PresentationContextAccepted >= 0) return RPC_S_PROTOCOL_ERROR; PresentationContextAccepted = i; } } if (PresentationContextAccepted < 0) // faster version of == -1 { if ( pResults->p_results[0].result != provider_rejection ) { return(RPC_S_CALL_FAILED_DNE); } switch (pResults->p_results[0].reason) { case abstract_syntax_not_supported: return(RPC_S_UNKNOWN_IF); case proposed_transfer_syntaxes_not_supported: return(RPC_S_UNSUPPORTED_TRANS_SYN); case local_limit_exceeded: return(RPC_S_SERVER_TOO_BUSY); default: return(RPC_S_CALL_FAILED_DNE); } } // we have bound successfully. Notify the call so that // it can fix what binding it will use Status = CCall->BindCompleteNotify( &pResults->p_results[PresentationContextAccepted], PresentationContextAccepted, BindingNegotiated); if (Status != RPC_S_OK) return Status; *NewGroupId = pBindAck->assoc_group_id; return RPC_S_OK; } if (Buffer->PTYPE == rpc_bind_nak) { if (BufferLength < MinimumBindNakLength) { RpcpErrorAddRecord (EEInfoGCRuntime, RPC_S_PROTOCOL_ERROR, EEInfoDLOSF_CASSOCIATION__ProcessBindAckOrNak10, (ULONG)BufferLength, (ULONG)MinimumBindNakLength); return(RPC_S_PROTOCOL_ERROR); } pBindNak = (rpcconn_bind_nak *) Buffer; if ( DataConvertEndian(Buffer->drep) != 0 ) { pBindNak->provider_reject_reason = RpcpByteSwapShort(pBindNak->provider_reject_reason); } if (pBindNak->common.frag_length > BindNakSizeWithoutEEInfo) { if (RpcpMemoryCompare(&pBindNak->Signature, BindNakEEInfoSignature, sizeof(UUID)) == 0) { size_t EEInfoSize = GetEEInfoSizeFromBindNakPacket(pBindNak); // Sanity-check the EEInfo length if (EEInfoSize > Buffer->frag_length - FaultSizeWithoutEEInfo) { CORRUPTION_ASSERT(0 && "Reported EEInfo size is larger then the packet"); } else { UnpickleEEInfoFromBuffer(pBindNak->buffer, EEInfoSize); } } } if ( (pBindNak->provider_reject_reason == temporary_congestion) || (pBindNak->provider_reject_reason == local_limit_exceeded_reject)) { Status = RPC_S_SERVER_TOO_BUSY; } else if ( pBindNak->provider_reject_reason == protocol_version_not_supported ) { Status = RPC_S_PROTOCOL_ERROR; } else if ( pBindNak->provider_reject_reason == authentication_type_not_recognized ) { Status = RPC_S_UNKNOWN_AUTHN_SERVICE; } else if ( pBindNak->provider_reject_reason == invalid_checksum ) { Status = RPC_S_ACCESS_DENIED; } else { FailureCount++; if (FailureCount >= 40) { LogEvent(SU_CASSOC, EV_ABORT, this, 0, FailureCount, 1, 0); AssociationValid = FALSE; AssociationShutdownError = RPC_S_CALL_FAILED_DNE; if (ARGUMENT_PRESENT(fFailureCountExceeded)) *fFailureCountExceeded = FailureCountExceeded; } else if (ARGUMENT_PRESENT(fFailureCountExceeded)) { *fFailureCountExceeded = FailureCountNotExceeded; } Status = RPC_S_CALL_FAILED_DNE; } RpcpErrorAddRecord(EEInfoGCRuntime, Status, EEInfoDLOSF_CASSOCIATION__ProcessBindAckOrNak20, (ULONG)pBindNak->provider_reject_reason, (ULONG)FailureCount); return (Status); } return(RPC_S_PROTOCOL_ERROR); } void OSF_CASSOCIATION::UnBind ( ) { OSF_CCONNECTION * CConnection; BOOL fWillLinger = FALSE; DWORD OldestAssociationTimestamp; OSF_CASSOCIATION *CurrentAssociation; OSF_CASSOCIATION *OldestAssociation = NULL; BOOL fEnableGarbageCollection = FALSE; DictionaryCursor cursor; RPC_CHAR *NetworkAddress; long LocalBindHandleCount; LogEvent(SU_CASSOC, EV_DEC, this, (PVOID)2, BindHandleCount.GetInteger(), 1, 0); LocalBindHandleCount = BindHandleCount.Decrement(); if (LocalBindHandleCount == 0) { // we don't linger remote named pipes, as sometimes this results in // credentials conflict NetworkAddress = DceBinding->InqNetworkAddress(); if ((OpenConnectionCount > 0) && AssocGroupId && AssociationValid && (!DontLinger) && ( (NetworkAddress == NULL) || (NetworkAddress[0] == 0) || (!DceBinding->IsNamedPipeTransport()) ) ) { if (IsGarbageCollectionAvailable()) { if (OsfLingeredAssociations >= MaxOsfLingeredAssociations) { OldestAssociationTimestamp = ~(DWORD)0; // need to walk the dictionary and clean up the oldest item AssociationDict->Reset(cursor); while ((CurrentAssociation = AssociationDict->Next(cursor)) != 0) { if (CurrentAssociation->Linger.fAssociationLingered) { // yes, if the tick count wraps around, we may make a // suboptimal decision and destroy a newer lingering // association. That's ok - it will be a slight perf hit once // every ~47 days - it won't be a bug if (OldestAssociationTimestamp > CurrentAssociation->Linger.Timestamp) { OldestAssociation = CurrentAssociation; } } } // there must be an oldest association here ASSERT(OldestAssociation); AssociationDict->Delete(OldestAssociation->Key); OldestAssociation->Key = -1; // no need to update OsfLingeredAssociations - we removed one, // but we add one, so the balance is the same } else { OsfLingeredAssociations ++; ASSERT(OsfLingeredAssociations <= MaxOsfLingeredAssociations); } Linger.Timestamp = GetTickCount() + gThreadTimeout; Linger.fAssociationLingered = TRUE; fWillLinger = TRUE; // Add one artifical reference. Once we release the AssocDictMutex, // a gc thread can come in and nuke the association, and if we // decide that we cannot do garbage collecting below and decide // to shutdown the association we may land on a freed object // CASSOC++ OSF_CASSOCIATION::AddReference(); } else { // good association, but can't linger it, because gc is not available // let's see if we can turn it on OsfDestroyedAssociations ++; fEnableGarbageCollection = CheckIfGCShouldBeTurnedOn( OsfDestroyedAssociations, NumberOfOsfDestroyedAssociationsToSample, DestroyedOsfAssociationBatchThreshold, &OsfLastDestroyedAssociationsBatchTimestamp ); } } if (!fWillLinger) { AssociationDict->Delete(Key); } AssocDictMutex->Clear(); if (!fWillLinger) { AssociationValid = FALSE; LogEvent(SU_CASSOC, EV_STOP, this, 0, 0, 1, 0); ShutdownRequested(RPC_S_CALL_FAILED_DNE, NULL); } AssociationMutex.Clear(); if (OldestAssociation) { #if defined (RPC_GC_AUDIT) int Diff; Diff = (int)(GetTickCount() - OldestAssociation->Linger.Timestamp); DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) OSF association sync gc'ed %d ms after expire\n", GetCurrentProcessId(), GetCurrentProcessId(), Diff); #endif OldestAssociation->AssociationMutex.Request(); OldestAssociation->AssociationValid = FALSE; OldestAssociation->ShutdownRequested(RPC_S_CALL_FAILED_DNE, NULL); OldestAssociation->AssociationMutex.Clear(); OldestAssociation->OSF_CASSOCIATION::RemoveReference(); } if (!fWillLinger) { // CASSOC-- OSF_CASSOCIATION::RemoveReference(); // N.B. If fWillLinger is FALSE, don't touch the this pointer // after here } if (fEnableGarbageCollection) { // ignore the return value - we'll make a best effort to // create the thread, but if there's no memory, that's // still ok as the garbage collection thread only // provides better perf in this case (void) CreateGarbageCollectionThread(); } if (fWillLinger) { fWillLinger = GarbageCollectionNeeded(TRUE, gThreadTimeout); if (fWillLinger == FALSE) { // uh-oh - we couldn't register for garbage collection - probably // extremely low on memory. If nobody has picked us up in the meantime, // delete this association. Otherwise, let it go - somebody is using // it and we don't need to worry about gc'ing it. We also need to guard // against the gc thread trying to do Delete on this also. If it does // so, it will set the Key to -1 before it releases // the mutex - therefore we can check for this. A gc thread cannot // completely kill the object as we will hold one reference on it AssocDictMutex->Request(); if ((Linger.fAssociationLingered) && (Key != -1)) { OsfLingeredAssociations --; ASSERT(OsfLingeredAssociations >= 0); AssociationDict->Delete(Key); Key = -1; AssociationShutdownError = RPC_S_CALL_FAILED_DNE; AssociationValid = FALSE; AssocDictMutex->Clear(); LogEvent(SU_CASSOC, EV_STOP, this, 0, 0, 1, 0); ShutdownRequested(RPC_S_CALL_FAILED_DNE, NULL); // CASSOC-- OSF_CASSOCIATION::RemoveReference(); } else { AssocDictMutex->Clear(); } } #if defined (RPC_GC_AUDIT) else { DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) OSF association lingered %X\n", GetCurrentProcessId(), GetCurrentProcessId(), this); } #endif // Remove the artifical reference we added above // CASSOC-- OSF_CASSOCIATION::RemoveReference(); // N.B. don't touch the this pointer after here } } else { AssocDictMutex->Clear(); AssociationMutex.Clear(); } } RPC_STATUS OSF_CASSOCIATION::FindOrCreateOsfBinding ( IN PRPC_CLIENT_INTERFACE RpcInterfaceInformation, IN RPC_MESSAGE *Message, OUT int *NumberOfBindings, IN OUT OSF_BINDING *BindingsForThisInterface[] ) /*++ Routine Description: This method gets called to find the osf bindings (a dictionary entry) corresponding to the specified rpc interface information. The caller of this routine must be holding (ie. requested) the association mutex. Arguments: RpcInterfaceInformation - Supplies the interface information for which we are looking for an osf binding object. Message - supplies the RPC_MESSAGE for this call NumberOfBindings - an out parameter that will return the number of retrieved bindings BindingsForThisInterface - a caller supplied array where the found bindings will be placed. Return Value: RPC_S_OK for success, other for failure --*/ { BOOL Ignored[MaximumNumberOfTransferSyntaxes]; AssociationMutex.VerifyOwned(); return MTSyntaxBinding::FindOrCreateBinding(RpcInterfaceInformation, Message, &Bindings, CreateOsfBinding, CheckOsfBindingForDestruction, this, // CallbackContext NumberOfBindings, (MTSyntaxBinding **)BindingsForThisInterface, Ignored); } BOOL OSF_CASSOCIATION::DoesBindingForInterfaceExist ( IN PRPC_CLIENT_INTERFACE RpcInterfaceInformation ) /*++ Routine Description: Checks if an association supports a binding for this interface. Arguments: RpcInterfaceInformation - Supplies the interface information for which we are looking for an osf binding object. Return Value: FALSE if it doesn't. Non-zero if it does. --*/ { OSF_BINDING *Binding; DictionaryCursor cursor; BOOL fRetVal = FALSE; BOOL Result; // we can't just ask for the mutex for two reasons: // 1. If we are in lsa we can't directly ask for the mutex // since we may deadlock. In lsa the security providers can make RPC // calls from within the security context establishment routine. If // they do, they will hold an association mutex, and ask for the // association dict mutex within the inner RPC call. This thread // already holds the assoc dict mutex and will ask for the assoc // mutex (reverse order) causing a deadlock. // 2. If you have a slow connection and two threads making calls on // it, you can have one thread holding the association mutex and // waiting for the slow connection to bind for the first time and // another thread holding the association dict mutex and waiting // here on the first thread. This will be really bad as this means // that while the association dict mutex is held, no OSF RPC from // this process will go out. // Since this routine // is used only to shortcut remote endpoint resolution, and as // optimization, in lsa, if we can't get the mutex, we forego the // optimization and return not found. This will cause caller to // do remote endpoint resolution Result = AssociationMutex.TryRequest(); if (!Result) return FALSE; Bindings.Reset(cursor); while ((Binding = Bindings.Next(cursor)) != 0) { // if we have a binding on the same interface, // return TRUE if (RpcpMemoryCompare(Binding->GetInterfaceId(), &RpcInterfaceInformation->InterfaceId, sizeof(RPC_SYNTAX_IDENTIFIER)) == 0) { fRetVal = TRUE; break; } } AssociationMutex.Clear(); return fRetVal; } void OSF_CASSOCIATION::ShutdownRequested ( IN RPC_STATUS AssociationShutdownError OPTIONAL, IN OSF_CCONNECTION *ExemptConnection OPTIONAL ) /*++ Routine Description: Aborts all connections in the association, except the Exempt connection, and marks the association as invalid. Arguments: AssociationShutdownError - the error with which the association is to be shutdown ExemptConnection - an optional pointer to a connection which is not to be aborted. --*/ { OSF_CCONNECTION * CConnection; DictionaryCursor cursor; BOOL fDontKill; BOOL fExemptConnectionSkipped = FALSE; AssociationMutex.Request(); if (!ExemptConnection) { ASSERT(AssociationShutdownError != RPC_S_OK); LogEvent(SU_CASSOC, EV_STOP, this, 0, 0, 1, 0); // we will abort all connections - the association is invalid // mark it as such AssociationValid = FALSE; this->AssociationShutdownError = AssociationShutdownError; } ActiveConnections.Reset(cursor); while ((CConnection = ActiveConnections.Next(cursor)) != NULL) { if (CConnection == ExemptConnection) { fExemptConnectionSkipped = TRUE; continue; } fDontKill = ConnectionAborted(CConnection); if (fDontKill == FALSE) { // CCONN++ CConnection->AddReference(); CConnection->DeleteConnection(); // CCONN-- CConnection->RemoveReference(); } } if (ExemptConnection) { ASSERT(fExemptConnectionSkipped); } // if at least one connection wasn't aborted, the association // is still valid - it just needs resetting if (ExemptConnection) ResetAssociation(); AssociationMutex.Clear(); } void OSF_CASSOCIATION::FreeAllBindings ( void ) /*++ Routine Description: Frees all bindings in the association. Arguments: Return Value: --*/ { OSF_BINDING *Binding; DictionaryCursor cursor; Bindings.Reset(cursor); while ((Binding = Bindings.Next(cursor)) != 0) { // if the lifetime reference is not removed, remove it if (Binding->IsCapabilityBitmapReset() == FALSE) { Binding->ResetCapabilityBitmap(); Binding->RemoveReference(); } if (Binding->IsRefCountZero()) DestroyBinding (Binding); } } void OSF_CASSOCIATION::DestroyBinding ( IN OSF_BINDING *Binding ) /*++ Routine Description: Destroys a given binding. At the time we're guaranteed to hold the association mutex and the reference count of the binding to be 0. Arguments: Binding - binding to destroy and remove from the dictionary Return Value: --*/ { OSF_CCONNECTION *Connection; DictionaryCursor cursor; ASSERT(Binding->IsRefCountZero()); ASSERT(Binding->IsCapabilityBitmapReset()); // first, walk the connection list and tell all connections that // thought they support this presentation context that they don't anymore ActiveConnections.Reset(cursor); while ((Connection = ActiveConnections.Next(cursor)) != 0) { // DeletePContext is idempotent - if the corresponding bit is // not set, nothing is done. Connection->DeletePContext(Binding->GetPresentationContext()); } Bindings.Delete(Binding->GetPresentationContext()); delete Binding; } RPC_STATUS OSF_CASSOCIATION::ToStringBinding ( OUT RPC_CHAR * * StringBinding, IN RPC_UUID * ObjectUuid ) /*++ Routine Description: We need to convert the binding handle into a string binding. If the binding handle has not yet been used to make a remote procedure call, then we can just use the information in the binding handle to create the string binding. Otherwise, we need to ask the association to do it for us. Arguments: StringBinding - Returns the string representation of the binding handle. ObjectUuid - Supplies the object uuid of the binding handle which is requesting that we create a string binding. Return Value: RPC_S_OK - The operation completed successfully. RPC_S_OUT_OF_MEMORY - We do not have enough memory available to allocate space for the string binding. --*/ { *StringBinding = DceBinding->StringBindingCompose(ObjectUuid); if (*StringBinding == 0) return(RPC_S_OUT_OF_MEMORY); return(RPC_S_OK); } int OSF_CASSOCIATION::CompareWithDceBinding ( IN DCE_BINDING * DceBinding, OUT BOOL *fOnlyEndpointDifferent ) /*++ Routine Description: This routine compares the specified binding information with the binding information in the object, this. Arguments: DceBinding - Supplies the binding information to compare against the binding information in this. fOnlyEndpointDiffers - this output variable will be set to TRUE if the result is non-zero and only the endpoint is different. It will be set to FALSE if the result is non-zero, and there is more than the endpoint different. If this function returns 0, the fOnlyEndpointDiffers argument is undefined. Return Value: Zero will be returned if the specified binding information, DceBinding, is the same as in this. Otherwise, non-zero will be returned. --*/ { int Result; if ((Result = this->DceBinding->Compare(DceBinding, fOnlyEndpointDifferent)) != 0) return(Result); return(0); } OSF_CCONNECTION * OSF_CASSOCIATION::FindIdleConnection ( void ) /*++ Routine Description: This routine is used to find a connection which has been idle more than the minimum number of seconds specified. If one is found, it is removed from the set of free connections and returned. The association dict mutex will be held when this routine is called. Arguments: Return Value: If one or more idle connections are found, the first will be returned. Next connections can be retrieved by following the u.NextConnection link until NULL is reached; if no connections are idle, NULL will be returned. --*/ { OSF_CCONNECTION *CConnection; OSF_CCONNECTION *FirstConnection; BOOL fMutexTaken; BOOL fThreadCreated = 0; DictionaryCursor cursor; ULONG TickCount; ULONG ClientDisconnectTime; #if defined (RPC_IDLE_CLEANUP_AUDIT) ULONG ConnectionsPickedForCleanup = 0; #endif // // If we need to maintain context with server, we do not want to close // the last open connection. To be on the safe side, we will make // sure that there is at least one free connection. // fMutexTaken = AssociationMutex.TryRequest(); if (!fMutexTaken) { #if defined (RPC_IDLE_CLEANUP_AUDIT) DbgPrintEx(77, DPFLTR_ERROR_LEVEL, "%d (0x%X) Association mutex busy - aborting\n", GetCurrentProcessId(), GetCurrentProcessId()); #endif return NULL; } if ( ActiveConnections.Size() <= 1 ) { if ( (MaintainContext != 0) || (ActiveConnections.Size() == 0)) { ClearIdleConnectionCleanupFlag(); AssociationMutex.Clear(); return(0); } } FirstConnection = NULL; if (ActiveConnections.Size() > AGGRESSIVE_TIMEOUT_THRESHOLD) { ClientDisconnectTime = CLIENT_DISCONNECT_TIME2; } else { ClientDisconnectTime = CLIENT_DISCONNECT_TIME1; } #if defined (RPC_IDLE_CLEANUP_AUDIT) DbgPrintEx(77, DPFLTR_ERROR_LEVEL, "%d (0x%X) Dictionary size for assoc %p is %d. Using timeout %d\n", GetCurrentProcessId(), GetCurrentProcessId(), this, ActiveConnections.Size(), ClientDisconnectTime); #endif ActiveConnections.Reset(cursor); while ( (CConnection = ActiveConnections.Next(cursor)) != 0 ) { if (CConnection->ThreadId == SYNC_CONN_FREE || CConnection->ThreadId == ASYNC_CONN_FREE) { TickCount = NtGetTickCount(); if ( CConnection->InquireLastTimeUsed() == 0 ) { CConnection->SetLastTimeUsedToNow(); } // TickCount is ULONG and InquireLastTimeUsed is ULONG // If the tick count has wrapped, the result will still // be valid. Note that even though it is not technically // impossible that the cleanup is not called for ~47 // days, and the next time we're called the connection // looks like just used, a call after that will still // timeout and destroy the connection. If the caller has // waited for 47 days, it will wait for 30 more seconds. else if ( TickCount - CConnection->InquireLastTimeUsed() > ClientDisconnectTime ) { if (!fThreadCreated) { RPC_STATUS Status; Status = TransInfo->CreateThread(); if (Status != RPC_S_OK) { AssociationMutex.Clear(); return (0); } fThreadCreated = 1; } // link all obtained connections on a list CConnection->u.NextConnection = FirstConnection; FirstConnection = CConnection; // // This reference will be removed by OsfDeleteIdleConnections, CCONN++ // CConnection->AddReference(); // remove the connection from the association dictionary so that nobody // picks it up ConnectionAborted(CConnection); #if defined (RPC_IDLE_CLEANUP_AUDIT) ConnectionsPickedForCleanup ++; #endif // are we up to the last connection? if ((ActiveConnections.Size() <= 1) && (MaintainContext != 0)) break; } } } AssociationMutex.Clear(); #if defined (RPC_IDLE_CLEANUP_AUDIT) DbgPrintEx(77, DPFLTR_ERROR_LEVEL, "%d (0x%X) Assoc %p - %d connections picked for cleanup\n", GetCurrentProcessId(), GetCurrentProcessId(), this, ConnectionsPickedForCleanup); #endif return(FirstConnection); } void OSF_CCONNECTION::OsfDeleteIdleConnections ( void ) /*++ Routine Description: This routine will be called to delete connections which have been idle for a certain amount of time. We need to be careful of a couple of things in writing this routine: (1) We dont want to grab the global mutex for too long, because this will prevent threads which are trying to do real work from doing it. (2) We dont want to be holding the global mutex when we delete the connection. --*/ { OSF_CASSOCIATION *Association; OSF_CCONNECTION *FirstConnection; OSF_CCONNECTION *CurrentConnection; OSF_CCONNECTION *NextConnection; BOOL fMutexTaken; DictionaryCursor cursor; ULONG ClientDisconnectTime; #if defined (RPC_IDLE_CLEANUP_AUDIT) DbgPrintEx(77, DPFLTR_ERROR_LEVEL, "%d (0x%X) Attempting OSF garbage collection\n", GetCurrentProcessId(), GetCurrentProcessId()); #endif fMutexTaken = AssocDictMutex->TryRequest(); if (!fMutexTaken) { #if defined (RPC_IDLE_CLEANUP_AUDIT) DbgPrintEx(77, DPFLTR_ERROR_LEVEL, "%d (0x%X) Association dict mutex busy - aborting\n", GetCurrentProcessId(), GetCurrentProcessId()); #endif return; } AssociationDict->Reset(cursor); while ( (Association = AssociationDict->Next(cursor)) != 0 ) { // // The architecture says that the client should disconnect // connections which have been idle too long. // FirstConnection = Association->FindIdleConnection(); if (FirstConnection != 0) { AssocDictMutex->Clear(); CurrentConnection = FirstConnection; while (CurrentConnection != NULL) { NextConnection = CurrentConnection->u.NextConnection; Association->ConnectionAborted(CurrentConnection); CurrentConnection->DeleteConnection(); // CCONN-- CurrentConnection->RemoveReference(); CurrentConnection = NextConnection; } fMutexTaken = AssocDictMutex->TryRequest(); if (!fMutexTaken) return; } } AssocDictMutex->Clear(); } RPC_STATUS OSF_CCONNECTION::TurnOnOffKeepAlives ( IN BOOL TurnOn, IN ULONG Time ) /*++ Routine Description: Turns on or off keepalives for the given connection Arguments: TurnOn - if non-zero, keep alives will be turned on with a value appropriate for this transport. If zero, keep alives will be turned off Timeout - if TurnOn is not zero, the interval between a response to the last keep alive and the next keep alive. If TurnOn is zero this parameter is ignored. The time scale is in runtime units - RPC_C_BINDING_MIN_TIMEOUT to RPC_C_BINDING_MAX_TIMEOUT Return Value: RPC_S_OK if the transport supports keep alives RPC_S_CANNOT_SUPPORT otherwise --*/ { KEEPALIVE_TIMEOUT uTime; uTime.RuntimeUnits = Time; if (ClientInfo->TurnOnOffKeepAlives) { // While turning on the keepalive we need to protect the IO // against connection closure. return ClientInfo->TurnOnOffKeepAlives(TransConnection(), TurnOn, TRUE, tuRuntime, uTime, 5000); } else { return RPC_S_CANNOT_SUPPORT; } } void OSF_CASSOCIATION::OsfDeleteLingeringAssociations ( void ) /*++ Routine Description: Will attempt to clean up lingering conn associations. Return Value: --*/ { BOOL fMutexTaken; OSF_CASSOCIATION *CurrentAssociation; OSF_CASSOCIATION *NextAssociation; OSF_CASSOCIATION *FirstAssociation; DictionaryCursor cursor; DWORD CurrentTickCount; int Diff; // if there are no osf associations, return if (!AssociationDict) return; fMutexTaken = AssocDictMutex->TryRequest(); if (!fMutexTaken) { // we couldn't cleanup anything - restore the flag if (!GarbageCollectionRequested) GarbageCollectionRequested = TRUE; return; } FirstAssociation = NULL; CurrentTickCount = GetTickCount(); // need to walk the dictionary and clean up all associations with // expired timeouts AssociationDict->Reset(cursor); while ((CurrentAssociation = AssociationDict->Next(cursor)) != 0) { if (CurrentAssociation->Linger.fAssociationLingered) { // this will work even for wrapped tick count Diff = (int)(CurrentTickCount - CurrentAssociation->Linger.Timestamp); if (Diff > 0) { #if defined (RPC_GC_AUDIT) DbgPrintEx(77, DPFLTR_WARNING_LEVEL, "%d (0x%X) OSF association gc'ed %d ms after expire\n", GetCurrentProcessId(), GetCurrentProcessId(), Diff); #endif // enlink the expired associations to a list - we'll clean it up // later CurrentAssociation->NextAssociation = FirstAssociation; FirstAssociation = CurrentAssociation; AssociationDict->Delete(CurrentAssociation->Key); // indicate to the other threads (needed once we release the mutex) // that this association is being cleaned up and they cannot clean it up CurrentAssociation->Key = -1; OsfLingeredAssociations --; // Add one artificial reference to the association. This will // prevent a thread that is doing unbind and lingered the // association, but later found out it couldn't destroy it // (see OSF_CASSOCIATION::UnBind) from deleteing the object // from underneath us // CASSOC++ CurrentAssociation->OSF_CASSOCIATION::AddReference(); } else { // this item hasn't expired yet - update the first gc time, and // raise the GarbageCollectionRequested flag if necessary if ((int)(CurrentAssociation->Linger.Timestamp - NextOneTimeCleanup) < 0) { // there is a race between this thread and threads calling // GarbageCollectionNeeded. Those threads may overwrite the // value we're about to write, which can result in delayed // garbage collection for this value - that's ok. NextOneTimeCleanup = CurrentAssociation->Linger.Timestamp; } if (!GarbageCollectionRequested) GarbageCollectionRequested = TRUE; } } } AssocDictMutex->Clear(); // destroy the associations at our leasure CurrentAssociation = FirstAssociation; while (CurrentAssociation != NULL) { CurrentAssociation->AssociationMutex.Request(); NextAssociation = CurrentAssociation->NextAssociation; CurrentAssociation->AssociationValid = FALSE; CurrentAssociation->ShutdownRequested(RPC_S_CALL_FAILED_DNE, NULL); CurrentAssociation->AssociationMutex.Clear(); // Remove the artificial reference we added above // CASSOC-- CurrentAssociation->OSF_CASSOCIATION::RemoveReference(); CurrentAssociation->OSF_CASSOCIATION::RemoveReference(); CurrentAssociation = NextAssociation; } } int InitializeRpcProtocolOfsClient ( ) /*++ Routine Description: We perform loadtime initialization necessary for the code in this file. In particular, it means we allocate the association dictionary and the association dictionary mutex. Return Value: Zero will be returned if initialization completes successfully; otherwise, non-zero will be returned. --*/ { RPC_STATUS Status = RPC_S_OK; if (AssocDictMutex == NULL) { AssocDictMutex = new MUTEX(&Status, TRUE // pre-allocate semaphore ); if (AssocDictMutex == 0 || Status != RPC_S_OK) { delete AssocDictMutex; AssocDictMutex = NULL; return 1; } } if (AssociationDict == NULL) { AssociationDict = new OSF_CASSOCIATION_DICT; if (AssociationDict == 0) { delete AssocDictMutex; AssocDictMutex = NULL; return(1); } } return(0); } RPC_STATUS OsfMapRpcProtocolSequence ( IN BOOL ServerSideFlag, IN RPC_CHAR * RpcProtocolSequence, OUT TRANS_INFO * *ClientTransInfo ) /*++ Routine Description: This routine is used to determine whether a given rpc protocol sequence is supported, and to get a pointer to the transport interface, so we do not have to keep looking it up. Arguments: RpcProtocolSequence - Supplies the rpc protocol sequence. A pointer to the transport interface is returned. This pointer must not be dereferenced by the caller. Return Value: RPC_S_OK - The rpc protocol sequence is supported. RPC_S_PROTSEQ_NOT_SUPPORTED - The rpc protocol sequence is not supported. RPC_S_OUT_OF_MEMORY - There is insufficient memory to perform the operation. --*/ { RPC_CHAR * DllName; RPC_STATUS Status; Status= RpcConfigMapRpcProtocolSequence(ServerSideFlag, RpcProtocolSequence, &DllName); if (Status != RPC_S_OK) { return Status; } Status = LoadableTransportInfo(DllName, RpcProtocolSequence, ClientTransInfo); delete DllName; return(Status); } BINDING_HANDLE * OsfCreateBindingHandle ( ) /*++ Routine Description: This routine does exactly one thing: it creates a binding handle of the appropriate type for the osf connection protocol module. Return Value: A new binding handle will be returned. Zero will be returned if insufficient memory is available to create the binding handle. --*/ { BINDING_HANDLE * BindingHandle; RPC_STATUS Status = RPC_S_OK; BindingHandle = new OSF_BINDING_HANDLE(&Status); if ( Status != RPC_S_OK ) { delete BindingHandle; return(0); } return(BindingHandle); } extern "C" { RPC_STATUS OsfTowerConstruct( IN char * ProtocolSeq, IN char * Endpoint, IN char * NetworkAddress, OUT unsigned short * Floors, OUT ULONG * ByteCount, OUT unsigned char * * Tower ) /*++ Routine Description: This routine constructs and returns the upper floors of a tower. It invokes the appropriate loadable transport and has them construct it. Return Value: --*/ { TRANS_INFO *ClientTransInfo; RPC_STATUS Status = RPC_S_OK; #ifdef UNICODE CStackUnicode Pseq; USES_CONVERSION; #else RPC_CHAR * Pseq; #endif #ifdef UNICODE ATTEMPT_STACK_A2W(Pseq, ProtocolSeq); #else Pseq = (RPC_CHAR * )ProtocolSeq; #endif if (Status == RPC_S_OK) { Status = OsfMapRpcProtocolSequence( 0, Pseq, &ClientTransInfo); } if (Status == RPC_S_OK) { Status = ClientTransInfo->InqTransInfo()->TowerConstruct( ProtocolSeq, NetworkAddress, Endpoint, Floors, ByteCount, Tower); } return(Status); } RPC_STATUS OsfTowerExplode( IN BYTE *Floor, IN BYTE *UpperBound, IN ULONG RemainingFloors, OUT char PAPI * PAPI * Protseq, OPTIONAL OUT char PAPI * PAPI * Endpoint, OPTIONAL OUT char PAPI * PAPI * NWAddress OPTIONAL ) /*++ Routine Description: This routine accepts upper floors of a tower [Floor 3 onwards] It invokes the appropriate loadable transport based on the opcode it finds in level 3 to return protocol sequence, endpoint and nwaddress. This routine doesn't do the decoding itself - rather it just dispatches to the appropriate transport routine. Return Value: --*/ { RPC_STATUS Status; unsigned short TransportId; unsigned char * ProtocolSequence; TRANS_INFO *ClientTransInfo; CStackUnicode Pseq; USES_CONVERSION; // first, do some basic verification to make sure we can read this floor if (Floor + sizeof(FLOOR_234) - 2 >= UpperBound) return EP_S_CANT_PERFORM_OP; if (RemainingFloors == 0) return EP_S_CANT_PERFORM_OP; TransportId = ((PFLOOR_234)Floor)->FloorId; ClientTransInfo = GetLoadedClientTransportInfoFromId(TransportId); if (ClientTransInfo == 0) { Status = RpcGetWellKnownTransportInfo(TransportId, Pseq.GetPUnicodeString()); if (Status != RPC_S_OK) { Status = RpcGetAdditionalTransportInfo(TransportId, &ProtocolSequence); if (Status == RPC_S_OK) { ATTEMPT_STACK_A2W(Pseq, ProtocolSequence); } else { return (Status); } } Status = OsfMapRpcProtocolSequence(0, Pseq, &ClientTransInfo); if (Status != RPC_S_OK) { return Status; } } ASSERT(ClientTransInfo != 0); Status = ClientTransInfo->InqTransInfo()->TowerExplode( Floor, UpperBound, RemainingFloors, Protseq, NWAddress, Endpoint); return(Status); } } // extern "C" void ReleaseBindingListWithException ( IN OSF_BINDING *ExemptBinding, OPTIONAL IN OSF_BINDING *BindingList ) /*++ Routine Description: Manages the selection of a binding from a list in terms of refcounting. This means releasing one refcount for every binding except the selected binding. Arguments: ExemptBinding - the binding that was selected. If NULL, no binding was selected. BindingList - the list of bindings. Return Value: --*/ { OSF_BINDING *CurrentBinding; OSF_BINDING *NextBinding; CurrentBinding = BindingList; while (CurrentBinding) { // grab the next binding before we release the refcount NextBinding = CurrentBinding->GetNextBinding(); if (CurrentBinding != ExemptBinding) CurrentBinding->RemoveReference(); CurrentBinding = NextBinding; } } RPC_STATUS SetAuthInformation ( IN RPC_BINDING_HANDLE BindingHandle, IN CLIENT_AUTH_INFO *AuthInfo ) /*++ Routine Description: Sets the auth info for a binding handle. Caller must take care to pass only OSF binding handles. Transport credentials are set, but normal are not (on purpose). Arguments: BindingHandle - the binding handle on which to set auth info. AuthInfo - the auth info to set. Return Value: RPC_S_OK or RPC_S_* for error --*/ { OSF_BINDING_HANDLE *OsfBindingHandle; SEC_WINNT_AUTH_IDENTITY_W *NewAuthIdentity; RPC_HTTP_TRANSPORT_CREDENTIALS_W *HttpCredentials; RPC_STATUS Status; OsfBindingHandle = (OSF_BINDING_HANDLE *)BindingHandle; // the only non-trivial piece to copy is the transport credentials // since they are encrypted in memory if (AuthInfo->AdditionalTransportCredentialsType == RPC_C_AUTHN_INFO_TYPE_HTTP) { ASSERT(AuthInfo->AdditionalCredentials); HttpCredentials = (RPC_HTTP_TRANSPORT_CREDENTIALS_W *)AuthInfo->AdditionalCredentials; HttpCredentials = DuplicateHttpTransportCredentials(HttpCredentials); if (HttpCredentials == NULL) return RPC_S_OUT_OF_MEMORY; if (HttpCredentials->TransportCredentials) { Status = DecryptAuthIdentity(HttpCredentials->TransportCredentials); if (Status != RPC_S_OK) { // on failure the credentials will be wiped out already by // DecryptAuthIdentity. Just free the credentials FreeHttpTransportCredentials (HttpCredentials); return Status; } // the credentials are not encrypted anymore HttpCredentials->Flags &= ~RPC_C_HTTP_FLAG_AUTH_IDENTITY_ENCRYPTED; } } else { HttpCredentials = NULL; } Status = OsfBindingHandle->SetAuthInformation( NULL, AuthInfo->AuthenticationLevel, RPC_C_AUTHN_NONE, AuthInfo->AuthIdentity, AuthInfo->AuthorizationService, NULL, // SecurityCredentials RPC_C_IMP_LEVEL_IMPERSONATE, RPC_C_QOS_IDENTITY_STATIC, RPC_C_QOS_CAPABILITIES_DEFAULT, TRUE, // Acquire new credentials AuthInfo->AdditionalTransportCredentialsType, HttpCredentials, NULL ); // success or not, we need to wipe out and free the transport credentials (if any) if (HttpCredentials) { WipeOutAuthIdentity (HttpCredentials->TransportCredentials); FreeHttpTransportCredentials (HttpCredentials); } return Status; } RPC_STATUS RPC_ENTRY I_RpcTransIoCancelled ( IN RPC_TRANSPORT_CONNECTION ThisConnection, OUT PDWORD Timeout ) /*++ Function Name:I_RpcTransIOCancelled Parameters: TransConnection: The connection on which the thread was partying on, when the alert was received. Timeout - If the call was cancelled, on return, this param will contain the cancel timeout. Description: This function is called by the transport interface, when it notices that an alert has been received. This function should only be called when the transport is dealing with a sync call. Returns: RPC_S_OK: The call was cancelled RPC_S_NO_CALL_ACTIVE: no call was cancelled others - If a failure occured. --*/ { THREAD *ThreadInfo = RpcpGetThreadPointer(); ASSERT(ThreadInfo != 0); if (ThreadInfo->GetCallCancelledFlag() == 0) { return RPC_S_NO_CALL_ACTIVE; } return InqTransCConnection(ThisConnection)->CallCancelled(Timeout); } unsigned short RPC_ENTRY I_RpcTransClientMaxFrag ( IN RPC_TRANSPORT_CONNECTION ThisConnection ) /*++ Routine Description: The client side transport interface modules will use this routine to determine the negotiated maximum fragment size. Arguments: ThisConnection - Supplies the connection for which we are returning the maximum fragment size. --*/ { return (unsigned short) InqTransCConnection(ThisConnection)->InqMaximumFragmentLength(); } BUFFER RPC_ENTRY I_RpcTransConnectionAllocatePacket( RPC_TRANSPORT_CONNECTION ThisConnection, UINT Size ) /*++ Function Name:I_RpcTransConnectionAllocatePacket Parameters: Description: Returns: --*/ { return(RpcAllocateBuffer(Size)); } void RPC_ENTRY I_RpcTransConnectionFreePacket( RPC_TRANSPORT_CONNECTION ThisConnection, BUFFER Ptr ) /*++ Function Name:I_RpcTransConnectionFreePacket Parameters: Description: Returns: --*/ { RpcFreeBuffer(Ptr); } RPC_STATUS RPC_ENTRY I_RpcTransConnectionReallocPacket( IN RPC_TRANSPORT_CONNECTION ThisConnection, IN BUFFER *ppBuffer, IN UINT OldSize, IN UINT NewSize ) /*++ Function Name:I_RpcTransConnectionReallocPacket Parameters: Description: Returns: --*/ { // We may be truncationg buffers using this function // when running with corruption injection. // In that case NewSize < OldSize and we should not assert. CORRUPTION_ASSERT(NewSize > OldSize); PVOID Buffer = RpcAllocateBuffer(NewSize); if (Buffer) { if (OldSize) { // When expanding the buffer: OldSizefPinging) { // // We are already pinging, it is best to give up // on the server // return FALSE; } ThisThread->fPinging = 1; EVENT ThreadEvent(&Status); EVENT WriteEvent(&Status); HANDLE hOldThreadEvent, hOldWriteEvent; ASSERT(ThisThread); if (Status != RPC_S_OK) { return TRUE; } hOldWriteEvent = ThisThread->hWriteEvent; hOldThreadEvent = ThisThread->hThreadEvent; ThisThread->hWriteEvent = WriteEvent.EventHandle; ThisThread->hThreadEvent = ThreadEvent.EventHandle; fRetVal = InqTransCConnection(ThisConnection)->PingServer(); ThisThread->hWriteEvent = hOldWriteEvent; ThisThread->hThreadEvent = hOldThreadEvent; ThisThread->fPinging = 0; return fRetVal; #else return(FALSE); #endif } void I_RpcTransVerifyClientRuntimeCallFromContext( void *SendContext ) /*++ Routine Description: Verifies that the supplied context follows a valid runtime client call object. Arguments: SendContext - the context as seen by the transport Return Value: --*/ { REFERENCED_OBJECT *pObj; pObj = (REFERENCED_OBJECT *) *((PVOID *) ((char *) SendContext - sizeof(void *))); ASSERT(pObj->InvalidHandle(OSF_CCALL_TYPE | OSF_CCONNECTION_TYPE) == 0); } BOOL I_RpcTransIsClientConnectionExclusive ( void *RuntimeConnection ) /*++ Routine Description: Checks whether the supplied runtime connection is exclusive. Arguments: RuntimeConnection - the connection to check. Return Value: non-zero if the connection is exclusive. 0 otherwise. --*/ { OSF_CCONNECTION *Connection = InqTransCConnection(RuntimeConnection); ASSERT(Connection->InvalidHandle(OSF_CCONNECTION_TYPE) == 0); return Connection->IsExclusive(); } RPC_STATUS RPC_ENTRY I_RpcBindingInqWireIdForSnego ( IN RPC_BINDING_HANDLE Binding, OUT unsigned char *WireId ) /*++ Routine Description: Arguments: Return Value: The status for the operation is returned. --*/ { OSF_CCALL * Call; InitializeIfNecessary(); Call = (OSF_CCALL *) Binding; if (Call->InvalidHandle(OSF_CCALL_TYPE)) return(RPC_S_INVALID_BINDING); return(Call->InqWireIdForSnego(WireId)); } RPCRTAPI RPC_STATUS RPC_ENTRY I_RpcBindingHandleToAsyncHandle ( IN RPC_BINDING_HANDLE Binding, OUT void **AsyncHandle ) { OSF_CCALL * Call; InitializeIfNecessary(); Call = (OSF_CCALL *) Binding; if (Call->InvalidHandle(OSF_CCALL_TYPE)) return(RPC_S_INVALID_BINDING); return Call->BindingHandleToAsyncHandle(AsyncHandle); } RPC_STATUS RPC_ENTRY I_RpcBindingInqMarshalledTargetInfo ( IN RPC_BINDING_HANDLE Binding, OUT unsigned long *MarshalledTargetInfoLength, OUT unsigned char **MarshalledTargetInfo ) /*++ Routine Description: Queries the security context for a given binding handle to obtain the MarshalledTargetInfo. Arguments: Binding - MarshalledTargetInfo - String allocated by the RPC runtime containing the marshalled TargetInfo. The user frees this string. MarshalledTargetInfoLength - Length of the string allocated by the RPC runtime containing the marshalled TargetInfo. Return Value: The status for the operation is returned. --*/ { OSF_CCALL * Call; InitializeIfNecessary(); Call = (OSF_CCALL *) Binding; if (Call->InvalidHandle(OSF_CCALL_TYPE)) return(RPC_S_INVALID_BINDING); return(Call->InqMarshalledTargetInfo(MarshalledTargetInfoLength,MarshalledTargetInfo)); } extern "C" { void I_Trace ( int IgnoreFirst, const char * IgnoreSecond, ... ) /*++ Routine Description: This is an old routine which is no longer used. Because it is exported by the dll, we need to leave an entry point. --*/ { UNUSED(IgnoreFirst); UNUSED(IgnoreSecond); } }; // extern "C"