windows-nt/Source/XPSP1/NT/com/rpc/runtime/mtrt/osfclnt.cxx
2020-09-26 16:20:57 +08:00

11976 lines
327 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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>
//
// 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;
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();
}
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(fNamedPipe == 1);
ASSERT(ClientAuthInfo.AuthenticationService == RPC_C_AUTHN_NONE);
ASSERT(Association);
if (OpenThreadToken (GetCurrentThread(),
TOKEN_IMPERSONATE | TOKEN_QUERY,
TRUE,
&ImpersonationToken) == FALSE)
{
ClientAuthInfo.DefaultLogonId = TRUE;
pToken = NULL;
if (GetLastError() != ERROR_NO_TOKEN)
{
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);
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;
}
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 && fNamedPipe))
{
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;
}
if (this->Association != 0)
{
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();
Unbind();
if (DceBinding == NULL)
{
BindingMutex.Clear();
return RPC_S_OUT_OF_MEMORY;
}
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.
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 && fNamedPipe)
{
if (TransAuthInitialized == 0)
{
Status = AcquireCredentialsForTransport();
if (Status != RPC_S_OK)
{
BindingMutex.Clear();
return Status;
}
TransAuthInitialized = 1;
}
}
}
else
{
if (Association->IsValid() == 0)
{
if (ReferenceCount == 1)
{
DceBinding = Association->DuplicateDceBinding();
if (DceBinding == 0)
{
Status = RPC_S_OUT_OF_MEMORY;
}
else
{
Status = Association->AssociationShutdownError;
Unbind();
*Retry = TRUE;
}
}
else
{
Status = Association->AssociationShutdownError;
}
BindingMutex.Clear();
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->fNamedPipe = fNamedPipe;
Binding->fDynamicEndpoint = fDynamicEndpoint;
if (pToken)
{
ASSERT(Association);
ASSERT(fNamedPipe);
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.
--*/
{
this->TransInfo = (TRANS_INFO *) TransInfo;
this->DceBinding = DceBinding;
fDynamicEndpoint = DceBinding->IsNullEndpoint();
// we count it as named pipes only in the remote case
fNamedPipe = DceBinding->IsNamedPipeTransport() && DceBinding->InqNetworkAddress();
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();
}
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(),
INFINITE, // 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())
{
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,
// choose the association
if (!Result
||
(
fOnlyEndpointDifferent
&& DceBinding->IsNullEndpoint()
&& InterfaceInfo
&& CAssociation->DoesBindingForInterfaceExist(InterfaceInfo)
)
)
{
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;
}
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_WRONG_KIND_OF_BINDING;
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)
OSF_CCONNECTION *
OSF_CASSOCIATION::LookForExistingConnection(
IN OSF_BINDING_HANDLE *BindingHandle,
IN BOOL fExclusive,
IN CLIENT_AUTH_INFO *ClientAuthInfo,
IN int *PresentationContexts,
IN int NumberOfPresentationContexts,
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.
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:
--*/
{
OSF_CCONNECTION *CConnection, *FirstMatch = 0;
DictionaryCursor cursor;
RPC_STATUS Status;
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
&& CConnection->SupportedAuthInfo(ClientAuthInfo,
BindingHandle->fNamedPipe) != 0)
{
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
} // while
if (0 == CConnection && FirstMatch)
{
CConnection = FirstMatch ;
CConnection->ThreadId = cConnectionBusy;
*InitialCallState = NeedAlterContext;
}
}
else
{
DWORD ThreadId = GetCurrentThreadId();
while ((CConnection = ActiveConnections.Next(cursor)) != 0)
{
if (CConnection->SupportedAuthInfo(ClientAuthInfo,
BindingHandle->fNamedPipe) != 0)
{
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();
}
return CConnection;
}
#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);
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;
}
}
}
// at least one binding must be the start of the list
ASSERT(BindingsList != 0);
CConnection = LookForExistingConnection (
BindingHandle,
fExclusive,
ClientAuthInfo,
PresentationContextsToUse,
NumberOfBindingsToUse,
&PresentationContextSupported,
&InitialCallState,
BOOL(fUseSeparateConnection)) ;
AssociationMutex.Clear();
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;
return Status;
}
Status = AssociationMutex.RequestSafe();
if (Status)
{
delete CConnection;
return Status;
}
if (!fExclusive)
{
Status = TransInfo->StartServerIfNecessary();
if (Status != RPC_S_OK)
{
AssociationMutex.Clear();
delete CConnection;
return Status;
}
}
CConnection->ConnectionKey = ActiveConnections.Insert(CConnection);
if (CConnection->ConnectionKey == -1)
{
AssociationMutex.Clear();
delete CConnection;
return RPC_S_OUT_OF_MEMORY;
}
if (IsValid() == FALSE)
{
ActiveConnections.Delete(CConnection->ConnectionKey);
CConnection->ConnectionKey = -1;
AssociationMutex.Clear();
delete CConnection;
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();
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;
}
Status = (*pCCall)->ActivateCall(
BindingHandle,
BindingToUse,
BindingsList,
CallIdToUse,
InitialCallState,
DispatchTableToUse,
CConnection);
if (Status != RPC_S_OK)
{
ConnectionAborted(CConnection);
delete CConnection;
return Status ;
}
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
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();
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)
{
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;
}
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;
}
}
}
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);
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;
*pStatus = TransInitialize(
Association->DceBinding->InqNetworkAddress(),
Association->DceBinding->InqNetworkOptions());
if (*pStatus == RPC_S_OK)
{
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;
}
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,
INFINITE);
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
} 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();
Status = ClientInfo->Open(TransConnection(),
RpcProtocolSequence,
NetworkAddress,
Endpoint,
NetworkOptions,
ComTimeout,
0,
0,
ResolverHint,
fHintInitialized,
CallTimeout,
ClientAuthInfo->AdditionalTransportCredentialsType,
ClientAuthInfo->AdditionalCredentials
);
if (fTokenSwapped)
{
ReplaceToken(OldToken);
if (OldToken)
{
CloseHandle(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 void * Buffer,
IN UINT BufferLength,
IN void *SendContext
)
/*++
Function Name:TransAsyncSend
Parameters:
Description:
Returns:
--*/
{
RPC_STATUS Status;
{
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);
}
DceSecurityInfo.SendSequenceNumber += 1;
Status = ClientInfo->Send(TransConnection(),
BufferLength,
(BUFFER) Buffer,
SendContext);
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)
{
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());
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_P_RECEIVE_ALERTED,
RPC_P_RECEIVE_FAILED,
RPC_P_CONNECTION_SHUTDOWN
} END_VALIDATE;
if (Status != RPC_S_OK)
{
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 void * Buffer,
IN UINT BufferLength,
IN BOOL fDisableShutdownCheck,
IN BOOL fDisableCancelCheck,
IN ULONG Timeout
)
/*++
Routine Description:
Arguments:
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;
{
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);
}
DceSecurityInfo.SendSequenceNumber += 1;
Status = ClientInfo->SyncSend(TransConnection(),
BufferLength,
(BUFFER) Buffer,
fDisableShutdownCheck,
fDisableCancelCheck,
INFINITE); // Timeout
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
} END_VALIDATE;
return(Status);
}
void
OSF_CCONNECTION::TransAbortConnection (
)
{
ClientInfo->Abort(TransConnection());
}
RPC_STATUS
OSF_CCONNECTION::TransSendReceive (
IN void * SendBuffer,
IN UINT SendBufferLength,
OUT void * * ReceiveBuffer,
OUT UINT * ReceiveBufferLength,
IN ULONG Timeout
)
/*++
Routine Description:
Arguments:
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;
{
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);
}
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);
}
}
}
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
} 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);
AddReference();
// make sure the connection gets removed from the dictionary
Association->ConnectionAborted(this);
if (fShutdownAssoc)
{
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))
{
fFreeLastBuffer = TRUE;
}
}
else if ((CCall->CurrentState == NeedOpenAndBind)
||
(CCall->CurrentState == NeedAlterContext)
||
(CCall->CurrentState == WaitingForAlterContext)
||
(CCall->CurrentState == SendingFirstBuffer)
)
{
fFreeLastBuffer = TRUE;
}
}
if (fFreeLastBuffer)
{
TransFreeBuffer(CCall->ActualBuffer(CCall->LastBuffer));
CCall->LastBuffer = NULL;
}
CCall->CallFailed(Status);
}
}
//
// 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;
*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:
Returns:
--*/
{
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;
}
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();
if (CCall->EEInfo)
{
FreeEEInfoChain(CCall->EEInfo);
CCall->EEInfo = NULL;
}
if (fExclusive == 0)
{
ActiveCalls.Delete(IntToPtr(CCall->CallId));
}
if (CCall == CachedCCall)
{
CachedCCallAvailable = 1;
}
else
{
delete CCall;
}
switch (Status)
{
case RPC_S_OUT_OF_MEMORY:
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 = CurrentCall;
switch (Packet->PTYPE)
{
case rpc_request:
case rpc_response:
TransFreeBuffer(BufferToFree);
if (EventStatus == RPC_S_OK)
{
if (Association->fMultiplex == mpx_yes)
{
//
// We are 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);
if (OldCall)
{
//
// The current I/O failed.
// Remove the send reference on the call, CCALL--
//
OldCall->RemoveReference();
}
}
}
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;
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);
ConnectionAborted(RPC_S_CALL_FAILED);
return;
}
ASSERT(Buffer);
unsigned long CallId = ((rpcconn_common *) Buffer)->call_id;
if (DataConvertEndian(((rpcconn_common *) Buffer)->drep) != 0)
{
CallId = RpcpByteSwapLong(CallId);
}
ConnMutex.Request();
CCall = ActiveCalls.Find(IntToPtr(CallId));
if (CCall)
{
if (CCall->CurrentState == Aborted)
{
ConnMutex.Clear();
TransAbortConnection();
return;
}
// CCALL++
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);
ConnMutex.Request();
CCall->RemoveReference(); // CCALL--
ConnMutex.Clear();
}
else
{
ConnMutex.Clear();
fSubmitReceive = 0;
TransAbortConnection();
}
if (fSubmitReceive)
{
//
// TransAsyncReceive will retry several times
// before giving up
//
TransAsyncReceive ();
}
}
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)
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);
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;
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( 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
} 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 )
{
Status = FinishSecurityContextSetup(
CCall,
MyAssocGroupId,
&Buffer,
&BufferLength,
Timeout
);
}
else
{
TransFreeBuffer(Buffer);
}
if ( Status == RPC_S_OK )
{
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(
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:
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)
{
ConnectionReady = 0;
CCall->SendAlterContextPDU();
}
else
{
*AlterContextToNDR20IfNDR64Negotiated = FALSE;
}
}
}
return Status;
}
RPC_STATUS
OSF_CCONNECTION::SendBindPacket (
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:
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, AuthPadLength, 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);
//
// 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);
}
}
//
// We align the packet length to a four byte boundary, and then
// 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.
//
AuthPadLength = Pad4(BindPacketLength);
BindPacketLength += AuthPadLength;
TokenLength = ClientSecurityContext.Credentials->MaximumTokenLength();
BindPacketLength += TokenLength + sizeof(sec_trailer);
}
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;
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.
//
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 = (unsigned char) AuthPadLength;
SecurityTrailer->auth_reserved = 0;
SecurityTrailer->auth_context_id = PtrToUlong(this);
//
// 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);
}
}
}
if (fAsync)
{
Status = TransAsyncSend(BindPacket,
BindPacketLength,
u.ConnSendContext);
}
else if (BindPacket->common.PTYPE == rpc_auth_3)
{
Status = TransSend(BindPacket,
BindPacketLength,
TRUE, // fDisableShutdownCheck
FALSE, // fDisableCancelCheck
Timeout
);
//
// Null out the reply buffer, because there is none !
//
*Buffer = NULL;
}
else
{
Status = TransSendReceive(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
} 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;
}
#ifndef WIN
ASSERT( MaxFrag >= MUST_RECV_FRAG_SIZE );
#endif // WIN
}
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;
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 ( 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));
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 = PtrToUlong(this);
BufferDescriptor.ulVersion = 0;
BufferDescriptor.cBuffers = 5;
BufferDescriptor.pBuffers = SecurityBuffers;
SecurityBuffers[0].cbBuffer = HeaderSize;
SecurityBuffers[0].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY;
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 | SECBUFFER_READONLY;
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 fragment
// after this (including the header), if you do it will cause a checksum error
//
ASSERT(SecurityBuffers[3].cbBuffer <= pFragment->auth_length);
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);
}
}
if (LastFragmentFlag != 0)
{
ASSERT(!RpcpCheckHeap());
if (fAsync)
{
Status = TransAsyncSend(pFragment,
pFragment->frag_length,
SendContext);
}
else
{
if (ReceiveBuffer)
{
Status = TransSendReceive(pFragment,
pFragment->frag_length,
ReceiveBuffer,
ReceiveBufferLength,
Timeout);
}
else
{
Status = TransSend(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))
{
return(RPC_S_CALL_FAILED_DNE);
}
if (Status == RPC_P_RECEIVE_FAILED)
{
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
} END_VALIDATE;
return(Status);
}
ClearFreshFromCacheFlag();
return(RPC_S_OK);
}
if (fAsync)
{
Status = TransAsyncSend(pFragment,
pFragment->frag_length,
CCall->CallSendContext);
}
else
{
Status = TransSend(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))
{
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
} END_VALIDATE;
return(Status);
}
ClearFreshFromCacheFlag();
return Status;
}
inline int
OSF_CCONNECTION::SupportedAuthInfo (
IN CLIENT_AUTH_INFO * ClientAuthInfo,
IN BOOL fNamedPipe
)
/*++
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.
Return Value:
Non-zero indicates that the connection has the requested authentication
and authorization information; otherwise, zero will be returned.
--*/
{
return (ClientSecurityContext.IsSupportedAuthInfo(ClientAuthInfo, fNamedPipe));
}
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);
}
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;
#ifdef DEBUGRPC
CallbackLevel = 0;
#endif
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;
}
}
}
// 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
if ((MaxSecuritySize != 0) || (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:
Description:
Only Binding or AvailableBindingsList can be used, but not both
Returns:
--*/
{
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;
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;
fDoFlowControl = 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 = ThreadSelf();
if (CallingThread == 0)
{
//
// If the thread pointer should have been initialized in
// GetBuffer.
return RPC_S_INTERNAL_ERROR;
}
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 (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;
}
//
// 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;
//
// 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. If the buffer is large, we allocate the new
call first, and copy the contents of the buffers directly. Otherwise,
we allocate the new call first, and then we copy directly from the old
call buffer to the new call buffer. This saves on double copying in the
large buffer case.
Returns:
--*/
{
void *TempBuffer;
RPC_STATUS Status;
UUID MyUuid;
UUID *UuidToUse;
BOOL fUuidSpecified;
OSF_CCALL *OldCall;
BOOL fLargeBuffer;
// any failure after this is unrelated
RpcpPurgeEEInfo();
OldCall = (OSF_CCALL *) Message->Handle;
// if the buffer is larger than 128K (absolutely arbitrary value),
// we'd rather open a new connection, than copy the buffer twice
fLargeBuffer = Message->BufferLength > (1 << 17);
ASSERT(OldCall->HeaderSize != 0);
fUuidSpecified = OldCall->UuidSpecified;
if (fUuidSpecified)
{
RpcpMemoryCopy(&MyUuid,
&(OldCall->ObjectUuid), sizeof(UUID));
}
if (fLargeBuffer)
{
TempBuffer = Message->Buffer;
}
else
{
TempBuffer = RpcpFarAllocate(Message->BufferLength);
if (TempBuffer != 0)
RpcpMemoryCopy(TempBuffer, Message->Buffer, Message->BufferLength);
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, 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);
}
if (fLargeBuffer)
OldCall->CleanupOldCallOnAutoRetry(TempBuffer, fSendReceivePath, CurrentStatus);
else
RpcpFarFree(TempBuffer);
return Status;
}
if (AsyncState)
{
Status = I_RpcAsyncSetHandle(Message, AsyncState);
if (Status != RPC_S_OK)
{
if (fLargeBuffer)
OldCall->CleanupOldCallOnAutoRetry(TempBuffer, fSendReceivePath, CurrentStatus);
else
RpcpFarFree(TempBuffer);
return Status;
}
}
RpcpMemoryCopy(Message->Buffer, TempBuffer, Message->BufferLength);
if (fLargeBuffer)
OldCall->CleanupOldCallOnAutoRetry(TempBuffer, fSendReceivePath, CurrentStatus);
else
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))
{
fDoFlowControl = 1;
CallMutex.Request();
if (RcvBufferLength < Size)
{
if (NOTIFY(Message))
{
NeededLength = Size;
}
CallMutex.Clear();
return RPC_S_ASYNC_CALL_PENDING;
}
else
{
Status = GetCoalescedBuffer(Message);
}
CallMutex.Clear();
}
else
{
return RPC_S_ASYNC_CALL_PENDING;
}
break;
}
break;
}
Message->DataRepresentation = Connection->Association->SavedDrep;
if (Status != RPC_S_OK)
{
//
// FreeBuffer is not going to be called. Cleanup now..
//
AsyncStatus = Status;
// remove the call reference, CCALL--
OSF_CCALL::RemoveReference();
}
return Status;
}
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;
int 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)
{
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;
}
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((char *) Message->Buffer-sizeof(rpcconn_request));
return Status;
}
Message->Buffer = (char *) Message->Buffer + sizeof(rpcconn_request);
BytesRemaining = NewMessage.BufferLength;
}
RpcpMemoryCopy((char *) Message->Buffer+BytesRead,
NewMessage.Buffer,
NewMessage.BufferLength);
Connection->TransFreeBuffer(NewBuffer);
BytesRead += NewMessage.BufferLength;
BytesRemaining -= NewMessage.BufferLength;
}
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 ;
ASSERT(CurrentState == InCallbackRequest);
if (CallStack == 1)
{
BindingHandle->AddRecursiveEntry(this,
(RPC_CLIENT_INTERFACE *)
Message->RpcInterfaceInformation);
}
#ifdef DEBUGRPC
EnterCallback();
#endif
if (!COMPLETE(Message))
{
ASSERT(Request);
ASSERT(Request->common.PTYPE == rpc_request);
//
// Receive the complete reply
//
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);
#ifdef DEBUGRPC
ExitCallback();
#endif
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:
if (CallStack == 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;
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;
while (1)
{
//
// This is the only place where we can receive a callback PDU
//
Status = ActuallyProcessPDU(
Request,
BufferLength,
Message);
if (Status != RPC_S_OK)
{
goto Cleanup;
}
VALIDATE(Request->PTYPE)
{
rpc_request,
rpc_response,
rpc_shutdown
} END_VALIDATE;
switch (Request->PTYPE)
{
case rpc_request:
Status = DealWithCallback(
(rpcconn_request *) Request,
Message);
Message->RpcFlags = 0;
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;
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;
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 because
if (pAsync)
{
if (EEInfo)
{
FreeEEInfoChain(EEInfo);
EEInfo = NULL;
}
pThreadEEInfo = RpcpGetEEInfo();
if (pThreadEEInfo)
{
EEInfo = pThreadEEInfo;
RpcpClearEEInfo();
}
if (fLastSendComplete)
{
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->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();
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;
if ((Request->common.pfc_flags & PFC_OBJECT_UUID) != 0)
{
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)
{
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;
}
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:
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)
{
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
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);
}
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 )
{
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:
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);
}
ASSERT(FaultStatus != 0);
Status = MapFromNcaStatusCode(FaultStatus);
ASSERT(Status != RPC_S_OK);
if (((rpcconn_fault *) Packet)->reserved & FaultEEInfoPresent)
{
ExtendedErrorInfo *EEInfo;
UnpickleEEInfoFromBuffer(((rpcconn_fault *) Packet)->buffer,
GetEEInfoSizeFromFaultPacket((rpcconn_fault *) Packet));
EEInfo = RpcpGetEEInfo();
if (EEInfo && pAsync)
{
ASSERT(this->EEInfo == NULL);
// move the eeinfo to the call. Even though it is possible
// that the call will be completed on this thread, it is
// still ok, as we will move it back during completion
this->EEInfo = EEInfo;
RpcpClearEEInfo();
}
}
//
// 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);
ActuallyFreeBuffer(Packet);
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 UUID *ObjectUuid
)
{
OSF_BINDING_HANDLE *BindingHandle;
OSF_CCALL *CCall;
RPC_STATUS Status;
RPC_SYNTAX_IDENTIFIER TransferSyntax;
BOOL fInterfaceSupportsMultipleTransferSyntaxes;
BindingHandle = (OSF_BINDING_HANDLE *)Message->Handle;
ASSERT(BindingHandle->Type(OSF_BINDING_HANDLE_TYPE));
fInterfaceSupportsMultipleTransferSyntaxes =
DoesInterfaceSupportMultipleTransferSyntaxes(Message->RpcInterfaceInformation);
if (fInterfaceSupportsMultipleTransferSyntaxes)
RpcpMemoryCopy(&TransferSyntax, Message->TransferSyntax, sizeof(RPC_SYNTAX_IDENTIFIER));
Status = BindingHandle->OSF_BINDING_HANDLE::NegotiateTransferSyntax(Message);
if (Status != RPC_S_OK)
return Status;
CCall = (OSF_CCALL *)Message->Handle;
if (fInterfaceSupportsMultipleTransferSyntaxes)
{
if (RpcpMemoryCompare(&TransferSyntax, 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)
{
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));
}
((rpcconn_request *) pFragment)->alloc_hint = CurrentBufferLength;
((rpcconn_request *) pFragment)->p_cont_id
= GetSelectedBinding()->GetOnTheWirePresentationContext();
((rpcconn_request *) pFragment)->opnum = (unsigned short) ProcNum;
}
else
{
((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;
}
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))
{
AsyncStatus = RPC_S_OK;
CurrentState = Complete;
CallMutex.Clear();
if (Connection->Association->fMultiplex == mpx_no
&& fOkToAdvanceCall())
{
//
// In the multiplexed case, the call is advanced
// when the send completes
//
Connection->AdvanceToNextCall();
}
IssueNotification();
}
else
{
if (pAsync == 0)
{
if (BufferQueue.Size() >= 4
&& pfSubmitReceive)
{
fPeerChoked = 1;
*pfSubmitReceive = 0;
}
CallMutex.Clear();
SyncEvent.Raise();
}
else
{
if (NeededLength > 0
&& RcvBufferLength > NeededLength)
{
IssueNotification(RpcReceiveComplete);
}
else
{
if (fDoFlowControl
&& 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(
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;
if ( Request->common.auth_length != 0 )
{
SecurityTrailer = (sec_trailer *)
(((unsigned char *) Request)
+ Request->common.frag_length
- Request->common.auth_length
- sizeof(sec_trailer));
ASSERT(SecurityTrailer->auth_context_id == PtrToUlong(Connection));
if ((Connection->ClientSecurityContext.AuthenticationLevel
== RPC_C_AUTHN_LEVEL_NONE))
{
return(RPC_S_PROTOCOL_ERROR);
}
*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 | SECBUFFER_READONLY;
SecurityBuffers[0].pvBuffer = ((unsigned char *) SavedHeader);
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 | SECBUFFER_READONLY;
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_SHUTDOWN_IN_PROGRESS) ||
(Status == ERROR_PASSWORD_MUST_CHANGE) ||
(Status == ERROR_PASSWORD_EXPIRED) ||
(Status == ERROR_ACCOUNT_DISABLED) ||
(Status == ERROR_INVALID_LOGON_HOURS));
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(&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(&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(&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();
}
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);
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
{
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 connection when the original remote
procedure call using it completes.
--*/
{
void *Buffer;
UINT BufferLength;
OSF_BINDING_HANDLE *MyBindingHandle;
ULONG Timeout;
ASSERT(BindingHandle != 0);
LogEvent(SU_CCALL, EV_DELETE, this, NULL, Status, 1, 1);
if (EEInfo)
{
ASSERT(pAsync);
ASSERT(RpcpGetEEInfo() == NULL);
// move the eeinfo to the thread
RpcpSetEEInfo(EEInfo);
EEInfo = NULL;
}
//
// Empty the buffer queue and nuke the buffers
//
while (Buffer = BufferQueue.TakeOffQueue(&BufferLength))
{
if (InReply)
{
ActuallyFreeBuffer((char *) Buffer-sizeof(rpcconn_request));
}
else
{
FreeBufferDo(Buffer);
}
}
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.
//
//
// 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();
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 == FALSE)
{
// 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)
{
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)
{
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);
Bindings.AvailableBindingsList = 0;
if (Bindings.SelectedBinding == NULL)
{
Bindings.SelectedBinding = BindingToUse;
}
*BindingNegotiated = BindingToUse;
DispatchTableCallback = BindingToUse->GetDispatchTable();
}
return RPC_S_OK;
}
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;
}
OSF_CASSOCIATION::~OSF_CASSOCIATION (
)
{
OSF_BINDING * Binding;
DictionaryCursor cursor;
if (ResolverHintInitialized)
{
FreeResolverHint(InqResolverHint());
}
if (DceBinding != 0)
{
delete DceBinding;
}
Bindings.Reset(cursor);
while ((Binding = Bindings.Next(cursor)))
delete Binding;
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
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
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)
{
UnpickleEEInfoFromBuffer(pBindNak->buffer,
GetEEInfoSizeFromBindNakPacket(pBindNak));
}
}
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, 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;
// if we are not in lsa, just ask for the mutex. If we are (or may be)
// we have some more work to do. 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. 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
if (!fMaybeLsa)
{
AssociationMutex.Request();
}
else
{
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();
}
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 time scale after which
to turn on keep alives. If TurnOn is zero this parameter
is ignored. The time scale is in runtime unitsd -
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);
}
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;
AssocDictMutex = new MUTEX(&Status,
TRUE // pre-allocate semaphore
);
if (AssocDictMutex == 0
|| Status != RPC_S_OK)
{
delete AssocDictMutex;
return 1;
}
AssociationDict = new OSF_CASSOCIATION_DICT;
if (AssociationDict == 0)
{
delete AssocDictMutex;
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 RPC_ENTRY
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 RPC_ENTRY
OsfTowerExplode(
IN unsigned char * Tower,
OUT char * * Protseq,
OUT char * * Endpoint,
OUT char * * NWAddress
)
/*++
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.
Return Value:
--*/
{
RPC_STATUS Status;
unsigned short TransportId;
unsigned char * Id;
unsigned char * ProtocolSequence;
TRANS_INFO *ClientTransInfo;
#ifdef UNICODE
CStackUnicode Pseq;
USES_CONVERSION;
#else
RPC_CHAR * Pseq;
#endif
Id = (Tower + sizeof(unsigned short));
TransportId = (0x00FF & *Id);
ClientTransInfo = GetLoadedClientTransportInfoFromId(TransportId);
if (ClientTransInfo != 0)
{
Status = ClientTransInfo->InqTransInfo()->TowerExplode(
Tower,
Protseq,
NWAddress,
Endpoint);
return(Status);
}
#ifndef UNICODE
Status = RpcGetWellKnownTransportInfo(TransportId, &Pseq);
#else
Status = RpcGetWellKnownTransportInfo(TransportId, Pseq.GetPUnicodeString());
#endif
if (Status != RPC_S_OK)
{
Status = RpcGetAdditionalTransportInfo(TransportId, &ProtocolSequence);
if (Status == RPC_S_OK)
{
#ifdef UNICODE
ATTEMPT_STACK_A2W(Pseq, ProtocolSequence);
#else
Pseq = (RPC_CHAR *) ProtocolSequence;
#endif
}
else
{
return (Status);
}
}
Status = OsfMapRpcProtocolSequence(0,
Pseq,
&ClientTransInfo);
if (Status == RPC_S_OK)
{
ASSERT(ClientTransInfo != 0);
Status = ClientTransInfo->InqTransInfo()->TowerExplode(
Tower,
Protseq,
NWAddress,
Endpoint);
}
return(Status);
}
} // extern "C"
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;
}
}
}
else
{
HttpCredentials = NULL;
}
Status = OsfBindingHandle->SetAuthInformation(
NULL,
AuthInfo->AuthenticationLevel,
RPC_C_AUTHN_NONE,
AuthInfo->AuthIdentity,
AuthInfo->AuthorizationService,
NULL, // SecurityCredentials
AuthInfo->ImpersonationType,
AuthInfo->IdentityTracking,
AuthInfo->Capabilities,
TRUE, // Acquire new credentials
AuthInfo->AdditionalTransportCredentialsType,
HttpCredentials
);
// 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:
--*/
{
ASSERT(NewSize > OldSize);
PVOID Buffer = RpcAllocateBuffer(NewSize);
if (Buffer)
{
if (OldSize)
{
RpcpMemoryCopy(Buffer, *ppBuffer, OldSize);
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 = (OSF_CCONNECTION *)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);
}
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"