mirror of https://github.com/lianthony/NT4.0
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.
6666 lines
160 KiB
6666 lines
160 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
dgsvr.cxx
|
|
|
|
Abstract:
|
|
|
|
This is the server protocol code for datagram rpc.
|
|
|
|
Author:
|
|
|
|
Dave Steckler (davidst) 15-Dec-1992
|
|
|
|
Revision History:
|
|
|
|
Jeff Roberts (jroberts) 11-22-1994
|
|
|
|
Rewrote it.
|
|
|
|
--*/
|
|
#include <precomp.hxx>
|
|
|
|
//
|
|
// Remember that any #defines must go AFTER the precompiled header in order
|
|
// to be noticed by the compiler.
|
|
//
|
|
|
|
#include "sdict2.hxx"
|
|
#include "secsvr.hxx"
|
|
#include "hndlsvr.hxx"
|
|
#include "dgpkt.hxx"
|
|
#include "delaytab.hxx"
|
|
#include "hashtabl.hxx"
|
|
#include "dgsvr.hxx"
|
|
#include <conv.h>
|
|
#ifdef WIN96
|
|
#include <time.h>
|
|
#endif
|
|
|
|
#if !defined(WIN96)
|
|
char __pure_virtual_called()
|
|
{
|
|
ASSERT(0 && "rpc: pure virtual fn called in dg");
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
const unsigned FRAG_RETRY_COUNT = 5;
|
|
|
|
#define EPMAP_LOOKUP_LIMIT 100
|
|
|
|
#define RPC_NCA_PACKET_FLAGS (RPC_NCA_FLAGS_IDEMPOTENT | RPC_NCA_FLAGS_BROADCAST | RPC_NCA_FLAGS_MAYBE)
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
unsigned long ServerBootTime;
|
|
|
|
ACTIVE_CALL_TABLE * ActiveCalls;
|
|
DELAYED_ACTION_NODE * ActiveCallTimer;
|
|
|
|
ASSOC_GROUP_TABLE * AssociationGroups;
|
|
|
|
#ifdef UNRELIABLE_TRANSPORT
|
|
|
|
unsigned SendDupRemaining;
|
|
unsigned ReceiveDupRemaining;
|
|
BOOL Chat;
|
|
|
|
unsigned SendFailPercentage;
|
|
unsigned ReceiveFailPercentage;
|
|
|
|
#endif
|
|
|
|
#ifdef MAJORDEBUG
|
|
|
|
DELAYED_ACTION_NODE * CheckupTimer;
|
|
|
|
void
|
|
CheckupFn(
|
|
void * Parms
|
|
)
|
|
{
|
|
unsigned OriginalTime = (unsigned) Parms;
|
|
unsigned CurrentTime = CurrentTimeInMsec();
|
|
|
|
DbgPrint("RPC DG: current time is %lx; scavenger thread delay is %u\n",
|
|
CurrentTime, (CurrentTime-OriginalTime) / 1000
|
|
);
|
|
|
|
CheckupTimer->Initialize(CheckupFn, (void *) (CurrentTimeInMsec() + ONE_MINUTE_IN_MSEC));
|
|
DelayedActions->Add(CheckupTimer, ONE_MINUTE_IN_MSEC, FALSE);
|
|
}
|
|
|
|
#endif
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
extern int
|
|
StringLengthWithEscape (
|
|
IN RPC_CHAR PAPI * String
|
|
);
|
|
|
|
extern RPC_CHAR PAPI *
|
|
StringCopyEscapeCharacters (
|
|
OUT RPC_CHAR PAPI * Destination,
|
|
IN RPC_CHAR PAPI * Source
|
|
);
|
|
|
|
|
|
void
|
|
FackTimerProc(
|
|
void * Parms
|
|
);
|
|
|
|
void
|
|
ActiveCallScavengerProc(
|
|
void * Parms
|
|
);
|
|
|
|
RPC_STATUS
|
|
InitializeServerGlobals(
|
|
);
|
|
|
|
void
|
|
InterpretFailureOptions(
|
|
);
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
|
|
RPC_STATUS
|
|
InitializeServerGlobals(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This fn initializes all the global variables used by the datagram
|
|
server. If anything fails, all the objects are destroyed.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK if ok
|
|
RPC_S_OUT_OF_MEMORY if an object could not be created
|
|
|
|
--*/
|
|
|
|
{
|
|
RPC_STATUS Status = RPC_S_OK;
|
|
|
|
//
|
|
// Don't take the global mutex if we can help it.
|
|
//
|
|
if (ServerBootTime)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
RequestGlobalMutex();
|
|
|
|
if (0 != InitializeRpcProtocolDgClient())
|
|
{
|
|
ClearGlobalMutex();
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
if (ServerBootTime == 0)
|
|
{
|
|
ActiveCalls = new ACTIVE_CALL_TABLE(&Status);
|
|
if (!ActiveCalls)
|
|
{
|
|
Status = RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
goto abend;
|
|
}
|
|
|
|
AssociationGroups = new ASSOC_GROUP_TABLE(&Status);
|
|
if (!AssociationGroups)
|
|
{
|
|
Status = RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
goto abend;
|
|
}
|
|
|
|
ActiveCallTimer = new DELAYED_ACTION_NODE;
|
|
if (!ActiveCallTimer)
|
|
{
|
|
Status = RPC_S_OUT_OF_MEMORY;
|
|
goto abend;
|
|
}
|
|
|
|
ActiveCallTimer->Initialize(ActiveCallScavengerProc, 0);
|
|
DelayedActions->Add(ActiveCallTimer, ONE_MINUTE_IN_MSEC, FALSE);
|
|
|
|
#ifdef MAJORDEBUG
|
|
|
|
CheckupTimer = new DELAYED_ACTION_NODE;
|
|
if (CheckupTimer)
|
|
{
|
|
CheckupTimer->Initialize(CheckupFn, (void *) (CurrentTimeInMsec() + ONE_MINUTE_IN_MSEC));
|
|
DelayedActions->Add(CheckupTimer, ONE_MINUTE_IN_MSEC, FALSE);
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef UNRELIABLE_TRANSPORT
|
|
|
|
InterpretFailureOptions();
|
|
|
|
#endif
|
|
|
|
//
|
|
// Server boot time is represented as the number of seconds
|
|
// since 1/1/1970. It must increase with each boot of the server.
|
|
//
|
|
#ifdef NTENV
|
|
|
|
LARGE_INTEGER CurrentTime;
|
|
NTSTATUS Nt_Status;
|
|
|
|
Nt_Status = NtQuerySystemTime(&CurrentTime);
|
|
|
|
ASSERT( NT_SUCCESS(Nt_Status) );
|
|
|
|
RtlTimeToSecondsSince1980(&CurrentTime, &ServerBootTime);
|
|
|
|
ServerBootTime += (60 * 60 * 24 * 365 * 10);
|
|
|
|
#else
|
|
|
|
ServerBootTime = (unsigned long) time(0);
|
|
|
|
#endif
|
|
}
|
|
|
|
ClearGlobalMutex();
|
|
|
|
return Status;
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
abend:
|
|
|
|
#ifdef MAJORDEBUG
|
|
|
|
if (CheckupTimer)
|
|
{
|
|
DelayedActions->Cancel(CheckupTimer);
|
|
delete CheckupTimer;
|
|
CheckupTimer = 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
if (ActiveCallTimer)
|
|
{
|
|
DelayedActions->Cancel(ActiveCallTimer);
|
|
delete ActiveCallTimer;
|
|
ActiveCallTimer = 0;
|
|
}
|
|
|
|
delete AssociationGroups;
|
|
AssociationGroups = 0;
|
|
|
|
delete ActiveCalls;
|
|
ActiveCalls = 0;
|
|
|
|
ClearGlobalMutex();
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
void
|
|
GlobalScavengerProc(
|
|
void * Unused
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This fn is called when GlobalScavengerTimer goes off.
|
|
It deletes stale packets from the free packet list.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
#ifdef MAJORDEBUG
|
|
|
|
unsigned TotalCount = 0;
|
|
unsigned OverdueCount = 0;
|
|
|
|
DelayedActions->QueueLength(&TotalCount, &OverdueCount);
|
|
|
|
if (OverdueCount > 2)
|
|
{
|
|
DbgPrint("RPC DG: %u of %u delayed actions are late\n",
|
|
OverdueCount, TotalCount
|
|
);
|
|
}
|
|
else
|
|
{
|
|
DbgPrint("global scavenger - %u entries\n", TotalCount);
|
|
}
|
|
|
|
#endif
|
|
|
|
DG_PACKET::ScavengePackets(ONE_MINUTE_IN_MSEC);
|
|
}
|
|
|
|
|
|
void
|
|
CleanupPacket(
|
|
NCA_PACKET_HEADER * pHeader
|
|
)
|
|
{
|
|
pHeader->RpcVersion = DG_RPC_PROTOCOL_VERSION;
|
|
pHeader->ServerBootTime = ServerBootTime;
|
|
|
|
SetMyDataRep(pHeader);
|
|
|
|
pHeader->PacketFlags &= DG_PF_IDEMPOTENT;
|
|
pHeader->PacketFlags2 = 0;
|
|
|
|
pHeader->AuthProto = 0;
|
|
}
|
|
|
|
|
|
inline void
|
|
InitErrorPacket(
|
|
NCA_PACKET_HEADER * pHeader,
|
|
unsigned char PacketType,
|
|
RPC_STATUS RpcStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Maps <ProcessStatus> to an NCA error code and sends
|
|
a FAULT or REJECT packet to the client, as appropriate.
|
|
|
|
Arguments:
|
|
|
|
pSendPacket - a packet to use, or zero if this fn should allocate one
|
|
|
|
ProcessStatus - NT RPC error code
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
CleanupPacket(pHeader);
|
|
|
|
pHeader->PacketType = PacketType;
|
|
|
|
pHeader->AuthProto = 0;
|
|
|
|
pHeader->PacketBodyLen = sizeof(unsigned long);
|
|
*(unsigned long *)(pHeader->Data) = MapToNcaStatusCode(RpcStatus);
|
|
}
|
|
|
|
|
|
RPC_ADDRESS *
|
|
DgCreateRpcAddress (
|
|
void *TransportInterface
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is a psuedo-constructor for the DG_ADDRESS class. This is done this
|
|
way so that the calling routine doesn't have to have any protocol-specific
|
|
knowledge.
|
|
|
|
Arguments:
|
|
|
|
TransportInterface - Pointer to a PDG_RPC_SERVER_TRANSPORT_INFO.
|
|
pStatus - Pointer to where to put the return value
|
|
|
|
Return Value:
|
|
|
|
pointer to new DG_ADDRESS.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// If the global active call table hasn't been initialized, then do
|
|
// so now.
|
|
//
|
|
if (0 != InitializeServerGlobals())
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
RPC_STATUS Status = RPC_S_OK;
|
|
|
|
PDG_ADDRESS pReturn = new DG_ADDRESS(
|
|
(PDG_RPC_SERVER_TRANSPORT_INFO) TransportInterface,
|
|
&Status
|
|
);
|
|
if (pReturn == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
delete pReturn;
|
|
return 0;
|
|
}
|
|
|
|
return pReturn;
|
|
}
|
|
|
|
|
|
DG_ADDRESS::DG_ADDRESS(
|
|
PDG_RPC_SERVER_TRANSPORT_INFO pAssociatedTransport,
|
|
RPC_STATUS * pStatus
|
|
)
|
|
: RPC_ADDRESS(pStatus),
|
|
|
|
#pragma warning(disable:4355)
|
|
ScavengerTimer(ScavengerProc, this),
|
|
#pragma warning(default:4355)
|
|
|
|
pTransport (pAssociatedTransport),
|
|
pTransAddress (0),
|
|
|
|
TotalThreadsThisEndpoint (0),
|
|
ThreadsReceivingThisEndpoint(0),
|
|
MinimumCallThreads (0),
|
|
MaximumConcurrentCalls (0),
|
|
|
|
FreeCalls (0),
|
|
|
|
ActiveCallCount (0)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the constructor for a DG_ADDRESS.
|
|
|
|
Arguments:
|
|
|
|
TransportInterface - Pointer to a PDG_RPC_SERVER_TRANSPORT_INFO
|
|
|
|
pStatus - Pointer to where to put the return value
|
|
|
|
Return Value:
|
|
|
|
None (this is a constructor)
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Make sure the RPC_ADDRESS (et. al.) initialized correctly.
|
|
//
|
|
if (*pStatus != RPC_S_OK)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Find initial max datagram size.
|
|
//
|
|
if (DefaultMaxDatagramLength)
|
|
{
|
|
LargestDataSize = min(pTransport->MaxPduSize, DefaultMaxDatagramLength);
|
|
}
|
|
else
|
|
{
|
|
LargestDataSize = pTransport->PreferredPduSize;
|
|
}
|
|
|
|
#ifdef UNRELIABLE_TRANSPORT
|
|
|
|
SendDupRemaining = 0;
|
|
ReceiveDupRemaining = 0;
|
|
|
|
pSavedSendPacket = new (LargestDataSize) DG_PACKET(LargestDataSize);
|
|
pSavedSendEndpoint = new char[pTransport->SizeOfAddress];
|
|
|
|
pSavedReceivePacket = new (LargestDataSize) DG_PACKET(LargestDataSize);
|
|
pSavedReceiveEndpoint = new char[pTransport->SizeOfAddress];
|
|
|
|
if (pSavedSendPacket == NULL ||
|
|
pSavedSendEndpoint == NULL ||
|
|
pSavedReceivePacket == NULL ||
|
|
pSavedReceiveEndpoint == NULL
|
|
)
|
|
{
|
|
*pStatus = RPC_S_OUT_OF_MEMORY;
|
|
|
|
return;
|
|
}
|
|
|
|
#endif
|
|
|
|
// implicitly "return" *pStatus
|
|
}
|
|
|
|
|
|
DG_ADDRESS::~DG_ADDRESS()
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the destructor for a DG_ADDRESS.
|
|
|
|
Arguments:
|
|
|
|
<None>
|
|
|
|
Return Value:
|
|
|
|
<None>
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Remove my packet scavenger callback fn.
|
|
//
|
|
if (DelayedActions)
|
|
{
|
|
DelayedActions->Cancel(&ScavengerTimer);
|
|
}
|
|
|
|
//
|
|
// If we already created a trans address, then delete it.
|
|
//
|
|
if (pTransAddress != 0)
|
|
{
|
|
(void)pTransport->DeregisterEndpoint(&pTransAddress);
|
|
}
|
|
|
|
//
|
|
// Free our cached SCALLs.
|
|
//
|
|
UUID_HASH_TABLE_NODE * pNode = FreeCalls;
|
|
while (pNode)
|
|
{
|
|
UUID_HASH_TABLE_NODE * pNext = pNode->pNext;
|
|
DG_SCALL * pCall = DG_SCALL::ContainingRecord(pNode);
|
|
|
|
pNode = pNext;
|
|
delete pCall;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_ADDRESS::SetupAddressWithEndpoint(
|
|
IN RPC_CHAR PAPI * Endpoint,
|
|
OUT RPC_CHAR PAPI * PAPI * lNetworkAddress,
|
|
OUT unsigned int PAPI * NumNetworkAddress,
|
|
IN void PAPI * SecurityDescriptor, OPTIONAL
|
|
IN unsigned int PendingQueueSize,
|
|
IN RPC_CHAR PAPI * RpcProtocolSequence,
|
|
IN unsigned long EndpointFlags,
|
|
IN unsigned long NICFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method sets up a known endpoint.
|
|
|
|
Arguments:
|
|
|
|
Endpoint - Supplies the endpoint to use for this rpc address.
|
|
|
|
SecurityDescriptor - Optionally supplies a security descriptor to
|
|
be placed on this rpc address. This is not allowed for
|
|
datagram.
|
|
|
|
NetworkAddress - Returns the network address for this rpc address.
|
|
Ownership of this string passes to the caller from the callee.
|
|
|
|
PendingQueueSize - Supplies the size of the queue of pending
|
|
requests which should be created by the transport. Some transports
|
|
will not be able to make use of this value, while others will.
|
|
|
|
RpcProtocolSequence - Supplies the protocol sequence for which we
|
|
are trying to setup an address. This argument is necessary so
|
|
that a single transport interface dll can support more than one
|
|
protocol sequence.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK
|
|
RPC_S_OUT_OF_MEMORY
|
|
RPC_S_INVALID_ARG - Returned if we're passed a SecurityDescriptor.
|
|
|
|
<return values from RegisterEndpoint>
|
|
|
|
Revision History:
|
|
|
|
++*/
|
|
{
|
|
RPC_STATUS Status;
|
|
|
|
unsigned int NetworkAddressLength = 20;
|
|
|
|
UNUSED(RpcProtocolSequence);
|
|
UNUSED(PendingQueueSize);
|
|
|
|
if (SecurityDescriptor)
|
|
{
|
|
return(RPC_S_INVALID_ARG);
|
|
}
|
|
|
|
// Get the network address. Negotiate the size.
|
|
|
|
while (1)
|
|
{
|
|
//
|
|
// Tell the dg transport to setup a new endpoint and get
|
|
// the network address.
|
|
//
|
|
|
|
*lNetworkAddress = new RPC_CHAR[NetworkAddressLength];
|
|
if (*lNetworkAddress == 0)
|
|
return(RPC_S_OUT_OF_MEMORY);
|
|
|
|
|
|
Status = pTransport->RegisterEndpoint(
|
|
this,
|
|
Endpoint,
|
|
&pTransAddress,
|
|
*lNetworkAddress,
|
|
NumNetworkAddress,
|
|
NetworkAddressLength,
|
|
EndpointFlags,
|
|
NICFlags
|
|
);
|
|
|
|
if (Status != RPC_P_NETWORK_ADDRESS_TOO_SMALL)
|
|
break; //oops, better increase the net addr buffer size.
|
|
|
|
delete *lNetworkAddress;
|
|
NetworkAddressLength *= 2;
|
|
}
|
|
|
|
if (Status != RPC_S_OK &&
|
|
Status != RPC_P_THREAD_LISTENING)
|
|
{
|
|
ASSERT(Status == RPC_S_INVALID_SECURITY_DESC ||
|
|
Status == RPC_S_INVALID_ARG ||
|
|
Status == RPC_S_CANT_CREATE_ENDPOINT ||
|
|
Status == RPC_S_INVALID_ENDPOINT_FORMAT ||
|
|
Status == RPC_S_OUT_OF_RESOURCES ||
|
|
Status == RPC_S_PROTSEQ_NOT_SUPPORTED ||
|
|
Status == RPC_S_DUPLICATE_ENDPOINT ||
|
|
Status == RPC_S_OUT_OF_MEMORY ||
|
|
Status == RPC_S_INTERNAL_ERROR
|
|
);
|
|
|
|
delete *lNetworkAddress;
|
|
pTransAddress = 0;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_ADDRESS::SetupAddressUnknownEndpoint (
|
|
OUT RPC_CHAR PAPI * PAPI * Endpoint,
|
|
OUT RPC_CHAR PAPI * PAPI * lNetworkAddress,
|
|
OUT unsigned int PAPI * NumNetworkAddress,
|
|
IN void PAPI * SecurityDescriptor, OPTIONAL
|
|
IN unsigned int PendingQueueSize,
|
|
IN RPC_CHAR PAPI * RpcProtocolSequence,
|
|
IN unsigned long EndpointFlags,
|
|
IN unsigned long NICFlags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets up and address by constructing any name we like.
|
|
|
|
Arguments:
|
|
|
|
Endpoint - Returns the endpoint created for this rpc address.
|
|
Ownership of this string passes to the caller from the callee.
|
|
|
|
NetworkAddress - Returns the network address for this rpc address.
|
|
Ownership of this string passes to the caller from the callee.
|
|
|
|
SecurityDescriptor - Optionally supplies a security descriptor to
|
|
be placed on this rpc address. This is not allowed for
|
|
datagram.
|
|
|
|
PendingQueueSize - Supplies the size of the queue of pending
|
|
requests which should be created by the transport. Some transports
|
|
will not be able to make use of this value, while others will.
|
|
|
|
RpcProtocolSequence - Supplies the protocol sequence for which we
|
|
are trying to setup an address. This argument is necessary so
|
|
that a single transport interface dll can support more than one
|
|
protocol sequence.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK
|
|
RPC_S_OUT_OF_MEMORY
|
|
RPC_S_INVALID_ARG - Returned if we're passed a SecurityDescriptor.
|
|
<error from transport>
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS Status;
|
|
|
|
unsigned int NetworkAddressLength = 20;
|
|
unsigned int EndpointLength = 16;
|
|
|
|
UNUSED(PendingQueueSize);
|
|
UNUSED(RpcProtocolSequence);
|
|
|
|
if (SecurityDescriptor)
|
|
{
|
|
return(RPC_S_INVALID_ARG);
|
|
}
|
|
|
|
*lNetworkAddress = new RPC_CHAR[NetworkAddressLength];
|
|
if (*lNetworkAddress == 0)
|
|
{
|
|
return(RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
|
|
*Endpoint = new RPC_CHAR[EndpointLength];
|
|
if ( *Endpoint == 0 )
|
|
{
|
|
delete *lNetworkAddress ;
|
|
return(RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
|
|
// Get the network address. Negotiate the size.
|
|
|
|
while (1)
|
|
{
|
|
//
|
|
// Tell the dg transport to setup a new endpoint and get
|
|
// the network address.
|
|
//
|
|
|
|
Status = pTransport->RegisterAnyEndpoint(
|
|
this,
|
|
*Endpoint,
|
|
&pTransAddress,
|
|
*lNetworkAddress,
|
|
NumNetworkAddress,
|
|
NetworkAddressLength,
|
|
EndpointLength,
|
|
EndpointFlags,
|
|
NICFlags
|
|
);
|
|
|
|
if ( Status == RPC_P_NETWORK_ADDRESS_TOO_SMALL )
|
|
{
|
|
delete *lNetworkAddress;
|
|
NetworkAddressLength *= 2;
|
|
*lNetworkAddress = new RPC_CHAR[NetworkAddressLength];
|
|
if (*lNetworkAddress == 0)
|
|
{
|
|
delete *Endpoint;
|
|
return(RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
}
|
|
else if ( Status == RPC_P_ENDPOINT_TOO_SMALL )
|
|
{
|
|
delete *Endpoint;
|
|
EndpointLength *= 2;
|
|
*Endpoint = new RPC_CHAR[EndpointLength];
|
|
if (*Endpoint == 0)
|
|
{
|
|
delete *lNetworkAddress;
|
|
return(RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
if (Status != RPC_S_OK &&
|
|
Status != RPC_P_THREAD_LISTENING)
|
|
{
|
|
delete *lNetworkAddress;
|
|
delete *Endpoint;
|
|
|
|
ASSERT(Status == RPC_S_INVALID_SECURITY_DESC ||
|
|
Status == RPC_S_INVALID_ARG ||
|
|
Status == RPC_S_CANT_CREATE_ENDPOINT ||
|
|
Status == RPC_S_INVALID_ENDPOINT_FORMAT ||
|
|
Status == RPC_S_OUT_OF_RESOURCES ||
|
|
Status == RPC_S_PROTSEQ_NOT_SUPPORTED ||
|
|
Status == RPC_S_DUPLICATE_ENDPOINT ||
|
|
Status == RPC_S_OUT_OF_MEMORY ||
|
|
Status == RPC_S_INTERNAL_ERROR
|
|
);
|
|
|
|
pTransAddress = 0;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_ADDRESS::StartListening(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is currently called during RpcServerUse*Protseq* iff
|
|
SetupAddressUnknownEndpoint or SetupAddressWithEndpoint returned
|
|
RPC_P_THREAD_LISTENING. Because of this, we don't create any threads.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
the result of the transport's StartListening call
|
|
|
|
--*/
|
|
{
|
|
ASSERT(pTransAddress);
|
|
|
|
if (pTransport->StartListening != NULL)
|
|
{
|
|
return pTransport->StartListening(pTransAddress);
|
|
}
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_ADDRESS::FireUpManager (
|
|
IN unsigned int MinimumConcurrentCalls
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This used to do something constructive, but the birth of auto-listen
|
|
interfaces made it obsolete.
|
|
|
|
Arguments:
|
|
|
|
MinimumConcurrentCalls - unused
|
|
|
|
Return Value:
|
|
|
|
always RPC_S_OK
|
|
|
|
--*/
|
|
{
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_ADDRESS::ServerStartingToListen (
|
|
IN unsigned int MinThreads,
|
|
IN unsigned int MaxCalls,
|
|
IN int ServerThreadsStarted
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The runtime calls this fn to ensure a thread is listening on the address's
|
|
endpoint. Currently, it may be called from RpcServerUse*Protseq*() or
|
|
from RpcServerRegisterIfEx().
|
|
|
|
Arguments:
|
|
|
|
MinimumCallThreads - Supplies a number indicating the minimum number
|
|
of call threads that should be created for this address. This is
|
|
a hint, and datagram ignores it.
|
|
|
|
MaximumConcurrentCalls - Supplies the maximum number of concurrent
|
|
calls that this server will support. RPC_INTERFACE::DispatchToStub
|
|
limits the number of threads dispatched to a stub; the argument
|
|
here is just a hint for the transport.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK if everything went ok.
|
|
RPC_S_OUT_OF_THREADS if we needed another thread and couldn't create one
|
|
|
|
--*/
|
|
{
|
|
MaximumConcurrentCalls = MaxCalls;
|
|
|
|
return CheckThreadPool();
|
|
}
|
|
|
|
|
|
RPC_STATUS RPC_ENTRY
|
|
I_RpcLaunchDatagramReceiveThread(
|
|
void __RPC_FAR * pVoid
|
|
)
|
|
{
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
If all of the following are true:
|
|
|
|
- the transport is part of our thread-sharing scheme
|
|
- this address's endpoint is being monitored by the shared thread
|
|
(hence no RPC thread is receiving on the endpoint)
|
|
- the shared thread detects data on this address's endpoint
|
|
|
|
then the shared thread will call this (exported) function to create
|
|
a thread to handle the incoming packet.
|
|
|
|
Arguments:
|
|
|
|
pVoid - the DG_ADDRESS of the endpoint with data
|
|
|
|
Return Value:
|
|
|
|
result from CreateThread()
|
|
|
|
--*/
|
|
|
|
PDG_ADDRESS pAddress = (PDG_ADDRESS) pVoid;
|
|
|
|
return pAddress->CheckThreadPool();
|
|
}
|
|
|
|
|
|
void
|
|
DG_ADDRESS::ServerStoppedListening (
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The runtime calls this fn to inform the address that the server is not
|
|
listening any more. Since auto-listen interfaces may still be present,
|
|
this doesn't mean much anymore.
|
|
|
|
Arguments:
|
|
|
|
<None>
|
|
|
|
Return Value:
|
|
|
|
<None>
|
|
|
|
--*/
|
|
{
|
|
}
|
|
|
|
|
|
unsigned int
|
|
DG_ADDRESS::InqNumberOfActiveCalls (
|
|
)
|
|
{
|
|
return ActiveCallCount;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_ADDRESS::ForwardFragment(
|
|
IN PDG_PACKET pReceivedPacket,
|
|
IN void * pFromEndpoint,
|
|
IN void * pDestEndpoint
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by ForwardPacket when a packet must
|
|
be forwarded but is found to be as large as the maximum packet
|
|
size and therefore has no room for the extra 'forwarding' info.
|
|
(ie: the packet is a fragment of a fragmented rpc call).
|
|
|
|
This routine will send the packet in two pieces. The first
|
|
packet will contain the forwarding information (Endpoint, endpoint
|
|
length and data rep of the originating client) in its data section.
|
|
|
|
The second packet will be identical to the original packet except
|
|
for the forward flag bits.
|
|
|
|
The first packet is indicated by Foward Flag bit one and two being set.
|
|
The second packet is indicated by Forward Flag bit one reset and bit
|
|
two set.
|
|
|
|
Arguments:
|
|
|
|
pReceivedPacket - Packet received
|
|
pFromEndpoint - Source (client) endpoint.
|
|
FromEndpointLen - Size of client endpoint.
|
|
pDestEndpoint - Endpoint of destination server
|
|
|
|
Return Value:
|
|
|
|
Return from MapStatusCode in transport
|
|
RPC_S_OUT_OF_MEMORY
|
|
RPC_S_OK
|
|
|
|
--*/
|
|
{
|
|
PDG_PACKET pSendPacket;
|
|
RPC_STATUS Status = RPC_S_OK;
|
|
unsigned long FromInfoLen = sizeof(FROM_ENDPOINT_INFO) + pTransport->SizeOfAddress;
|
|
unsigned long Length;
|
|
|
|
// Used to point to the source endpoint info stored at
|
|
// beginning of data in forwarded packet.
|
|
//
|
|
FROM_ENDPOINT_INFO * pFromEndpointInfo;
|
|
|
|
Length = FromInfoLen + sizeof(NCA_PACKET_HEADER);
|
|
|
|
pSendPacket = AllocatePacket();
|
|
if (!pSendPacket)
|
|
{
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
// Copy the original packet header to the send packet
|
|
|
|
pSendPacket->Header = pReceivedPacket->Header;
|
|
|
|
// Copy the source endpoint info into the data area so that the real
|
|
// destination server runtime will know where to send its response.
|
|
|
|
pFromEndpointInfo = (FROM_ENDPOINT_INFO *) pSendPacket->Header.Data;
|
|
|
|
pFromEndpointInfo->FromEndpointLen = pTransport->SizeOfAddress;
|
|
|
|
RpcpMemoryCopy(
|
|
pFromEndpointInfo->FromDataRepresentation,
|
|
pReceivedPacket->Header.DataRep,
|
|
sizeof(DREP)
|
|
);
|
|
|
|
RpcpMemoryCopy(
|
|
pFromEndpointInfo->FromEndpoint,
|
|
pFromEndpoint,
|
|
pTransport->SizeOfAddress
|
|
);
|
|
|
|
// Fix the length
|
|
|
|
pSendPacket->Header.PacketBodyLen = (unsigned short) FromInfoLen;
|
|
|
|
// Set the sender data representation in the pkt header.
|
|
|
|
SetMyDataRep(&pSendPacket->Header);
|
|
|
|
// Initialize the PacketFlags.
|
|
|
|
pSendPacket->Header.PacketFlags |= DG_PF_FORWARDED;
|
|
pSendPacket->Header.PacketFlags2 |= DG_PF_FORWARDED_2;
|
|
|
|
// Send packet to the REAL destination server.
|
|
|
|
pSendPacket->Header.AuthProto = 0;
|
|
pSendPacket->Header.PacketBodyLen = 0;
|
|
|
|
Status = pTransport->ForwardPacket(
|
|
pTransAddress,
|
|
(char *) &pSendPacket->Header,
|
|
Length,
|
|
pDestEndpoint
|
|
);
|
|
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
FreePacket(pSendPacket);
|
|
return Status;
|
|
}
|
|
|
|
// Setup original pkt to send. Initialize the PacketFlags.
|
|
|
|
pReceivedPacket->Header.PacketFlags &= ~DG_PF_FORWARDED;
|
|
pReceivedPacket->Header.PacketFlags2 |= DG_PF_FORWARDED_2;
|
|
|
|
// Set the sender data representation in the pkt header.
|
|
|
|
SetMyDataRep(&pReceivedPacket->Header);
|
|
|
|
// Send packet to the REAL destination server.
|
|
|
|
Status = pTransport->ForwardPacket(
|
|
pTransAddress,
|
|
(char *) &pReceivedPacket->Header,
|
|
pReceivedPacket->DataLength,
|
|
pDestEndpoint
|
|
);
|
|
FreePacket(pSendPacket);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_ADDRESS::ForwardPacket(
|
|
IN PDG_PACKET pReceivedPacket,
|
|
IN void * pFromEndpoint,
|
|
IN void * pDestEndpoint
|
|
)
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method will be called to forward a packet that was just
|
|
received to the intended destination endpoint.
|
|
|
|
The runtime has received a packet for an unknkown i/f.
|
|
It has passed this packet to the epmapper who has found the
|
|
correct destination enpoint in its table and has instructed the
|
|
runtime to forward the packet to this Endpoint. This procedure
|
|
will do just that.
|
|
|
|
Arguments:
|
|
|
|
pReceivedPacket - Packet received
|
|
pFromEndpoint - Source (client) endpoint.
|
|
pDestEndpoint - Endpoint of destination server
|
|
|
|
|
|
Return Value:
|
|
|
|
Return from MapStatusCode in transport
|
|
RPC_S_OUT_OF_MEMORY
|
|
RPC_S_OK
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
PDG_PACKET pSendPacket;
|
|
RPC_STATUS Status = RPC_S_OK;
|
|
unsigned long Length;
|
|
FROM_ENDPOINT_INFO * pFromEndpointInfo;
|
|
unsigned long FromInfoLen = sizeof(FROM_ENDPOINT_INFO) + pTransport->SizeOfAddress;
|
|
|
|
//
|
|
// We have not yet subtracted the header from the packet's DataLength.
|
|
//
|
|
Length = pReceivedPacket->DataLength
|
|
+ FromInfoLen;
|
|
|
|
if (Length <= LargestDataSize)
|
|
{
|
|
pSendPacket = AllocatePacket();
|
|
if (!pSendPacket)
|
|
{
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
// Copy the original packet header to the send packet
|
|
|
|
pSendPacket->Header = pReceivedPacket->Header;
|
|
|
|
// Copy the source endpoint info into the data area so that the real
|
|
// destination server runtime will know where to send its response.
|
|
|
|
pFromEndpointInfo = (FROM_ENDPOINT_INFO *) pSendPacket->Header.Data;
|
|
|
|
pFromEndpointInfo->FromEndpointLen = pTransport->SizeOfAddress;
|
|
|
|
RpcpMemoryCopy(
|
|
pFromEndpointInfo->FromDataRepresentation,
|
|
pReceivedPacket->Header.DataRep,
|
|
sizeof(DREP)
|
|
);
|
|
|
|
RpcpMemoryCopy(
|
|
pFromEndpointInfo->FromEndpoint,
|
|
pFromEndpoint,
|
|
pTransport->SizeOfAddress
|
|
);
|
|
|
|
//
|
|
// Copy the stub data and security trailer of the original packet
|
|
// into the send packet.
|
|
//
|
|
RpcpMemoryCopy(pSendPacket->Header.Data + FromInfoLen,
|
|
pReceivedPacket->Header.Data,
|
|
pReceivedPacket->DataLength - sizeof(NCA_PACKET_HEADER)
|
|
);
|
|
//
|
|
// The packet header is already byte-swapped, so mark it so.
|
|
//
|
|
SetMyDataRep(&pSendPacket->Header);
|
|
|
|
// Initialize the PacketFlags.
|
|
|
|
pSendPacket->Header.PacketFlags |= DG_PF_FORWARDED;
|
|
pSendPacket->Header.PacketFlags2 &= ~DG_PF_FORWARDED_2;
|
|
|
|
// Send packet to the REAL destination server.
|
|
|
|
Status = pTransport->ForwardPacket(
|
|
pTransAddress,
|
|
(char *) &pSendPacket->Header,
|
|
Length,
|
|
pDestEndpoint
|
|
);
|
|
|
|
FreePacket(pSendPacket);
|
|
}
|
|
else
|
|
{
|
|
// Received packet is already a full packet size. Send
|
|
// two forwarded packets.
|
|
|
|
Status = ForwardFragment(pReceivedPacket, pFromEndpoint, pDestEndpoint);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
BOOL
|
|
DG_ADDRESS::ForwardPacketIfNecessary(
|
|
IN PDG_PACKET pReceivedPacket,
|
|
IN void * pFromEndpoint
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
(courtesy of Connie)
|
|
|
|
The runtime has determined that it is dedicated to the
|
|
Epmapper and that pkts may arrive that are really
|
|
destined for an endpoint other than that of the epmapper
|
|
(ie: this is the beginning of dynamic endpoint resolution
|
|
by the forwarding mechanism).
|
|
|
|
The runtime has just received a packet and has called
|
|
this routine to determine if (a) the packet is destined
|
|
for the epmapper (in which case it returns indicating that
|
|
the packet should be processed as is) OR
|
|
(b) the packet is destined for another local server (in
|
|
which case it forwarded to its intented destination) OR
|
|
(c) is in error (in which case returns indicating an error).
|
|
|
|
It searches for the i/f. If not found it calls the
|
|
epmapper get forward function to determine the real destination
|
|
endpoint for this i/f. If the epmapper recognizes the i/f,
|
|
it calls ForwardPacket to forward the packet.
|
|
|
|
|
|
Arguments:
|
|
|
|
pReceivedPacket - Packet received
|
|
pFromEndpoint - Source (client) endpoint.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the packet needed to be forwarded
|
|
FALSE if it should be handled locally
|
|
|
|
--*/
|
|
{
|
|
RPC_INTERFACE PAPI * pRpcInterface;
|
|
RPC_SYNTAX_IDENTIFIER RpcIfSyntaxIdentifier;
|
|
RPC_STATUS Status;
|
|
void * pDestEndpoint;
|
|
|
|
static LONG LookupThreadCount = 0;
|
|
|
|
//
|
|
// Build an interface syntax identifier from the packet.
|
|
//
|
|
RpcpMemoryCopy(
|
|
&(RpcIfSyntaxIdentifier.SyntaxGUID),
|
|
&(pReceivedPacket->Header.InterfaceId),
|
|
sizeof(RPC_UUID)
|
|
);
|
|
|
|
RpcIfSyntaxIdentifier.SyntaxVersion.MajorVersion =
|
|
pReceivedPacket->Header.InterfaceVersion.MajorVersion;
|
|
RpcIfSyntaxIdentifier.SyntaxVersion.MinorVersion =
|
|
pReceivedPacket->Header.InterfaceVersion.MinorVersion;
|
|
//
|
|
// Try to find the appropriate interface to dispatch to.
|
|
//
|
|
pRpcInterface = (Server->FindInterface(&RpcIfSyntaxIdentifier));
|
|
|
|
//
|
|
// If the Interface is Mgmt If .. EpMapper has registered it and will be found
|
|
// The criteria then is .. If Packet has a Non NULL Obj Id forward .. else process
|
|
//
|
|
if ((pRpcInterface) &&
|
|
(RpcpMemoryCompare((UUID *)&pReceivedPacket->Header.ObjectId,
|
|
&NullUuid, sizeof(UUID)) == 0) )
|
|
{
|
|
//Interface found, just process as normal
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The endpoint mapper lookup can be a bottleneck. Let's toss the
|
|
// packet if things are getting out of hand.
|
|
//
|
|
if (LookupThreadCount > EPMAP_LOOKUP_LIMIT)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
InterlockedIncrement(&LookupThreadCount);
|
|
|
|
//Interface wasn't found. Let's ask endpoint mapper to resolve it
|
|
//for us.
|
|
|
|
unsigned char * AnsiProtseq;
|
|
|
|
#ifdef NTENV
|
|
// Must convert the protocol sequence into an ansi string.
|
|
|
|
unsigned Length = 1 + RpcpStringLength(InqRpcProtocolSequence());
|
|
AnsiProtseq = (unsigned char *) _alloca(Length);
|
|
if (!AnsiProtseq)
|
|
{
|
|
InterlockedDecrement(&LookupThreadCount);
|
|
return TRUE;
|
|
}
|
|
|
|
NTSTATUS NtStatus;
|
|
NtStatus = RtlUnicodeToMultiByteN((char *) AnsiProtseq,
|
|
Length,
|
|
NULL,
|
|
InqRpcProtocolSequence(),
|
|
Length * sizeof(RPC_CHAR)
|
|
);
|
|
ASSERT(NT_SUCCESS(NtStatus));
|
|
|
|
#else // NTENV
|
|
|
|
AnsiProtseq = InqRpcProtocolSequence();
|
|
|
|
#endif // NTENV
|
|
|
|
RpcTryExcept
|
|
{
|
|
// Call the epmapper get forward function. It returns the
|
|
// endpoint of the server this packet is really destined for.
|
|
|
|
Status = (*(Server->pRpcForwardFunction))(
|
|
((UUID *)((void *)(&(pReceivedPacket->Header.InterfaceId)))),
|
|
((RPC_VERSION *)(&(pReceivedPacket->Header.InterfaceVersion))),
|
|
((UUID *)((void *)(&(pReceivedPacket->Header.ObjectId)))),
|
|
AnsiProtseq,
|
|
&(pDestEndpoint));
|
|
}
|
|
RpcExcept( EXCEPTION_EXECUTE_HANDLER )
|
|
{
|
|
Status = RpcExceptionCode();
|
|
}
|
|
RpcEndExcept
|
|
|
|
InterlockedDecrement(&LookupThreadCount);
|
|
|
|
if (Status)
|
|
{
|
|
if (0 == (pReceivedPacket->Header.PacketFlags & DG_PF_BROADCAST))
|
|
{
|
|
// couldn't find the interface, or some other error occurred.
|
|
//
|
|
InitErrorPacket(&pReceivedPacket->Header, DG_REJECT, RPC_S_UNKNOWN_IF);
|
|
SendPacketBack(&pReceivedPacket->Header, sizeof(unsigned long), pFromEndpoint);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
if (pDestEndpoint)
|
|
{
|
|
// Server i/f was found. Forward to correct endpoint.
|
|
|
|
Status = ForwardPacket(pReceivedPacket,
|
|
pFromEndpoint,
|
|
pDestEndpoint
|
|
);
|
|
|
|
I_RpcFree(pDestEndpoint);
|
|
}
|
|
else
|
|
{
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
DG_ADDRESS::StripForwardedPacket(
|
|
IN PDG_PACKET pPacket,
|
|
IN void * pFromEndpoint
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method is called when a packet with the forward
|
|
bit set arrives. This means the packet has been
|
|
forwarded to us by the epmapper and is originally
|
|
from a client that did not know our address (dynamic
|
|
endpoint).
|
|
|
|
Restore the packet's DREP field.
|
|
|
|
If the packet was forwarded and contains the original endpoint,
|
|
change pFromEndpoint to point there.
|
|
|
|
If the packet was forwarded and not fragmented, restore it to its
|
|
original state, zapping the forward flags.
|
|
|
|
|
|
Arguments:
|
|
|
|
pPacket - Packet received.
|
|
pFromEndpoint - Pointer to an endoint. *pFromEndpoint
|
|
will be filled in with the souce endpoint structure.
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// If this is a packet-data fragment then we are done, as no endpoint info
|
|
// is present.
|
|
//
|
|
if (((pPacket->Header.PacketFlags & DG_PF_FORWARDED) == 0) &&
|
|
(pPacket->Header.PacketFlags2 & DG_PF_FORWARDED_2))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
FROM_ENDPOINT_INFO * pFromEndpointInfo;
|
|
|
|
pFromEndpointInfo = (FROM_ENDPOINT_INFO *) (pPacket->Header.Data);
|
|
|
|
//
|
|
// Check for bogus or truncated packet.
|
|
//
|
|
if (pPacket->DataLength < sizeof(FROM_ENDPOINT_INFO) ||
|
|
pPacket->DataLength - sizeof(FROM_ENDPOINT_INFO) < pFromEndpointInfo->FromEndpointLen)
|
|
{
|
|
#ifdef DEBUGRPC
|
|
|
|
DbgPrint("RPC DG: received malformed packet\n");
|
|
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
|
|
unsigned FromInfoLen;
|
|
|
|
FromInfoLen = sizeof(FROM_ENDPOINT_INFO) + pFromEndpointInfo->FromEndpointLen;
|
|
|
|
//
|
|
// Set DataRep in pkt header to that of the original sender.
|
|
//
|
|
RpcpMemoryCopy(
|
|
pPacket->Header.DataRep,
|
|
pFromEndpointInfo->FromDataRepresentation,
|
|
sizeof(DREP)
|
|
);
|
|
//
|
|
// Change pFromEndpoint to be the client's endpoint.
|
|
//
|
|
RpcpMemoryCopy(
|
|
pFromEndpoint,
|
|
pFromEndpointInfo->FromEndpoint,
|
|
pFromEndpointInfo->FromEndpointLen
|
|
);
|
|
|
|
//
|
|
// If this is a nonfragmented packet, move the original stub data
|
|
// and security trailer to their accustomed place.
|
|
//
|
|
if ((pPacket->Header.PacketFlags & DG_PF_FORWARDED) &&
|
|
(pPacket->Header.PacketFlags2 & DG_PF_FORWARDED_2) == 0)
|
|
{
|
|
RpcpMemoryMove(
|
|
pPacket->Header.Data,
|
|
pPacket->Header.Data + FromInfoLen,
|
|
pPacket->DataLength - FromInfoLen
|
|
);
|
|
}
|
|
|
|
//
|
|
// Change packet length to exclude the original endpoint information.
|
|
//
|
|
pPacket->DataLength -= FromInfoLen;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
DG_ADDRESS::ReceiveLotsaCalls(void)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the thread proc for each thread on this address.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
<None>
|
|
|
|
--*/
|
|
{
|
|
PDG_PACKET pPacket;
|
|
RPC_STATUS Status;
|
|
PDG_SCALL pCall;
|
|
BOOL ProcessPacket = FALSE;
|
|
RPC_INTERFACE PAPI * pRpcInterface;
|
|
RPC_SYNTAX_IDENTIFIER RpcIfSyntaxIdentifier;
|
|
BOOL FirstTime = TRUE;
|
|
|
|
unsigned Timeout = FIFTEEN_SECS_IN_MSEC;
|
|
|
|
PDG_TRANS_CLIENT_ENDPOINT pFromEndpoint = 0;
|
|
unsigned long FromEndpointLen = 0;
|
|
|
|
|
|
for (;;)
|
|
{
|
|
//
|
|
// Allocate a packet from the transport (this will associate the
|
|
// packet with the specified transport address).
|
|
//
|
|
pPacket = AllocatePacket();
|
|
if (!pPacket)
|
|
{
|
|
Sleep(100); // arbitrary sleep time
|
|
continue;
|
|
}
|
|
|
|
ASSERT(pPacket->TimeReceived == 0x31415926);
|
|
|
|
if (!pFromEndpoint)
|
|
{
|
|
pFromEndpoint = (PDG_TRANS_CLIENT_ENDPOINT) new char[pTransport->SizeOfAddress];
|
|
if (!pFromEndpoint)
|
|
{
|
|
FreePacket(pPacket);
|
|
|
|
Sleep(100); // arbitrary sleep time
|
|
continue;
|
|
}
|
|
}
|
|
|
|
AddressMutex.Request();
|
|
|
|
if (FirstTime)
|
|
{
|
|
FirstTime = FALSE;
|
|
}
|
|
else
|
|
{
|
|
++ThreadsReceivingThisEndpoint;
|
|
}
|
|
|
|
if (ThreadsReceivingThisEndpoint > MIN_THREADS_WHILE_ACTIVE)
|
|
{
|
|
--TotalThreadsThisEndpoint;
|
|
--ThreadsReceivingThisEndpoint;
|
|
AddressMutex.Clear();
|
|
|
|
FreePacket(pPacket);
|
|
delete pFromEndpoint;
|
|
return;
|
|
}
|
|
|
|
AddressMutex.Clear();
|
|
|
|
//
|
|
// Receive a packet from the network and byte-swap if necessary.
|
|
//
|
|
#ifndef UNRELIABLE_TRANSPORT
|
|
Status = pTransport->ReceivePacket(this,
|
|
pTransAddress,
|
|
LargestDataSize,
|
|
(char *) &pPacket->Header,
|
|
&pPacket->DataLength,
|
|
(Timeout == INFINITE) ? 0 : Timeout,
|
|
pFromEndpoint
|
|
);
|
|
|
|
#else
|
|
|
|
Status = UnreliableReceive(
|
|
pTransAddress,
|
|
LargestDataSize,
|
|
&pPacket->Header,
|
|
&pPacket->DataLength,
|
|
(Timeout == INFINITE) ? 0 : Timeout,
|
|
pFromEndpoint
|
|
);
|
|
|
|
#endif
|
|
AddressMutex.Request();
|
|
--ThreadsReceivingThisEndpoint;
|
|
|
|
ASSERT(pPacket->TimeReceived == 0x31415926);
|
|
|
|
if (Status == RPC_P_OVERSIZE_PACKET)
|
|
{
|
|
pPacket->Header.PacketFlags |= DG_PF_OVERSIZE_PACKET;
|
|
Status = RPC_S_OK;
|
|
}
|
|
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
FreePacket(pPacket);
|
|
|
|
if (Status != RPC_P_TIMEOUT)
|
|
{
|
|
#ifdef DEBUGRPC
|
|
DbgPrint("RPC DG: error 0x%lx (%lu) from ReceivePacket\n", Status, Status);
|
|
#endif
|
|
Sleep(100); // arbitrary sleep time
|
|
AddressMutex.Clear();
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Once all the 15-second threads have timed out, switch to an
|
|
// infinite timeout. If the transport can add its socket to the
|
|
// set watched by the ReceiveAny thread, it will return
|
|
// RPC_P_TIMEOUT. Othrewise it will just wait.
|
|
//
|
|
if (Timeout == INFINITE || ThreadsReceivingThisEndpoint > 0)
|
|
{
|
|
--TotalThreadsThisEndpoint;
|
|
|
|
AddressMutex.Clear();
|
|
|
|
delete pFromEndpoint;
|
|
return;
|
|
}
|
|
|
|
if (1 == TotalThreadsThisEndpoint)
|
|
{
|
|
Timeout = INFINITE;
|
|
}
|
|
|
|
AddressMutex.Clear();
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Once we receive a packet we switch back to a short receive timeout.
|
|
//
|
|
Timeout = FIFTEEN_SECS_IN_MSEC;
|
|
|
|
AddressMutex.Clear();
|
|
|
|
//
|
|
// The X/Open standard does not give these fields a full byte.
|
|
// Notice that the current arrangement strips the extra bits before
|
|
// forwarding, so if these bits become important the code will have to
|
|
// be rearranged.
|
|
//
|
|
pPacket->Header.RpcVersion &= 0x0F;
|
|
pPacket->Header.PacketType &= 0x1F;
|
|
|
|
if (pPacket->Header.RpcVersion != DG_RPC_PROTOCOL_VERSION)
|
|
{
|
|
#ifdef DEBUGRPC
|
|
DbgPrint("dg rpc: pitching packet with version %u\n", pPacket->Header.RpcVersion);
|
|
#endif
|
|
InitErrorPacket(&pPacket->Header,
|
|
DG_REJECT,
|
|
NCA_STATUS_VERSION_MISMATCH
|
|
);
|
|
|
|
SendPacketBack(&pPacket->Header,
|
|
sizeof(unsigned long),
|
|
pFromEndpoint
|
|
);
|
|
FreePacket(pPacket);
|
|
continue;
|
|
}
|
|
|
|
ByteSwapPacketHeaderIfNecessary(pPacket);
|
|
|
|
//
|
|
// Make sure the header is intact.
|
|
// Allow a packet with truncated stub data to pass; the SCALL
|
|
// will send a FACK-with-body to tell the client our max packet size.
|
|
//
|
|
if (pPacket->DataLength < sizeof(NCA_PACKET_HEADER))
|
|
{
|
|
#ifdef DEBUGRPC
|
|
DbgPrint("dg rpc: pitching truncated packet of length %u\n", pPacket->DataLength);
|
|
#endif
|
|
FreePacket(pPacket);
|
|
continue;
|
|
}
|
|
|
|
PNCA_PACKET_HEADER pHeader = &pPacket->Header;
|
|
|
|
//
|
|
// If we are the endpoint mapper, forward packet if necessary.
|
|
//
|
|
if (Server->pRpcForwardFunction &&
|
|
pHeader->PacketType != DG_RESPONSE)
|
|
{
|
|
if (RPC_S_OK != CheckThreadPool())
|
|
{
|
|
FreePacket(pPacket);
|
|
continue;
|
|
}
|
|
|
|
if (TRUE == ForwardPacketIfNecessary(pPacket, pFromEndpoint))
|
|
{
|
|
FreePacket(pPacket);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Exclude RPC header from DataLength.
|
|
//
|
|
pPacket->DataLength -= sizeof(NCA_PACKET_HEADER);
|
|
|
|
//
|
|
// If the packet was forwarded and contains the original endpoint,
|
|
// change pFromEndpoint to point there.
|
|
//
|
|
// If the packet was forwarded and not fragmented, restore it to its
|
|
// original state.
|
|
//
|
|
// If the packet is bogus, delete it.
|
|
//
|
|
if ((pHeader->PacketFlags & DG_PF_FORWARDED) ||
|
|
(pHeader->PacketFlags2 & DG_PF_FORWARDED_2))
|
|
{
|
|
if (FALSE == StripForwardedPacket(pPacket, pFromEndpoint))
|
|
{
|
|
FreePacket(pPacket);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Reject pkt if boot time in pkt does not match
|
|
// the server's boot time.
|
|
//
|
|
if (pHeader->ServerBootTime != ServerBootTime &&
|
|
pHeader->ServerBootTime != 0)
|
|
{
|
|
#ifdef DEBUGRPC
|
|
DbgPrint("dg rpc: wrong boot time %lx in client packet - I must have crashed and restarted\n", pHeader->ServerBootTime);
|
|
#endif
|
|
InitErrorPacket(pHeader,
|
|
DG_REJECT,
|
|
NCA_STATUS_WRONG_BOOT_TIME
|
|
);
|
|
|
|
SendPacketBack(pHeader,
|
|
sizeof(unsigned long),
|
|
pFromEndpoint
|
|
);
|
|
|
|
FreePacket(pPacket);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Fix up bogus OSF packets.
|
|
//
|
|
DeleteSpuriousAuthProto(pPacket);
|
|
|
|
//
|
|
// Finally we can dispatch the packet to its DG_SCALL.
|
|
//
|
|
switch (pHeader->PacketType)
|
|
{
|
|
case DG_REQUEST:
|
|
{
|
|
//
|
|
// Find or create an SCALL, taking the relevant hash mutex.
|
|
//
|
|
pCall = ActiveCalls->LookupActivity(this, pPacket, TRUE);
|
|
if (!pCall)
|
|
{
|
|
if (pHeader->PacketFlags & DG_PF_BROADCAST)
|
|
{
|
|
//
|
|
// not much point sending an error packet in this case
|
|
//
|
|
}
|
|
else
|
|
{
|
|
InitErrorPacket(pHeader,
|
|
DG_REJECT,
|
|
RPC_S_OUT_OF_MEMORY
|
|
);
|
|
|
|
SendPacketBack(pHeader,
|
|
sizeof(unsigned long),
|
|
pFromEndpoint
|
|
);
|
|
}
|
|
|
|
FreePacket(pPacket);
|
|
break;
|
|
}
|
|
|
|
pFromEndpoint = pCall->SetClientEndpoint(pFromEndpoint);
|
|
pCall->DealWithRequest(pPacket);
|
|
|
|
break;
|
|
}
|
|
|
|
case DG_PING:
|
|
{
|
|
pCall = ActiveCalls->Lookup(pPacket);
|
|
if (pCall)
|
|
{
|
|
pFromEndpoint = pCall->SetClientEndpoint(pFromEndpoint);
|
|
pCall->DealWithPing(pPacket);
|
|
}
|
|
else
|
|
{
|
|
CleanupPacket(pHeader);
|
|
|
|
pHeader->PacketType = DG_NOCALL;
|
|
|
|
SendPacketBack(pHeader, 0, pFromEndpoint);
|
|
FreePacket(pPacket);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case DG_FACK:
|
|
{
|
|
pCall = ActiveCalls->Lookup(pPacket);
|
|
if (pCall)
|
|
{
|
|
pFromEndpoint = pCall->SetClientEndpoint(pFromEndpoint);
|
|
pCall->DealWithFack(pPacket);
|
|
}
|
|
else
|
|
{
|
|
FreePacket(pPacket);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case DG_QUIT:
|
|
{
|
|
pCall = ActiveCalls->Lookup(pPacket);
|
|
if (pCall)
|
|
{
|
|
pFromEndpoint = pCall->SetClientEndpoint(pFromEndpoint);
|
|
pCall->DealWithQuit(pPacket);
|
|
}
|
|
else
|
|
{
|
|
FreePacket(pPacket);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case DG_ACK:
|
|
{
|
|
pCall = ActiveCalls->Lookup(pPacket);
|
|
if (pCall)
|
|
{
|
|
pFromEndpoint = pCall->SetClientEndpoint(pFromEndpoint);
|
|
pCall->DealWithAck(pPacket);
|
|
}
|
|
else
|
|
{
|
|
FreePacket(pPacket);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case DG_RESPONSE:
|
|
{
|
|
FreePacket(pPacket);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
InitErrorPacket(pHeader,
|
|
DG_REJECT,
|
|
RPC_S_PROTOCOL_ERROR
|
|
);
|
|
|
|
SendPacketBack(pHeader,
|
|
sizeof(unsigned long),
|
|
pFromEndpoint
|
|
);
|
|
FreePacket(pPacket);
|
|
break;
|
|
}
|
|
} // switch (PacketType)
|
|
} // for (ever)
|
|
|
|
ASSERT(0 && "RPC DG: dg_address::recvlotsacalls() terminated");
|
|
}
|
|
|
|
|
|
void
|
|
DG_ADDRESS::ScavengerProc(
|
|
void * address
|
|
)
|
|
{
|
|
DG_ADDRESS * pAddress = (DG_ADDRESS *) address;
|
|
|
|
unsigned CallCount = pAddress->ScavengeCalls();
|
|
|
|
//
|
|
// If the free list is at its minimal value, there is no need
|
|
// to continue checking.
|
|
//
|
|
if (CallCount > MIN_FREE_CALLS)
|
|
{
|
|
DelayedActions->Add(&pAddress->ScavengerTimer, ONE_MINUTE_IN_MSEC, TRUE);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
unsigned
|
|
DG_ADDRESS::ScavengeCalls(
|
|
)
|
|
{
|
|
unsigned CutoffTime;
|
|
|
|
CutoffTime = CurrentTimeInMsec() - ONE_MINUTE_IN_MSEC;
|
|
|
|
AddressMutex.Request();
|
|
|
|
UUID_HASH_TABLE_NODE * pNode = FreeCalls;
|
|
UUID_HASH_TABLE_NODE * pPrev = 0;
|
|
|
|
unsigned Count = 0;
|
|
|
|
for (pNode = FreeCalls; pNode; pNode = pNode->pNext)
|
|
{
|
|
DG_SCALL * Call = DG_SCALL::ContainingRecord(pNode);
|
|
|
|
ASSERT(0 == Call->InvalidHandle(SCONNECTION_TYPE));
|
|
|
|
if (Count >= MIN_FREE_CALLS && Call->ExpirationTime < CutoffTime)
|
|
{
|
|
pPrev->pNext = 0;
|
|
break;
|
|
}
|
|
pPrev = pNode;
|
|
++Count;
|
|
}
|
|
|
|
AddressMutex.Clear();
|
|
|
|
unsigned Freed = 0;
|
|
|
|
while (pNode)
|
|
{
|
|
DG_SCALL * Call = DG_SCALL::ContainingRecord(pNode);
|
|
|
|
ASSERT(0 == Call->InvalidHandle(SCONNECTION_TYPE));
|
|
|
|
UUID_HASH_TABLE_NODE * pNext = pNode->pNext;
|
|
pNode = pNext;
|
|
|
|
#ifdef DEBUGRPC
|
|
unsigned StartTime, EndTime;
|
|
StartTime = GetTickCount();
|
|
#endif
|
|
|
|
delete Call;
|
|
|
|
#ifdef MAJORDEBUG
|
|
EndTime = GetTickCount();
|
|
if (EndTime - StartTime > 1000)
|
|
{
|
|
DbgPrint("RPC DG perf: deleting a call took %lu msec\n", EndTime - StartTime);
|
|
}
|
|
#endif
|
|
++Freed;
|
|
}
|
|
|
|
#ifdef MAJORDEBUG
|
|
DbgPrint("DG_SCALL scavenger deleted %lu of %lu calls\n", Freed, Freed+Count);
|
|
#endif
|
|
|
|
return Count;
|
|
}
|
|
|
|
|
|
PDG_SCALL
|
|
DG_ADDRESS::AllocateCall(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocates a new DG_SCALL from the cache or from the heap.
|
|
This is better than writing a DG_SCALL::operator new() because
|
|
heavyweight members of DG_SCALL, like the mutex, are not created and
|
|
destroyed between each use of the DG_SCALL.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
the call
|
|
|
|
--*/
|
|
|
|
{
|
|
DG_SCALL * pCall;
|
|
|
|
AddressMutex.Request();
|
|
|
|
UUID_HASH_TABLE_NODE * pNode = FreeCalls;
|
|
|
|
if (pNode)
|
|
{
|
|
FreeCalls = pNode->pNext;
|
|
|
|
AddressMutex.Clear();
|
|
|
|
pCall = DG_SCALL::ContainingRecord(pNode);
|
|
}
|
|
else
|
|
{
|
|
AddressMutex.Clear();
|
|
|
|
RPC_STATUS Status = RPC_S_OK;
|
|
|
|
pCall = new DG_SCALL(this, &Status);
|
|
|
|
if (!pCall)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
delete pCall;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
DelayedActions->Add(GlobalScavengerTimer, ONE_MINUTE_IN_MSEC, FALSE);
|
|
DelayedActions->Add(ActiveCallTimer, 500, FALSE);
|
|
|
|
return pCall;
|
|
}
|
|
|
|
|
|
void
|
|
DG_ADDRESS::FreeCall(
|
|
PDG_SCALL pCall
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns an unused DG_SCALL to the cache or the heap.
|
|
See AllocateCall() for reasons not to use DG_SCALL::operator delete.
|
|
|
|
Arguments:
|
|
|
|
the call to zap
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
#ifdef NO_CACHED_SCALL
|
|
delete pCall;
|
|
#else
|
|
|
|
AddressMutex.Request();
|
|
|
|
pCall->ActivityNode.pNext = FreeCalls;
|
|
|
|
FreeCalls = &pCall->ActivityNode;
|
|
|
|
AddressMutex.Clear();
|
|
|
|
#endif
|
|
|
|
DelayedActions->Add(&ScavengerTimer, ONE_MINUTE_IN_MSEC, FALSE);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_ADDRESS::CheckThreadPool(
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks the number of threads listening. If too low, launches another.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS Status;
|
|
|
|
if (ThreadsReceivingThisEndpoint > 0)
|
|
{
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
AddressMutex.Request();
|
|
|
|
++TotalThreadsThisEndpoint;
|
|
++ThreadsReceivingThisEndpoint;
|
|
|
|
AddressMutex.Clear();
|
|
|
|
Status = Server->CreateThread((THREAD_PROC)ReceiveLotsaCallsWrapper, this);
|
|
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
AddressMutex.Request();
|
|
--TotalThreadsThisEndpoint;
|
|
--ThreadsReceivingThisEndpoint;
|
|
AddressMutex.Clear();
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
DG_SCALL::DG_SCALL(
|
|
PDG_ADDRESS Address,
|
|
RPC_STATUS * pStatus
|
|
)
|
|
: DG_PACKET_ENGINE(Address->pTransport->BaselinePduSize,
|
|
DefaultMaxDatagramLength
|
|
? min(Address->pTransport->MaxPduSize, DefaultMaxDatagramLength)
|
|
: Address->pTransport->PreferredPduSize,
|
|
Address->pTransport->MaxPacketSize,
|
|
1,
|
|
MAX_WINDOW_SIZE * Address->pTransport->MaxPacketSize, // BUGBUG
|
|
pStatus),
|
|
|
|
CallMutex (pStatus),
|
|
pAddress (Address),
|
|
|
|
ReferenceCount (0),
|
|
pRealEndpoint (0),
|
|
pClientEndpoint(0),
|
|
pAssocGroup (0),
|
|
ExpirationTime (0),
|
|
PipeThreadId (0),
|
|
PipeWaitType (PWT_NONE),
|
|
PipeWaitEvent (0),
|
|
CallInProgress (FALSE),
|
|
LastInterface (0),
|
|
Privileges (0),
|
|
|
|
ThreadExecutingManager (0),
|
|
ThreadsWaitingToDispatch (0),
|
|
ThreadDispatchEvent (pStatus)
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the constructor for the DG_SCALL class. This class represents a
|
|
call in progress on a server.
|
|
|
|
Arguments:
|
|
|
|
pAddress - The address this call is taking place on.
|
|
pStatus - Where to put a construction error code.
|
|
|
|
Return Value:
|
|
|
|
<None>
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
{
|
|
CancelEventId = 0;
|
|
|
|
FackTimer.Initialize(FackTimerProc, this);
|
|
|
|
SetState(CallInit);
|
|
}
|
|
|
|
|
|
void
|
|
DG_SCALL::CleanupAfterCall(
|
|
)
|
|
{
|
|
DelayedActions->Cancel(&FackTimer);
|
|
|
|
EndOfCall();
|
|
|
|
CleanupReceiveWindow();
|
|
|
|
if (PWT_NONE != PipeWaitType)
|
|
{
|
|
PipeWaitType = PWT_NONE;
|
|
PipeWaitEvent->Raise();
|
|
}
|
|
|
|
if (BufferFlags & RPC_BUFFER_PARTIAL)
|
|
{
|
|
//
|
|
// the send buffer is a pipe buffer and NDR will clean it up.
|
|
//
|
|
}
|
|
else
|
|
{
|
|
RPC_MESSAGE Message;
|
|
Message.Buffer = Buffer;
|
|
FreeBuffer(&Message);
|
|
}
|
|
|
|
Buffer = 0;
|
|
}
|
|
|
|
void
|
|
DG_SCALL::EndOfCall(
|
|
)
|
|
{
|
|
++SequenceNumber;
|
|
|
|
SetState(CallInit);
|
|
|
|
if (CallInProgress)
|
|
{
|
|
pAddress->DecrementActiveCallCount();
|
|
|
|
if (LastInterface->IsAutoListenInterface())
|
|
{
|
|
LastInterface->EndAutoListenCall() ;
|
|
pAddress->EndAutoListenCall() ;
|
|
}
|
|
|
|
CallInProgress = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
DG_SCALL::FreeCall(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is a mini-destructor, called when a cached SCALL returns to the cache.
|
|
It frees any queued packets and decrements the client process refcount
|
|
(for context handles).
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
Exceptions:
|
|
|
|
|
|
|
|
--*/
|
|
{
|
|
CleanupReceiveWindow();
|
|
|
|
#ifdef DEBUGRPC
|
|
|
|
SetState(CallBogus);
|
|
|
|
#endif
|
|
|
|
for (unsigned u = 0; u <= MaxKeySeq; ++u)
|
|
{
|
|
delete SecurityContextDict.Delete(u);
|
|
}
|
|
|
|
if (pAssocGroup)
|
|
{
|
|
AssociationGroups->DecrementRefCount(pAssocGroup);
|
|
pAssocGroup = 0;
|
|
}
|
|
|
|
if (PipeWaitEvent)
|
|
{
|
|
delete PipeWaitEvent;
|
|
PipeWaitEvent = 0;
|
|
}
|
|
|
|
pAddress->FreeCall(this);
|
|
}
|
|
|
|
//
|
|
// This is a separate fn for readability, and since it is only used in one
|
|
// place it is inlined.
|
|
//
|
|
inline void
|
|
DG_SCALL::Initialize(
|
|
PNCA_PACKET_HEADER pHeader,
|
|
unsigned short NewHash
|
|
)
|
|
{
|
|
SetState(CallInit);
|
|
|
|
CurrentPduSize = pAddress->pTransport->BaselinePduSize;
|
|
NextCallPduSize = CurrentPduSize;
|
|
|
|
ActivityHint = NewHash;
|
|
pSavedPacket->Header = *pHeader;
|
|
pSavedPacket->Header.ActivityHint = NewHash;
|
|
pSavedPacket->Header.InterfaceHint = 0xffff;
|
|
pSavedPacket->Header.ServerBootTime = ServerBootTime;
|
|
|
|
SetMyDataRep(&pSavedPacket->Header);
|
|
|
|
SequenceNumber = pSavedPacket->Header.SequenceNumber;
|
|
DispatchSequenceNumber = pSavedPacket->Header.SequenceNumber;
|
|
|
|
CallbackThread = 0;
|
|
MaxKeySeq = 0;
|
|
|
|
ExpirationTime = CurrentTimeInMsec();
|
|
ActivityNode.Initialize(&pSavedPacket->Header.ActivityId);
|
|
|
|
ActiveSecurityContext = 0;
|
|
|
|
AuthInfo.AuthenticationService = pHeader->AuthProto;
|
|
if (AuthInfo.AuthenticationService)
|
|
{
|
|
DG_SECURITY_TRAILER * pVerifier = (DG_SECURITY_TRAILER *)
|
|
(pHeader->Data + pHeader->PacketBodyLen);
|
|
|
|
AuthInfo.AuthenticationLevel = pVerifier->protection_level;
|
|
AuthInfo.AuthorizationService = 0;
|
|
AuthInfo.ServerPrincipalName = 0;
|
|
AuthInfo.AuthIdentity = 0;
|
|
}
|
|
else
|
|
{
|
|
AuthInfo.AuthenticationLevel = RPC_C_AUTHN_LEVEL_NONE;
|
|
AuthInfo.AuthorizationService = RPC_C_AUTHZ_NONE;
|
|
AuthInfo.ServerPrincipalName = 0;
|
|
AuthInfo.AuthIdentity = 0;
|
|
}
|
|
|
|
CancelEventId = 0;
|
|
Cancelled = FALSE;
|
|
CancelPending = FALSE;
|
|
}
|
|
|
|
|
|
|
|
DG_SCALL::~DG_SCALL()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Destructor for the DG_SCALL object.
|
|
|
|
Arguments:
|
|
|
|
<None>
|
|
|
|
Return Value:
|
|
|
|
<None>
|
|
|
|
--*/
|
|
{
|
|
delete pClientEndpoint;
|
|
delete pRealEndpoint;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_SCALL::GetBuffer (
|
|
IN OUT PRPC_MESSAGE Message
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the stub to allocate space. This space is to
|
|
be used for output arguments.
|
|
If these args fit into a single packet, then use the first packet
|
|
on the to-be-deleted list.
|
|
|
|
|
|
Arguments:
|
|
|
|
Message - The RPC_MESSAGE structure associated with this call.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK
|
|
RPC_S_OUT_OF_MEMORY
|
|
|
|
--*/
|
|
{
|
|
unsigned Length;
|
|
PDG_PACKET pPacket;
|
|
|
|
Length = sizeof(NCA_PACKET_HEADER)
|
|
+ Align8(Message->BufferLength)
|
|
+ SecurityTrailerSize;
|
|
|
|
if (Length <= pAddress->pTransport->BaselinePduSize)
|
|
{
|
|
pPacket = DG_PACKET::AllocatePacket(pAddress->pTransport->BaselinePduSize);
|
|
}
|
|
else if (Length <= CurrentPduSize)
|
|
{
|
|
pPacket = AllocatePacket();
|
|
}
|
|
else
|
|
{
|
|
pPacket = DG_PACKET::AllocatePacket(Length);
|
|
}
|
|
|
|
if (0 == pPacket)
|
|
{
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
//
|
|
// Point the buffer at the appropriate place in the packet.
|
|
//
|
|
Message->Buffer = pPacket->Header.Data;
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
|
|
void
|
|
DG_SCALL::FreeBuffer (
|
|
IN PRPC_MESSAGE Message
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to free up the marshalled data after the stub
|
|
is through with it.
|
|
|
|
Arguments:
|
|
|
|
Message - The RPC_MESSAGE structure associated with this call
|
|
|
|
Return Value:
|
|
|
|
<none>
|
|
--*/
|
|
|
|
{
|
|
if (Message->Buffer)
|
|
{
|
|
if (Message->Buffer == LastReceiveBuffer)
|
|
{
|
|
LastReceiveBuffer = 0;
|
|
LastReceiveBufferLength = 0;
|
|
}
|
|
|
|
PDG_PACKET Packet = DG_PACKET::ContainingRecord(Message->Buffer);
|
|
|
|
if (Packet->MaxDataLength == MaxPduSize)
|
|
{
|
|
FreePacket(Packet);
|
|
}
|
|
else if (Packet->MaxDataLength == pAddress->pTransport->BaselinePduSize)
|
|
{
|
|
FreePacket(Packet);
|
|
}
|
|
else
|
|
{
|
|
delete Packet;
|
|
}
|
|
|
|
Message->Buffer = 0;
|
|
}
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_SCALL::SendReceive (
|
|
IN OUT PRPC_MESSAGE Message
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called for a user-level callback.
|
|
|
|
Arguments:
|
|
|
|
Message - The RPC_MESSAGE structure associated with this call
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK
|
|
RPC_S_OUT_OF_MEMORY
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
{
|
|
FreeBuffer(Message);
|
|
|
|
return RPC_S_CALL_FAILED;
|
|
}
|
|
|
|
|
|
void
|
|
DG_SCALL::SaveOriginalClientInfo(
|
|
PDG_PACKET pPacket
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given a forwarded packet, save the original client's endpoint and
|
|
data representation in the DG_SCALL.
|
|
|
|
Arguments:
|
|
|
|
pPacket - the packet
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
if (pPacket->Header.PacketFlags2 & DG_PF_FORWARDED_2)
|
|
{
|
|
//
|
|
// This is the header fragment of a fragmented packet.
|
|
// The endpoint and DREP are stored in the body data.
|
|
//
|
|
if (!pRealEndpoint)
|
|
{
|
|
//
|
|
// Update endpoint.
|
|
//
|
|
pRealEndpoint = (PDG_TRANS_CLIENT_ENDPOINT) new char[pAddress->pTransport->SizeOfAddress];
|
|
|
|
unsigned long FromInfoLen;
|
|
FROM_ENDPOINT_INFO * pFromEndpointInfo;
|
|
|
|
pFromEndpointInfo = (FROM_ENDPOINT_INFO *) pPacket->Header.Data;
|
|
FromInfoLen = sizeof(FROM_ENDPOINT_INFO) + pFromEndpointInfo->FromEndpointLen;
|
|
|
|
RpcpMemoryCopy(
|
|
pRealEndpoint,
|
|
pFromEndpointInfo->FromEndpoint,
|
|
pFromEndpointInfo->FromEndpointLen
|
|
);
|
|
//
|
|
// Update data rep.
|
|
//
|
|
RealDataRep = 0x00ffffff & (*(unsigned long *) &pFromEndpointInfo->FromDataRepresentation);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This packet was forwarded whole and touched up already.
|
|
// The endpoint was stored in pClientEndpoint and the DREP was
|
|
// stored in the packet header.
|
|
//
|
|
if (!pRealEndpoint)
|
|
{
|
|
pRealEndpoint = pClientEndpoint;
|
|
pClientEndpoint = 0;
|
|
|
|
RealDataRep = 0x00ffffff & (*(unsigned long *) &pPacket->Header.DataRep);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
DG_SCALL::DealWithResponse(
|
|
PDG_PACKET pPacket
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is unused.
|
|
|
|
Arguments:
|
|
|
|
pPacket - the packet
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
ASSERT(0 && "dealwithresponse called");
|
|
|
|
CallMutex.Clear();
|
|
FreePacket(pPacket);
|
|
}
|
|
|
|
|
|
void
|
|
DG_SCALL::DealWithPing(
|
|
PDG_PACKET pPacket
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Figures out what to do with a PING packet. It may send a WORKING
|
|
or NOCALL packet, or retransmit response fragments.
|
|
|
|
Arguments:
|
|
|
|
pPacket - the PING packet
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Ignore security trailer. The only way extra PINGs can hose me is by
|
|
// chewing up CPU, and authenticating would only make it worse.
|
|
//
|
|
|
|
NCA_PACKET_HEADER * pHeader = &pPacket->Header;
|
|
|
|
unsigned PacketSeq = pHeader->SequenceNumber;
|
|
|
|
if (PacketSeq == SequenceNumber)
|
|
{
|
|
unsigned short Serial = ReadSerialNumber(&pPacket->Header);
|
|
if (Serial < ReceiveSerialNumber)
|
|
{
|
|
CallMutex.Clear();
|
|
FreePacket(pPacket);
|
|
return;
|
|
}
|
|
|
|
ReceiveSerialNumber = Serial;
|
|
|
|
ExpirationTime = CurrentTimeInMsec();
|
|
|
|
if (pHeader->PacketFlags2 & DG_PF_FORWARDED_2)
|
|
{
|
|
CallWasForwarded = TRUE;
|
|
}
|
|
|
|
if (pHeader->PacketFlags & DG_PF_FORWARDED)
|
|
{
|
|
//
|
|
// This is either a complete forwarded packet or the header fragment
|
|
// of a fragmented forwarded packet. Either way, it contains the
|
|
// original client address and DREP.
|
|
//
|
|
CallWasForwarded = TRUE;
|
|
SaveOriginalClientInfo(pPacket);
|
|
}
|
|
|
|
switch (State)
|
|
{
|
|
case CallInit:
|
|
case CallWaitingForFrags:
|
|
{
|
|
if (CallbackThread)
|
|
{
|
|
pHeader->PacketType = DG_WORKING;
|
|
}
|
|
else
|
|
{
|
|
pHeader->PacketType = DG_NOCALL;
|
|
}
|
|
|
|
CallMutex.Clear();
|
|
|
|
pHeader->ServerBootTime = ServerBootTime;
|
|
SetMyDataRep(pHeader);
|
|
|
|
SealAndSendPacket(pHeader);
|
|
break;
|
|
}
|
|
|
|
case CallWorking:
|
|
{
|
|
if (fReceivedAllFragments)
|
|
{
|
|
CallMutex.Clear();
|
|
|
|
pHeader->PacketType = DG_WORKING;
|
|
pHeader->ServerBootTime = ServerBootTime;
|
|
SetMyDataRep(pHeader);
|
|
|
|
SealAndSendPacket(pHeader);
|
|
}
|
|
else
|
|
{
|
|
SendFack(pPacket);
|
|
CallMutex.Clear();
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case CallSendingFrags:
|
|
{
|
|
TimeoutCount = 0;
|
|
if (TRUE == DelayedActions->Cancel(&FackTimer))
|
|
{
|
|
SendSomeFragments();
|
|
}
|
|
|
|
CallMutex.Clear();
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
ASSERT(0 && "invalid call state");
|
|
CallMutex.Clear();
|
|
}
|
|
}
|
|
}
|
|
else if (PacketSeq < SequenceNumber)
|
|
{
|
|
// Duplicate of an old packet.
|
|
CallMutex.Clear();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// I've never seen this call.
|
|
//
|
|
ExpirationTime = CurrentTimeInMsec();
|
|
|
|
CallMutex.Clear();
|
|
|
|
pHeader->PacketType = DG_NOCALL;
|
|
pHeader->ServerBootTime = ServerBootTime;
|
|
SetMyDataRep(pHeader);
|
|
|
|
SealAndSendPacket(pHeader);
|
|
}
|
|
|
|
CallMutex.VerifyNotOwned();
|
|
FreePacket(pPacket);
|
|
}
|
|
|
|
|
|
void
|
|
DG_SCALL::DealWithQuit(
|
|
PDG_PACKET pPacket
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handles a QUIT packet:
|
|
|
|
- If the cancel event ID is new, we cancel the current call and send a QUACK.
|
|
- If the event ID is the current one, we retransmit the QUACK.
|
|
- If the event ID is older than the current one, we ignore the packet.
|
|
|
|
Arguments:
|
|
|
|
the packet
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// If this is a secure call, accept only an authenticated QUIT.
|
|
//
|
|
// Sometimes OSF clients will omit the sec trailer from the QUIT.
|
|
//
|
|
if (ActiveSecurityContext &&
|
|
ActiveSecurityContext->AuthenticationService != RPC_C_AUTHN_DCE_PRIVATE)
|
|
{
|
|
if (VerifySecurePacket(pPacket, ActiveSecurityContext) != RPC_S_OK)
|
|
{
|
|
CallMutex.Clear();
|
|
FreePacket(pPacket);
|
|
return;
|
|
}
|
|
}
|
|
|
|
QUIT_BODY_0 * pBody = (QUIT_BODY_0 *) pPacket->Header.Data;
|
|
|
|
if (pPacket->Header.PacketBodyLen < sizeof(QUIT_BODY_0) ||
|
|
pBody->Version != 0)
|
|
{
|
|
#ifdef DEBUGRPC
|
|
DbgPrint("RPC DG: unknown quit format: version 0x%lx, length 0x%hx\n",
|
|
pBody->Version, pPacket->Header.PacketBodyLen
|
|
);
|
|
#endif
|
|
|
|
CallMutex.Clear();
|
|
FreePacket(pPacket);
|
|
return;
|
|
}
|
|
|
|
if (pBody->EventId > CancelEventId)
|
|
{
|
|
CancelEventId = pBody->EventId;
|
|
InterlockedIncrement(&Cancelled);
|
|
}
|
|
|
|
if (pBody->EventId == CancelEventId)
|
|
{
|
|
pSavedPacket->Header.PacketType = DG_QUACK;
|
|
pSavedPacket->Header.SequenceNumber = SequenceNumber;
|
|
pSavedPacket->Header.PacketBodyLen = sizeof(QUACK_BODY_0);
|
|
|
|
QUACK_BODY_0 PAPI * pAckBody = (QUACK_BODY_0 PAPI *) pSavedPacket->Header.Data;
|
|
|
|
pAckBody->Version = 0;
|
|
pAckBody->EventId = CancelEventId;
|
|
pAckBody->Accepted = TRUE;
|
|
|
|
SealAndSendPacket(&pSavedPacket->Header);
|
|
}
|
|
else
|
|
{
|
|
#ifdef MAJORDEBUG
|
|
DbgPrint("RPC DG: stale cancel event id %lu\n", pBody->EventId);
|
|
#endif
|
|
}
|
|
|
|
CallMutex.Clear();
|
|
FreePacket(pPacket);
|
|
}
|
|
|
|
|
|
void
|
|
DG_SCALL::DealWithAck(
|
|
PDG_PACKET pPacket
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Figures out what to do with an ACK packet.
|
|
It turns off the fragment-retransmission timer.
|
|
|
|
Arguments:
|
|
|
|
pPacket - the ACK packet
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
#ifdef DEBUGRPC
|
|
if (State != CallSendingFrags)
|
|
{
|
|
DbgPrint("RPC DG: ACK received before response was sent\n");
|
|
}
|
|
#endif
|
|
|
|
if (State == CallSendingFrags)
|
|
{
|
|
//
|
|
// Accept only an authenticated ACK if the call is secure.
|
|
//
|
|
// Sometimes OSF clients will omit the sec trailer from the ACK.
|
|
//
|
|
if (ActiveSecurityContext &&
|
|
ActiveSecurityContext->AuthenticationService != RPC_C_AUTHN_DCE_PRIVATE)
|
|
{
|
|
if (VerifySecurePacket(pPacket, ActiveSecurityContext) != RPC_S_OK)
|
|
{
|
|
CallMutex.Clear();
|
|
FreePacket(pPacket);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// He must have all the data; we don't need the data buffer any more.
|
|
//
|
|
ExpirationTime = CurrentTimeInMsec();
|
|
CleanupAfterCall();
|
|
}
|
|
|
|
CallMutex.Clear();
|
|
FreePacket(pPacket);
|
|
}
|
|
|
|
|
|
void
|
|
DG_SCALL::DealWithFack(
|
|
PDG_PACKET pPacket
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Figures out what to do with a FACK packet.
|
|
If there is more data to send, it sends the next fragment
|
|
and restarts the fragment-retransmission timer.
|
|
|
|
Arguments:
|
|
|
|
pPacket - the packet
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
// is call finished?
|
|
if (State != CallSendingFrags)
|
|
{
|
|
CallMutex.Clear();
|
|
FreePacket(pPacket);
|
|
return;
|
|
}
|
|
|
|
ExpirationTime = CurrentTimeInMsec();
|
|
|
|
//
|
|
// If this is a secure call, accept only an authenticated FACK.
|
|
//
|
|
// Sometimes OSF clients will omit the sec trailer from the FACK.
|
|
//
|
|
if (ActiveSecurityContext &&
|
|
ActiveSecurityContext->AuthenticationService != RPC_C_AUTHN_DCE_PRIVATE)
|
|
{
|
|
if (VerifySecurePacket(pPacket, ActiveSecurityContext) != RPC_S_OK)
|
|
{
|
|
CallMutex.Clear();
|
|
FreePacket(pPacket);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Note fack arrival, and send more packets if necessary.
|
|
//
|
|
DelayedActions->Cancel(&FackTimer);
|
|
TimeoutCount = 0;
|
|
|
|
SendBurstLength += 1;
|
|
|
|
UpdateSendWindow(pPacket, ActiveSecurityContext, pAssocGroup);
|
|
|
|
SendSomeFragments();
|
|
|
|
//
|
|
// See whether we need to wake up a call to I_RpcSend.
|
|
//
|
|
if (PWT_SEND == PipeWaitType)
|
|
{
|
|
if (IsBufferAcknowledged())
|
|
{
|
|
PipeWaitType = PWT_NONE;
|
|
PipeWaitEvent->Raise();
|
|
}
|
|
}
|
|
|
|
CallMutex.Clear();
|
|
FreePacket(pPacket);
|
|
}
|
|
|
|
|
|
void
|
|
FackTimerProc(
|
|
void * Parms
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is a non-member-fn wrapper around DG_SCALL::FackTimerExpired.
|
|
It is called when the call's FackTimer goes off.
|
|
|
|
Arguments:
|
|
|
|
Parms - the DG_SCALL
|
|
|
|
--*/
|
|
{
|
|
PDG_SCALL pCall = (PDG_SCALL) Parms;
|
|
|
|
pCall->FackTimerExpired();
|
|
}
|
|
|
|
|
|
void
|
|
DG_SCALL::FackTimerExpired(
|
|
)
|
|
{
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This fn is called if a frag was sent but no FACK arrived.
|
|
It retransmits the packet and restarts the FACK timer.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
CLAIM_MUTEX Lock(CallMutex);
|
|
|
|
if (State != CallSendingFrags)
|
|
{
|
|
//
|
|
// Someone aborted the call but didn't catch us in time.
|
|
//
|
|
return;
|
|
}
|
|
|
|
++TimeoutCount;
|
|
if (TimeoutCount > FRAG_RETRY_COUNT)
|
|
{
|
|
CleanupAfterCall();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If the timer is set, a frag was sent between the time this proc was
|
|
// triggered and the time we grabbed the call mutex. Ergo a FACK arrived
|
|
// and this retransmission is unnecessary.
|
|
//
|
|
if (FALSE == FackTimer.IsActive())
|
|
{
|
|
SendBurstLength = (1+SendBurstLength)/2;
|
|
SendSomeFragments();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_SCALL::SealAndSendPacket(
|
|
PNCA_PACKET_HEADER pHeader
|
|
)
|
|
{
|
|
BOOL SaveBufferData = TRUE;
|
|
RPC_STATUS Status = RPC_S_OK;
|
|
|
|
void * pDataUnderTrailer = 0;
|
|
void * pSecurityTrailer = 0;
|
|
unsigned TrailerLength = 0;
|
|
|
|
pHeader->SequenceNumber = SequenceNumber;
|
|
pHeader->AuthProto = 0;
|
|
|
|
if (CancelPending)
|
|
{
|
|
pHeader->PacketFlags2 |= DG_PF_CANCEL_PENDING;
|
|
}
|
|
|
|
if (pHeader->PacketType != DG_RESPONSE)
|
|
{
|
|
SaveBufferData = FALSE;
|
|
}
|
|
|
|
//
|
|
// Construct security trailer if necessary.
|
|
// We need to save data under the security trailer if we are sending
|
|
// a fragment that is not the last fragment.
|
|
//
|
|
if (ActiveSecurityContext && DG_REJECT != pHeader->PacketType)
|
|
{
|
|
if (ActiveSecurityContext->AuthenticationLevel == RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
|
|
{
|
|
if (&pSavedPacket->Header != pHeader)
|
|
{
|
|
memcpy(&pSavedPacket->Header, pHeader, sizeof(NCA_PACKET_HEADER) + pHeader->PacketBodyLen);
|
|
pHeader = &pSavedPacket->Header;
|
|
}
|
|
|
|
SaveBufferData = FALSE;
|
|
}
|
|
|
|
// Pad the stub data length to a multiple of 8, so the security
|
|
// trailer is properly aligned. OSF requires that the pad bytes
|
|
// be included in PacketBodyLen.
|
|
//
|
|
pHeader->PacketBodyLen = Align8(pHeader->PacketBodyLen);
|
|
|
|
pHeader->AuthProto = (unsigned char) ActiveSecurityContext->AuthenticationService;
|
|
|
|
pSecurityTrailer = pHeader->Data + pHeader->PacketBodyLen;
|
|
|
|
if (SaveBufferData &&
|
|
(pHeader->PacketFlags & DG_PF_FRAG) &&
|
|
!(pHeader->PacketFlags & DG_PF_LAST_FRAG) )
|
|
{
|
|
//
|
|
// This is not a persistent datum; allocate space on the stack.
|
|
//
|
|
pDataUnderTrailer = _alloca(SecurityTrailerSize);
|
|
memcpy(pDataUnderTrailer, pSecurityTrailer, SecurityTrailerSize);
|
|
}
|
|
|
|
switch (ActiveSecurityContext->AuthenticationLevel)
|
|
{
|
|
case RPC_C_AUTHN_LEVEL_PKT:
|
|
case RPC_C_AUTHN_LEVEL_PKT_INTEGRITY:
|
|
case RPC_C_AUTHN_LEVEL_PKT_PRIVACY:
|
|
{
|
|
SECURITY_BUFFER_DESCRIPTOR BufferDescriptor;
|
|
SECURITY_BUFFER SecurityBuffers[5];
|
|
DCE_MSG_SECURITY_INFO MsgSecurityInfo;
|
|
|
|
DG_SECURITY_TRAILER * pVerifier = (DG_SECURITY_TRAILER *) pSecurityTrailer;
|
|
|
|
pVerifier->protection_level = ActiveSecurityContext->AuthenticationLevel;
|
|
pVerifier->key_vers_num = ActiveSecurityContext->AuthContextId;
|
|
|
|
ASSERT(pHeader->AuthProto != 0);
|
|
ASSERT(pVerifier->protection_level >= RPC_C_AUTHN_LEVEL_PKT);
|
|
ASSERT(pVerifier->protection_level <= RPC_C_AUTHN_LEVEL_PKT_PRIVACY);
|
|
|
|
BufferDescriptor.ulVersion = 0;
|
|
BufferDescriptor.cBuffers = 5;
|
|
BufferDescriptor.pBuffers = SecurityBuffers;
|
|
|
|
SecurityBuffers[0].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY;
|
|
SecurityBuffers[0].pvBuffer = pHeader;
|
|
SecurityBuffers[0].cbBuffer = sizeof(NCA_PACKET_HEADER);
|
|
|
|
SecurityBuffers[1].BufferType = SECBUFFER_DATA;
|
|
SecurityBuffers[1].pvBuffer = pHeader->Data;
|
|
SecurityBuffers[1].cbBuffer = pHeader->PacketBodyLen;
|
|
|
|
SecurityBuffers[2].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY;
|
|
SecurityBuffers[2].pvBuffer = pVerifier;
|
|
|
|
if (pVerifier->protection_level == RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
|
|
{
|
|
unsigned Alignment = Align4(ActiveSecurityContext->BlockSize());
|
|
|
|
SecurityBuffers[2].cbBuffer = Align(sizeof(DG_SECURITY_TRAILER), Alignment);
|
|
|
|
SecurityBuffers[3].BufferType = SECBUFFER_TOKEN;
|
|
SecurityBuffers[3].pvBuffer = Align(pVerifier + 1, Alignment);
|
|
SecurityBuffers[3].cbBuffer = ActiveSecurityContext->MaximumHeaderLength();
|
|
}
|
|
else
|
|
{
|
|
SecurityBuffers[2].cbBuffer = Align4(sizeof(DG_SECURITY_TRAILER));
|
|
SecurityBuffers[3].BufferType = SECBUFFER_TOKEN;
|
|
SecurityBuffers[3].pvBuffer = Align4(pVerifier + 1);
|
|
SecurityBuffers[3].cbBuffer = ActiveSecurityContext->MaximumSignatureLength();
|
|
}
|
|
|
|
SecurityBuffers[4].BufferType = SECBUFFER_PKG_PARAMS | SECBUFFER_READONLY;
|
|
SecurityBuffers[4].pvBuffer = &MsgSecurityInfo;
|
|
SecurityBuffers[4].cbBuffer = sizeof(DCE_MSG_SECURITY_INFO);
|
|
|
|
MsgSecurityInfo.SendSequenceNumber = pHeader->FragmentNumber;
|
|
MsgSecurityInfo.ReceiveSequenceNumber = ActiveSecurityContext->AuthContextId;
|
|
MsgSecurityInfo.PacketType = ~0;
|
|
|
|
TrailerLength = SecurityBuffers[2].cbBuffer;
|
|
|
|
if (RPC_C_AUTHN_LEVEL_PKT_PRIVACY == ActiveSecurityContext->AuthenticationLevel)
|
|
{
|
|
Status = ActiveSecurityContext->SignOrSeal(SequenceNumber, FALSE, &BufferDescriptor);
|
|
}
|
|
else
|
|
{
|
|
Status = ActiveSecurityContext->SignOrSeal(SequenceNumber, TRUE, &BufferDescriptor);
|
|
}
|
|
|
|
TrailerLength += SecurityBuffers[3].cbBuffer;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
ASSERT(0 && "RPC DG: unknown protection level");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Unsecure call.
|
|
//
|
|
pHeader->AuthProto = 0;
|
|
}
|
|
|
|
//
|
|
// Send the fragment to the client.
|
|
//
|
|
if (RPC_S_OK == Status)
|
|
{
|
|
SendPacketBack(pHeader, pHeader->PacketBodyLen + TrailerLength);
|
|
}
|
|
|
|
//
|
|
// Restore data underneath security trailer.
|
|
//
|
|
if (pDataUnderTrailer)
|
|
{
|
|
memcpy(pSecurityTrailer, pDataUnderTrailer, SecurityTrailerSize);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_SCALL::UnauthenticatedCallback(
|
|
unsigned * pClientSequenceNumber
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine calls conv_who_are_you2, then checks the sequence number
|
|
and server boot time returned by the client.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
It makes an RPC call. The usual errors may occur.
|
|
|
|
--*/
|
|
|
|
{
|
|
CallbackThread = GetThreadIdentifier();
|
|
|
|
RPC_STATUS Status = RPC_S_OK;
|
|
RPC_BINDING_HANDLE CallbackBinding = 0;
|
|
RPC_UUID CasUuid;
|
|
unsigned long ClientSequence;
|
|
|
|
//
|
|
// Construct a string binding to the client.
|
|
//
|
|
RPC_CHAR * StringBinding;
|
|
RPC_CHAR * Address;
|
|
RPC_CHAR * Endpoint;
|
|
|
|
void * pEndpoint;
|
|
|
|
unsigned Length;
|
|
unsigned AddressLength;
|
|
unsigned EndpointLength;
|
|
unsigned ProtocolLength;
|
|
|
|
Address = (RPC_CHAR *) _alloca(sizeof(RPC_CHAR) * pAddress->pTransport->SizeOfAddressString);
|
|
Endpoint = (RPC_CHAR *) _alloca(sizeof(RPC_CHAR) * pAddress->pTransport->SizeOfEndpointString);
|
|
|
|
if (!Address || !Endpoint)
|
|
{
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
if (CallWasForwarded)
|
|
{
|
|
pEndpoint = pRealEndpoint;
|
|
}
|
|
else
|
|
{
|
|
pEndpoint = pClientEndpoint;
|
|
}
|
|
|
|
Status = pAddress->pTransport->TranslateClientAddress(pEndpoint, Address);
|
|
if ( Status != RPC_S_OK )
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
Status = pAddress->pTransport->TranslateClientEndpoint(pEndpoint, Endpoint);
|
|
if ( Status != RPC_S_OK )
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
ProtocolLength = StringLengthWithEscape(pAddress->InqRpcProtocolSequence());
|
|
AddressLength = StringLengthWithEscape(Address);
|
|
EndpointLength = StringLengthWithEscape(Endpoint);
|
|
|
|
StringBinding = (RPC_CHAR *) _alloca(sizeof(RPC_CHAR) *
|
|
( ProtocolLength
|
|
+ 1
|
|
+ AddressLength
|
|
+ 1
|
|
+ EndpointLength
|
|
+ 1
|
|
+ 1
|
|
));
|
|
|
|
if (!StringBinding)
|
|
{
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
StringCopyEscapeCharacters(StringBinding, pAddress->InqRpcProtocolSequence());
|
|
|
|
Length = ProtocolLength;
|
|
|
|
StringBinding[Length++] = RPC_CONST_CHAR(':');
|
|
|
|
StringCopyEscapeCharacters(StringBinding + Length, Address);
|
|
|
|
Length += AddressLength;
|
|
|
|
StringBinding[Length++] = RPC_CONST_CHAR('[');
|
|
|
|
StringCopyEscapeCharacters(StringBinding + Length, Endpoint);
|
|
|
|
Length += EndpointLength;
|
|
|
|
StringBinding[Length++] = RPC_CONST_CHAR(']');
|
|
StringBinding[Length] = 0;
|
|
|
|
//
|
|
// We're finished with member variables, so we can release the mutex.
|
|
//
|
|
IncrementRefCount();
|
|
CallMutex.Clear();
|
|
|
|
//
|
|
// Create a binding handle to the client endpoint. It's important to do it
|
|
// outside the call mutex because it causes a lot of memory allocation.
|
|
//
|
|
#ifdef NTENV
|
|
Status = RpcBindingFromStringBindingW(StringBinding, &CallbackBinding);
|
|
#else
|
|
Status = RpcBindingFromStringBindingA(StringBinding, &CallbackBinding);
|
|
#endif
|
|
|
|
if (RPC_S_OK == Status)
|
|
{
|
|
//
|
|
// First we try who_are_you2, which gives us the CAS UUID for context
|
|
// handles. If that fails, try who_are_you which is the only thing
|
|
// supported by NT 3.50 (build 807).
|
|
//
|
|
_conv_who_are_you2(CallbackBinding,
|
|
(UUID *) &pSavedPacket->Header.ActivityId,
|
|
ServerBootTime,
|
|
&ClientSequence,
|
|
(UUID *) &CasUuid,
|
|
(unsigned long *) &Status
|
|
);
|
|
|
|
if (RPC_S_OK != Status &&
|
|
RPC_S_OUT_OF_MEMORY != Status)
|
|
{
|
|
_conv_who_are_you(CallbackBinding,
|
|
(UUID *) &pSavedPacket->Header.ActivityId,
|
|
ServerBootTime,
|
|
&ClientSequence,
|
|
(unsigned long *) &Status
|
|
);
|
|
|
|
if (RPC_S_OK == Status)
|
|
{
|
|
Status = UuidCreate((UUID *) &CasUuid);
|
|
}
|
|
}
|
|
|
|
if (RPC_S_SERVER_UNAVAILABLE == Status ||
|
|
RPC_S_CALL_FAILED_DNE == Status ||
|
|
RPC_S_CALL_FAILED == Status ||
|
|
RPC_S_PROTOCOL_ERROR == Status)
|
|
{
|
|
Status = NCA_STATUS_WHO_ARE_YOU_FAILED;
|
|
}
|
|
|
|
RpcBindingFree(&CallbackBinding);
|
|
}
|
|
|
|
CallMutex.Request();
|
|
DecrementRefCount();
|
|
|
|
//
|
|
// The sequence number of the SCALL may have changed while we were gone.
|
|
// But we are updating stuff that is not sequence-specific.
|
|
//
|
|
CallbackThread = 0;
|
|
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
if (0 == pAssocGroup)
|
|
{
|
|
ASSOCIATION_GROUP * Cas;
|
|
|
|
Cas = AssociationGroups->FindOrCreate(&CasUuid, pAddress->pTransport->BaselinePduSize);
|
|
if (0 == Cas)
|
|
{
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
ASSERT(pAssocGroup == 0);
|
|
|
|
pAssocGroup = Cas;
|
|
}
|
|
|
|
*pClientSequenceNumber = ClientSequence;
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
|
|
void
|
|
DG_SCALL::DealWithRequest(
|
|
PDG_PACKET pPacket
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handles a request packet. The packet's sequence number need not
|
|
match the one in the SCALL.
|
|
|
|
Arguments:
|
|
|
|
pPacket - The incoming packet.
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
{
|
|
RPC_UUID * pInActivityUuid=&(pPacket->Header.ActivityId);
|
|
unsigned long InSequenceNumber=pPacket->Header.SequenceNumber;
|
|
|
|
ASSERT(0 == ActivityNode.CompareUuid(pInActivityUuid));
|
|
|
|
if (InSequenceNumber < SequenceNumber)
|
|
{
|
|
CallMutex.Clear();
|
|
FreePacket(pPacket);
|
|
return;
|
|
}
|
|
|
|
ExpirationTime = CurrentTimeInMsec();
|
|
|
|
if (InSequenceNumber > SequenceNumber)
|
|
{
|
|
if (CallInit != State)
|
|
{
|
|
CleanupAfterCall();
|
|
}
|
|
SequenceNumber = InSequenceNumber;
|
|
}
|
|
|
|
ASSERT(pPacket->Header.SequenceNumber == SequenceNumber);
|
|
|
|
//
|
|
// At this point we know the request pertains to the current seqnum.
|
|
//
|
|
RPC_STATUS Status = RPC_S_OK;
|
|
|
|
PNCA_PACKET_HEADER pHeader = &pPacket->Header;
|
|
|
|
switch (State)
|
|
{
|
|
case CallInit:
|
|
{
|
|
//
|
|
// Reset the DG_PACKET_ENGINE.
|
|
//
|
|
NewCall();
|
|
|
|
//
|
|
// This should occur only if the server crashed during a
|
|
// multifragment send and restarted before the client timed out.
|
|
//
|
|
if (pHeader->FragmentNumber != 0)
|
|
{
|
|
GUID * pActivity = (GUID *) &pPacket->Header.ActivityId;
|
|
}
|
|
|
|
//
|
|
// Initialize all our data for this call.
|
|
//
|
|
CallWasForwarded = FALSE;
|
|
if (pRealEndpoint)
|
|
{
|
|
delete pRealEndpoint;
|
|
pRealEndpoint = 0;
|
|
}
|
|
|
|
RPC_SYNTAX_IDENTIFIER InterfaceInfo;
|
|
|
|
InterfaceInfo.SyntaxVersion = pHeader->InterfaceVersion;
|
|
RpcpMemoryCopy(
|
|
&(InterfaceInfo.SyntaxGUID),
|
|
&(pHeader->InterfaceId),
|
|
sizeof(RPC_UUID)
|
|
);
|
|
|
|
if (!LastInterface || LastInterface->MatchInterfaceIdentifier(&InterfaceInfo))
|
|
{
|
|
LastInterface = pAddress->Server->FindInterface(&InterfaceInfo);
|
|
}
|
|
|
|
if (!LastInterface)
|
|
{
|
|
Status = RPC_S_UNKNOWN_IF;
|
|
}
|
|
else if (!GlobalRpcServer->IsServerListening() &&
|
|
!LastInterface->IsAutoListenInterface())
|
|
{
|
|
Status = RPC_S_SERVER_TOO_BUSY;
|
|
}
|
|
else
|
|
{
|
|
Status = LastInterface->IsObjectSupported(&pHeader->ObjectId);
|
|
}
|
|
|
|
if (RPC_S_OK == Status)
|
|
{
|
|
if (!PipeWaitEvent && LastInterface->IsPipeInterface() )
|
|
{
|
|
PipeWaitEvent = new EVENT(&Status, FALSE);
|
|
if (!PipeWaitEvent)
|
|
{
|
|
Status = RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
else if (Status != RPC_S_OK)
|
|
{
|
|
delete PipeWaitEvent;
|
|
PipeWaitEvent = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
pSavedPacket->Header.SequenceNumber = SequenceNumber;
|
|
InitErrorPacket(&pSavedPacket->Header, DG_REJECT, Status);
|
|
SendPacketBack(&pSavedPacket->Header, sizeof(unsigned long));
|
|
|
|
CleanupAfterCall();
|
|
|
|
CallMutex.Clear();
|
|
break;
|
|
}
|
|
|
|
//
|
|
// The server is listening and the interface is present.
|
|
// We will increment these counters to declare that a call
|
|
// is in progress.
|
|
//
|
|
ASSERT(FALSE == CallInProgress);
|
|
|
|
CallInProgress = TRUE;
|
|
pAddress->IncrementActiveCallCount();
|
|
|
|
if (LastInterface->IsAutoListenInterface())
|
|
{
|
|
LastInterface->BeginAutoListenCall() ;
|
|
pAddress->BeginAutoListenCall() ;
|
|
}
|
|
|
|
SetState(CallWaitingForFrags);
|
|
|
|
//
|
|
// No "break" here.
|
|
//
|
|
}
|
|
|
|
case CallWaitingForFrags:
|
|
{
|
|
//
|
|
// Once we have all the [in] data, we are logically in "working"
|
|
// state.
|
|
//
|
|
if (fReceivedAllFragments && CallbackThread)
|
|
{
|
|
CallMutex.Clear();
|
|
|
|
//
|
|
// Only reply to the last fragment of the call.
|
|
//
|
|
if ((pHeader->PacketFlags & DG_PF_LAST_FRAG) ||
|
|
0 == (pHeader->PacketFlags & DG_PF_FRAG))
|
|
{
|
|
CleanupPacket(pHeader);
|
|
|
|
pHeader->PacketType = DG_WORKING;
|
|
|
|
SealAndSendPacket(pHeader);
|
|
}
|
|
FreePacket(pPacket);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If the client sent an oversize fragment, send a FACK-with-body
|
|
// telling him our limit.
|
|
//
|
|
if (pPacket->Header.PacketFlags & DG_PF_OVERSIZE_PACKET)
|
|
{
|
|
SendFack(pPacket);
|
|
|
|
CallMutex.Clear();
|
|
|
|
FreePacket(pPacket);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// We use PacketBodyLength in a lot of places, so it had better be reasonable.
|
|
//
|
|
if (pPacket->DataLength < pPacket->Header.PacketBodyLen)
|
|
{
|
|
CallMutex.Clear();
|
|
#ifdef DEBUGRPC
|
|
DbgPrint("dg rpc: packet truncated from %lu to %lu\n",
|
|
pPacket->Header.PacketBodyLen, pPacket->DataLength);
|
|
#endif
|
|
FreePacket(pPacket);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Add the fragment to the call's packet list.
|
|
//
|
|
AddPacketToReceiveList(pPacket);
|
|
|
|
//
|
|
// At the first non-idempotent call on an activity,
|
|
// we must call conv_who_are_you2 to ensure the call was not
|
|
// already executed. The same applies if a call arrives after the
|
|
// server has freed its activity due to disuse.
|
|
//
|
|
// conv_who_are_you_auth is used to construct
|
|
// a new security context, and subsumes the action of
|
|
// conv_who_are_you2.
|
|
//
|
|
|
|
//
|
|
// If we are able to make a callback now, check whether we need
|
|
// to call back.
|
|
//
|
|
Status = RPC_S_OK;
|
|
|
|
unsigned ClientSequenceNumber = SequenceNumber;
|
|
|
|
if (0 == CallbackThread &&
|
|
pReceivedPackets &&
|
|
TRUE == KnowClientEndpoint())
|
|
{
|
|
unsigned SavedSequence = SequenceNumber;
|
|
|
|
if (pReceivedPackets->Header.AuthProto == 0)
|
|
{
|
|
ActiveSecurityContext = 0;
|
|
|
|
if (0 == pAssocGroup &&
|
|
0 == (pReceivedPackets->Header.PacketFlags & DG_PF_IDEMPOTENT))
|
|
{
|
|
if (RPC_S_OK == pAddress->CheckThreadPool())
|
|
{
|
|
Status = UnauthenticatedCallback(&ClientSequenceNumber);
|
|
}
|
|
else
|
|
{
|
|
Status = RPC_S_SERVER_TOO_BUSY;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Status = pAddress->CheckThreadPool();
|
|
if (RPC_S_OK == Status)
|
|
{
|
|
Status = FindOrCreateSecurityContext(pReceivedPackets, &ClientSequenceNumber);
|
|
|
|
if (0 == ActiveSecurityContext && RPC_S_OK == Status)
|
|
{
|
|
ActiveSecurityContext = SecurityContextDict.Find(MaxKeySeq);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Status = RPC_S_SERVER_TOO_BUSY;
|
|
}
|
|
}
|
|
|
|
if (SavedSequence != SequenceNumber)
|
|
{
|
|
CallMutex.Clear();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
pSavedPacket->Header.SequenceNumber = SequenceNumber;
|
|
InitErrorPacket(&pSavedPacket->Header, DG_REJECT, Status);
|
|
SendPacketBack(&pSavedPacket->Header, sizeof(unsigned long));
|
|
|
|
CleanupAfterCall();
|
|
|
|
CallMutex.Clear();
|
|
break;
|
|
}
|
|
|
|
if (ClientSequenceNumber != SequenceNumber)
|
|
{
|
|
CleanupAfterCall();
|
|
|
|
CallMutex.Clear();
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Before we can execute the call, we need
|
|
// - the client's true endpoint, for a forwarded call
|
|
// - a successful callback completion, if necessary
|
|
//
|
|
// For a pipes interface, we need only fragment zero;
|
|
// for ordinary interfaces we need all the fragments.
|
|
//
|
|
if (KnowClientEndpoint() &&
|
|
0 == CallbackThread &&
|
|
RPC_S_OK == Status)
|
|
{
|
|
//
|
|
// See if we are ready to dispatch to the stub.
|
|
//
|
|
BOOL fReadyToDispatch = FALSE;
|
|
|
|
if (LastInterface->IsPipeInterface())
|
|
{
|
|
if (0 == pReceivedPackets->Header.FragmentNumber)
|
|
{
|
|
fReadyToDispatch = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (fReceivedAllFragments)
|
|
{
|
|
fReadyToDispatch = TRUE;
|
|
}
|
|
}
|
|
|
|
if (fReadyToDispatch)
|
|
{
|
|
//
|
|
// Make sure enough threads are listening for new packets.
|
|
//
|
|
Status = pAddress->CheckThreadPool();
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
pSavedPacket->Header.SequenceNumber = SequenceNumber;
|
|
InitErrorPacket(&pSavedPacket->Header, DG_REJECT, Status);
|
|
SendPacketBack(&pSavedPacket->Header, sizeof(unsigned long));
|
|
|
|
CleanupAfterCall();
|
|
|
|
CallMutex.Clear();
|
|
break;
|
|
}
|
|
|
|
SetFragmentLengths(ActiveSecurityContext);
|
|
|
|
//
|
|
// Execute the server stub and send back a response.
|
|
//
|
|
ProcessRpcCall();
|
|
}
|
|
}
|
|
|
|
CallMutex.Clear();
|
|
break;
|
|
}
|
|
|
|
case CallWorking:
|
|
{
|
|
//
|
|
// Non-pipe interfaces will follow this path.
|
|
//
|
|
if (fReceivedAllFragments)
|
|
{
|
|
CallMutex.Clear();
|
|
|
|
pHeader->PacketType = DG_WORKING;
|
|
pHeader->ServerBootTime = ServerBootTime;
|
|
SetMyDataRep(pHeader);
|
|
|
|
SealAndSendPacket(pHeader);
|
|
FreePacket(pPacket);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If the client sent an oversize fragment, send a FACK-with-body
|
|
// telling him our limit.
|
|
//
|
|
if (pPacket->Header.PacketFlags & DG_PF_OVERSIZE_PACKET)
|
|
{
|
|
SendFack(pPacket);
|
|
|
|
CallMutex.Clear();
|
|
|
|
FreePacket(pPacket);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// We use PacketBodyLength in a lot of places, so it had better be reasonable.
|
|
//
|
|
if (pPacket->DataLength < pPacket->Header.PacketBodyLen)
|
|
{
|
|
CallMutex.Clear();
|
|
#ifdef DEBUGRPC
|
|
DbgPrint("dg rpc: packet truncated from %lu to %lu\n",
|
|
pPacket->Header.PacketBodyLen, pPacket->DataLength);
|
|
#endif
|
|
FreePacket(pPacket);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Add the fragment to the call's packet list.
|
|
//
|
|
AddPacketToReceiveList(pPacket);
|
|
|
|
CallMutex.Clear();
|
|
break;
|
|
}
|
|
|
|
case CallSendingFrags:
|
|
{
|
|
TimeoutCount = 0;
|
|
if (TRUE == DelayedActions->Cancel(&FackTimer))
|
|
{
|
|
SendSomeFragments();
|
|
}
|
|
|
|
CallMutex.Clear();
|
|
|
|
FreePacket(pPacket);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
ASSERT(0 && "invalid call state");
|
|
|
|
CallMutex.Clear();
|
|
break;
|
|
}
|
|
}
|
|
|
|
CallMutex.VerifyNotOwned();
|
|
}
|
|
|
|
|
|
void
|
|
DG_SCALL::AddPacketToReceiveList(
|
|
PDG_PACKET pPacket
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds a packet to the receive list and lets the caller know whether this
|
|
call is ready to be processed.
|
|
|
|
Arguments:
|
|
|
|
pPacket - the packet to add to the list.
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
{
|
|
PNCA_PACKET_HEADER pHeader = &pPacket->Header;
|
|
|
|
if (pHeader->PacketFlags & DG_PF_FORWARDED)
|
|
{
|
|
//
|
|
// This is either a complete forwarded packet or the header fragment
|
|
// of a fragmented forwarded packet. Either way, it contains the
|
|
// original client address and DREP.
|
|
//
|
|
CallWasForwarded = TRUE;
|
|
SaveOriginalClientInfo(pPacket);
|
|
|
|
if (pHeader->PacketFlags2 & DG_PF_FORWARDED_2)
|
|
{
|
|
//
|
|
// This packet doesn't contain the original packet data.
|
|
//
|
|
FreePacket(pPacket);
|
|
|
|
//
|
|
// If we have previously received all the data fragments and this is
|
|
// the first header fragment received, we now have enough information
|
|
// to call the stub.
|
|
//
|
|
return;
|
|
}
|
|
}
|
|
else if (pHeader->PacketFlags2 & DG_PF_FORWARDED_2)
|
|
{
|
|
CallWasForwarded = TRUE;
|
|
}
|
|
|
|
UpdateReceiveWindow(pPacket);
|
|
|
|
//
|
|
// See whether we need to wake up a call to I_RpcReceive.
|
|
//
|
|
if (PWT_RECEIVE == PipeWaitType)
|
|
{
|
|
if (fReceivedAllFragments ||
|
|
(PipeWaitLength && ConsecutiveDataBytes >= PipeWaitLength))
|
|
{
|
|
PipeWaitType = PWT_NONE;
|
|
PipeWaitEvent->Raise();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
DG_SCALL::ProcessRpcCall(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when we determine that all the packets for a
|
|
given call have been received.
|
|
|
|
Arguments:
|
|
|
|
<none> all data is in the received packet list.
|
|
|
|
Return Value:
|
|
|
|
<void>
|
|
|
|
--*/
|
|
{
|
|
BOOL ObjectUuidSpecified;
|
|
PNCA_PACKET_HEADER pHeader = &pReceivedPackets->Header;
|
|
|
|
ASSERT(State == CallWaitingForFrags);
|
|
|
|
SetState(CallWorking);
|
|
|
|
//
|
|
// Save the object uuid if necessary.
|
|
//
|
|
if (pHeader->ObjectId.IsNullUuid())
|
|
{
|
|
ObjectUuidSpecified = FALSE;
|
|
}
|
|
else
|
|
{
|
|
ObjectUuidSpecified = TRUE;
|
|
pSavedPacket->Header.ObjectId.CopyUuid(&pHeader->ObjectId);
|
|
}
|
|
|
|
RPC_MESSAGE Message;
|
|
RPC_RUNTIME_INFO RuntimeInfo ;
|
|
|
|
RuntimeInfo.Length = sizeof(RPC_RUNTIME_INFO) ;
|
|
Message.ReservedForRuntime = &RuntimeInfo ;
|
|
|
|
Message.Handle = (RPC_BINDING_HANDLE) this;
|
|
Message.ProcNum = pHeader->OperationNumber;
|
|
Message.TransferSyntax = 0;
|
|
Message.ImportContext = 0;
|
|
|
|
Message.RpcFlags = PacketToRpcFlags(pHeader->PacketFlags);
|
|
|
|
unsigned OriginalSequenceNumber = SequenceNumber;
|
|
unsigned long SavedAwayRpcFlags = Message.RpcFlags;
|
|
|
|
RPC_STATUS Status = RPC_S_OK;
|
|
|
|
//
|
|
// For secure RPC, verify packet integrity.
|
|
//
|
|
if (ActiveSecurityContext)
|
|
{
|
|
PDG_PACKET pScan = pReceivedPackets;
|
|
|
|
do
|
|
{
|
|
Status = VerifySecurePacket(pScan, ActiveSecurityContext);
|
|
pScan = pScan->pNext;
|
|
}
|
|
while (pScan && Status == RPC_S_OK);
|
|
}
|
|
|
|
//
|
|
// Coalesce packet data.
|
|
//
|
|
if (RPC_S_OK == Status)
|
|
{
|
|
Message.Buffer = 0;
|
|
Message.BufferLength = 0;
|
|
Status = AssembleBufferFromPackets(&Message);
|
|
}
|
|
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
#ifdef MAJORDEBUG
|
|
DbgPrint("scall at %lx: error %lu\n", this, Status);
|
|
#endif
|
|
InitErrorPacket(&pSavedPacket->Header, DG_REJECT, Status);
|
|
SealAndSendPacket(&pSavedPacket->Header);
|
|
|
|
CleanupAfterCall();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// The thread context is used by routines like RpcBindingInqAuthClient
|
|
// when the user specifies hBinding == 0.
|
|
//
|
|
RpcpSetThreadContext(this);
|
|
|
|
//
|
|
// Make sure the thread is not impersonating.
|
|
//
|
|
ASSERT(0 == QueryThreadSecurityContext(&CallMutex));
|
|
|
|
RevertToSelf();
|
|
|
|
//
|
|
// Time to deal with the interface security callback. If it is required,
|
|
// the call must be secure. If we have already made a callback using
|
|
// the current auth context, we can use the cached results; otherwise.
|
|
// we should call back.
|
|
//
|
|
if (LastInterface->IsSecurityCallbackReqd())
|
|
{
|
|
if (!ActiveSecurityContext)
|
|
{
|
|
Status = RPC_S_ACCESS_DENIED;
|
|
}
|
|
else
|
|
{
|
|
unsigned Info = InterfaceCallbackResults.Find(LastInterface);
|
|
|
|
if ((Info & CBI_VALID) == 0 ||
|
|
(Info & CBI_CONTEXT_MASK) != ActiveSecurityContext->AuthContextId ||
|
|
(Info & CBI_SEQUENCE_MASK) != (LastInterface->SequenceNumber << CBI_SEQUENCE_SHIFT))
|
|
{
|
|
if (RPC_S_OK == LastInterface->CheckSecurityIfNecessary(this))
|
|
{
|
|
Info = CBI_ALLOWED;
|
|
}
|
|
else
|
|
{
|
|
Info = 0;
|
|
}
|
|
|
|
Info |= CBI_VALID;
|
|
Info |= ActiveSecurityContext->AuthContextId;
|
|
Info |= (LastInterface->SequenceNumber << CBI_SEQUENCE_SHIFT);
|
|
|
|
InterfaceCallbackResults.Update(LastInterface, Info);
|
|
|
|
//
|
|
// If the callback routine impersonated the client,
|
|
// restore the thread to its native security context.
|
|
//
|
|
RevertToSelf();
|
|
}
|
|
|
|
if (0 == (Info & CBI_ALLOWED))
|
|
{
|
|
Status = RPC_S_ACCESS_DENIED;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make sure no other thread is executing a manager routine.
|
|
//
|
|
if (RPC_S_OK == Status)
|
|
{
|
|
while (ThreadExecutingManager)
|
|
{
|
|
++ThreadsWaitingToDispatch;
|
|
IncrementRefCount();
|
|
CallMutex.Clear();
|
|
|
|
ThreadDispatchEvent.Wait();
|
|
|
|
CallMutex.Request();
|
|
DecrementRefCount();
|
|
--ThreadsWaitingToDispatch;
|
|
|
|
ThreadDispatchEvent.Clear();
|
|
|
|
if (SequenceNumber > OriginalSequenceNumber)
|
|
{
|
|
Status = RPC_S_CALL_FAILED_DNE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If no errors have occurred, we are ready to dispatch. Release
|
|
// the call mutex, call the server stub, and grab the mutex again.
|
|
//
|
|
BOOL StubWasCalled = FALSE;
|
|
|
|
if (RPC_S_OK == Status)
|
|
{
|
|
RPC_INTERFACE * Interface = LastInterface;
|
|
|
|
ASSERT(Interface);
|
|
|
|
ThreadExecutingManager = GetCurrentThreadId();
|
|
|
|
DispatchSequenceNumber = SequenceNumber;
|
|
|
|
IncrementRefCount();
|
|
CallMutex.Clear();
|
|
|
|
StubWasCalled = TRUE;
|
|
|
|
RPC_STATUS ExceptionCode = 0;
|
|
|
|
if ( !ObjectUuidSpecified )
|
|
{
|
|
Status = Interface->DispatchToStub(
|
|
&Message, // msg
|
|
0, // callback flag
|
|
&ExceptionCode // exception code
|
|
);
|
|
}
|
|
else
|
|
{
|
|
Status = Interface->DispatchToStubWithObject(
|
|
&Message, // msg
|
|
&pSavedPacket->Header.ObjectId, // object uuid
|
|
0, // callback flag
|
|
&ExceptionCode // exception code
|
|
);
|
|
}
|
|
|
|
Message.RpcFlags = SavedAwayRpcFlags;
|
|
|
|
if (Status == RPC_S_PROCNUM_OUT_OF_RANGE ||
|
|
Status == RPC_S_UNSUPPORTED_TYPE ||
|
|
Status == RPC_S_SERVER_TOO_BUSY ||
|
|
Status == RPC_S_NOT_LISTENING ||
|
|
Status == RPC_S_UNKNOWN_IF)
|
|
{
|
|
StubWasCalled = FALSE;
|
|
}
|
|
|
|
if (Status == RPC_P_EXCEPTION_OCCURED)
|
|
{
|
|
Status = ExceptionCode;
|
|
}
|
|
|
|
CallMutex.Request();
|
|
DecrementRefCount();
|
|
|
|
//
|
|
// If the manager routine impersonated the client,
|
|
// restore the thread to its native security context.
|
|
//
|
|
RevertToSelf();
|
|
|
|
//
|
|
// Free the privileges token.
|
|
//
|
|
// If the client abandoned this thread's call and began a new,
|
|
// unauthenticated call, then ActiveSecurityContext will be zero
|
|
// and this thread should not change it.
|
|
//
|
|
// Note that Privileges should be freed even if we are no longer
|
|
// the current sequence number, as privs may change between calls.
|
|
// No other thread can dispatch yet, so deletion is safe.
|
|
//
|
|
if (Privileges)
|
|
{
|
|
if (ActiveSecurityContext)
|
|
{
|
|
ActiveSecurityContext->DeletePac(Privileges);
|
|
}
|
|
else
|
|
{
|
|
SecurityContextDict.Find(MaxKeySeq)->DeletePac(Privileges);
|
|
}
|
|
|
|
Privileges = 0;
|
|
}
|
|
|
|
//
|
|
// Awaken threads waiting to dispatch.
|
|
//
|
|
ASSERT(ThreadExecutingManager == GetCurrentThreadId());
|
|
ThreadExecutingManager = 0;
|
|
if (ThreadsWaitingToDispatch)
|
|
{
|
|
ThreadDispatchEvent.Raise();
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the client has abandoned this call, this thread should not the SCALL.
|
|
//
|
|
if (SequenceNumber != OriginalSequenceNumber)
|
|
{
|
|
FreeBuffer(&Message);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// We will still be in WORKING state if no pipe data was sent.
|
|
//
|
|
ASSERT(State == CallWorking ||
|
|
State == CallSendingFrags);
|
|
|
|
//
|
|
// This will be needed if the interface uses pipes and
|
|
// the generated stub or pipe routines encounter problems.
|
|
//
|
|
if (Status)
|
|
{
|
|
CleanupReceiveWindow();
|
|
}
|
|
|
|
//
|
|
// Don't send a response buffer for a [maybe] call.
|
|
//
|
|
if (Message.RpcFlags & RPC_NCA_FLAGS_MAYBE)
|
|
{
|
|
FreeBuffer(&Message);
|
|
EndOfCall();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Don't send "unknown interface" for a [broadcast] call.
|
|
//
|
|
if ((Message.RpcFlags & RPC_NCA_FLAGS_BROADCAST) &&
|
|
(Status == RPC_S_UNKNOWN_IF || Status == RPC_S_UNSUPPORTED_TYPE))
|
|
{
|
|
EndOfCall();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Ordinary error?
|
|
//
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
if (StubWasCalled)
|
|
{
|
|
InitErrorPacket(&pSavedPacket->Header, DG_FAULT, Status);
|
|
}
|
|
else
|
|
{
|
|
InitErrorPacket(&pSavedPacket->Header, DG_REJECT, Status);
|
|
}
|
|
|
|
SealAndSendPacket(&pSavedPacket->Header);
|
|
|
|
EndOfCall();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Send the static [out] call parameters; [out] pipes were sent by the stub.
|
|
//
|
|
SetState(CallSendingFrags);
|
|
|
|
Status = SetupSendWindow(&Message);
|
|
ASSERT(RPC_S_OK == Status);
|
|
|
|
SendSomeFragments();
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_SCALL::ImpersonateClient (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Force the current thread to impersonate the client of this DG_SCALL.
|
|
Note that the current thread might not be the thread executing the
|
|
server manager routine.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
result of impersonating, or RPC_S_NO_CONTEXT_AVAILABLE if this is
|
|
an insecure call.
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS Status;
|
|
|
|
//
|
|
// Copying ActiveSecurityContext to a local variable means that we don't
|
|
// need to take the DG_SCALL mutex in the common case.
|
|
//
|
|
SSECURITY_CONTEXT * SecurityContext = ActiveSecurityContext;
|
|
|
|
if (!SecurityContext)
|
|
{
|
|
return RPC_S_NO_CONTEXT_AVAILABLE;
|
|
}
|
|
|
|
Status = SetThreadSecurityContext(SecurityContext, &CallMutex);
|
|
if (RPC_S_OK != Status)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
Status = SecurityContext->ImpersonateClient();
|
|
if (RPC_S_OK != Status)
|
|
{
|
|
ClearThreadSecurityContext(&CallMutex);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
RPC_STATUS
|
|
DG_SCALL::RevertToSelf (
|
|
)
|
|
{
|
|
SSECURITY_CONTEXT * SecurityContext = ClearThreadSecurityContext(&CallMutex);
|
|
|
|
if (SecurityContext)
|
|
{
|
|
SecurityContext->RevertToSelf();
|
|
}
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_SCALL::MonitorAssociation (
|
|
IN PRPC_RUNDOWN RundownRoutine,
|
|
IN void * pContext
|
|
)
|
|
{
|
|
CallMutex.Request();
|
|
|
|
unsigned ClientSequence = SequenceNumber;
|
|
|
|
if (0 == pAssocGroup)
|
|
{
|
|
RPC_STATUS Status = UnauthenticatedCallback(&ClientSequence);
|
|
if (RPC_S_OK != Status)
|
|
{
|
|
CallMutex.Clear();
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
CallMutex.Clear();
|
|
|
|
return pAssocGroup->MonitorAssociation(RundownRoutine,pContext);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_SCALL::StopMonitorAssociation (
|
|
)
|
|
{
|
|
CallMutex.Request();
|
|
|
|
unsigned ClientSequenceNumber = SequenceNumber;
|
|
|
|
if (0 == pAssocGroup)
|
|
{
|
|
RPC_STATUS Status = UnauthenticatedCallback(&ClientSequenceNumber);
|
|
if (RPC_S_OK != Status)
|
|
{
|
|
CallMutex.Clear();
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
CallMutex.Clear();
|
|
|
|
return pAssocGroup->StopMonitorAssociation();
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_SCALL::GetAssociationContext (
|
|
OUT void ** AssociationContext
|
|
)
|
|
{
|
|
CallMutex.Request();
|
|
|
|
unsigned ClientSequenceNumber = SequenceNumber;
|
|
|
|
if (0 == pAssocGroup)
|
|
{
|
|
RPC_STATUS Status = UnauthenticatedCallback(&ClientSequenceNumber);
|
|
if (RPC_S_OK != Status)
|
|
{
|
|
CallMutex.Clear();
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
CallMutex.Clear();
|
|
|
|
*AssociationContext = pAssocGroup->AssociationContext();
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_SCALL::SetAssociationContext (
|
|
IN void * pContext
|
|
)
|
|
{
|
|
CallMutex.Request();
|
|
|
|
unsigned ClientSequenceNumber = SequenceNumber;
|
|
|
|
if (0 == pAssocGroup)
|
|
{
|
|
RPC_STATUS Status = UnauthenticatedCallback(&ClientSequenceNumber);
|
|
if (RPC_S_OK != Status)
|
|
{
|
|
CallMutex.Clear();
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
CallMutex.Clear();
|
|
|
|
pAssocGroup->SetAssociationContext(pContext);
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
|
|
void
|
|
DG_SCALL::InquireObjectUuid (
|
|
OUT RPC_UUID PAPI * ObjectUuid
|
|
)
|
|
{
|
|
ObjectUuid->CopyUuid(&pSavedPacket->Header.ObjectId);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_SCALL::ToStringBinding (
|
|
OUT RPC_CHAR PAPI * PAPI * StringBinding
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We need to convert this particular SCALL into a string binding.
|
|
Typically, we get the SCALL in Message structure. An SCall is associated
|
|
with a particular address. We just ask the address to create a string
|
|
binding
|
|
|
|
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.
|
|
|
|
--*/
|
|
{
|
|
BINDING_HANDLE * BindingHandle;
|
|
RPC_STATUS Status;
|
|
|
|
BindingHandle = pAddress->InquireBinding();
|
|
if (BindingHandle == 0)
|
|
return(RPC_S_OUT_OF_MEMORY);
|
|
|
|
BindingHandle->SetObjectUuid(&pSavedPacket->Header.ObjectId);
|
|
Status = BindingHandle->ToStringBinding(StringBinding);
|
|
BindingHandle->BindingFree();
|
|
return Status;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_SCALL::InquireAuthClient (
|
|
OUT RPC_AUTHZ_HANDLE PAPI * Privs,
|
|
OUT RPC_CHAR PAPI * PAPI * ServerPrincipalName, OPTIONAL
|
|
OUT unsigned long PAPI * AuthenticationLevel,
|
|
OUT unsigned long PAPI * AuthenticationService,
|
|
OUT unsigned long PAPI * pAuthorizationService
|
|
)
|
|
{
|
|
SSECURITY_CONTEXT * Context = ActiveSecurityContext;
|
|
|
|
if (0 == Context)
|
|
{
|
|
return RPC_S_BINDING_HAS_NO_AUTH;
|
|
}
|
|
|
|
if (AuthenticationLevel)
|
|
{
|
|
*AuthenticationLevel = Context->AuthenticationLevel;
|
|
}
|
|
|
|
if (AuthenticationService)
|
|
{
|
|
*AuthenticationService = Context->AuthenticationService;
|
|
}
|
|
|
|
if (Privs || pAuthorizationService)
|
|
{
|
|
if (!Privileges)
|
|
{
|
|
Context->GetDceInfo(&Privileges, &AuthorizationService);
|
|
}
|
|
|
|
if (Privs)
|
|
{
|
|
*Privs = Privileges;
|
|
}
|
|
|
|
if (pAuthorizationService)
|
|
{
|
|
*pAuthorizationService = AuthorizationService;
|
|
}
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(ServerPrincipalName))
|
|
{
|
|
RPC_STATUS Status;
|
|
|
|
Status = pAddress->Server->InquirePrincipalName(
|
|
*AuthenticationService,
|
|
ServerPrincipalName
|
|
);
|
|
|
|
ASSERT(Status == RPC_S_OK ||
|
|
Status == RPC_S_OUT_OF_MEMORY );
|
|
|
|
return Status;
|
|
}
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_SCALL::ConvertToServerBinding (
|
|
OUT RPC_BINDING_HANDLE __RPC_FAR * pServerBinding
|
|
)
|
|
{
|
|
return CreateReverseBinding(pServerBinding, FALSE);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_SCALL::CreateReverseBinding (
|
|
OUT RPC_BINDING_HANDLE __RPC_FAR * pServerBinding,
|
|
BOOL IncludeEndpoint
|
|
)
|
|
{
|
|
RPC_STATUS Status;
|
|
RPC_CHAR * ClientAddress;
|
|
RPC_CHAR * ClientEndpoint;
|
|
RPC_CHAR * StringBinding;
|
|
void * pEndpoint;
|
|
|
|
if (CallWasForwarded)
|
|
{
|
|
pEndpoint = pRealEndpoint;
|
|
}
|
|
else
|
|
{
|
|
pEndpoint = pClientEndpoint;
|
|
}
|
|
|
|
ClientAddress = (RPC_CHAR *) _alloca(sizeof(RPC_CHAR) * pAddress->pTransport->SizeOfAddressString);
|
|
if (!ClientAddress)
|
|
{
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
Status = pAddress->pTransport->TranslateClientAddress(pEndpoint, ClientAddress);
|
|
|
|
if ( Status != RPC_S_OK )
|
|
{
|
|
return(Status);
|
|
}
|
|
|
|
if (IncludeEndpoint)
|
|
{
|
|
ClientEndpoint = (RPC_CHAR *) _alloca(sizeof(RPC_CHAR) * pAddress->pTransport->SizeOfEndpointString);
|
|
if (!ClientEndpoint)
|
|
{
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
Status = pAddress->pTransport->TranslateClientEndpoint(pEndpoint, ClientEndpoint);
|
|
if ( Status != RPC_S_OK )
|
|
{
|
|
return(Status);
|
|
}
|
|
|
|
#ifdef NTENV
|
|
Status = RpcStringBindingComposeW(0,
|
|
#else
|
|
Status = RpcStringBindingComposeA(0,
|
|
#endif
|
|
pAddress->InqRpcProtocolSequence(),
|
|
ClientAddress,
|
|
ClientEndpoint,
|
|
0,
|
|
&StringBinding
|
|
);
|
|
}
|
|
else
|
|
{
|
|
#ifdef NTENV
|
|
Status = RpcStringBindingComposeW(0,
|
|
#else
|
|
Status = RpcStringBindingComposeA(0,
|
|
#endif
|
|
pAddress->InqRpcProtocolSequence(),
|
|
ClientAddress,
|
|
0,
|
|
0,
|
|
&StringBinding
|
|
);
|
|
}
|
|
|
|
if ( Status != RPC_S_OK )
|
|
{
|
|
return(Status);
|
|
}
|
|
|
|
#ifdef NTENV
|
|
Status = RpcBindingFromStringBindingW(StringBinding, pServerBinding);
|
|
#else
|
|
Status = RpcBindingFromStringBindingA(StringBinding, pServerBinding);
|
|
#endif
|
|
if (RPC_S_OK == Status)
|
|
{
|
|
Status = RpcBindingSetObject(*pServerBinding,
|
|
(UUID *) &pSavedPacket->Header.ObjectId
|
|
);
|
|
|
|
#ifdef NTENV
|
|
RpcStringFreeW(&StringBinding);
|
|
#else
|
|
RpcStringFreeA(&StringBinding);
|
|
#endif
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_SCALL::SendPacketBack(
|
|
PNCA_PACKET_HEADER pNcaPacketHeader,
|
|
unsigned DataAfterHeader
|
|
)
|
|
{
|
|
if (CallWasForwarded)
|
|
{
|
|
if (pRealEndpoint)
|
|
{
|
|
return pAddress->SendPacketBack(
|
|
pNcaPacketHeader,
|
|
DataAfterHeader,
|
|
pRealEndpoint
|
|
);
|
|
}
|
|
else
|
|
{
|
|
return RPC_S_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return pAddress->SendPacketBack(
|
|
pNcaPacketHeader,
|
|
DataAfterHeader,
|
|
pClientEndpoint
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
DG_SCALL::HasExpired(
|
|
)
|
|
{
|
|
unsigned CurrentTime = CurrentTimeInMsec();
|
|
|
|
//
|
|
// Quick tests that don't require taking the call mutex.
|
|
//
|
|
if (ExpirationTime > CurrentTime)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (ReferenceCount && !PipeWaitEvent)
|
|
{
|
|
#ifdef DEBUGRPC
|
|
if (CurrentTime - ExpirationTime > 3 * 60 * 1000)
|
|
{
|
|
DbgPrint("RPC DG: scall %lx inactive for %lu seconds, but has %lx references\n",
|
|
this, (CurrentTime - ExpirationTime)/1000, ReferenceCount
|
|
);
|
|
}
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// The quick tests failed; take the call mutex and look more closely.
|
|
//
|
|
CallMutex.Request();
|
|
|
|
if (ReferenceCount && !PipeWaitEvent)
|
|
{
|
|
CallMutex.Clear();
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Update the clock in case it took a long time to acquire the mutex.
|
|
//
|
|
CurrentTime = CurrentTimeInMsec();
|
|
|
|
unsigned Cutoff = FIVE_MINUTES_IN_MSEC;
|
|
|
|
if (BufferFlags & RPC_NCA_FLAGS_MAYBE)
|
|
{
|
|
Cutoff = ONE_MINUTE_IN_MSEC;
|
|
}
|
|
else if (BufferFlags & RPC_NCA_FLAGS_IDEMPOTENT)
|
|
{
|
|
Cutoff = 3 * ONE_MINUTE_IN_MSEC;
|
|
}
|
|
|
|
if (CurrentTime - ExpirationTime >= Cutoff)
|
|
{
|
|
if (State == CallInit)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
if (PWT_RECEIVE == PipeWaitType)
|
|
{
|
|
#ifdef DEBUGRPC
|
|
DbgPrint("RPC DG: scall %lx is stuck in a pipe receive - aborting\n",
|
|
this );
|
|
#endif
|
|
CleanupAfterCall();
|
|
CallMutex.Clear();
|
|
return FALSE;
|
|
}
|
|
|
|
if (State == CallWaitingForFrags && CallbackThread == 0)
|
|
{
|
|
CleanupAfterCall();
|
|
return TRUE;
|
|
}
|
|
|
|
if (State == CallSendingFrags)
|
|
{
|
|
CleanupAfterCall();
|
|
return TRUE;
|
|
}
|
|
|
|
#ifdef DEBUGRPC
|
|
|
|
DbgPrint("RPC DG: scall %lx inactive for %u seconds, but is in state %u\n",
|
|
this, (CurrentTime - ExpirationTime)/1000, State
|
|
);
|
|
|
|
#endif
|
|
}
|
|
|
|
CallMutex.Clear();
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
DG_SCALL *
|
|
ACTIVE_CALL_TABLE::LookupActivity(
|
|
DG_ADDRESS * Address,
|
|
PDG_PACKET pPacket,
|
|
BOOL fCreateIfAbsent
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Looks for a call, matching only the activity uuid. If the activity hint
|
|
is not 0xffff, we search in that hash bucket, otherwise we create a hash
|
|
and look there.
|
|
|
|
If a call is found or created, its mutex is taken.
|
|
|
|
Arguments:
|
|
|
|
pPacket - data to find call (activity uuid and activity hint)
|
|
|
|
fCreateIfAbsent - do we initialize a new call if none already exists
|
|
|
|
Return Value:
|
|
|
|
the new call, or zero
|
|
|
|
--*/
|
|
{
|
|
unsigned StartTime, EndTime;
|
|
|
|
unsigned Hash = pPacket->Header.ActivityHint;
|
|
|
|
if (Hash == 0xffff)
|
|
{
|
|
Hash = MakeHash(&pPacket->Header.ActivityId);
|
|
}
|
|
|
|
RequestHashMutex(Hash);
|
|
|
|
StartTime = GetTickCount();
|
|
|
|
UUID_HASH_TABLE_NODE * pNode = UUID_HASH_TABLE::Lookup(
|
|
&pPacket->Header.ActivityId,
|
|
Hash
|
|
);
|
|
|
|
PDG_SCALL pCall = 0;
|
|
if (pNode)
|
|
{
|
|
pCall = DG_SCALL::ContainingRecord(pNode);
|
|
|
|
pCall->IncrementRefCount();
|
|
ReleaseHashMutex(Hash);
|
|
pCall->CallMutex.Request();
|
|
pCall->DecrementRefCount();
|
|
}
|
|
else
|
|
{
|
|
if (fCreateIfAbsent)
|
|
{
|
|
pCall = Address->AllocateCall();
|
|
if (pCall)
|
|
{
|
|
pCall->Initialize(&pPacket->Header, Hash);
|
|
pCall->CallMutex.Request();
|
|
UUID_HASH_TABLE::Add(&pCall->ActivityNode, Hash);
|
|
}
|
|
}
|
|
|
|
ReleaseHashMutex(Hash);
|
|
}
|
|
|
|
|
|
return pCall;
|
|
}
|
|
|
|
|
|
DG_SCALL *
|
|
ACTIVE_CALL_TABLE::Lookup(
|
|
PDG_PACKET pPacket
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Looks for a call matching both activity uuid and sequence number.
|
|
If a call is found, its mutex is taken.
|
|
|
|
Arguments:
|
|
|
|
pPacket - packet containing call info (activity, activity hint, and seqnum)
|
|
|
|
Return Value:
|
|
|
|
the call, if a match is found
|
|
zero, if no match
|
|
|
|
--*/
|
|
|
|
{
|
|
unsigned Hash;
|
|
DG_SCALL * pCall;
|
|
|
|
pCall = LookupActivity(0, pPacket, FALSE);
|
|
|
|
if (!pCall)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (pCall->SequenceNumber == pPacket->Header.SequenceNumber)
|
|
{
|
|
return pCall;
|
|
}
|
|
|
|
pCall->CallMutex.Clear();
|
|
return 0;
|
|
}
|
|
|
|
|
|
void
|
|
ActiveCallScavengerProc(
|
|
void * Parms
|
|
)
|
|
{
|
|
ActiveCalls->DeleteExpiredCalls();
|
|
}
|
|
|
|
void
|
|
ACTIVE_CALL_TABLE::DeleteExpiredCalls(
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Scans the active call table and deletes any old entries.
|
|
|
|
Arguments:
|
|
|
|
<none>
|
|
|
|
Return Value:
|
|
|
|
<none>
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
{
|
|
static unsigned AccumulatedCalls = 0;
|
|
static unsigned Bucket = 0;
|
|
|
|
unsigned long CurrentTime;
|
|
UUID_HASH_TABLE_NODE * pNode;
|
|
UUID_HASH_TABLE_NODE * pNext;
|
|
|
|
unsigned Calls = 0;
|
|
unsigned Freed = 0;
|
|
|
|
UUID_HASH_TABLE_NODE * FreeList = 0;
|
|
|
|
#ifdef MAJORDEBUG
|
|
DbgPrint("(RPC: remove before checkin) scavenge bucket %lu\n", Bucket);
|
|
#endif
|
|
|
|
#ifdef DEBUGRPC
|
|
unsigned StartTime, EndTime;
|
|
StartTime = GetTickCount();
|
|
#endif
|
|
|
|
RequestHashMutex(Bucket);
|
|
|
|
pNode = Buckets[Bucket];
|
|
while (pNode)
|
|
{
|
|
++Calls;
|
|
|
|
pNext = pNode->pNext;
|
|
|
|
DG_SCALL * pCall = DG_SCALL::ContainingRecord(pNode);
|
|
|
|
//
|
|
// If HasExpired() returns TRUE, the call should be deleted, and
|
|
// the current thread owns the call mutex. If FALSE, the mutex is
|
|
// not held.
|
|
//
|
|
if (pCall->HasExpired())
|
|
{
|
|
UUID_HASH_TABLE::Remove(pNode, Bucket);
|
|
|
|
pNode->pNext = FreeList;
|
|
FreeList = pNode;
|
|
}
|
|
|
|
pNode = pNext;
|
|
}
|
|
|
|
ReleaseHashMutex(Bucket);
|
|
|
|
while (FreeList)
|
|
{
|
|
DG_SCALL * pCall = DG_SCALL::ContainingRecord(FreeList);
|
|
|
|
FreeList = FreeList->pNext;
|
|
|
|
pCall->CallMutex.Clear();
|
|
pCall->FreeCall();
|
|
|
|
++Freed;
|
|
}
|
|
|
|
#ifdef MAJORDEBUG
|
|
if (Freed)
|
|
{
|
|
DbgPrint("(RPC: remove before checkin) %lu of %lu active calls freed in %lu\n", Freed, Calls, Bucket);
|
|
}
|
|
#endif
|
|
|
|
AccumulatedCalls += Calls;
|
|
|
|
++Bucket;
|
|
if (Bucket < BUCKET_COUNT)
|
|
{
|
|
DelayedActions->Add(ActiveCallTimer, 500, TRUE);
|
|
}
|
|
else
|
|
{
|
|
if (AccumulatedCalls)
|
|
{
|
|
DelayedActions->Add(ActiveCallTimer, 500, TRUE);
|
|
}
|
|
|
|
Bucket = 0;
|
|
AccumulatedCalls = 0;
|
|
}
|
|
|
|
#ifdef DEBUGRPC
|
|
EndTime = GetTickCount();
|
|
if (EndTime - StartTime > 4000)
|
|
{
|
|
DbgPrint("RPC DG perf: active call bucket %lu took %lu msec to scan\n", Bucket, EndTime - StartTime);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
ASSOCIATION_GROUP *
|
|
ASSOC_GROUP_TABLE::FindOrCreate(
|
|
RPC_UUID * pUuid,
|
|
unsigned short InitialPduSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Looks for an association group with the given UUID.
|
|
If one is not found then a new one is created.
|
|
|
|
Arguments:
|
|
|
|
pUuid - CAS uuid to find or create
|
|
|
|
Return Value:
|
|
|
|
ptr to the association group if found or created
|
|
zero if not found and a new one could not be created
|
|
|
|
--*/
|
|
{
|
|
ASSOCIATION_GROUP * pGroup;
|
|
unsigned Hash = MakeHash(pUuid);
|
|
|
|
RequestHashMutex(Hash);
|
|
|
|
UUID_HASH_TABLE_NODE * pNode = UUID_HASH_TABLE::Lookup(pUuid, Hash);
|
|
if (pNode)
|
|
{
|
|
ASSOCIATION_GROUP::ContainingRecord(pNode)->IncrementRefCount();
|
|
}
|
|
else
|
|
{
|
|
RPC_STATUS Status = RPC_S_OK;
|
|
|
|
pGroup = new ASSOCIATION_GROUP(pUuid, InitialPduSize, &Status);
|
|
if (!pGroup || Status != RPC_S_OK)
|
|
{
|
|
delete pGroup;
|
|
pGroup = 0;
|
|
}
|
|
else
|
|
{
|
|
pNode = &pGroup->Node;
|
|
|
|
UUID_HASH_TABLE::Add(pNode, Hash);
|
|
}
|
|
}
|
|
|
|
ReleaseHashMutex(Hash);
|
|
|
|
if (pNode)
|
|
{
|
|
return ASSOCIATION_GROUP::ContainingRecord(pNode);
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
ASSOC_GROUP_TABLE::DecrementRefCount(
|
|
ASSOCIATION_GROUP * pClient
|
|
)
|
|
{
|
|
UUID_HASH_TABLE_NODE * pNode = &pClient->Node;
|
|
|
|
unsigned Hash = MakeHash(&pNode->Uuid);
|
|
|
|
RequestHashMutex(Hash);
|
|
|
|
pClient->RequestMutex();
|
|
|
|
if (0 == pClient->DecrementRefCount())
|
|
{
|
|
UUID_HASH_TABLE::Remove(pNode, Hash);
|
|
delete pClient;
|
|
}
|
|
else
|
|
{
|
|
pClient->ClearMutex();
|
|
}
|
|
|
|
ReleaseHashMutex(Hash);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_SCALL::FindOrCreateSecurityContext(
|
|
PDG_PACKET pPacket,
|
|
unsigned * pClientSequenceNumber
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This fn looks for the security context specified by the key_vers_num field
|
|
of the auth trailer. If the context is not found in our dictionary,
|
|
the fn uses conv_who_are_you_auth() to negotiate the context, and adds it
|
|
to the SCALL's dictionary.
|
|
|
|
Arguments:
|
|
|
|
pPacket - the secure packet whose context we want
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK, RPC_S_OUT_OF_MEMORY, and various callback errors
|
|
|
|
--*/
|
|
|
|
{
|
|
RPC_STATUS Status = RPC_S_OK;
|
|
|
|
DG_SECURITY_TRAILER * pVerifier = (DG_SECURITY_TRAILER *)
|
|
(pPacket->Header.Data + pPacket->Header.PacketBodyLen);
|
|
|
|
unsigned long AuthenticationService = pPacket->Header.AuthProto;
|
|
unsigned long AuthenticationLevel = pVerifier->protection_level;
|
|
unsigned char KeySequenceNumber = pVerifier->key_vers_num;
|
|
|
|
//
|
|
// OSF clients sometimes send these levels.
|
|
//
|
|
if (AuthenticationLevel == RPC_C_AUTHN_LEVEL_CONNECT ||
|
|
AuthenticationLevel == RPC_C_AUTHN_LEVEL_CALL)
|
|
{
|
|
AuthenticationLevel = RPC_C_AUTHN_LEVEL_PKT;
|
|
}
|
|
|
|
//
|
|
// See if we have already established a security context
|
|
// for this client.
|
|
//
|
|
SSECURITY_CONTEXT * CurrentContext = SecurityContextDict.Find(KeySequenceNumber);
|
|
if (CurrentContext)
|
|
{
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
//
|
|
// Nope, it's a new context. Construct a string binding to the client.
|
|
//
|
|
RPC_CHAR * StringBinding;
|
|
RPC_CHAR * Address;
|
|
RPC_CHAR * Endpoint;
|
|
|
|
void * pEndpoint;
|
|
|
|
unsigned Length;
|
|
unsigned AddressLength;
|
|
unsigned EndpointLength;
|
|
unsigned ProtocolLength;
|
|
|
|
Address = (RPC_CHAR *) _alloca(sizeof(RPC_CHAR) * pAddress->pTransport->SizeOfAddressString);
|
|
Endpoint = (RPC_CHAR *) _alloca(sizeof(RPC_CHAR) * pAddress->pTransport->SizeOfEndpointString);
|
|
|
|
if (!Address || !Endpoint)
|
|
{
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
if (CallWasForwarded)
|
|
{
|
|
pEndpoint = pRealEndpoint;
|
|
}
|
|
else
|
|
{
|
|
pEndpoint = pClientEndpoint;
|
|
}
|
|
|
|
Status = pAddress->pTransport->TranslateClientAddress(pEndpoint, Address);
|
|
if ( Status != RPC_S_OK )
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
Status = pAddress->pTransport->TranslateClientEndpoint(pEndpoint, Endpoint);
|
|
if ( Status != RPC_S_OK )
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
ProtocolLength = StringLengthWithEscape(pAddress->InqRpcProtocolSequence());
|
|
AddressLength = StringLengthWithEscape(Address);
|
|
EndpointLength = StringLengthWithEscape(Endpoint);
|
|
|
|
StringBinding = (RPC_CHAR *) _alloca(sizeof(RPC_CHAR) *
|
|
( ProtocolLength
|
|
+ 1
|
|
+ AddressLength
|
|
+ 1
|
|
+ EndpointLength
|
|
+ 1
|
|
+ 1
|
|
));
|
|
if (!StringBinding)
|
|
{
|
|
return RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
StringCopyEscapeCharacters(StringBinding, pAddress->InqRpcProtocolSequence());
|
|
|
|
Length = ProtocolLength;
|
|
|
|
StringBinding[Length++] = RPC_CONST_CHAR(':');
|
|
|
|
StringCopyEscapeCharacters(StringBinding + Length, Address);
|
|
|
|
Length += AddressLength;
|
|
|
|
StringBinding[Length++] = RPC_CONST_CHAR('[');
|
|
|
|
StringCopyEscapeCharacters(StringBinding + Length, Endpoint);
|
|
|
|
Length += EndpointLength;
|
|
|
|
StringBinding[Length++] = RPC_CONST_CHAR(']');
|
|
StringBinding[Length] = 0;
|
|
|
|
//
|
|
// We are entering the expensive phase of the callback. Let's release
|
|
// the call mutex so that other threads can respond to client PINGs etc.
|
|
// All error paths between here and the 'cleanup' label must do the same
|
|
// steps in reverse.
|
|
//
|
|
CallbackThread = GetThreadIdentifier();
|
|
IncrementRefCount();
|
|
CallMutex.Clear();
|
|
|
|
|
|
RPC_BINDING_HANDLE CallbackBinding = 0;
|
|
SECURITY_CREDENTIALS * pCredentials = 0;
|
|
|
|
void * TokenBuffer = 0;
|
|
void * ResponseBuffer = 0;
|
|
|
|
long TokenLength;
|
|
long ResponseLength;
|
|
|
|
//
|
|
// Create an empty security context.
|
|
//
|
|
SSECURITY_CONTEXT * NewSecurityContext;
|
|
CLIENT_AUTH_INFO Info;
|
|
|
|
Info.AuthenticationService = AuthenticationService;
|
|
Info.AuthenticationLevel = AuthenticationLevel;
|
|
Info.ServerPrincipalName = 0;
|
|
Info.AuthIdentity = 0;
|
|
Info.AuthorizationService= 0;
|
|
|
|
NewSecurityContext = new SSECURITY_CONTEXT(&Info, KeySequenceNumber, TRUE, &Status);
|
|
|
|
if (!NewSecurityContext)
|
|
{
|
|
Status = RPC_S_OUT_OF_MEMORY;
|
|
}
|
|
|
|
if (RPC_S_OK != Status)
|
|
{
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Get my security credentials.
|
|
//
|
|
Status = pAddress->Server->AcquireCredentials(
|
|
AuthenticationService,
|
|
AuthenticationLevel,
|
|
&pCredentials
|
|
);
|
|
if (RPC_S_OK != Status)
|
|
{
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Allocate challenge and response buffers.
|
|
//
|
|
TokenLength = pCredentials->MaximumTokenLength();
|
|
|
|
TokenBuffer = _alloca(TokenLength);
|
|
ResponseBuffer = _alloca(TokenLength);
|
|
|
|
if (!TokenBuffer || !ResponseBuffer)
|
|
{
|
|
Status = RPC_S_OUT_OF_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Get a skeletal context and a challenge from the security package.
|
|
//
|
|
DCE_INIT_SECURITY_INFO DceInitSecurityInfo;
|
|
|
|
SECURITY_BUFFER_DESCRIPTOR InputBufferDescriptor;
|
|
SECURITY_BUFFER_DESCRIPTOR OutputBufferDescriptor;
|
|
SECURITY_BUFFER InputBuffers[4];
|
|
SECURITY_BUFFER OutputBuffers[4];
|
|
DCE_INIT_SECURITY_INFO InitSecurityInfo;
|
|
|
|
InputBufferDescriptor.ulVersion = 0;
|
|
InputBufferDescriptor.cBuffers = 4;
|
|
InputBufferDescriptor.pBuffers = InputBuffers;
|
|
|
|
InputBuffers[0].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY;
|
|
InputBuffers[0].pvBuffer = &pPacket->Header;
|
|
InputBuffers[0].cbBuffer = sizeof(NCA_PACKET_HEADER);
|
|
|
|
InputBuffers[1].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY;
|
|
InputBuffers[1].pvBuffer = pPacket->Header.Data;
|
|
InputBuffers[1].cbBuffer = pPacket->Header.PacketBodyLen;
|
|
|
|
InputBuffers[2].BufferType = SECBUFFER_TOKEN;
|
|
InputBuffers[2].pvBuffer = pVerifier;
|
|
InputBuffers[2].cbBuffer = sizeof(DG_SECURITY_TRAILER);
|
|
|
|
InputBuffers[3].BufferType = SECBUFFER_PKG_PARAMS | SECBUFFER_READONLY;
|
|
InputBuffers[3].pvBuffer = &DceInitSecurityInfo;
|
|
InputBuffers[3].cbBuffer = sizeof(DCE_INIT_SECURITY_INFO);
|
|
|
|
OutputBufferDescriptor.ulVersion = 0;
|
|
OutputBufferDescriptor.cBuffers = 4;
|
|
OutputBufferDescriptor.pBuffers = OutputBuffers;
|
|
|
|
OutputBuffers[0].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY;
|
|
OutputBuffers[0].pvBuffer = 0;
|
|
OutputBuffers[0].cbBuffer = 0;
|
|
|
|
OutputBuffers[1].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY;
|
|
OutputBuffers[1].pvBuffer = 0;
|
|
OutputBuffers[1].cbBuffer = 0;
|
|
|
|
OutputBuffers[2].BufferType = SECBUFFER_TOKEN;
|
|
OutputBuffers[2].pvBuffer = TokenBuffer;
|
|
OutputBuffers[2].cbBuffer = TokenLength;
|
|
|
|
OutputBuffers[3].BufferType = SECBUFFER_PKG_PARAMS | SECBUFFER_READONLY;
|
|
OutputBuffers[3].pvBuffer = &DceInitSecurityInfo;
|
|
OutputBuffers[3].cbBuffer = sizeof(DCE_INIT_SECURITY_INFO);
|
|
|
|
DceInitSecurityInfo.PacketType = ~0;
|
|
DceInitSecurityInfo.AuthorizationService = ~0;
|
|
DceInitSecurityInfo.DceSecurityInfo.SendSequenceNumber = ~0;
|
|
DceInitSecurityInfo.DceSecurityInfo.ReceiveSequenceNumber = KeySequenceNumber;
|
|
RpcpMemoryCopy(&DceInitSecurityInfo.DceSecurityInfo.AssociationUuid, &pPacket->Header.ActivityId, sizeof(UUID));
|
|
|
|
Status = NewSecurityContext->AcceptFirstTime(pCredentials,
|
|
&InputBufferDescriptor,
|
|
&OutputBufferDescriptor,
|
|
AuthenticationLevel,
|
|
*(unsigned long *) pPacket->Header.DataRep,
|
|
FALSE
|
|
);
|
|
BOOL ThirdLegNeeded;
|
|
BOOL CompleteNeeded;
|
|
|
|
switch (Status)
|
|
{
|
|
case RPC_S_OK:
|
|
{
|
|
ThirdLegNeeded = FALSE;
|
|
CompleteNeeded = FALSE;
|
|
break;
|
|
}
|
|
|
|
case RPC_P_COMPLETE_NEEDED:
|
|
{
|
|
ThirdLegNeeded = FALSE;
|
|
CompleteNeeded = TRUE;
|
|
break;
|
|
}
|
|
|
|
case RPC_P_CONTINUE_NEEDED:
|
|
{
|
|
ThirdLegNeeded = TRUE;
|
|
CompleteNeeded = FALSE;
|
|
break;
|
|
}
|
|
case RPC_P_COMPLETE_AND_CONTINUE:
|
|
{
|
|
ThirdLegNeeded = TRUE;
|
|
CompleteNeeded = TRUE;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
ASSERT( CompleteNeeded == FALSE );
|
|
|
|
TokenLength = (unsigned int) OutputBuffers[2].cbBuffer;
|
|
|
|
//
|
|
// Create a binding handle to the client endpoint.
|
|
//
|
|
#ifdef NTENV
|
|
Status = RpcBindingFromStringBindingW(StringBinding, &CallbackBinding);
|
|
#else
|
|
Status = RpcBindingFromStringBindingA(StringBinding, &CallbackBinding);
|
|
#endif
|
|
|
|
if (RPC_S_OK != Status)
|
|
{
|
|
goto cleanup;
|
|
}
|
|
|
|
RPC_UUID CasUuid;
|
|
unsigned long ClientSequence;
|
|
|
|
_conv_who_are_you_auth(
|
|
CallbackBinding,
|
|
(UUID *) &pSavedPacket->Header.ActivityId,
|
|
ServerBootTime,
|
|
(unsigned char *) TokenBuffer,
|
|
TokenLength,
|
|
pCredentials->MaximumTokenLength(),
|
|
&ClientSequence,
|
|
(UUID *) &CasUuid,
|
|
(unsigned char *) ResponseBuffer,
|
|
&ResponseLength,
|
|
(unsigned long *) &Status
|
|
);
|
|
|
|
RpcBindingFree(&CallbackBinding);
|
|
|
|
if (RPC_S_SERVER_UNAVAILABLE == Status ||
|
|
RPC_S_CALL_FAILED_DNE == Status ||
|
|
RPC_S_CALL_FAILED == Status ||
|
|
RPC_S_PROTOCOL_ERROR == Status)
|
|
{
|
|
Status = NCA_STATUS_WHO_ARE_YOU_FAILED;
|
|
}
|
|
|
|
if (RPC_S_OK != Status)
|
|
{
|
|
goto cleanup;
|
|
}
|
|
|
|
if (0 == pAssocGroup)
|
|
{
|
|
pAssocGroup = AssociationGroups->FindOrCreate(&CasUuid, pAddress->pTransport->BaselinePduSize);
|
|
if (0 == pAssocGroup)
|
|
{
|
|
Status = RPC_S_OUT_OF_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if (ThirdLegNeeded)
|
|
{
|
|
//
|
|
// Give the challenge response to the security package.
|
|
//
|
|
InputBuffers[0].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY;
|
|
InputBuffers[0].pvBuffer = 0;
|
|
InputBuffers[0].cbBuffer = 0;
|
|
|
|
InputBuffers[1].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY;
|
|
InputBuffers[1].pvBuffer = 0;
|
|
InputBuffers[1].cbBuffer = 0;
|
|
|
|
InputBuffers[2].BufferType = SECBUFFER_TOKEN;
|
|
InputBuffers[2].pvBuffer = ResponseBuffer;
|
|
InputBuffers[2].cbBuffer = ResponseLength;
|
|
|
|
InputBuffers[3].BufferType = SECBUFFER_PKG_PARAMS | SECBUFFER_READONLY;
|
|
InputBuffers[3].pvBuffer = &DceInitSecurityInfo;
|
|
InputBuffers[3].cbBuffer = sizeof(DCE_INIT_SECURITY_INFO);
|
|
|
|
OutputBufferDescriptor.ulVersion = 0;
|
|
OutputBufferDescriptor.cBuffers = 0;
|
|
OutputBufferDescriptor.pBuffers = 0;
|
|
|
|
DceInitSecurityInfo.AuthorizationService = 0xffff;
|
|
DceInitSecurityInfo.PacketType = 0xffff;
|
|
DceInitSecurityInfo.DceSecurityInfo.SendSequenceNumber = ~0;
|
|
DceInitSecurityInfo.DceSecurityInfo.ReceiveSequenceNumber = KeySequenceNumber;
|
|
RpcpMemoryCopy(&DceInitSecurityInfo.DceSecurityInfo.AssociationUuid, &pPacket->Header.ActivityId, sizeof(UUID));
|
|
|
|
Status = NewSecurityContext->AcceptThirdLeg(*(unsigned long *) pPacket->Header.DataRep,
|
|
&InputBufferDescriptor,
|
|
&OutputBufferDescriptor
|
|
);
|
|
}
|
|
|
|
cleanup:
|
|
|
|
//
|
|
// Clean up.
|
|
//
|
|
if (pCredentials)
|
|
{
|
|
pAddress->Server->FreeCredentials(pCredentials);
|
|
}
|
|
|
|
if (RPC_S_OK == Status)
|
|
{
|
|
CallMutex.Request();
|
|
DecrementRefCount();
|
|
CallbackThread = 0;
|
|
|
|
SecurityContextDict.Insert(
|
|
NewSecurityContext->AuthContextId,
|
|
NewSecurityContext
|
|
);
|
|
|
|
if (MaxKeySeq <= KeySequenceNumber)
|
|
{
|
|
MaxKeySeq = KeySequenceNumber;
|
|
ActiveSecurityContext = NewSecurityContext;
|
|
}
|
|
|
|
*pClientSequenceNumber = ClientSequence;
|
|
}
|
|
else
|
|
{
|
|
delete NewSecurityContext;
|
|
|
|
CallMutex.Request();
|
|
DecrementRefCount();
|
|
CallbackThread = 0;
|
|
}
|
|
|
|
//
|
|
// Map security errors to access-denied.
|
|
//
|
|
if (0x80090000UL == (Status & 0xffff0000UL))
|
|
{
|
|
#ifdef DEBUGRPC
|
|
if (Status != SEC_E_NO_IMPERSONATION &&
|
|
Status != SEC_E_UNSUPPORTED_FUNCTION )
|
|
{
|
|
PrintToDebugger("RPC DG: mapping security error %lx to access-denied\n", Status);
|
|
}
|
|
#endif
|
|
Status = RPC_S_SEC_PKG_ERROR;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_SCALL::SendSomeFragments(
|
|
)
|
|
{
|
|
RPC_STATUS Status;
|
|
|
|
Status = DG_PACKET_ENGINE::SendSomeFragments(DG_RESPONSE);
|
|
|
|
//
|
|
// The client should send us an ACK or a FACK.
|
|
// Actually, we won't get an ACK after an idempotent response
|
|
// if the response was only a single packet, or if the client
|
|
// was NT 3.51 regardless of size. But that's close enough.
|
|
//
|
|
DelayedActions->Add(&FackTimer, THREE_SECS_IN_MSEC, TRUE);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_SCALL::Cancel(
|
|
void * ThreadHandle
|
|
)
|
|
{
|
|
InterlockedIncrement(&Cancelled);
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
unsigned
|
|
DG_SCALL::TestCancel(
|
|
)
|
|
{
|
|
return InterlockedExchange(&Cancelled, 0);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_SCALL::Receive(
|
|
PRPC_MESSAGE Message,
|
|
unsigned Size
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
When a server stub calls I_RpcReceive, this fn will be called
|
|
in short order. It waits until the requested buffer bytes are available,
|
|
copies them to Message->Buffer, and returns.
|
|
|
|
The action depends upon Message->RpcFlags:
|
|
|
|
RPC_BUFFER_PARTIAL:
|
|
|
|
Data is stored beginning at Message->Buffer[0].
|
|
|
|
Wait only until <Message->BufferLength> bytes are available;
|
|
we may resize the buffer if the fragment data exceeds the
|
|
current buffer length.
|
|
|
|
RPC_BUFFER_EXTRA:
|
|
|
|
Data is stored beginning at Message->Buffer[Message->BufferLength].
|
|
|
|
Arguments:
|
|
|
|
Message - the request.
|
|
|
|
Message->Buffer is explicitly allowed to be zero, in which case this
|
|
fn is responsible for allocating it.
|
|
|
|
Return Value:
|
|
|
|
the usual error codes
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// We need to look at the call data. Time to take the call mutex.
|
|
//
|
|
CallMutex.Request();
|
|
|
|
if (DispatchSequenceNumber != SequenceNumber)
|
|
{
|
|
CallMutex.Clear();
|
|
return RPC_S_CALL_FAILED;
|
|
}
|
|
|
|
//
|
|
// Determine whether we already have enough data on hand.
|
|
//
|
|
BOOL fEnoughData;
|
|
if (Message->RpcFlags & RPC_BUFFER_PARTIAL)
|
|
{
|
|
ASSERT(Size);
|
|
|
|
if (fReceivedAllFragments || ConsecutiveDataBytes >= Size)
|
|
{
|
|
fEnoughData = TRUE;
|
|
}
|
|
else
|
|
{
|
|
fEnoughData = FALSE;
|
|
PipeWaitLength = Size;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fEnoughData = fReceivedAllFragments;
|
|
PipeWaitLength = 0;
|
|
}
|
|
|
|
//
|
|
// Wait for enough data.
|
|
//
|
|
if (!fEnoughData)
|
|
{
|
|
ASSERT(PWT_NONE == PipeWaitType);
|
|
ASSERT(0 == PipeThreadId);
|
|
|
|
PipeWaitType = PWT_RECEIVE;
|
|
PipeThreadId = GetCurrentThreadId();
|
|
|
|
IncrementRefCount();
|
|
CallMutex.Clear();
|
|
|
|
PipeWaitEvent->Wait();
|
|
|
|
CallMutex.Request();
|
|
DecrementRefCount();
|
|
|
|
ASSERT(PipeThreadId == GetCurrentThreadId());
|
|
PipeThreadId = 0;
|
|
|
|
if (DispatchSequenceNumber != SequenceNumber)
|
|
{
|
|
CallMutex.Clear();
|
|
return RPC_S_CALL_FAILED;
|
|
}
|
|
}
|
|
|
|
//
|
|
// For secure RPC, verify packet integrity.
|
|
//
|
|
RPC_STATUS Status = RPC_S_OK;
|
|
|
|
if (ActiveSecurityContext)
|
|
{
|
|
PDG_PACKET pScan = pReceivedPackets;
|
|
|
|
do
|
|
{
|
|
Status = VerifySecurePacket(pScan, ActiveSecurityContext);
|
|
pScan = pScan->pNext;
|
|
}
|
|
while (pScan && Status == RPC_S_OK);
|
|
}
|
|
|
|
if (RPC_S_OK == Status)
|
|
{
|
|
Status = AssembleBufferFromPackets(Message);
|
|
}
|
|
|
|
CallMutex.Clear();
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
DG_SCALL::Send(
|
|
PRPC_MESSAGE Message
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Transfers a buffer to the client. If the RPC_BUFFER_PARTIAL bit is set,
|
|
this is pipe data and the function should not return until the client has
|
|
acknowledged all of it. Otherwise this is an ordinary call buffer or the
|
|
static data of a pipe call, and we can return immediately.
|
|
|
|
Arguments:
|
|
|
|
Message - the usual data
|
|
|
|
Return Value:
|
|
|
|
the usual suspects
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS Status;
|
|
|
|
CallMutex.Request();
|
|
|
|
if (DispatchSequenceNumber != SequenceNumber)
|
|
{
|
|
CallMutex.Clear();
|
|
return RPC_S_CALL_FAILED;
|
|
}
|
|
|
|
ASSERT(Message->RpcFlags & RPC_BUFFER_PARTIAL);
|
|
|
|
ASSERT(fReceivedAllFragments);
|
|
|
|
SetState(CallSendingFrags);
|
|
|
|
//
|
|
// Set the message buffer as the current send buffer. If there is enough
|
|
// data to send one or more packets, send them and wait for confirmation.
|
|
//
|
|
Status = SetupSendWindow(Message);
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
ASSERT(Status == RPC_S_SEND_INCOMPLETE);
|
|
CallMutex.Clear();
|
|
return Status;
|
|
}
|
|
|
|
unsigned FractionalPacket = Message->BufferLength - BufferLength;
|
|
|
|
Status = SendSomeFragments();
|
|
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
CallMutex.Clear();
|
|
return Status;
|
|
}
|
|
|
|
ASSERT(PWT_NONE == PipeWaitType);
|
|
ASSERT(0 == PipeThreadId);
|
|
|
|
PipeWaitType = PWT_SEND;
|
|
PipeThreadId = GetCurrentThreadId();
|
|
|
|
IncrementRefCount();
|
|
CallMutex.Clear();
|
|
|
|
PipeWaitEvent->Wait();
|
|
|
|
//
|
|
// We were awakened, either because the buffer was sent or because
|
|
// the call was aborted.
|
|
//
|
|
CallMutex.Request();
|
|
DecrementRefCount();
|
|
|
|
ASSERT(PipeThreadId == GetCurrentThreadId());
|
|
PipeThreadId = 0;
|
|
|
|
if (DispatchSequenceNumber != SequenceNumber)
|
|
{
|
|
CallMutex.Clear();
|
|
return RPC_S_CALL_FAILED;
|
|
}
|
|
|
|
//
|
|
// if this was a PARTIAL send and the buffer did not occupy an even
|
|
// number of packets, Status is RPC_S_SEND_INCOMPLETE. We need to
|
|
// change the message to point to the unsent portion.
|
|
//
|
|
if (FractionalPacket)
|
|
{
|
|
ASSERT( FirstUnsentOffset < Message->BufferLength );
|
|
|
|
char __RPC_FAR * Temp = (char __RPC_FAR *) Buffer;
|
|
|
|
Message->BufferLength -= FirstUnsentOffset;
|
|
|
|
ASSERT( Message->BufferLength < CurrentPduSize );
|
|
|
|
RpcpMemoryMove(Message->Buffer, Temp + FirstUnsentOffset, Message->BufferLength);
|
|
|
|
CallMutex.Clear();
|
|
return RPC_S_SEND_INCOMPLETE;
|
|
}
|
|
else
|
|
{
|
|
CallMutex.Clear();
|
|
return RPC_S_OK;
|
|
}
|
|
}
|
|
|
|
void DG_SCALL::FreePipeBuffer (
|
|
IN PRPC_MESSAGE Message
|
|
)
|
|
{
|
|
FreeBuffer(Message);
|
|
}
|
|
|
|
RPC_STATUS
|
|
DG_SCALL::ReallocPipeBuffer (
|
|
IN PRPC_MESSAGE Message,
|
|
IN unsigned int NewSize
|
|
)
|
|
{
|
|
CallMutex.Request();
|
|
|
|
if (DispatchSequenceNumber != SequenceNumber)
|
|
{
|
|
CallMutex.Clear();
|
|
return RPC_S_CALL_FAILED;
|
|
}
|
|
|
|
if (Message->Buffer == LastReceiveBuffer &&
|
|
NewSize <= LastReceiveBufferLength)
|
|
{
|
|
Message->BufferLength = NewSize;
|
|
|
|
CallMutex.Clear();
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
RPC_STATUS Status;
|
|
RPC_MESSAGE NewMessage;
|
|
|
|
NewMessage.BufferLength = NewSize;
|
|
|
|
Status = GetBuffer(&NewMessage);
|
|
if (RPC_S_OK != Status)
|
|
{
|
|
CallMutex.Clear();
|
|
return Status;
|
|
}
|
|
|
|
LastReceiveBuffer = NewMessage.Buffer;
|
|
LastReceiveBufferLength = NewMessage.BufferLength;
|
|
|
|
CallMutex.Clear();
|
|
|
|
//
|
|
// If the length is being revised downward, the old data
|
|
// is clearly not being used.
|
|
//
|
|
if (NewSize >= Message->BufferLength)
|
|
{
|
|
RpcpMemoryCopy(NewMessage.Buffer,
|
|
Message->Buffer,
|
|
Message->BufferLength
|
|
);
|
|
}
|
|
|
|
//
|
|
// If we are updating the [in] buffer, we need to inform
|
|
// DisaptchToStubWorker of the new [in] buffer.
|
|
//
|
|
PRPC_RUNTIME_INFO Info = (PRPC_RUNTIME_INFO) Message->ReservedForRuntime;
|
|
if (Message->Buffer == Info->OldBuffer)
|
|
{
|
|
Info->OldBuffer = NewMessage.Buffer;
|
|
}
|
|
|
|
//
|
|
// Get rid of the old data.
|
|
//
|
|
FreeBuffer(Message);
|
|
|
|
Message->Buffer = NewMessage.Buffer;
|
|
Message->BufferLength = NewMessage.BufferLength;
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
#if defined(NTENV)
|
|
|
|
|
|
HANDLE RPC_ENTRY
|
|
I_RpcGetThreadEvent(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns an event specific to this thread.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
0 - unable to create the event
|
|
|
|
non-zero - A handle to an event for this thread.
|
|
|
|
--*/
|
|
{
|
|
THREAD *pThis = RpcpGetThreadPointer();
|
|
|
|
ASSERT(pThis);
|
|
|
|
if (pThis->hThreadEvent == 0)
|
|
{
|
|
pThis->hThreadEvent = CreateEventW(0, TRUE, FALSE, 0);
|
|
}
|
|
|
|
return(pThis->hThreadEvent);
|
|
}
|
|
|
|
#endif // NTENV
|
|
|
|
//------------------------------------------------------------------------
|
|
//------------------------------------------------------------------------
|
|
//------------------------------------------------------------------------
|
|
|
|
#ifdef UNRELIABLE_TRANSPORT
|
|
|
|
#define UR_DROP 0
|
|
#define UR_DELAY 1
|
|
#define UR_DUP2 2
|
|
// #define UR_NO_MEMORY 3
|
|
#define UR_COUNT 3
|
|
|
|
#define UR_LATER_DUP UR_COUNT+1
|
|
|
|
double random();
|
|
void randomize(long val);
|
|
char * strtok(
|
|
char * string,
|
|
char * chaff
|
|
);
|
|
|
|
inline unsigned long rand()
|
|
{
|
|
static unsigned long Foo = CurrentTimeInMsec() >> 3;
|
|
|
|
Foo *= 47;
|
|
Foo += 0x7c1f8e11;
|
|
|
|
DbgPrint("rand %% 100 = %lu\n", Foo % 100);
|
|
return Foo;
|
|
}
|
|
|
|
|
|
void
|
|
NoteFailure(
|
|
NCA_PACKET_HEADER * pHeader,
|
|
char * Direction,
|
|
unsigned Type
|
|
)
|
|
{
|
|
if (!Chat)
|
|
{
|
|
return;
|
|
}
|
|
|
|
char * Action;
|
|
|
|
switch (Type)
|
|
{
|
|
case UR_DROP:
|
|
{
|
|
Action = "drop";
|
|
break;
|
|
}
|
|
|
|
// case UR_NO_MEMORY:
|
|
// {
|
|
// Action = "no memory";
|
|
// break;
|
|
// }
|
|
|
|
case UR_DUP2:
|
|
{
|
|
Action = "1st duplicate";
|
|
break;
|
|
}
|
|
|
|
case UR_LATER_DUP:
|
|
{
|
|
Action = "2nd duplicate";
|
|
break;
|
|
}
|
|
|
|
case UR_DELAY:
|
|
{
|
|
Action = "delay";
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
DbgPrint("unknown fail type %u", Type);
|
|
Action = " ";
|
|
break;
|
|
}
|
|
}
|
|
|
|
DbgPrint("%s %s act %8.8lx frag %4.4hx serial %3.3hx\n",
|
|
Direction,
|
|
Action,
|
|
(unsigned long *) &pHeader->ActivityId,
|
|
pHeader->FragmentNumber,
|
|
ReadSerialNumber(pHeader)
|
|
);
|
|
|
|
}
|
|
|
|
|
|
unsigned
|
|
ChooseFailure(
|
|
NCA_PACKET_HEADER * pHeader
|
|
)
|
|
{
|
|
unsigned FailType = (rand() >> 3) % UR_COUNT;
|
|
|
|
if (Chat == TRUE)
|
|
{
|
|
NoteFailure(pHeader, "receive", FailType);
|
|
}
|
|
|
|
return FailType;
|
|
}
|
|
|
|
RPC_STATUS
|
|
DG_ADDRESS::SendPacketBack(
|
|
NCA_PACKET_HEADER * pHeader,
|
|
unsigned DataAfterHeader,
|
|
void * pClientEndpoint
|
|
)
|
|
{
|
|
pHeader->PacketFlags &= ~DG_PF_FORWARDED;
|
|
pHeader->PacketFlags2 &= ~DG_PF_FORWARDED_2;
|
|
|
|
if (SendFailPercentage)
|
|
{
|
|
AddressMutex.Request();
|
|
|
|
if (SendDupRemaining && CurrentTimeInMsec() >= SendTime )
|
|
{
|
|
--SendDupRemaining;
|
|
|
|
AddressMutex.Clear();
|
|
|
|
pTransport->SendPacketBack(
|
|
pTransAddress,
|
|
(char *) &pSavedSendPacket->Header,
|
|
sizeof(NCA_PACKET_HEADER) + SavedDataAfterHeader,
|
|
pSavedSendEndpoint
|
|
);
|
|
}
|
|
else
|
|
{
|
|
AddressMutex.Clear();
|
|
}
|
|
|
|
if (rand() % 100 > SendFailPercentage)
|
|
{
|
|
unsigned FailType = ChooseFailure(pHeader);
|
|
|
|
switch (FailType)
|
|
{
|
|
case UR_DROP:
|
|
{
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
// case UR_NO_MEMORY:
|
|
// {
|
|
// return RPC_S_OUT_OF_MEMORY;
|
|
// }
|
|
|
|
case UR_DUP2:
|
|
{
|
|
pTransport->SendPacketBack(
|
|
pTransAddress,
|
|
(char *) pHeader,
|
|
sizeof(NCA_PACKET_HEADER) + DataAfterHeader,
|
|
pClientEndpoint
|
|
);
|
|
|
|
pTransport->SendPacketBack(
|
|
pTransAddress,
|
|
(char *) pHeader,
|
|
sizeof(NCA_PACKET_HEADER) + DataAfterHeader,
|
|
pClientEndpoint
|
|
);
|
|
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
case UR_DELAY:
|
|
{
|
|
//
|
|
// Save the packet and endpoint information for next time.
|
|
//
|
|
AddressMutex.Request();
|
|
if (0 == SendDupRemaining)
|
|
{
|
|
memcpy(&pSavedSendPacket->Header, pHeader, pHeader->PacketBodyLen+DataAfterHeader);
|
|
memcpy(pSavedSendEndpoint, pClientEndpoint, pTransport->SizeOfAddress);
|
|
SavedDataAfterHeader = DataAfterHeader;
|
|
|
|
SendDupRemaining = 1;
|
|
|
|
SendTime = CurrentTimeInMsec() + 1000*(rand() % 3 + 1);
|
|
}
|
|
AddressMutex.Clear();
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
default:
|
|
{
|
|
return RPC_S_OK;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return pTransport->SendPacketBack(
|
|
pTransAddress,
|
|
(char *) pHeader,
|
|
sizeof(NCA_PACKET_HEADER) + DataAfterHeader,
|
|
pClientEndpoint
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return pTransport->SendPacketBack(
|
|
pTransAddress,
|
|
(char *) pHeader,
|
|
sizeof(NCA_PACKET_HEADER) + DataAfterHeader,
|
|
pClientEndpoint
|
|
);
|
|
}
|
|
}
|
|
|
|
RPC_STATUS
|
|
DG_ADDRESS::UnreliableReceive(
|
|
// IN void __RPC_FAR * pAddress,
|
|
PDG_SERVER_TRANS_ADDRESS pTransAddress,
|
|
unsigned long LargestPacketSize,
|
|
PNCA_PACKET_HEADER pHeader,
|
|
unsigned * pDataLength,
|
|
unsigned long Timeout,
|
|
void * pClientEndpoint
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We need to be careful not to return RPC_P_TIMEOUT artificially, because
|
|
the runtime may think all its threads can go away. That is why we goto
|
|
restart instead.
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
Exceptions:
|
|
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
restart:
|
|
|
|
RPC_STATUS Status;
|
|
|
|
if (ReceiveFailPercentage)
|
|
{
|
|
AddressMutex.Request();
|
|
|
|
if (ReceiveDupRemaining)
|
|
{
|
|
NoteFailure(pHeader, "receive", UR_LATER_DUP);
|
|
|
|
--ReceiveDupRemaining;
|
|
|
|
memcpy(pHeader, &pSavedReceivePacket->Header, *pDataLength);
|
|
memcpy(pClientEndpoint, pSavedReceiveEndpoint, pTransport->SizeOfAddress);
|
|
*pDataLength = SavedReceiveDataLength;
|
|
|
|
AddressMutex.Clear();
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
AddressMutex.Clear();
|
|
|
|
Status = pTransport->ReceivePacket(this,
|
|
pTransAddress,
|
|
LargestPacketSize,
|
|
(char *) pHeader,
|
|
pDataLength,
|
|
Timeout,
|
|
pClientEndpoint
|
|
);
|
|
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
if (rand() % 100 > ReceiveFailPercentage)
|
|
{
|
|
unsigned FailType = ChooseFailure(pHeader);
|
|
|
|
switch (FailType)
|
|
{
|
|
case UR_DROP:
|
|
{
|
|
Sleep(1000);
|
|
goto restart;
|
|
}
|
|
|
|
// case UR_NO_MEMORY:
|
|
// {
|
|
// return RPC_S_OUT_OF_MEMORY;
|
|
// }
|
|
|
|
case UR_DUP2:
|
|
{
|
|
//
|
|
// Save the packet and endpoint information for next time.
|
|
//
|
|
AddressMutex.Request();
|
|
if (0 == ReceiveDupRemaining)
|
|
{
|
|
memcpy(&pSavedReceivePacket->Header, pHeader, *pDataLength);
|
|
memcpy(pSavedReceiveEndpoint, pClientEndpoint, pTransport->SizeOfAddress);
|
|
SavedReceiveDataLength = *pDataLength;
|
|
|
|
ReceiveDupRemaining = 1;
|
|
}
|
|
AddressMutex.Clear();
|
|
return RPC_S_OK;
|
|
}
|
|
|
|
case UR_DELAY:
|
|
{
|
|
//
|
|
// Save the packet and endpoint information for next time.
|
|
//
|
|
AddressMutex.Request();
|
|
if (0 == ReceiveDupRemaining)
|
|
{
|
|
memcpy(&pSavedReceivePacket->Header, pHeader, *pDataLength);
|
|
memcpy(pSavedReceiveEndpoint, pClientEndpoint, pTransport->SizeOfAddress);
|
|
SavedReceiveDataLength = *pDataLength;
|
|
|
|
ReceiveDupRemaining = 1;
|
|
}
|
|
AddressMutex.Clear();
|
|
Sleep(1000);
|
|
goto restart;
|
|
}
|
|
|
|
default:
|
|
{
|
|
return RPC_S_OK;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return RPC_S_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Status = pTransport->ReceivePacket(this,
|
|
pTransAddress,
|
|
LargestPacketSize,
|
|
(char *) pHeader,
|
|
pDataLength,
|
|
Timeout,
|
|
pClientEndpoint
|
|
);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
InterpretFailureOptions(
|
|
)
|
|
{
|
|
randomize(CurrentTimeInMsec());
|
|
|
|
char Options[400];
|
|
if (0 == GetEnvironmentVariable("rpc-Failure-Options", Options, sizeof(Options)-1))
|
|
{
|
|
return;
|
|
}
|
|
|
|
char * keyword = strtok(Options, " ");
|
|
|
|
while (keyword)
|
|
{
|
|
if (0 == stricmp(keyword, "send-percentage"))
|
|
{
|
|
char * percentage = strtok(0, " ");
|
|
if (1 != sscanf(percentage, "%u", &SendFailPercentage))
|
|
{
|
|
if (Chat)
|
|
{
|
|
DbgPrint("%s is not an integer\n", percentage);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else if (0 == stricmp(keyword, "receive-percentage"))
|
|
{
|
|
char * percentage = strtok(0, " ");
|
|
if (1 != sscanf(percentage, "%u", &ReceiveFailPercentage))
|
|
{
|
|
if (Chat)
|
|
{
|
|
DbgPrint("%s is not an integer\n", percentage);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else if (0 == stricmp(keyword, "chat"))
|
|
{
|
|
char * bool = strtok(0, " ");
|
|
if (0 == stricmp(bool, "on"))
|
|
{
|
|
Chat = TRUE;
|
|
}
|
|
else if (0 == stricmp(bool, "off"))
|
|
{
|
|
Chat = FALSE;
|
|
}
|
|
else
|
|
{
|
|
if (Chat)
|
|
{
|
|
DbgPrint("%s is not 'on' or 'off'\n", bool);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Chat)
|
|
{
|
|
DbgPrint("%s is not a keyword\n", keyword);
|
|
break;
|
|
}
|
|
}
|
|
|
|
keyword = strtok(0, " ");
|
|
}
|
|
|
|
if (Chat)
|
|
{
|
|
DbgPrint("send fails %u%%, receive fails %u%%\n",
|
|
SendFailPercentage,
|
|
ReceiveFailPercentage
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
char * strtok(
|
|
char * string,
|
|
char * chaff
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
WARNING - only uses first letter of the chaff string
|
|
|
|
WARNING - not thread-safe (obviously)
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
Exceptions:
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
static char * cursor = 0;
|
|
static char * end = 0;
|
|
|
|
if (string)
|
|
{
|
|
cursor = string;
|
|
}
|
|
else
|
|
{
|
|
if (end)
|
|
{
|
|
cursor = end + 1;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
while (*cursor && *cursor == *chaff)
|
|
{
|
|
++cursor;
|
|
}
|
|
|
|
end = cursor;
|
|
|
|
while (*end && *end != *chaff)
|
|
{
|
|
++end;
|
|
}
|
|
|
|
if (*end)
|
|
{
|
|
*end = 0;
|
|
}
|
|
else
|
|
{
|
|
end = 0;
|
|
}
|
|
|
|
return cursor;
|
|
}
|
|
|
|
#endif
|