Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

13406 lines
393 KiB

/*++
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 <precomp.hxx>
#include <osfpcket.hxx>
#include <bitset.hxx>
#include <queue.hxx>
#include <ProtBind.hxx>
#include <osfclnt.hxx>
#include <rpccfg.h>
#include <epmap.h>
#include <twrtypes.h>
#include <hndlsvr.hxx>
#include <schnlsp.h>
#include <charconv.hxx>
#include <rpcqos.h>
#include <epmp.h>
//
// 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: OldSize<NewSize and we must copy over OldSize bytes.
// When truncating the buffer: NewSize<OldSize and we must copy over NewSize bytes.
RpcpMemoryCopy(Buffer, *ppBuffer, min(OldSize, NewSize));
RpcFreeBuffer(*ppBuffer);
}
*ppBuffer = Buffer;
return(RPC_S_OK);
}
return(RPC_S_OUT_OF_MEMORY);
}
BOOL RPC_ENTRY
I_RpcTransPingServer (
IN RPC_TRANSPORT_CONNECTION ThisConnection
)
{
#if 0
RPC_STATUS Status = RPC_S_OK;
THREAD *ThisThread = ThreadSelf();
BOOL fRetVal;
if (ThisThread->fPinging)
{
//
// 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"