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