6327 lines
157 KiB
C++
6327 lines
157 KiB
C++
|
/*++
|
|||
|
|
|||
|
Copyright (C) Microsoft Corporation, 1992 - 1999
|
|||
|
|
|||
|
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.
|
|||
|
|
|||
|
Jeff Roberts (jroberts) 9-30-1996
|
|||
|
|
|||
|
Began asynchronous call support.
|
|||
|
|
|||
|
Edward Reus (edwardr) 7-09-1997
|
|||
|
|
|||
|
Support for large packets (Falcon).
|
|||
|
|
|||
|
--*/
|
|||
|
#include <precomp.hxx>
|
|||
|
|
|||
|
//
|
|||
|
// Remember that any #defines must go AFTER the precompiled header in order
|
|||
|
// to be noticed by the compiler.
|
|||
|
//
|
|||
|
|
|||
|
// uncomment this to have the server try the msconv interface, used by NT5 beta 1.
|
|||
|
//
|
|||
|
// #define TRY_MSCONV_INTERFACE
|
|||
|
|
|||
|
|
|||
|
#include "sdict2.hxx"
|
|||
|
#include "secsvr.hxx"
|
|||
|
#include "hndlsvr.hxx"
|
|||
|
#include "dgpkt.hxx"
|
|||
|
#include "delaytab.hxx"
|
|||
|
#include "hashtabl.hxx"
|
|||
|
#include "locks.hxx"
|
|||
|
#include "dgclnt.hxx"
|
|||
|
#include "dgsvr.hxx"
|
|||
|
#include <conv.h>
|
|||
|
#include <convc.h>
|
|||
|
|
|||
|
#ifdef TRY_MSCONV_INTERFACE
|
|||
|
#include <msconv.h>
|
|||
|
#endif
|
|||
|
|
|||
|
#define IDLE_SCONNECTION_LIFETIME (3 * 60 * 1000)
|
|||
|
#define IDLE_SCONNECTION_SWEEP_INTERVAL (30 * 1000)
|
|||
|
#define IDLE_SCALL_LIFETIME (15 * 1000)
|
|||
|
|
|||
|
|
|||
|
struct REMOTE_ADDRESS_INFO
|
|||
|
{
|
|||
|
#pragma warning(disable:4200)
|
|||
|
|
|||
|
unsigned RemoteInfoLength;
|
|||
|
unsigned SecurityInfoPadLength;
|
|||
|
byte Address[];
|
|||
|
|
|||
|
#pragma warning(default:4200)
|
|||
|
};
|
|||
|
|
|||
|
//------------------------------------------------------------------------
|
|||
|
|
|||
|
LONG fPruning = FALSE;
|
|||
|
|
|||
|
SERVER_ACTIVITY_TABLE * ServerConnections;
|
|||
|
ASSOC_GROUP_TABLE * AssociationGroups;
|
|||
|
|
|||
|
LONG ServerConnectionCount = 0;
|
|||
|
LONG ServerCallCount = 0;
|
|||
|
|
|||
|
#ifdef INTRODUCE_ERRORS
|
|||
|
|
|||
|
extern long ServerDelayTime;
|
|||
|
extern long ServerDelayRate;
|
|||
|
extern long ServerDropRate;
|
|||
|
|
|||
|
#endif
|
|||
|
//--------------------------------------------------------------------
|
|||
|
|
|||
|
extern int
|
|||
|
StringLengthWithEscape (
|
|||
|
IN RPC_CHAR * String
|
|||
|
);
|
|||
|
|
|||
|
extern RPC_CHAR *
|
|||
|
StringCopyEscapeCharacters (
|
|||
|
OUT RPC_CHAR * Destination,
|
|||
|
IN RPC_CHAR * Source
|
|||
|
);
|
|||
|
|
|||
|
void
|
|||
|
ServerCallScavengerProc(
|
|||
|
void * Parms
|
|||
|
);
|
|||
|
|
|||
|
RPC_STATUS
|
|||
|
InitializeServerGlobals(
|
|||
|
);
|
|||
|
|
|||
|
void
|
|||
|
InterpretFailureOptions(
|
|||
|
);
|
|||
|
|
|||
|
//--------------------------------------------------------------------
|
|||
|
|
|||
|
#if !defined(WIN96)
|
|||
|
char __pure_virtual_called()
|
|||
|
{
|
|||
|
ASSERT(0 && "rpc: pure virtual fn called in dg");
|
|||
|
return 0;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
boolean ServerGlobalsInitialized = FALSE;
|
|||
|
|
|||
|
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 (ServerGlobalsInitialized)
|
|||
|
{
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
RequestGlobalMutex();
|
|||
|
|
|||
|
if (0 != InitializeRpcProtocolDgClient())
|
|||
|
{
|
|||
|
ClearGlobalMutex();
|
|||
|
return RPC_S_OUT_OF_MEMORY;
|
|||
|
}
|
|||
|
|
|||
|
if (!ServerGlobalsInitialized)
|
|||
|
{
|
|||
|
ServerConnections = new SERVER_ACTIVITY_TABLE(&Status);
|
|||
|
if (!ServerConnections)
|
|||
|
{
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
ServerGlobalsInitialized = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
ClearGlobalMutex();
|
|||
|
|
|||
|
return Status;
|
|||
|
|
|||
|
//--------------------------------------------------------------------
|
|||
|
|
|||
|
abend:
|
|||
|
|
|||
|
delete AssociationGroups;
|
|||
|
AssociationGroups = 0;
|
|||
|
|
|||
|
delete ServerConnections;
|
|||
|
ServerConnections = 0;
|
|||
|
|
|||
|
ClearGlobalMutex();
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
RPC_ADDRESS *
|
|||
|
DgCreateRpcAddress (
|
|||
|
IN TRANS_INFO * TransportInfo
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
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 Address;
|
|||
|
|
|||
|
|
|||
|
Address = new (TransportInfo) DG_ADDRESS(
|
|||
|
TransportInfo,
|
|||
|
&Status
|
|||
|
);
|
|||
|
if (!Address)
|
|||
|
{
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
if (Status != RPC_S_OK)
|
|||
|
{
|
|||
|
delete Address;
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
return Address;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DG_ADDRESS::DG_ADDRESS(
|
|||
|
TRANS_INFO * a_LoadableTransport,
|
|||
|
RPC_STATUS * pStatus
|
|||
|
)
|
|||
|
: RPC_ADDRESS(pStatus),
|
|||
|
|
|||
|
TotalThreadsThisEndpoint (0),
|
|||
|
ThreadsReceivingThisEndpoint(0),
|
|||
|
MinimumCallThreads (0),
|
|||
|
MaximumConcurrentCalls (0),
|
|||
|
CachedConnections (0),
|
|||
|
ActiveCallCount (0),
|
|||
|
AutoListenCallCount (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:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
TransInfo = a_LoadableTransport;
|
|||
|
ObjectType = DG_ADDRESS_TYPE;
|
|||
|
|
|||
|
//
|
|||
|
// Make sure the RPC_ADDRESS (et. al.) initialized correctly.
|
|||
|
//
|
|||
|
if (*pStatus != RPC_S_OK)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
Endpoint.TransportInterface = (RPC_DATAGRAM_TRANSPORT *)
|
|||
|
TransInfo->InqTransInfo();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DG_ADDRESS::~DG_ADDRESS()
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is the destructor for a DG_ADDRESS.
|
|||
|
It is called only if the endpoint failed!
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
<None>
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
<None>
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
inline void
|
|||
|
DG_ADDRESS::BeginAutoListenCall (
|
|||
|
)
|
|||
|
{
|
|||
|
AutoListenCallCount.Increment() ;
|
|||
|
}
|
|||
|
|
|||
|
inline void
|
|||
|
DG_ADDRESS::EndAutoListenCall (
|
|||
|
)
|
|||
|
{
|
|||
|
ASSERT(AutoListenCallCount.GetInteger() >= 1);
|
|||
|
AutoListenCallCount.Decrement() ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
void
|
|||
|
DG_ADDRESS::WaitForCalls(
|
|||
|
)
|
|||
|
{
|
|||
|
while (InqNumberOfActiveCalls() > AutoListenCallCount.GetInteger())
|
|||
|
{
|
|||
|
ServerConnections->PruneEntireTable(0);
|
|||
|
PauseExecution(500);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void
|
|||
|
DG_ADDRESS::EncourageCallCleanup(
|
|||
|
RPC_INTERFACE * Interface
|
|||
|
)
|
|||
|
{
|
|||
|
if (ServerGlobalsInitialized && AutoListenCallCount.GetInteger() > 0)
|
|||
|
{
|
|||
|
ServerConnections->PruneEntireTable(Interface);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
RPC_STATUS
|
|||
|
DG_ADDRESS::ServerSetupAddress (
|
|||
|
IN RPC_CHAR * NetworkAddress,
|
|||
|
IN RPC_CHAR * * pEndpointString,
|
|||
|
IN unsigned int PendingQueueSize,
|
|||
|
IN void * SecurityDescriptor,
|
|||
|
IN unsigned long EndpointFlags,
|
|||
|
IN unsigned long NICFlags,
|
|||
|
OUT NETWORK_ADDRESS_VECTOR * * ppNetworkAddressVector
|
|||
|
)
|
|||
|
{
|
|||
|
RPC_STATUS Status;
|
|||
|
|
|||
|
Status = Endpoint.TransportInterface->Listen(
|
|||
|
Endpoint.TransportEndpoint,
|
|||
|
NetworkAddress,
|
|||
|
pEndpointString,
|
|||
|
SecurityDescriptor,
|
|||
|
EndpointFlags,
|
|||
|
NICFlags,
|
|||
|
ppNetworkAddressVector
|
|||
|
);
|
|||
|
if (!Status)
|
|||
|
{
|
|||
|
Status = Endpoint.TransportInterface->QueryEndpointStats(
|
|||
|
&Endpoint.TransportEndpoint,
|
|||
|
&Endpoint.Stats
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
Endpoint.Flags = EndpointFlags;
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
#ifndef NO_PLUG_AND_PLAY
|
|||
|
|
|||
|
void
|
|||
|
DG_ADDRESS::PnpNotify (
|
|||
|
)
|
|||
|
{
|
|||
|
Endpoint.TransportInterface->PnpNotify();
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
RPC_STATUS
|
|||
|
DG_ADDRESS::ServerStartingToListen (
|
|||
|
IN unsigned int MinThreads,
|
|||
|
IN unsigned int MaxCalls
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
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
|
|||
|
DG_ADDRESS::CompleteListen(
|
|||
|
)
|
|||
|
{
|
|||
|
Endpoint.TransportInterface->CompleteListen(Endpoint.TransportEndpoint);
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
RPC_STATUS
|
|||
|
DG_ADDRESS::CheckThreadPool(
|
|||
|
)
|
|||
|
{
|
|||
|
return TransInfo->CreateThread();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
RPC_STATUS RPC_ENTRY
|
|||
|
I_RpcLaunchDatagramReceiveThread(
|
|||
|
void * 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>
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
long
|
|||
|
DG_ADDRESS::InqNumberOfActiveCalls (
|
|||
|
)
|
|||
|
{
|
|||
|
return ActiveCallCount;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
DG_ADDRESS::ForwardPacketIfNecessary(
|
|||
|
IN PDG_PACKET Packet,
|
|||
|
IN DG_TRANSPORT_ADDRESS RemoteAddress
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
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.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if the packet needed to be forwarded
|
|||
|
FALSE if it should be handled locally
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
RPC_STATUS Status;
|
|||
|
PNCA_PACKET_HEADER pHeader = &Packet->Header;
|
|||
|
|
|||
|
RPC_INTERFACE * pRpcInterface;
|
|||
|
RPC_SYNTAX_IDENTIFIER RpcIfSyntaxIdentifier;
|
|||
|
char * EndpointString = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Build an interface syntax identifier from the packet.
|
|||
|
//
|
|||
|
RpcpMemoryCopy(
|
|||
|
&RpcIfSyntaxIdentifier.SyntaxGUID,
|
|||
|
&pHeader->InterfaceId,
|
|||
|
sizeof(RPC_UUID)
|
|||
|
);
|
|||
|
|
|||
|
RpcIfSyntaxIdentifier.SyntaxVersion.MajorVersion =
|
|||
|
pHeader->InterfaceVersion.MajorVersion;
|
|||
|
RpcIfSyntaxIdentifier.SyntaxVersion.MinorVersion =
|
|||
|
pHeader->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 &&
|
|||
|
0 == RpcpMemoryCompare(&pHeader->ObjectId, &NullUuid, sizeof(UUID)) )
|
|||
|
{
|
|||
|
//Interface found, just process as normal
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//Interface wasn't found. Let's ask endpoint mapper to resolve it
|
|||
|
//for us.
|
|||
|
|
|||
|
unsigned char * AnsiProtseq;
|
|||
|
|
|||
|
// Must convert the protocol sequence into an ansi string.
|
|||
|
|
|||
|
unsigned Length = 1 + RpcpStringLength(InqRpcProtocolSequence());
|
|||
|
AnsiProtseq = (unsigned char *) _alloca(Length);
|
|||
|
if (!AnsiProtseq)
|
|||
|
{
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS NtStatus;
|
|||
|
NtStatus = RtlUnicodeToMultiByteN((char *) AnsiProtseq,
|
|||
|
Length,
|
|||
|
NULL,
|
|||
|
InqRpcProtocolSequence(),
|
|||
|
Length * sizeof(RPC_CHAR)
|
|||
|
);
|
|||
|
ASSERT(NT_SUCCESS(NtStatus));
|
|||
|
|
|||
|
RpcTryExcept
|
|||
|
{
|
|||
|
// Call the epmapper get forward function. It returns the
|
|||
|
// endpoint of the server this packet is really destined for.
|
|||
|
|
|||
|
Status = (*Server->pRpcForwardFunction)(
|
|||
|
(UUID *) &pHeader->InterfaceId,
|
|||
|
(RPC_VERSION *) &pHeader->InterfaceVersion,
|
|||
|
(UUID *) &pHeader->ObjectId,
|
|||
|
AnsiProtseq,
|
|||
|
(void * *) &EndpointString
|
|||
|
);
|
|||
|
}
|
|||
|
RpcExcept( I_RpcExceptionFilter(RpcExceptionCode()) )
|
|||
|
{
|
|||
|
Status = RpcExceptionCode();
|
|||
|
}
|
|||
|
RpcEndExcept
|
|||
|
|
|||
|
if (Status != RPC_S_OK)
|
|||
|
{
|
|||
|
if (!(pHeader->PacketFlags & DG_PF_BROADCAST) &&
|
|||
|
!(pHeader->PacketFlags & DG_PF_MAYBE))
|
|||
|
{
|
|||
|
// couldn't find the interface, or some other error occurred.
|
|||
|
// The X/Open version of the AES, available in 1997, says we should
|
|||
|
// set the boot time to zero in this situation.
|
|||
|
//
|
|||
|
InitErrorPacket(Packet, DG_REJECT, RPC_S_UNKNOWN_IF );
|
|||
|
Packet->Header.ServerBootTime = 0;
|
|||
|
SendPacketBack (&Packet->Header, Packet->GetPacketBodyLen(), RemoteAddress);
|
|||
|
}
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
if (EndpointString)
|
|||
|
{
|
|||
|
ForwardPacket(Packet, RemoteAddress, EndpointString);
|
|||
|
I_RpcFree(EndpointString);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
RPC_STATUS
|
|||
|
DG_ADDRESS::ForwardPacket(
|
|||
|
IN PDG_PACKET Packet,
|
|||
|
IN DG_TRANSPORT_ADDRESS RemoteAddress,
|
|||
|
IN char * ServerEndpointString
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
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.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
//
|
|||
|
// Avoid a loop if the server gets confused.
|
|||
|
//
|
|||
|
if (Packet->Header.PacketType != DG_REQUEST &&
|
|||
|
Packet->Header.PacketType != DG_PING &&
|
|||
|
Packet->Header.PacketType != DG_QUIT)
|
|||
|
{
|
|||
|
#ifdef DEBUGRPC
|
|||
|
DbgPrint("DG RPC: not forwarding packet of type %u\n", Packet->Header.PacketType);
|
|||
|
#endif
|
|||
|
return RPC_S_OK;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Use the same value for 32 and 64 bit.
|
|||
|
//
|
|||
|
#define REMOTE_ADDRESS_PAD 8
|
|||
|
|
|||
|
unsigned Length;
|
|||
|
REMOTE_ADDRESS_INFO *pRemoteAddressInfo;
|
|||
|
unsigned EndpointInfoStart = Align( Packet->DataLength, REMOTE_ADDRESS_PAD );
|
|||
|
unsigned EndpointInfoLength = sizeof(REMOTE_ADDRESS_INFO)
|
|||
|
+ Endpoint.TransportInterface->AddressSize;
|
|||
|
//
|
|||
|
// We have not yet subtracted the header from the packet's DataLength.
|
|||
|
//
|
|||
|
Length = EndpointInfoStart + EndpointInfoLength;
|
|||
|
|
|||
|
//
|
|||
|
// If the packet header was byte-swapped, restore it to its original format.
|
|||
|
//
|
|||
|
ByteSwapPacketHeaderIfNecessary(Packet);
|
|||
|
|
|||
|
|
|||
|
// BE CAREFUL READING OR WRITING THE PACKET HEADER AFTER IT IS BYTE-SWAPPED.
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// If the original packet is short enough, we can append out data
|
|||
|
// to the end; otherwise, we need to send one packet with the original
|
|||
|
// data and another with the client address info.
|
|||
|
//
|
|||
|
if (Length <= Endpoint.Stats.PreferredPduSize)
|
|||
|
{
|
|||
|
//
|
|||
|
// Mark it "forwarded with appended endpoint info"
|
|||
|
//
|
|||
|
Packet->Header.PacketFlags |= DG_PF_FORWARDED;
|
|||
|
Packet->Header.PacketFlags2 &= ~DG_PF2_FORWARDED_2;
|
|||
|
|
|||
|
pRemoteAddressInfo = (REMOTE_ADDRESS_INFO *) ( Packet->Header.Data
|
|||
|
+ EndpointInfoStart
|
|||
|
- sizeof(NCA_PACKET_HEADER) );
|
|||
|
|
|||
|
pRemoteAddressInfo->SecurityInfoPadLength = EndpointInfoStart - Packet->DataLength;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Length = EndpointInfoLength + sizeof(NCA_PACKET_HEADER);
|
|||
|
|
|||
|
//
|
|||
|
// Mark it "forwarded without appended endpoint info"
|
|||
|
//
|
|||
|
Packet->Header.PacketFlags &= ~DG_PF_FORWARDED;
|
|||
|
Packet->Header.PacketFlags2 |= DG_PF2_FORWARDED_2;
|
|||
|
|
|||
|
Endpoint.TransportInterface->ForwardPacket( Endpoint.TransportEndpoint,
|
|||
|
0,
|
|||
|
0,
|
|||
|
&Packet->Header,
|
|||
|
Packet->DataLength,
|
|||
|
0,
|
|||
|
0,
|
|||
|
ServerEndpointString
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Mark it "endpoint info only".
|
|||
|
//
|
|||
|
Packet->Header.PacketFlags |= DG_PF_FORWARDED;
|
|||
|
Packet->Header.PacketFlags2 |= DG_PF2_FORWARDED_2;
|
|||
|
|
|||
|
Packet->SetPacketBodyLen(EndpointInfoLength);
|
|||
|
Packet->Header.AuthProto = 0;
|
|||
|
|
|||
|
pRemoteAddressInfo = (REMOTE_ADDRESS_INFO *) Packet->Header.Data;
|
|||
|
|
|||
|
pRemoteAddressInfo->SecurityInfoPadLength = 0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Add endpoint info and send it.
|
|||
|
//
|
|||
|
pRemoteAddressInfo->RemoteInfoLength = sizeof(REMOTE_ADDRESS_INFO)
|
|||
|
+ Endpoint.TransportInterface->AddressSize;
|
|||
|
RpcpMemoryCopy(
|
|||
|
pRemoteAddressInfo->Address,
|
|||
|
RemoteAddress,
|
|||
|
Endpoint.TransportInterface->AddressSize
|
|||
|
);
|
|||
|
|
|||
|
Endpoint.TransportInterface->ForwardPacket( Endpoint.TransportEndpoint,
|
|||
|
0,
|
|||
|
0,
|
|||
|
&Packet->Header,
|
|||
|
Length,
|
|||
|
0,
|
|||
|
0,
|
|||
|
ServerEndpointString
|
|||
|
);
|
|||
|
return RPC_S_OK;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
DG_ADDRESS::CaptureClientAddress(
|
|||
|
IN PDG_PACKET Packet,
|
|||
|
OUT DG_TRANSPORT_ADDRESS RemoteAddress
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This method is called when a packet with DG_PF_FORWARDED arrives.
|
|||
|
This means the endpoint mapper sent it and it includes a
|
|||
|
REMOTE_ADDRESS_INFO structure. The fn will remove it and store the
|
|||
|
client's remote address in RemoteAddress.
|
|||
|
|
|||
|
If the packet was forwarded and not fragmented, we restore it to its
|
|||
|
original state and zap the DG_PF_FORWARDED bit.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if the packet was valid
|
|||
|
FALSE if it appeared malformed
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
ASSERT( Packet->DataLength >= Packet->GetPacketBodyLen() );
|
|||
|
|
|||
|
//
|
|||
|
// Watch for packets that might crash us.
|
|||
|
//
|
|||
|
unsigned AddressInfoLength = sizeof(REMOTE_ADDRESS_INFO)
|
|||
|
+ Endpoint.TransportInterface->AddressSize;
|
|||
|
|
|||
|
if (Packet->DataLength < Packet->GetPacketBodyLen() ||
|
|||
|
Packet->DataLength - Packet->GetPacketBodyLen() < AddressInfoLength)
|
|||
|
{
|
|||
|
#ifdef DEBUGRPC
|
|||
|
DbgPrint("RPC DG: forwarded packet data is impossibly short\n");
|
|||
|
#endif
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
Packet->DataLength -= AddressInfoLength;
|
|||
|
|
|||
|
//
|
|||
|
// If this is a nonfragmented packet, the endpoint info is beyond
|
|||
|
// the stub data and security trailer.
|
|||
|
//
|
|||
|
REMOTE_ADDRESS_INFO * pAddressInfo;
|
|||
|
|
|||
|
if (Packet->Header.PacketFlags2 & DG_PF2_FORWARDED_2)
|
|||
|
{
|
|||
|
pAddressInfo = (REMOTE_ADDRESS_INFO *) Packet->Header.Data;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// After fixing up the length, we can treat this like a packet
|
|||
|
// sent directly from the client.
|
|||
|
//
|
|||
|
Packet->Header.PacketFlags &= ~DG_PF_FORWARDED;
|
|||
|
|
|||
|
pAddressInfo = (REMOTE_ADDRESS_INFO *) ( Packet->Header.Data
|
|||
|
+ Packet->DataLength );
|
|||
|
|
|||
|
if (pAddressInfo->RemoteInfoLength != AddressInfoLength)
|
|||
|
{
|
|||
|
#ifdef DEBUGRPC
|
|||
|
DbgPrint("RPC DG: forwarded packet contains wrong address structure length\n");
|
|||
|
#endif
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if (pAddressInfo != AlignPtr( pAddressInfo, REMOTE_ADDRESS_PAD ))
|
|||
|
{
|
|||
|
#ifdef DEBUGRPC
|
|||
|
DbgPrint("RPC DG: forwarded packet contains a misaligned address structure\n");
|
|||
|
#endif
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (pAddressInfo->SecurityInfoPadLength > REMOTE_ADDRESS_PAD ||
|
|||
|
Packet->DataLength < pAddressInfo->SecurityInfoPadLength)
|
|||
|
{
|
|||
|
#ifdef DEBUGRPC
|
|||
|
DbgPrint("RPC DG: forwarded packet's address struct has invalid pad length\n");
|
|||
|
#endif
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
Packet->DataLength -= pAddressInfo->SecurityInfoPadLength;
|
|||
|
|
|||
|
//
|
|||
|
// Record the client's true endpoint.
|
|||
|
//
|
|||
|
RpcpMemoryCopy(
|
|||
|
RemoteAddress,
|
|||
|
pAddressInfo->Address,
|
|||
|
Endpoint.TransportInterface->AddressSize
|
|||
|
);
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
#pragma optimize("t", on)
|
|||
|
|
|||
|
|
|||
|
void
|
|||
|
ProcessDgServerPacket(
|
|||
|
IN DWORD Status,
|
|||
|
IN DG_TRANSPORT_ENDPOINT LocalEndpoint,
|
|||
|
IN void * PacketHeader,
|
|||
|
IN unsigned long PacketLength,
|
|||
|
IN DatagramTransportPair *AddressPair
|
|||
|
)
|
|||
|
{
|
|||
|
PDG_PACKET Packet = DG_PACKET::FromPacketHeader(PacketHeader);
|
|||
|
|
|||
|
Packet->DataLength = PacketLength;
|
|||
|
|
|||
|
#ifdef INTRODUCE_ERRORS
|
|||
|
|
|||
|
if (::ServerDropRate)
|
|||
|
{
|
|||
|
if ((GetRandomCounter() % 100) < ::ServerDropRate)
|
|||
|
{
|
|||
|
unsigned Frag = (Packet->Header.PacketType << 16) | Packet->Header.GetFragmentNumber();
|
|||
|
unsigned Uuid = *(unsigned *) &Packet->Header.ActivityId;
|
|||
|
unsigned Type = Packet->Header.PacketType;
|
|||
|
|
|||
|
LogError(SU_PACKET, EV_DROP, (void *) Uuid, (void *) Type, Frag);
|
|||
|
|
|||
|
Packet->Free();
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (::ServerDelayRate)
|
|||
|
{
|
|||
|
if ((GetRandomCounter() % 100) < ::ServerDelayRate)
|
|||
|
{
|
|||
|
unsigned Frag = (Packet->Header.PacketType << 16) | Packet->Header.GetFragmentNumber();
|
|||
|
unsigned Uuid = *(unsigned *) &Packet->Header.ActivityId;
|
|||
|
unsigned Type = Packet->Header.PacketType;
|
|||
|
|
|||
|
LogError(SU_PACKET, EV_DELAY, (void *) Uuid, (void *) Type, Frag);
|
|||
|
|
|||
|
Sleep(::ServerDelayTime);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
if (Status == RPC_P_OVERSIZE_PACKET)
|
|||
|
{
|
|||
|
#ifdef DEBUGRPC
|
|||
|
PrintToDebugger("RPC DG: async packet is too large\n");
|
|||
|
#endif
|
|||
|
Packet->Flags |= DG_PF_PARTIAL;
|
|||
|
Status = RPC_S_OK;
|
|||
|
}
|
|||
|
|
|||
|
if (Status != RPC_S_OK)
|
|||
|
{
|
|||
|
LogError(SU_PACKET, EV_STATUS, Packet, 0, Status);
|
|||
|
#ifdef DEBUGRPC
|
|||
|
PrintToDebugger("RPC DG: async receive completed with status 0x%lx\n", Status);
|
|||
|
#endif
|
|||
|
Packet->Free();
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
DG_ADDRESS * Address = DG_ADDRESS::FromEndpoint(LocalEndpoint);
|
|||
|
|
|||
|
Address->DispatchPacket(Packet, AddressPair);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
void
|
|||
|
DG_ADDRESS::DispatchPacket(
|
|||
|
DG_PACKET * Packet,
|
|||
|
IN DatagramTransportPair *AddressPair
|
|||
|
)
|
|||
|
{
|
|||
|
RPC_INTERFACE * pRpcInterface;
|
|||
|
RPC_SYNTAX_IDENTIFIER RpcIfSyntaxIdentifier;
|
|||
|
|
|||
|
//
|
|||
|
// Mask off bits not used by X/Open or by us.
|
|||
|
// Notice that the current arrangement strips the extra bits before
|
|||
|
// forwarding, so if these bits become important the code will have to
|
|||
|
// be rearranged.
|
|||
|
//
|
|||
|
Packet->Header.RpcVersion &= 0x0F;
|
|||
|
Packet->Header.PacketType &= 0x1F;
|
|||
|
|
|||
|
Packet->Header.PacketFlags &= 0x7f;
|
|||
|
Packet->Header.PacketFlags2 &= 0x87;
|
|||
|
|
|||
|
//
|
|||
|
// Filter out packets that clients shouldn't send.
|
|||
|
//
|
|||
|
switch (Packet->Header.PacketType)
|
|||
|
{
|
|||
|
case DG_REQUEST:
|
|||
|
case DG_PING:
|
|||
|
case DG_FACK:
|
|||
|
case DG_QUIT:
|
|||
|
case DG_ACK:
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
default:
|
|||
|
{
|
|||
|
LogError(SU_ADDRESS, EV_PKT_IN, this, (void *) 1, (Packet->Header.PacketType << 16) | Packet->GetFragmentNumber());
|
|||
|
FreePacket(Packet);
|
|||
|
return;
|
|||
|
}
|
|||
|
} // switch (PacketType)
|
|||
|
|
|||
|
if (Packet->Header.RpcVersion != DG_RPC_PROTOCOL_VERSION)
|
|||
|
{
|
|||
|
#ifdef DEBUGRPC
|
|||
|
DbgPrint("dg rpc: packet %x has version %u\n", Packet, Packet->Header.RpcVersion);
|
|||
|
DbgPrint(" length %u\n", Packet->DataLength);
|
|||
|
|
|||
|
if (Packet->DataLength > 80)
|
|||
|
{
|
|||
|
Packet->DataLength = 80;
|
|||
|
}
|
|||
|
|
|||
|
DumpBuffer(&Packet->Header, Packet->DataLength);
|
|||
|
#endif
|
|||
|
SendRejectPacket(Packet, NCA_STATUS_VERSION_MISMATCH, AddressPair->RemoteAddress);
|
|||
|
FreePacket(Packet);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
ByteSwapPacketHeaderIfNecessary(Packet);
|
|||
|
|
|||
|
//
|
|||
|
// 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 (Packet->DataLength < sizeof(NCA_PACKET_HEADER))
|
|||
|
{
|
|||
|
#ifdef DEBUGRPC
|
|||
|
DbgPrint("dg rpc: packet %x has invalid length\n", Packet);
|
|||
|
DbgPrint(" length %u\n", Packet->DataLength);
|
|||
|
|
|||
|
if (Packet->DataLength > 80)
|
|||
|
{
|
|||
|
Packet->DataLength = 80;
|
|||
|
}
|
|||
|
|
|||
|
DumpBuffer(&Packet->Header, Packet->DataLength);
|
|||
|
RpcpBreakPoint();
|
|||
|
#endif
|
|||
|
LogError(SU_ADDRESS, EV_PKT_IN, this, (void *) 2, (Packet->Header.PacketType << 16) | Packet->GetFragmentNumber());
|
|||
|
FreePacket(Packet);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If we are the endpoint mapper, forward packet if necessary.
|
|||
|
//
|
|||
|
if (GlobalRpcServer->pRpcForwardFunction)
|
|||
|
{
|
|||
|
if (TRUE == ForwardPacketIfNecessary(Packet, AddressPair->RemoteAddress))
|
|||
|
{
|
|||
|
LogEvent(SU_ADDRESS, EV_PKT_IN, this, (void *) 3, (Packet->Header.PacketType << 16) | Packet->GetFragmentNumber());
|
|||
|
FreePacket(Packet);
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Exclude RPC header from DataLength.
|
|||
|
//
|
|||
|
Packet->DataLength -= sizeof(NCA_PACKET_HEADER);
|
|||
|
|
|||
|
//
|
|||
|
// If the packet includes a client address trailer from the endpoint mapper,
|
|||
|
// remove it and stick the client address in <RemoteAddres>.
|
|||
|
//
|
|||
|
if (Packet->Header.PacketFlags & DG_PF_FORWARDED)
|
|||
|
{
|
|||
|
if (FALSE == CaptureClientAddress(Packet, AddressPair->RemoteAddress))
|
|||
|
{
|
|||
|
LogEvent(SU_ADDRESS, EV_PKT_IN, this, (void *) 4, (Packet->Header.PacketType << 16) | Packet->GetFragmentNumber());
|
|||
|
FreePacket(Packet);
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Reject pkt if boot time in pkt does not match
|
|||
|
// the server's boot time.
|
|||
|
//
|
|||
|
if (Packet->Header.ServerBootTime != ProcessStartTime &&
|
|||
|
Packet->Header.ServerBootTime != 0)
|
|||
|
{
|
|||
|
if (!(Packet->Header.PacketFlags & DG_PF_MAYBE))
|
|||
|
{
|
|||
|
SendRejectPacket(Packet, NCA_STATUS_WRONG_BOOT_TIME, AddressPair->RemoteAddress);
|
|||
|
}
|
|||
|
|
|||
|
LogError(SU_ADDRESS, EV_PKT_IN, this, (void *) 5, (Packet->Header.PacketType << 16) | Packet->GetFragmentNumber());
|
|||
|
FreePacket(Packet);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// sometimes OSF packets say they include security info
|
|||
|
// when they don't.
|
|||
|
//
|
|||
|
DeleteSpuriousAuthProto(Packet);
|
|||
|
|
|||
|
//
|
|||
|
// Find or create a connection and then give it the packet.
|
|||
|
//
|
|||
|
DG_SCONNECTION * Connection = ServerConnections->FindOrCreate(this, Packet);
|
|||
|
|
|||
|
if (!Connection)
|
|||
|
{
|
|||
|
if (Packet->Header.PacketFlags & (DG_PF_BROADCAST | DG_PF_MAYBE))
|
|||
|
{
|
|||
|
//
|
|||
|
// not much point sending an error packet in this case
|
|||
|
//
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (Packet->Header.PacketType == DG_REQUEST)
|
|||
|
{
|
|||
|
SendRejectPacket(Packet, RPC_S_OUT_OF_MEMORY, AddressPair->RemoteAddress);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
CleanupPacket(&Packet->Header);
|
|||
|
Packet->Header.PacketType = DG_NOCALL;
|
|||
|
Packet->SetPacketBodyLen(0);
|
|||
|
Packet->SetFragmentNumber(0xffff);
|
|||
|
SetSerialNumber( &Packet->Header, 0);
|
|||
|
SendPacketBack( &Packet->Header, 0, AddressPair->RemoteAddress);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
LogError(SU_ADDRESS, EV_PKT_IN, this, (void *) 6, (Packet->Header.PacketType << 16) | Packet->GetFragmentNumber());
|
|||
|
FreePacket(Packet);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
Connection->DispatchPacket(Packet, AddressPair);
|
|||
|
|
|||
|
ServerConnections->Prune();
|
|||
|
}
|
|||
|
|
|||
|
#pragma optimize("", on)
|
|||
|
|
|||
|
|
|||
|
PDG_SCONNECTION
|
|||
|
DG_ADDRESS::AllocateConnection(
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Allocates a new DG_SCONNECTION from the cache or from the heap.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
none
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
the call
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDG_SCONNECTION Connection;
|
|||
|
|
|||
|
AddressMutex.Request();
|
|||
|
|
|||
|
UUID_HASH_TABLE_NODE * Node = CachedConnections;
|
|||
|
|
|||
|
if (Node)
|
|||
|
{
|
|||
|
CachedConnections = Node->pNext;
|
|||
|
|
|||
|
AddressMutex.Clear();
|
|||
|
|
|||
|
Connection = DG_SCONNECTION::FromHashNode(Node);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
AddressMutex.Clear();
|
|||
|
|
|||
|
RPC_STATUS Status = RPC_S_OK;
|
|||
|
|
|||
|
Connection = new DG_SCONNECTION(this, &Status);
|
|||
|
|
|||
|
if (!Connection)
|
|||
|
{
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
if (Status != RPC_S_OK)
|
|||
|
{
|
|||
|
delete Connection;
|
|||
|
return 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
LogEvent(SU_SCONN, EV_START, Connection, this);
|
|||
|
|
|||
|
return Connection;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
void
|
|||
|
DG_ADDRESS::FreeConnection(
|
|||
|
PDG_SCONNECTION Connection
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Returns an unused DG_SCONNECTION to the cache or the heap.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
the connection
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
none
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
AddressMutex.Request();
|
|||
|
|
|||
|
if (CachedConnections)
|
|||
|
{
|
|||
|
AddressMutex.Clear();
|
|||
|
delete Connection;
|
|||
|
return;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
CachedConnections = &Connection->ActivityNode;
|
|||
|
Connection->ActivityNode.pNext = 0;
|
|||
|
AddressMutex.Clear();
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
AddressMutex.Request();
|
|||
|
|
|||
|
Connection->ActivityNode.pNext = CachedConnections;
|
|||
|
|
|||
|
CachedConnections = &Connection->ActivityNode;
|
|||
|
|
|||
|
AddressMutex.Clear();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DG_SCALL::DG_SCALL(
|
|||
|
DG_ADDRESS * Address,
|
|||
|
RPC_STATUS * pStatus
|
|||
|
) :
|
|||
|
DG_PACKET_ENGINE(DG_RESPONSE, Address->AllocatePacket(), pStatus),
|
|||
|
Connection (0),
|
|||
|
PipeWaitEvent (0),
|
|||
|
PipeWaitType (PWT_NONE),
|
|||
|
PipeThreadId (0),
|
|||
|
State (CallInit),
|
|||
|
pCachedSid (0),
|
|||
|
pwsCachedUserName(0),
|
|||
|
dwCachedUserNameSize(0)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
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.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
#ifdef MONITOR_SERVER_PACKET_COUNT
|
|||
|
OutstandingPacketCount = 0;
|
|||
|
#endif
|
|||
|
|
|||
|
DispatchBuffer = 0;
|
|||
|
|
|||
|
LogEvent(SU_SCALL, EV_CREATE, this, Address);
|
|||
|
ObjectType = DG_SCALL_TYPE;
|
|||
|
|
|||
|
InterlockedIncrement(&ServerCallCount);
|
|||
|
|
|||
|
if (*pStatus)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
SourceEndpoint = &Address->Endpoint;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
DG_SCALL::Cleanup()
|
|||
|
{
|
|||
|
if (ReferenceCount)
|
|||
|
{
|
|||
|
LogEvent(SU_SCALL, EV_CLEANUP, this, 0, ReferenceCount);
|
|||
|
|
|||
|
ASSERT( State != CallComplete && State != CallInit );
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if (pAsync)
|
|||
|
{
|
|||
|
DoPostDispatchProcessing();
|
|||
|
}
|
|||
|
|
|||
|
switch (State)
|
|||
|
{
|
|||
|
case CallBeforeDispatch:
|
|||
|
case CallDispatched:
|
|||
|
|
|||
|
CleanupReceiveWindow();
|
|||
|
|
|||
|
case CallSendingResponse:
|
|||
|
|
|||
|
RPC_MESSAGE Message;
|
|||
|
|
|||
|
CleanupSendWindow();
|
|||
|
|
|||
|
Message.Buffer = DispatchBuffer;
|
|||
|
Message.ReservedForRuntime = 0;
|
|||
|
FreeBuffer(&Message);
|
|||
|
|
|||
|
ASSERT( !DispatchBuffer );
|
|||
|
|
|||
|
if (Privileges)
|
|||
|
{
|
|||
|
ASSERT( Connection->ActiveSecurityContext );
|
|||
|
|
|||
|
Connection->ActiveSecurityContext->DeletePac( Privileges );
|
|||
|
Privileges = 0;
|
|||
|
}
|
|||
|
|
|||
|
if (Interface->IsAutoListenInterface())
|
|||
|
{
|
|||
|
Connection->pAddress->EndAutoListenCall();
|
|||
|
Interface->EndAutoListenCall();
|
|||
|
}
|
|||
|
|
|||
|
Connection->pAddress->DecrementActiveCallCount();
|
|||
|
if (pAsync)
|
|||
|
{
|
|||
|
Interface->EndCall( FALSE, TRUE );
|
|||
|
}
|
|||
|
|
|||
|
Cancelled = FALSE;
|
|||
|
|
|||
|
ASSERT( PipeWaitType == PWT_NONE );
|
|||
|
|
|||
|
PipeThreadId = 0;
|
|||
|
|
|||
|
case CallInit:
|
|||
|
|
|||
|
if ( LastReceiveBuffer )
|
|||
|
{
|
|||
|
RPC_MESSAGE Message;
|
|||
|
|
|||
|
Message.Buffer = LastReceiveBuffer;
|
|||
|
Message.ReservedForRuntime = 0;
|
|||
|
FreeBuffer(&Message);
|
|||
|
}
|
|||
|
|
|||
|
#ifdef MONITOR_SERVER_PACKET_COUNT
|
|||
|
ASSERT( OutstandingPacketCount == 0 );
|
|||
|
#endif
|
|||
|
Connection->FreeCall(this);
|
|||
|
SetState(CallComplete);
|
|||
|
break;
|
|||
|
|
|||
|
case CallComplete:
|
|||
|
{
|
|||
|
LogEvent(SU_SCALL, EV_CLEANUP, this, 0, CallComplete);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
default:
|
|||
|
{
|
|||
|
LogEvent(SU_SCALL, EV_CLEANUP, this, 0, State);
|
|||
|
#ifdef DEBUGRPC
|
|||
|
DbgPrint("RPC: process %x bad call state %x in DG_SCALL::Cleanup\n",
|
|||
|
GetCurrentProcessId(), State);
|
|||
|
#endif
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
inline BOOL
|
|||
|
DG_SCALL_TABLE::Add(
|
|||
|
PDG_SCALL Call,
|
|||
|
unsigned long Sequence
|
|||
|
)
|
|||
|
{
|
|||
|
//
|
|||
|
// Add the call to the end of the active call list.
|
|||
|
//
|
|||
|
Call->Next = 0;
|
|||
|
Call->Previous = ActiveCallTail;
|
|||
|
|
|||
|
if (ActiveCallHead)
|
|||
|
{
|
|||
|
ASSERT( Call->GetSequenceNumber() > ActiveCallTail->GetSequenceNumber() );
|
|||
|
|
|||
|
ActiveCallTail->Next = Call;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
ActiveCallHead = Call;
|
|||
|
}
|
|||
|
|
|||
|
ActiveCallTail = Call;
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
inline void
|
|||
|
DG_SCALL_TABLE::Remove(
|
|||
|
PDG_SCALL Call
|
|||
|
)
|
|||
|
{
|
|||
|
LogEvent(SU_SCALL, EV_REMOVED, Call, this);
|
|||
|
|
|||
|
if (Call->Previous)
|
|||
|
{
|
|||
|
ASSERT( ActiveCallHead != Call );
|
|||
|
Call->Previous->Next = Call->Next;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
ASSERT( ActiveCallHead == Call );
|
|||
|
ActiveCallHead = Call->Next;
|
|||
|
}
|
|||
|
|
|||
|
if (Call->Next)
|
|||
|
{
|
|||
|
ASSERT( ActiveCallTail != Call );
|
|||
|
Call->Next->Previous = Call->Previous;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
ASSERT( ActiveCallTail == Call );
|
|||
|
ActiveCallTail = Call->Previous;
|
|||
|
}
|
|||
|
|
|||
|
Call->Next = 0;
|
|||
|
Call->Previous = 0;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
inline PDG_SCALL
|
|||
|
DG_SCALL_TABLE::Successor(
|
|||
|
PDG_SCALL Call
|
|||
|
)
|
|||
|
{
|
|||
|
// ASSERT( Call == Find(Call->SequenceNumber) );
|
|||
|
|
|||
|
return Call->Next;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
inline PDG_SCALL
|
|||
|
DG_SCALL_TABLE::Find(
|
|||
|
unsigned long Sequence
|
|||
|
)
|
|||
|
{
|
|||
|
PDG_SCALL Call;
|
|||
|
|
|||
|
for (Call = ActiveCallHead; Call; Call = Call->Next)
|
|||
|
{
|
|||
|
if (Call->GetSequenceNumber() == Sequence)
|
|||
|
{
|
|||
|
return Call;
|
|||
|
}
|
|||
|
|
|||
|
if (Call->GetSequenceNumber() > Sequence)
|
|||
|
{
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
inline PDG_SCALL
|
|||
|
DG_SCALL_TABLE::Predecessor(
|
|||
|
unsigned long Sequence
|
|||
|
)
|
|||
|
{
|
|||
|
PDG_SCALL Call, Previous = 0;
|
|||
|
|
|||
|
for (Call = ActiveCallHead; Call; Call = Call->Next)
|
|||
|
{
|
|||
|
if (Call->GetSequenceNumber() >= Sequence)
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
Previous = Call;
|
|||
|
}
|
|||
|
|
|||
|
return Previous;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
inline void
|
|||
|
DG_SCALL_TABLE::RemoveIdleCalls(
|
|||
|
BOOL Aggressive,
|
|||
|
RPC_INTERFACE * Interface
|
|||
|
)
|
|||
|
{
|
|||
|
PDG_SCALL Call;
|
|||
|
|
|||
|
for (Call = ActiveCallHead; Call; )
|
|||
|
{
|
|||
|
ASSERT( !Call->InvalidHandle(DG_SCALL_TYPE) );
|
|||
|
|
|||
|
PDG_SCALL Next = Call->Next;
|
|||
|
|
|||
|
// If the call has in fact expired, it will be removed from this list
|
|||
|
// and added to the connection's cached call list.
|
|||
|
//
|
|||
|
Call->HasExpired( Aggressive, Interface );
|
|||
|
|
|||
|
Call = Next;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DG_SCONNECTION::DG_SCONNECTION(
|
|||
|
DG_ADDRESS * a_Address,
|
|||
|
RPC_STATUS * pStatus
|
|||
|
) :
|
|||
|
DG_COMMON_CONNECTION(a_Address->Endpoint.TransportInterface, pStatus),
|
|||
|
pAddress (a_Address),
|
|||
|
pAssocGroup (0),
|
|||
|
CachedCalls (0),
|
|||
|
LastInterface (0),
|
|||
|
CurrentCall (0),
|
|||
|
MaxKeySeq (0),
|
|||
|
fFirstCall (0),
|
|||
|
BlockIdleCallRemoval(0),
|
|||
|
pMessageMutex (0)
|
|||
|
{
|
|||
|
LogEvent(SU_SCONN, EV_CREATE, this, a_Address);
|
|||
|
ObjectType = DG_SCONNECTION_TYPE;
|
|||
|
fFirstCall = 0;
|
|||
|
InterlockedIncrement(&ServerConnectionCount);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DG_SCONNECTION::~DG_SCONNECTION()
|
|||
|
{
|
|||
|
InterlockedDecrement(&ServerConnectionCount);
|
|||
|
delete CachedCalls;
|
|||
|
|
|||
|
if (pMessageMutex)
|
|||
|
{
|
|||
|
delete pMessageMutex;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
void
|
|||
|
DG_SCONNECTION::Activate(
|
|||
|
PNCA_PACKET_HEADER pHeader,
|
|||
|
unsigned short NewHash
|
|||
|
)
|
|||
|
{
|
|||
|
AuthInfo.AuthenticationService = pHeader->AuthProto;
|
|||
|
if (AuthInfo.AuthenticationService)
|
|||
|
{
|
|||
|
DG_SECURITY_TRAILER * pVerifier = (DG_SECURITY_TRAILER *)
|
|||
|
(pHeader->Data + pHeader->GetPacketBodyLen());
|
|||
|
|
|||
|
AuthInfo.AuthenticationLevel = pVerifier->protection_level;
|
|||
|
AuthInfo.AuthorizationService = 0;
|
|||
|
AuthInfo.ServerPrincipalName = 0;
|
|||
|
AuthInfo.PacHandle = 0;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
AuthInfo.AuthenticationLevel = RPC_C_AUTHN_LEVEL_NONE;
|
|||
|
AuthInfo.AuthorizationService = RPC_C_AUTHZ_NONE;
|
|||
|
AuthInfo.ServerPrincipalName = 0;
|
|||
|
AuthInfo.PacHandle = 0;
|
|||
|
}
|
|||
|
|
|||
|
ActivityNode.Initialize(&pHeader->ActivityId);
|
|||
|
ActivityHint = NewHash;
|
|||
|
|
|||
|
LogEvent( SU_SCONN, EV_START, this, IntToPtr(pHeader->ActivityId.Data1), ActivityHint );
|
|||
|
|
|||
|
PDG_SCALL Call;
|
|||
|
|
|||
|
for (Call = CachedCalls; Call; Call = Call->Next)
|
|||
|
{
|
|||
|
Call->BindToConnection(this);
|
|||
|
}
|
|||
|
|
|||
|
Callback.State = NoCallbackAttempted;
|
|||
|
MaxKeySeq = 0;
|
|||
|
LowestActiveSequence = 0;
|
|||
|
LowestUnusedSequence = 0;
|
|||
|
|
|||
|
TimeStamp = GetTickCount();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
void
|
|||
|
DG_SCONNECTION::Deactivate(
|
|||
|
)
|
|||
|
{
|
|||
|
LogEvent(SU_SCONN, EV_STOP, this, pAddress);
|
|||
|
|
|||
|
ASSERT( !CurrentCall );
|
|||
|
|
|||
|
while (CachedCalls && CachedCalls->Next)
|
|||
|
{
|
|||
|
PDG_SCALL Call = CachedCalls;
|
|||
|
CachedCalls = Call->Next;
|
|||
|
delete Call;
|
|||
|
}
|
|||
|
|
|||
|
for (unsigned u = 0; u <= MaxKeySeq; ++u)
|
|||
|
{
|
|||
|
delete SecurityContextDict.Delete(u);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Delete all previous interface callback results.
|
|||
|
//
|
|||
|
int cursor;
|
|||
|
InterfaceCallbackResults.Reset(cursor);
|
|||
|
while (InterfaceCallbackResults.Next(cursor, TRUE))
|
|||
|
;
|
|||
|
|
|||
|
ActiveSecurityContext = 0;
|
|||
|
|
|||
|
if (pAssocGroup)
|
|||
|
{
|
|||
|
AssociationGroups->DecrementRefCount(pAssocGroup);
|
|||
|
pAssocGroup = 0;
|
|||
|
}
|
|||
|
|
|||
|
pAddress->FreeConnection(this);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
PDG_SCALL
|
|||
|
DG_SCONNECTION::AllocateCall()
|
|||
|
{
|
|||
|
PDG_SCALL Call = 0;
|
|||
|
|
|||
|
if (CachedCalls)
|
|||
|
{
|
|||
|
Call = CachedCalls;
|
|||
|
CachedCalls = CachedCalls->Next;
|
|||
|
|
|||
|
ASSERT( !Call->InvalidHandle(DG_SCALL_TYPE) );
|
|||
|
ASSERT( !CachedCalls || !CachedCalls->InvalidHandle(DG_SCALL_TYPE) );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
RPC_STATUS Status = RPC_S_OK;
|
|||
|
|
|||
|
Call = new (pAddress->Endpoint.TransportInterface) DG_SCALL(pAddress, &Status);
|
|||
|
if (!Call)
|
|||
|
{
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
if (Status)
|
|||
|
{
|
|||
|
delete Call;
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
Call->BindToConnection(this);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Allow ACTIVE_CALL_TABLE::Remove to succeed even if the call is not in the table.
|
|||
|
//
|
|||
|
Call->Next = Call;
|
|||
|
Call->Previous = Call;
|
|||
|
|
|||
|
IncrementRefCount();
|
|||
|
|
|||
|
return Call;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
void
|
|||
|
DG_SCONNECTION::FreeCall(
|
|||
|
PDG_SCALL Call
|
|||
|
)
|
|||
|
{
|
|||
|
DecrementRefCount();
|
|||
|
|
|||
|
if (CurrentCall == Call)
|
|||
|
{
|
|||
|
CurrentCall = ActiveCalls.Successor(Call);
|
|||
|
}
|
|||
|
|
|||
|
if (Call->GetSequenceNumber() == LowestActiveSequence)
|
|||
|
{
|
|||
|
PDG_SCALL Successor = ActiveCalls.Successor(Call);
|
|||
|
|
|||
|
if (Successor)
|
|||
|
{
|
|||
|
LowestActiveSequence = Successor->GetSequenceNumber();
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
LowestActiveSequence = LowestUnusedSequence;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
ActiveCalls.Remove(Call);
|
|||
|
AddCallToCache(Call);
|
|||
|
}
|
|||
|
|
|||
|
void
|
|||
|
DG_SCALL::NewCall(
|
|||
|
PDG_PACKET a_Packet,
|
|||
|
DatagramTransportPair *AddressPair
|
|||
|
)
|
|||
|
{
|
|||
|
ASSERT( State == CallInit || State == CallComplete );
|
|||
|
ASSERT( DispatchBuffer == 0 );
|
|||
|
|
|||
|
SetState(CallInit);
|
|||
|
|
|||
|
SequenceNumber = a_Packet->Header.SequenceNumber;
|
|||
|
|
|||
|
pSavedPacket->Header.ObjectId = a_Packet->Header.ObjectId;
|
|||
|
pSavedPacket->Header.SequenceNumber = SequenceNumber;
|
|||
|
pSavedPacket->Header.PacketFlags = 0;
|
|||
|
|
|||
|
BasePacketFlags = 0;
|
|||
|
|
|||
|
DG_PACKET_ENGINE::NewCall();
|
|||
|
|
|||
|
RpcpMemoryCopy(RemoteAddress, AddressPair->RemoteAddress, Connection->TransportInterface->AddressSize);
|
|||
|
LocalAddress = *(DWORD *)AddressPair->LocalAddress;
|
|||
|
|
|||
|
if ((a_Packet->Header.PacketFlags2 & DG_PF2_FORWARDED_2) &&
|
|||
|
(a_Packet->Header.PacketFlags & DG_PF_FORWARDED) == 0)
|
|||
|
{
|
|||
|
KnowClientAddress = FALSE;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
KnowClientAddress = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
CallWasForwarded = FALSE;
|
|||
|
CallInProgress = FALSE;
|
|||
|
Privileges = 0;
|
|||
|
CancelEventId = 0;
|
|||
|
pAsync = 0;
|
|||
|
|
|||
|
FinalSendBufferPresent = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DG_SCALL::~DG_SCALL()
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Destructor for the DG_SCALL object.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
<None>
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
<None>
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
#ifdef MONITOR_SERVER_PACKET_COUNT
|
|||
|
ASSERT( OutstandingPacketCount == 0 );
|
|||
|
#endif
|
|||
|
|
|||
|
InterlockedDecrement(&ServerCallCount);
|
|||
|
|
|||
|
delete PipeWaitEvent;
|
|||
|
PipeWaitEvent = 0;
|
|||
|
|
|||
|
if (pCachedSid)
|
|||
|
{
|
|||
|
I_RpcFree(pCachedSid);
|
|||
|
}
|
|||
|
|
|||
|
if (pwsCachedUserName)
|
|||
|
{
|
|||
|
I_RpcFree(pwsCachedUserName);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
RPC_STATUS
|
|||
|
DG_SCALL::NegotiateTransferSyntax (
|
|||
|
IN OUT PRPC_MESSAGE Message
|
|||
|
)
|
|||
|
{
|
|||
|
// this can happen in the callback case only.
|
|||
|
// Datagrams don't really support multiple transfer
|
|||
|
// syntaxes. Return whatever the stub wants, which is
|
|||
|
// already recorded in the transfer syntax field
|
|||
|
|
|||
|
return RPC_S_OK;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
RPC_STATUS
|
|||
|
DG_SCALL::GetBuffer (
|
|||
|
IN OUT PRPC_MESSAGE Message,
|
|||
|
IN UUID *ObjectUuid
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
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
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
LogEvent(SU_SCALL, EV_PROC, this, IntToPtr(Message->BufferLength), 'G' + (('B' + (('u' + ('f' << 8)) << 8)) << 8));
|
|||
|
|
|||
|
RPC_STATUS Status = CommonGetBuffer(Message);
|
|||
|
|
|||
|
LogEvent(SU_SCALL, EV_BUFFER_OUT, this, Message->Buffer, (Message->RpcFlags << 4) | Message->BufferLength);
|
|||
|
if (Status)
|
|||
|
{
|
|||
|
LogError(SU_SCALL, EV_STATUS, this, 0, Status);
|
|||
|
}
|
|||
|
|
|||
|
#ifdef MONITOR_SERVER_PACKET_COUNT
|
|||
|
DG_PACKET::FromStubData(Message->Buffer)->pCount = &OutstandingPacketCount;
|
|||
|
InterlockedIncrement( &OutstandingPacketCount );
|
|||
|
LogEvent( SU_SCALL, '(', this, DG_PACKET::FromStubData(Message->Buffer), OutstandingPacketCount );
|
|||
|
#endif
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
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>
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
LogEvent(SU_SCALL, EV_PROC, this, Message, 'F' + (('B' + (('u' + ('f' << 8)) << 8)) << 8));
|
|||
|
LogEvent(SU_SCALL, EV_BUFFER_IN, this, Message->Buffer, 0);
|
|||
|
|
|||
|
if (Message->Buffer)
|
|||
|
{
|
|||
|
if (Message->Buffer == DispatchBuffer)
|
|||
|
{
|
|||
|
DispatchBuffer = 0;
|
|||
|
}
|
|||
|
|
|||
|
// The ReservedForRuntime field is a local variable of ProcessRpcCall,
|
|||
|
// so it is valid only during dispatch.
|
|||
|
//
|
|||
|
if (State == CallDispatched && Message->ReservedForRuntime)
|
|||
|
{
|
|||
|
PRPC_RUNTIME_INFO Info = (PRPC_RUNTIME_INFO) Message->ReservedForRuntime;
|
|||
|
if (Message->Buffer == Info->OldBuffer)
|
|||
|
{
|
|||
|
Info->OldBuffer = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
CommonFreeBuffer(Message);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
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:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
Message->ReservedForRuntime = 0;
|
|||
|
FreeBuffer(Message);
|
|||
|
|
|||
|
return RPC_S_CALL_FAILED;
|
|||
|
}
|
|||
|
|
|||
|
#pragma optimize("t", on)
|
|||
|
|
|||
|
|
|||
|
void
|
|||
|
DG_SCONNECTION::DispatchPacket(
|
|||
|
IN DG_PACKET * Packet,
|
|||
|
IN DatagramTransportPair *AddressPair
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Once a packet's activity UUID is known, this fn dispatches the packet
|
|||
|
to the appropriate DG_SCALL, creating one if necessary.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Packet - the packet to dispatch
|
|||
|
AddressPair - the remote/local address that sent/received the packet
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PNCA_PACKET_HEADER pHeader = &Packet->Header;
|
|||
|
|
|||
|
#ifdef DEBUGRPC
|
|||
|
|
|||
|
RPC_UUID * pInActivityUuid = &pHeader->ActivityId;
|
|||
|
|
|||
|
ASSERT(0 == ActivityNode.CompareUuid(pInActivityUuid));
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
TimeStamp = GetTickCount();
|
|||
|
|
|||
|
//
|
|||
|
// Make sure the security info is consistent.
|
|||
|
//
|
|||
|
if (AuthInfo.AuthenticationService != pHeader->AuthProto)
|
|||
|
{
|
|||
|
if (pHeader->PacketType != DG_PING &&
|
|||
|
(0 == (pHeader->PacketFlags & DG_PF_MAYBE) || pHeader->AuthProto))
|
|||
|
{
|
|||
|
LogError(SU_SCONN, EV_PKT_IN, this, (void *) 10, (Packet->Header.PacketType << 16) | Packet->GetFragmentNumber());
|
|||
|
#ifdef DEBUGRPC
|
|||
|
DbgPrint("dg rpc: %lx dropping %u whose auth service %u is inconsistent w/the activity %u\n",
|
|||
|
GetCurrentProcessId(), pHeader->PacketType, pHeader->AuthProto, AuthInfo.AuthenticationService);
|
|||
|
#endif
|
|||
|
Mutex.Clear();
|
|||
|
Packet->Free();
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
else if (pHeader->AuthProto)
|
|||
|
{
|
|||
|
DG_SECURITY_TRAILER * Verifier = (DG_SECURITY_TRAILER *)
|
|||
|
(Packet->Header.Data + Packet->GetPacketBodyLen());
|
|||
|
|
|||
|
if (Verifier->protection_level != AuthInfo.AuthenticationLevel)
|
|||
|
{
|
|||
|
LogError(SU_SCONN, EV_PKT_IN, this, (void *) 11, (Packet->Header.PacketType << 16) | Packet->GetFragmentNumber());
|
|||
|
#ifdef DEBUGRPC
|
|||
|
DbgPrint("dg rpc: %lx dropping pkt whose auth level is inconsistent with the activity\n", GetCurrentProcessId());
|
|||
|
#endif
|
|||
|
Mutex.Clear();
|
|||
|
Packet->Free();
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Sanity-check the packet length.
|
|||
|
//
|
|||
|
if (0 == (Packet->Flags & DG_PF_PARTIAL))
|
|||
|
{
|
|||
|
if (pHeader->AuthProto)
|
|||
|
{
|
|||
|
if (Packet->DataLength < Packet->GetPacketBodyLen() + sizeof(DG_SECURITY_TRAILER))
|
|||
|
{
|
|||
|
#ifdef DEBUGRPC
|
|||
|
DbgPrint("dg rpc: secure packet truncated from at least %lu to %lu\n",
|
|||
|
Packet->GetPacketBodyLen() + sizeof(DG_SECURITY_TRAILER), Packet->DataLength);
|
|||
|
#endif
|
|||
|
|
|||
|
Mutex.Clear();
|
|||
|
|
|||
|
LogError(SU_SCONN, EV_PKT_IN, this, (void *) 8, (Packet->Header.PacketType << 16) | Packet->GetFragmentNumber());
|
|||
|
Packet->Free();
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (Packet->DataLength < Packet->GetPacketBodyLen())
|
|||
|
{
|
|||
|
#ifdef DEBUGRPC
|
|||
|
DbgPrint("dg rpc: packet truncated from %lu to %lu\n", Packet->GetPacketBodyLen(), Packet->DataLength);
|
|||
|
#endif
|
|||
|
Mutex.Clear();
|
|||
|
LogError(SU_SCONN, EV_PKT_IN, this, (void *) 9, (Packet->Header.PacketType << 16) | Packet->GetFragmentNumber());
|
|||
|
Packet->Free();
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// [maybe] calls require little of our advanced technology.
|
|||
|
//
|
|||
|
if (pHeader->PacketFlags & DG_PF_MAYBE)
|
|||
|
{
|
|||
|
if (HandleMaybeCall(Packet, AddressPair))
|
|||
|
{
|
|||
|
// mutex released in HandleMaybeCall.
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Calls below LowestActiveSequence are dead.
|
|||
|
//
|
|||
|
if (pHeader->SequenceNumber < LowestActiveSequence)
|
|||
|
{
|
|||
|
Mutex.Clear();
|
|||
|
LogEvent(SU_SCONN, EV_PKT_IN, this, (void *) 0, (Packet->Header.PacketType << 16) | Packet->GetFragmentNumber());
|
|||
|
Packet->Free();
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Find or allocate the DG_SCALL for this packet.
|
|||
|
//
|
|||
|
PDG_SCALL Call = 0;
|
|||
|
|
|||
|
if (pHeader->SequenceNumber < LowestUnusedSequence)
|
|||
|
{
|
|||
|
Call = ActiveCalls.Find(pHeader->SequenceNumber);
|
|||
|
if (!Call)
|
|||
|
{
|
|||
|
Mutex.Clear();
|
|||
|
LogError(SU_SCONN, EV_PKT_IN, this, (void *) 1, (Packet->Header.PacketType << 16) | Packet->GetFragmentNumber());
|
|||
|
Packet->Free();
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Call = HandleNewCall(Packet, AddressPair);
|
|||
|
if (!Call)
|
|||
|
{
|
|||
|
Mutex.Clear();
|
|||
|
Packet->Free();
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Mutex.VerifyOwned();
|
|||
|
|
|||
|
Call->DispatchPacket(Packet, AddressPair->RemoteAddress);
|
|||
|
|
|||
|
CallDispatchLoop();
|
|||
|
|
|||
|
Mutex.Clear();
|
|||
|
|
|||
|
Mutex.VerifyNotOwned();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
void
|
|||
|
DG_SCONNECTION::CallDispatchLoop()
|
|||
|
{
|
|||
|
Mutex.VerifyOwned();
|
|||
|
|
|||
|
//
|
|||
|
// The loop below is to handle the case where a bunch of async calls arrive
|
|||
|
// more or less simultaneously. When stub #1 has finished, we should
|
|||
|
// dispatch stub #2 immediately instead of waiting for the client to ping.
|
|||
|
//
|
|||
|
while (CurrentCall && CurrentCall->ReadyToDispatch() &&
|
|||
|
(Callback.State == CallbackSucceeded ||
|
|||
|
Callback.State == NoCallbackAttempted ))
|
|||
|
{
|
|||
|
PDG_SCALL Call = CurrentCall;
|
|||
|
|
|||
|
Call->ProcessRpcCall();
|
|||
|
|
|||
|
if (CurrentCall == Call)
|
|||
|
{
|
|||
|
CurrentCall = ActiveCalls.Successor(Call);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Mutex.VerifyOwned();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
PDG_SCALL
|
|||
|
DG_SCONNECTION::HandleNewCall(
|
|||
|
IN DG_PACKET * Packet,
|
|||
|
IN DatagramTransportPair *AddressPair
|
|||
|
)
|
|||
|
{
|
|||
|
PDG_SCALL Call = 0;
|
|||
|
|
|||
|
PNCA_PACKET_HEADER pHeader = &Packet->Header;
|
|||
|
|
|||
|
//
|
|||
|
// Only a REQUEST can instantiate a new call.
|
|||
|
//
|
|||
|
if (pHeader->PacketType != DG_REQUEST)
|
|||
|
{
|
|||
|
LogError(SU_SCONN, EV_PKT_IN, this, (void *) 2, (Packet->Header.PacketType << 16) | Packet->GetFragmentNumber());
|
|||
|
|
|||
|
CleanupPacket(&Packet->Header);
|
|||
|
|
|||
|
Packet->Header.PacketType = DG_NOCALL;
|
|||
|
|
|||
|
Packet->SetPacketBodyLen(0);
|
|||
|
Packet->SetFragmentNumber(0xffff);
|
|||
|
|
|||
|
SealAndSendPacket( &pAddress->Endpoint,
|
|||
|
AddressPair->RemoteAddress,
|
|||
|
&Packet->Header,
|
|||
|
0
|
|||
|
);
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
if ((Packet->Header.PacketFlags & DG_PF_FORWARDED) &&
|
|||
|
(Packet->Header.PacketFlags2 & DG_PF2_FORWARDED_2) == 0)
|
|||
|
{
|
|||
|
//
|
|||
|
// This packet is not a true request.
|
|||
|
//
|
|||
|
LogError(SU_SCONN, EV_PKT_IN, this, (void *) 3, (Packet->Header.PacketType << 16) | Packet->GetFragmentNumber());
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// See if the call closes a previous call.
|
|||
|
//
|
|||
|
if (0 == (pHeader->PacketFlags2 & DG_PF2_UNRELATED))
|
|||
|
{
|
|||
|
PDG_SCALL PreviousCall = ActiveCalls.Predecessor(pHeader->SequenceNumber);
|
|||
|
|
|||
|
if (PreviousCall)
|
|||
|
{
|
|||
|
PreviousCall->FinishSendOrReceive(TRUE);
|
|||
|
PreviousCall->Cleanup();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
LowestUnusedSequence = pHeader->SequenceNumber + 1;
|
|||
|
|
|||
|
//
|
|||
|
// Prepare a DG_SCALL to handle the packet.
|
|||
|
//
|
|||
|
Call = AllocateCall();
|
|||
|
LogEvent(SU_SCALL, EV_START, Call, this, pHeader->SequenceNumber);
|
|||
|
|
|||
|
if (!Call)
|
|||
|
{
|
|||
|
LogError(SU_SCONN, EV_PKT_IN, this, (void *) 4, (Packet->Header.PacketType << 16) | Packet->GetFragmentNumber());
|
|||
|
pAddress->SendRejectPacket(Packet, RPC_S_OUT_OF_MEMORY, AddressPair->RemoteAddress);
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
Call->NewCall(Packet, AddressPair);
|
|||
|
|
|||
|
if (FALSE == ActiveCalls.Add(Call, pHeader->SequenceNumber))
|
|||
|
{
|
|||
|
AddCallToCache(Call);
|
|||
|
LogError(SU_SCONN, EV_PKT_IN, this, (void *) 5, (Packet->Header.PacketType << 16) | Packet->GetFragmentNumber());
|
|||
|
pAddress->SendRejectPacket(Packet, RPC_S_OUT_OF_MEMORY, AddressPair->RemoteAddress);
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
if (!CurrentCall)
|
|||
|
{
|
|||
|
CurrentCall = Call;
|
|||
|
}
|
|||
|
|
|||
|
return Call;
|
|||
|
}
|
|||
|
|
|||
|
#pragma optimize("", on)
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
DG_SCONNECTION::HandleMaybeCall(
|
|||
|
IN DG_PACKET * Packet,
|
|||
|
IN DatagramTransportPair *AddressPair
|
|||
|
)
|
|||
|
{
|
|||
|
PNCA_PACKET_HEADER pHeader = &Packet->Header;
|
|||
|
|
|||
|
if (pHeader->PacketType != DG_REQUEST)
|
|||
|
{
|
|||
|
Mutex.Clear();
|
|||
|
Packet->Free();
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
if (!(pHeader->PacketFlags & DG_PF_FORWARDED) &&
|
|||
|
!(pHeader->PacketFlags2 & DG_PF2_FORWARDED_2) )
|
|||
|
{
|
|||
|
DG_SCALL * Call = AllocateCall();
|
|||
|
|
|||
|
LogEvent(SU_SCALL, EV_START, Call, this, pHeader->SequenceNumber);
|
|||
|
|
|||
|
if (!Call)
|
|||
|
{
|
|||
|
Mutex.Clear();
|
|||
|
Packet->Free();
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
Call->NewCall(Packet, AddressPair);
|
|||
|
|
|||
|
|
|||
|
Call->DealWithRequest( Packet );
|
|||
|
|
|||
|
if (Call->ReadyToDispatch())
|
|||
|
{
|
|||
|
Call->ProcessRpcCall();
|
|||
|
}
|
|||
|
|
|||
|
BOOL Result = Call->Cleanup();
|
|||
|
ASSERT( Result );
|
|||
|
|
|||
|
Mutex.Clear();
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// [maybe] calls fragmented by the ep mapper go through the normal route
|
|||
|
//
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
DG_SCALL::ReadyToDispatch(
|
|||
|
)
|
|||
|
{
|
|||
|
BOOL fReadyToDispatch = FALSE;
|
|||
|
|
|||
|
//
|
|||
|
// Before we can execute the call, we need the client's true endpoint,
|
|||
|
// for a forwarded call. For a non-idempotent call we also need a
|
|||
|
// successful callback.
|
|||
|
//
|
|||
|
// For a stub in a pipes interface, we need only fragment zero;
|
|||
|
// for ordinary interfaces we need all the fragments.
|
|||
|
//
|
|||
|
if (KnowClientAddress && State == CallBeforeDispatch)
|
|||
|
{
|
|||
|
//
|
|||
|
// See if we are ready to dispatch to the stub.
|
|||
|
//
|
|||
|
if (Interface->IsPipeInterface())
|
|||
|
{
|
|||
|
if (pReceivedPackets && 0 == pReceivedPackets->GetFragmentNumber())
|
|||
|
{
|
|||
|
fReadyToDispatch = TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (fReceivedAllFragments)
|
|||
|
{
|
|||
|
fReadyToDispatch = TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Make sure the callback succeeded.
|
|||
|
//
|
|||
|
if (fReadyToDispatch)
|
|||
|
{
|
|||
|
if (0 == (pReceivedPackets->Header.PacketFlags & DG_PF_IDEMPOTENT) &&
|
|||
|
!Connection->pAssocGroup)
|
|||
|
{
|
|||
|
fReadyToDispatch = FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return fReadyToDispatch;
|
|||
|
}
|
|||
|
|
|||
|
#pragma optimize("t", on)
|
|||
|
|
|||
|
|
|||
|
void
|
|||
|
DG_SCALL::DispatchPacket(
|
|||
|
IN PDG_PACKET Packet,
|
|||
|
IN DG_TRANSPORT_ADDRESS a_RemoteAddress
|
|||
|
)
|
|||
|
{
|
|||
|
#ifdef MONITOR_SERVER_PACKET_COUNT
|
|||
|
if (Packet->Header.PacketType == DG_REQUEST)
|
|||
|
{
|
|||
|
InterlockedIncrement( &OutstandingPacketCount );
|
|||
|
Packet->pCount = &OutstandingPacketCount;
|
|||
|
LogEvent( SU_SCALL, '(', this, Packet, OutstandingPacketCount );
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
TimeStamp = GetTickCount();
|
|||
|
|
|||
|
if (State == CallInit &&
|
|||
|
Packet->Header.PacketType != DG_REQUEST)
|
|||
|
{
|
|||
|
LogError(SU_SCALL, EV_PKT_IN, this, (void *) 1, (Packet->Header.PacketType << 16) | Packet->GetFragmentNumber());
|
|||
|
if (KnowClientAddress)
|
|||
|
{
|
|||
|
SendFackOrNocall(Packet, DG_NOCALL);
|
|||
|
}
|
|||
|
|
|||
|
FreePacket(Packet);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if (State == CallComplete)
|
|||
|
{
|
|||
|
LogEvent(SU_SCALL, EV_PKT_IN, this, (void *) 2, (Packet->Header.PacketType << 16) | Packet->GetFragmentNumber());
|
|||
|
FreePacket(Packet);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// FORWARDED FORWARDED_2 Meaning
|
|||
|
// --------- ----------- -------
|
|||
|
// no no packet sent directly from client
|
|||
|
// yes no packet forwarded by ep mapper (with DREP/ep trailer)
|
|||
|
// no yes packet data forwarded by ep mapper (no trailer)
|
|||
|
// yes yes sent from ep mapper; contains only the DREP/ep trailer
|
|||
|
//
|
|||
|
if (Packet->Header.PacketFlags2 & DG_PF2_FORWARDED_2)
|
|||
|
{
|
|||
|
CallWasForwarded = TRUE;
|
|||
|
|
|||
|
if (Packet->Header.PacketFlags & DG_PF_FORWARDED)
|
|||
|
{
|
|||
|
if (!KnowClientAddress)
|
|||
|
{
|
|||
|
//
|
|||
|
// Record the client's true endpoint.
|
|||
|
//
|
|||
|
RpcpMemoryCopy(
|
|||
|
RemoteAddress,
|
|||
|
a_RemoteAddress,
|
|||
|
Connection->pAddress->Endpoint.TransportInterface->AddressSize
|
|||
|
);
|
|||
|
|
|||
|
KnowClientAddress = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
LogEvent(SU_SCALL, EV_PKT_IN, this, (void *) 3, (Packet->Header.PacketType << 16) | Packet->GetFragmentNumber());
|
|||
|
|
|||
|
FreePacket(Packet);
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
ASSERT( (Packet->Header.PacketFlags & DG_PF_FORWARDED) == 0 );
|
|||
|
}
|
|||
|
|
|||
|
LogEvent(SU_SCALL, EV_PKT_IN, this, 0, (Packet->Header.PacketType << 16) | Packet->GetFragmentNumber());
|
|||
|
|
|||
|
switch (Packet->Header.PacketType)
|
|||
|
{
|
|||
|
case DG_REQUEST: DealWithRequest (Packet); break;
|
|||
|
case DG_PING: DealWithPing (Packet); break;
|
|||
|
case DG_FACK: DealWithFack (Packet); break;
|
|||
|
case DG_QUIT: DealWithQuit (Packet); break;
|
|||
|
case DG_ACK: DealWithAck (Packet); break;
|
|||
|
default:
|
|||
|
{
|
|||
|
FreePacket(Packet);
|
|||
|
FinishSendOrReceive(TRUE);
|
|||
|
Cleanup();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#pragma optimize("", on)
|
|||
|
|
|||
|
|
|||
|
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;
|
|||
|
|
|||
|
unsigned short Serial = ReadSerialNumber(&pPacket->Header);
|
|||
|
if (Serial < ReceiveSerialNumber)
|
|||
|
{
|
|||
|
FreePacket(pPacket);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
ReceiveSerialNumber = Serial;
|
|||
|
|
|||
|
switch (State)
|
|||
|
{
|
|||
|
case CallInit:
|
|||
|
{
|
|||
|
SendFackOrNocall(pPacket, DG_NOCALL);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case CallBeforeDispatch:
|
|||
|
case CallDispatched:
|
|||
|
case CallAfterDispatch:
|
|||
|
{
|
|||
|
if (fReceivedAllFragments)
|
|||
|
{
|
|||
|
pHeader->PacketType = DG_WORKING;
|
|||
|
pHeader->SetPacketBodyLen(0);
|
|||
|
|
|||
|
SealAndSendPacket(pHeader);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
SendFackOrNocall(pPacket, DG_FACK);
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case CallSendingResponse:
|
|||
|
{
|
|||
|
SendSomeFragments();
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case CallComplete:
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
default:
|
|||
|
{
|
|||
|
ASSERT(0 && "invalid call state");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
FreePacket(pPacket);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
RPC_STATUS
|
|||
|
DG_SCONNECTION::VerifyNonRequestPacket(
|
|||
|
DG_PACKET * Packet
|
|||
|
)
|
|||
|
{
|
|||
|
if (AuthInfo.AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE)
|
|||
|
{
|
|||
|
if (AuthInfo.AuthenticationService != RPC_C_AUTHN_DCE_PRIVATE )
|
|||
|
{
|
|||
|
SECURITY_CONTEXT * Context = FindMatchingSecurityContext(Packet);
|
|||
|
|
|||
|
if (!Context)
|
|||
|
{
|
|||
|
LogError(SU_SCONN, EV_STATUS, this, (void *) 110, RPC_S_ACCESS_DENIED );
|
|||
|
return RPC_S_ACCESS_DENIED;
|
|||
|
}
|
|||
|
|
|||
|
return VerifySecurePacket(Packet, Context);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return RPC_S_OK;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
RPC_STATUS
|
|||
|
DG_SCONNECTION::VerifyRequestPacket(
|
|||
|
DG_PACKET * Packet
|
|||
|
)
|
|||
|
{
|
|||
|
if (AuthInfo.AuthenticationLevel != RPC_C_AUTHN_LEVEL_NONE)
|
|||
|
{
|
|||
|
SECURITY_CONTEXT * Context = FindMatchingSecurityContext(Packet);
|
|||
|
|
|||
|
if (!Context)
|
|||
|
{
|
|||
|
LogError(SU_SCONN, EV_STATUS, this, (void *) 111, RPC_S_ACCESS_DENIED );
|
|||
|
return RPC_S_ACCESS_DENIED;
|
|||
|
}
|
|||
|
|
|||
|
return VerifySecurePacket(Packet, Context);
|
|||
|
}
|
|||
|
|
|||
|
return RPC_S_OK;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
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 (RPC_S_OK != Connection->VerifyNonRequestPacket(pPacket))
|
|||
|
{
|
|||
|
FreePacket(pPacket);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
QUIT_BODY_0 * pBody = (QUIT_BODY_0 *) pPacket->Header.Data;
|
|||
|
|
|||
|
if (pPacket->GetPacketBodyLen() < 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->GetPacketBodyLen()
|
|||
|
);
|
|||
|
#endif
|
|||
|
|
|||
|
FreePacket(pPacket);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if (pBody->EventId > CancelEventId)
|
|||
|
{
|
|||
|
CancelEventId = pBody->EventId;
|
|||
|
Cancel(0);
|
|||
|
}
|
|||
|
|
|||
|
if (pBody->EventId == CancelEventId)
|
|||
|
{
|
|||
|
pSavedPacket->Header.PacketType = DG_QUACK;
|
|||
|
pSavedPacket->Header.SequenceNumber = SequenceNumber;
|
|||
|
pSavedPacket->SetPacketBodyLen(sizeof(QUACK_BODY_0));
|
|||
|
|
|||
|
QUACK_BODY_0 * pAckBody = (QUACK_BODY_0 *) pSavedPacket->Header.Data;
|
|||
|
|
|||
|
pAckBody->Version = 0;
|
|||
|
pAckBody->EventId = CancelEventId;
|
|||
|
|
|||
|
//
|
|||
|
// If the app has picked up the cancel notification, set Accepted == TRUE.
|
|||
|
// This is likely only if the QUIT was retransmitted.
|
|||
|
//
|
|||
|
if (Cancelled)
|
|||
|
{
|
|||
|
pAckBody->Accepted = FALSE;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
pAckBody->Accepted = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
SealAndSendPacket(&pSavedPacket->Header);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
#ifdef DEBUGRPC
|
|||
|
DbgPrint("RPC DG: stale cancel event id %lu\n", pBody->EventId);
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
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
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
if (State == CallSendingResponse)
|
|||
|
{
|
|||
|
//
|
|||
|
// Accept only an authenticated ACK if the call is secure.
|
|||
|
//
|
|||
|
// Sometimes OSF clients will omit the sec trailer from the ACK.
|
|||
|
//
|
|||
|
if (RPC_S_OK != Connection->VerifyNonRequestPacket(pPacket))
|
|||
|
{
|
|||
|
FreePacket(pPacket);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
FinishSendOrReceive(FALSE);
|
|||
|
Cleanup();
|
|||
|
}
|
|||
|
|
|||
|
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
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
BOOL Ignore;
|
|||
|
|
|||
|
// is call finished?
|
|||
|
if (State != CallSendingResponse)
|
|||
|
{
|
|||
|
FreePacket(pPacket);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if (RPC_S_OK != Connection->VerifyNonRequestPacket(pPacket))
|
|||
|
{
|
|||
|
FreePacket(pPacket);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Note fack arrival, and send more packets if necessary.
|
|||
|
//
|
|||
|
TimeoutCount = 0;
|
|||
|
|
|||
|
SendBurstLength += 1;
|
|||
|
|
|||
|
UpdateSendWindow(pPacket, &Ignore);
|
|||
|
|
|||
|
//
|
|||
|
// See whether we need to wake up a call to I_RpcSend, or generate an APC.
|
|||
|
//
|
|||
|
if (IsBufferAcknowledged())
|
|||
|
{
|
|||
|
FinishSendOrReceive(FALSE);
|
|||
|
}
|
|||
|
|
|||
|
FreePacket(pPacket);
|
|||
|
}
|
|||
|
|
|||
|
#pragma optimize("t", on)
|
|||
|
|
|||
|
|
|||
|
void
|
|||
|
DG_SCALL::DealWithRequest(
|
|||
|
PDG_PACKET pPacket
|
|||
|
)
|
|||
|
{
|
|||
|
RPC_STATUS Status = RPC_S_OK;
|
|||
|
PNCA_PACKET_HEADER pHeader = &pPacket->Header;
|
|||
|
|
|||
|
ASSERT(pHeader->SequenceNumber == SequenceNumber);
|
|||
|
|
|||
|
switch (State)
|
|||
|
{
|
|||
|
case CallInit:
|
|||
|
{
|
|||
|
//
|
|||
|
// Be sure the server is listening and the interface is supported.
|
|||
|
//
|
|||
|
RPC_SYNTAX_IDENTIFIER InterfaceInfo;
|
|||
|
|
|||
|
InterfaceInfo.SyntaxVersion = pHeader->InterfaceVersion;
|
|||
|
RpcpMemoryCopy(
|
|||
|
&(InterfaceInfo.SyntaxGUID),
|
|||
|
&(pHeader->InterfaceId),
|
|||
|
sizeof(RPC_UUID)
|
|||
|
);
|
|||
|
|
|||
|
if (Connection->LastInterface &&
|
|||
|
0 == Connection->LastInterface->MatchInterfaceIdentifier(&InterfaceInfo))
|
|||
|
{
|
|||
|
Interface = Connection->LastInterface;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Interface = GlobalRpcServer->FindInterface(&InterfaceInfo);
|
|||
|
Connection->LastInterface = Interface;
|
|||
|
}
|
|||
|
|
|||
|
if (!Interface)
|
|||
|
{
|
|||
|
Status = RPC_S_UNKNOWN_IF;
|
|||
|
}
|
|||
|
else if (!GlobalRpcServer->IsServerListening() &&
|
|||
|
!Interface->IsAutoListenInterface())
|
|||
|
{
|
|||
|
Status = RPC_S_SERVER_TOO_BUSY;
|
|||
|
|
|||
|
// If this is a message transport (i.e. Falcon) then
|
|||
|
// wait around for a while before giving up. The user
|
|||
|
// may not have called RpcServerListen() yet...
|
|||
|
if (Connection->TransportInterface->IsMessageTransport)
|
|||
|
{
|
|||
|
for (int i=0; i<100; i++)
|
|||
|
{
|
|||
|
if (GlobalRpcServer->IsServerListening())
|
|||
|
{
|
|||
|
Status = RPC_S_OK;
|
|||
|
break;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Sleep(100); // Loop 100 * Sleep 100 = 10 seconds.
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (Status == RPC_S_OK)
|
|||
|
{
|
|||
|
Status = Interface->IsObjectSupported(&pHeader->ObjectId);
|
|||
|
}
|
|||
|
|
|||
|
if (Status == RPC_S_OK &&
|
|||
|
Connection->DidCallbackFail())
|
|||
|
{
|
|||
|
Status = RPC_S_CALL_FAILED_DNE;
|
|||
|
}
|
|||
|
|
|||
|
if (Status != RPC_S_OK)
|
|||
|
{
|
|||
|
BOOL fSendReject = !(pHeader->PacketFlags & DG_PF_MAYBE);
|
|||
|
|
|||
|
FreePacket(pPacket);
|
|||
|
if (fSendReject)
|
|||
|
{
|
|||
|
pSavedPacket->Header.SequenceNumber = SequenceNumber;
|
|||
|
Connection->pAddress->SendRejectPacket(pSavedPacket, Status, RemoteAddress);
|
|||
|
}
|
|||
|
|
|||
|
Cleanup();
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// The server is listening and the interface is present.
|
|||
|
// We will increment these counters to declare that a call
|
|||
|
// is in progress.
|
|||
|
//
|
|||
|
CallInProgress = TRUE;
|
|||
|
|
|||
|
Connection->pAddress->IncrementActiveCallCount();
|
|||
|
|
|||
|
if (Interface->IsAutoListenInterface())
|
|||
|
{
|
|||
|
Interface->BeginAutoListenCall() ;
|
|||
|
Connection->pAddress->BeginAutoListenCall() ;
|
|||
|
}
|
|||
|
|
|||
|
SetState(CallBeforeDispatch);
|
|||
|
|
|||
|
//
|
|||
|
// No "break" here.
|
|||
|
//
|
|||
|
}
|
|||
|
|
|||
|
case CallBeforeDispatch:
|
|||
|
case CallDispatched:
|
|||
|
case CallAfterDispatch:
|
|||
|
{
|
|||
|
if (fReceivedAllFragments)
|
|||
|
{
|
|||
|
pHeader->PacketType = DG_WORKING;
|
|||
|
pHeader->SetPacketBodyLen(0);
|
|||
|
|
|||
|
SealAndSendPacket(pHeader);
|
|||
|
FreePacket(pPacket);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the client sent an oversize fragment, send a FACK-with-body
|
|||
|
// telling him our limit.
|
|||
|
//
|
|||
|
if (pPacket->Flags & DG_PF_PARTIAL)
|
|||
|
{
|
|||
|
SendFackOrNocall(pPacket, DG_FACK);
|
|||
|
|
|||
|
FreePacket(pPacket);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Add the fragment to the call's packet list.
|
|||
|
//
|
|||
|
BOOL Added = UpdateReceiveWindow(pPacket);
|
|||
|
|
|||
|
if (!Added)
|
|||
|
{
|
|||
|
unsigned Frag = (pPacket->Header.PacketType << 16) | pPacket->GetFragmentNumber();
|
|||
|
|
|||
|
LogEvent(SU_SCALL, EV_PKT_IN, this, (void *) '!', Frag);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// See whether we need to wake up a call to I_RpcReceive.
|
|||
|
//
|
|||
|
if (PipeWaitType == PWT_RECEIVE &&
|
|||
|
(fReceivedAllFragments ||
|
|||
|
(PipeWaitLength && ConsecutiveDataBytes >= PipeWaitLength)))
|
|||
|
{
|
|||
|
FinishSendOrReceive(FALSE);
|
|||
|
}
|
|||
|
|
|||
|
if (KnowClientAddress)
|
|||
|
{
|
|||
|
Connection->SubmitCallbackIfNecessary(this, pPacket, RemoteAddress);
|
|||
|
}
|
|||
|
|
|||
|
if (!Added)
|
|||
|
{
|
|||
|
FreePacket(pPacket);
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case CallSendingResponse:
|
|||
|
{
|
|||
|
SendSomeFragments();
|
|||
|
FreePacket(pPacket);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
default:
|
|||
|
{
|
|||
|
ASSERT(0 && "invalid call state");
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
void
|
|||
|
DG_SCALL::ProcessRpcCall()
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called when we determine that all the packets for a
|
|||
|
given call have been received.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
BOOL ObjectUuidSpecified;
|
|||
|
PNCA_PACKET_HEADER pHeader = &pReceivedPackets->Header;
|
|||
|
PRPC_DISPATCH_TABLE DispatchTableToUse;
|
|||
|
|
|||
|
ASSERT(State == CallBeforeDispatch);
|
|||
|
|
|||
|
SetState(CallDispatched);
|
|||
|
|
|||
|
//
|
|||
|
// Save the object uuid if necessary.
|
|||
|
//
|
|||
|
if (pHeader->ObjectId.IsNullUuid())
|
|||
|
{
|
|||
|
ObjectUuidSpecified = FALSE;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
ObjectUuidSpecified = TRUE;
|
|||
|
pSavedPacket->Header.ObjectId.CopyUuid(&pHeader->ObjectId);
|
|||
|
}
|
|||
|
|
|||
|
RpcRuntimeInfo.Length = sizeof(RPC_RUNTIME_INFO) ;
|
|||
|
RpcMessage.ReservedForRuntime = &RpcRuntimeInfo ;
|
|||
|
|
|||
|
RpcMessage.Handle = (RPC_BINDING_HANDLE) this;
|
|||
|
RpcMessage.ProcNum = pHeader->OperationNumber;
|
|||
|
RpcMessage.TransferSyntax = 0;
|
|||
|
RpcMessage.ImportContext = 0;
|
|||
|
|
|||
|
RpcMessage.RpcFlags = PacketToRpcFlags(pHeader->PacketFlags);
|
|||
|
|
|||
|
unsigned OriginalSequenceNumber = SequenceNumber;
|
|||
|
unsigned long SavedAwayRpcFlags = RpcMessage.RpcFlags;
|
|||
|
|
|||
|
RPC_STATUS Status = RPC_S_OK;
|
|||
|
|
|||
|
//
|
|||
|
// For secure RPC, verify packet integrity.
|
|||
|
//
|
|||
|
if (pHeader->AuthProto)
|
|||
|
{
|
|||
|
PDG_PACKET pScan = pReceivedPackets;
|
|||
|
|
|||
|
do
|
|||
|
{
|
|||
|
Status = Connection->VerifyRequestPacket(pScan);
|
|||
|
if (Status)
|
|||
|
{
|
|||
|
LogError( SU_SCALL, EV_STATUS, this, UlongToPtr(0x200), Status );
|
|||
|
}
|
|||
|
pScan = pScan->pNext;
|
|||
|
}
|
|||
|
while (pScan && Status == RPC_S_OK);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Coalesce packet data.
|
|||
|
//
|
|||
|
if (RPC_S_OK == Status)
|
|||
|
{
|
|||
|
RpcMessage.Buffer = 0;
|
|||
|
RpcMessage.BufferLength = 0;
|
|||
|
Status = AssembleBufferFromPackets(&RpcMessage);
|
|||
|
}
|
|||
|
|
|||
|
if (Status != RPC_S_OK)
|
|||
|
{
|
|||
|
InitErrorPacket(pSavedPacket, DG_REJECT, Status);
|
|||
|
SealAndSendPacket(&pSavedPacket->Header);
|
|||
|
|
|||
|
Cleanup();
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
DispatchBuffer = RpcMessage.Buffer;
|
|||
|
|
|||
|
//
|
|||
|
// 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());
|
|||
|
|
|||
|
TerminateWhenConvenient = FALSE;
|
|||
|
AsyncStatus = RPC_S_OK;
|
|||
|
|
|||
|
//
|
|||
|
// 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 (Interface->IsSecurityCallbackReqd())
|
|||
|
{
|
|||
|
Status = Connection->MakeApplicationSecurityCallback(Interface, this);
|
|||
|
if (Status)
|
|||
|
{
|
|||
|
LogError( SU_SCALL, EV_STATUS, this, UlongToPtr(0x201), Status );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (TerminateWhenConvenient)
|
|||
|
{
|
|||
|
RpcpSetThreadContext(0);
|
|||
|
Cleanup();
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If this is a message based transport then we will need the
|
|||
|
// message mutex for call serialization:
|
|||
|
//
|
|||
|
if (Connection->TransportInterface->IsMessageTransport)
|
|||
|
{
|
|||
|
if (!Connection->pMessageMutex)
|
|||
|
{
|
|||
|
Connection->MessageMutexInitialize(&Status);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// 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)
|
|||
|
{
|
|||
|
ASSERT(Interface);
|
|||
|
|
|||
|
//
|
|||
|
// doc:appref
|
|||
|
//
|
|||
|
// SetAsyncHandle adds a reference to the call on behalf of the app,
|
|||
|
// so that the call cannot be deleted even though the manager routine
|
|||
|
// has completed and no Send or Receive calls are pending.
|
|||
|
// That reference should be removed when one of the following occurs:
|
|||
|
//
|
|||
|
// a) (obsolete)
|
|||
|
// b) the app sends the last buffer (RPC_BUFFER_PARTIAL not set)
|
|||
|
// c) a Send() returns something other than OK or SEND_INCOMPLETE
|
|||
|
// d) the app calls AbortAsyncCall
|
|||
|
//
|
|||
|
IncrementRefCount();
|
|||
|
|
|||
|
Connection->Mutex.Clear();
|
|||
|
|
|||
|
//
|
|||
|
// If this is a message based call (MQ) then take a mutex for
|
|||
|
// serialization:
|
|||
|
//
|
|||
|
if (Connection->TransportInterface->IsMessageTransport)
|
|||
|
{
|
|||
|
ASSERT(Connection->pMessageMutex);
|
|||
|
|
|||
|
Connection->pMessageMutex->Request();
|
|||
|
|
|||
|
Connection->TransportInterface->AllowReceives(
|
|||
|
SourceEndpoint->TransportEndpoint,
|
|||
|
FALSE,
|
|||
|
FALSE );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//
|
|||
|
// Normal, non-message based calls:
|
|||
|
//
|
|||
|
Connection->pAddress->CheckThreadPool();
|
|||
|
}
|
|||
|
|
|||
|
StubWasCalled = TRUE;
|
|||
|
|
|||
|
RPC_STATUS ExceptionCode = 0;
|
|||
|
|
|||
|
DispatchTableToUse = Interface->GetDefaultDispatchTable();
|
|||
|
if ( !ObjectUuidSpecified )
|
|||
|
{
|
|||
|
Status = Interface->DispatchToStub(
|
|||
|
&RpcMessage, // msg
|
|||
|
0, // callback flag
|
|||
|
DispatchTableToUse, // dispatch table to use for
|
|||
|
// this interface
|
|||
|
&ExceptionCode // exception code
|
|||
|
);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Status = Interface->DispatchToStubWithObject(
|
|||
|
&RpcMessage, // msg
|
|||
|
&pSavedPacket->Header.ObjectId, // object uuid
|
|||
|
0, // callback flag
|
|||
|
DispatchTableToUse, // dispatch table to use for
|
|||
|
// this interface
|
|||
|
&ExceptionCode // exception code
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If this is a message based call (MQ) then clear the mutex:
|
|||
|
//
|
|||
|
if (Connection->TransportInterface->IsMessageTransport)
|
|||
|
{
|
|||
|
Connection->pMessageMutex->Clear();
|
|||
|
|
|||
|
Connection->TransportInterface->AllowReceives(
|
|||
|
SourceEndpoint->TransportEndpoint,
|
|||
|
TRUE,
|
|||
|
FALSE );
|
|||
|
|
|||
|
Connection->pAddress->CheckThreadPool();
|
|||
|
}
|
|||
|
|
|||
|
RpcMessage.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;
|
|||
|
}
|
|||
|
|
|||
|
Connection->Mutex.Request();
|
|||
|
DecrementRefCount();
|
|||
|
|
|||
|
if (Status == RPC_P_EXCEPTION_OCCURED)
|
|||
|
{
|
|||
|
if (pAsync)
|
|||
|
{
|
|||
|
//
|
|||
|
// Abort any lingering receive.
|
|||
|
//
|
|||
|
FinishSendOrReceive(TRUE);
|
|||
|
DecrementRefCount();
|
|||
|
|
|||
|
//
|
|||
|
// Later code will see TerminateWhenConvenient and will not send a fault packet.
|
|||
|
//
|
|||
|
InitErrorPacket(pSavedPacket, DG_FAULT, ExceptionCode);
|
|||
|
SealAndSendPacket(&pSavedPacket->Header);
|
|||
|
}
|
|||
|
|
|||
|
Status = ExceptionCode;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the manager routine impersonated the client,
|
|||
|
// restore the thread to its native security context.
|
|||
|
//
|
|||
|
RevertToSelf();
|
|||
|
}
|
|||
|
|
|||
|
RpcpSetThreadContext(0);
|
|||
|
|
|||
|
//
|
|||
|
// Remember that a sync stub may have called the completion routine,
|
|||
|
// or sent some [out] pipe data.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Don't send a response of any sort for a [maybe] call.
|
|||
|
//
|
|||
|
if (RpcMessage.RpcFlags & RPC_NCA_FLAGS_MAYBE)
|
|||
|
{
|
|||
|
if (!Status)
|
|||
|
{
|
|||
|
FreeBuffer(&RpcMessage);
|
|||
|
}
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Has the client cancelled the call or another server thread aborted it?
|
|||
|
//
|
|||
|
if (TerminateWhenConvenient)
|
|||
|
{
|
|||
|
// if (!Status)
|
|||
|
// {
|
|||
|
// FreeBuffer(&Message);
|
|||
|
// }
|
|||
|
Cleanup();
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
ASSERT( State == CallDispatched ||
|
|||
|
State == CallSendingResponse );
|
|||
|
|
|||
|
//
|
|||
|
// Ordinary error?
|
|||
|
//
|
|||
|
if (Status != RPC_S_OK)
|
|||
|
{
|
|||
|
if (StubWasCalled)
|
|||
|
{
|
|||
|
InitErrorPacket(pSavedPacket, DG_FAULT, Status);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
InitErrorPacket(pSavedPacket, DG_REJECT, Status);
|
|||
|
}
|
|||
|
|
|||
|
SealAndSendPacket(&pSavedPacket->Header);
|
|||
|
|
|||
|
Cleanup();
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// No error; the call is still in progress.
|
|||
|
//
|
|||
|
if (pAsync)
|
|||
|
{
|
|||
|
if (State == CallDispatched)
|
|||
|
{
|
|||
|
ASSERT( fReceivedAllFragments || Interface->IsPipeInterface() );
|
|||
|
|
|||
|
SetState(CallAfterDispatch);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
ASSERT( fReceivedAllFragments );
|
|||
|
|
|||
|
if (State == CallSendingResponse)
|
|||
|
{
|
|||
|
ASSERT( Interface->IsPipeInterface() );
|
|||
|
}
|
|||
|
|
|||
|
SetState(CallSendingResponse);
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Send the static [out] call parameters; [out] pipes were sent by the stub.
|
|||
|
//
|
|||
|
SetFragmentLengths();
|
|||
|
|
|||
|
PushBuffer(&RpcMessage);
|
|||
|
|
|||
|
CleanupReceiveWindow();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#pragma optimize("", on)
|
|||
|
|
|||
|
|
|||
|
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;
|
|||
|
|
|||
|
unsigned Frag = (pPacket->Header.PacketType << 16) | pPacket->GetFragmentNumber();
|
|||
|
|
|||
|
BOOL Added = UpdateReceiveWindow(pPacket);
|
|||
|
|
|||
|
if (!Added)
|
|||
|
{
|
|||
|
LogEvent(SU_SCALL, EV_PKT_IN, this, (void *) '!', Frag);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// See whether we need to wake up a call to I_RpcReceive.
|
|||
|
//
|
|||
|
if (fReceivedAllFragments ||
|
|||
|
(PipeWaitLength && ConsecutiveDataBytes >= PipeWaitLength))
|
|||
|
{
|
|||
|
FinishSendOrReceive(FALSE);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
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;
|
|||
|
|
|||
|
if (!Connection->ActiveSecurityContext)
|
|||
|
{
|
|||
|
return RPC_S_NO_CONTEXT_AVAILABLE;
|
|||
|
}
|
|||
|
|
|||
|
Status = SetThreadSecurityContext(Connection->ActiveSecurityContext);
|
|||
|
if (RPC_S_OK != Status)
|
|||
|
{
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
Status = Connection->ActiveSecurityContext->ImpersonateClient();
|
|||
|
if (RPC_S_OK != Status)
|
|||
|
{
|
|||
|
ClearThreadSecurityContext();
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
RPC_STATUS
|
|||
|
DG_SCALL::RevertToSelf (
|
|||
|
)
|
|||
|
{
|
|||
|
SECURITY_CONTEXT * SecurityContext = ClearThreadSecurityContext();
|
|||
|
|
|||
|
if (SecurityContext)
|
|||
|
{
|
|||
|
SecurityContext->RevertToSelf();
|
|||
|
}
|
|||
|
|
|||
|
return(RPC_S_OK);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
RPC_STATUS
|
|||
|
DG_SCALL::InquireAuthClient (
|
|||
|
OUT RPC_AUTHZ_HANDLE * Privs,
|
|||
|
OUT RPC_CHAR * * ServerPrincipalName, OPTIONAL
|
|||
|
OUT unsigned long * AuthenticationLevel,
|
|||
|
OUT unsigned long * AuthenticationService,
|
|||
|
OUT unsigned long * pAuthorizationService,
|
|||
|
IN unsigned long Flags
|
|||
|
)
|
|||
|
{
|
|||
|
if (0 == Connection->ActiveSecurityContext)
|
|||
|
{
|
|||
|
if ( (Connection->TransportInterface->InquireAuthClient) )
|
|||
|
{
|
|||
|
RPC_STATUS Status;
|
|||
|
SID *pSid;
|
|||
|
|
|||
|
Status = Connection->TransportInterface->InquireAuthClient(
|
|||
|
RemoteAddress,
|
|||
|
ServerPrincipalName,
|
|||
|
&pSid,
|
|||
|
AuthenticationLevel,
|
|||
|
AuthenticationService,
|
|||
|
pAuthorizationService );
|
|||
|
if ( (Status == RPC_S_OK)
|
|||
|
&& (ARGUMENT_PRESENT(ServerPrincipalName)))
|
|||
|
{
|
|||
|
Status = ConvertSidToUserW(pSid,(RPC_CHAR**)Privs);
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
return RPC_S_BINDING_HAS_NO_AUTH;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (AuthenticationLevel)
|
|||
|
{
|
|||
|
*AuthenticationLevel = Connection->ActiveSecurityContext->AuthenticationLevel;
|
|||
|
}
|
|||
|
|
|||
|
if (AuthenticationService)
|
|||
|
{
|
|||
|
*AuthenticationService = Connection->ActiveSecurityContext->AuthenticationService;
|
|||
|
}
|
|||
|
|
|||
|
if (Privs || pAuthorizationService)
|
|||
|
{
|
|||
|
if (!Privileges)
|
|||
|
{
|
|||
|
Connection->ActiveSecurityContext->GetDceInfo(&Privileges, &AuthorizationService);
|
|||
|
}
|
|||
|
|
|||
|
if (Privs)
|
|||
|
{
|
|||
|
*Privs = Privileges;
|
|||
|
}
|
|||
|
|
|||
|
if (pAuthorizationService)
|
|||
|
{
|
|||
|
*pAuthorizationService = AuthorizationService;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (ARGUMENT_PRESENT(ServerPrincipalName))
|
|||
|
{
|
|||
|
RPC_STATUS Status;
|
|||
|
|
|||
|
Status = GlobalRpcServer->InquirePrincipalName(
|
|||
|
*AuthenticationService,
|
|||
|
ServerPrincipalName
|
|||
|
);
|
|||
|
|
|||
|
ASSERT(Status == RPC_S_OK ||
|
|||
|
Status == RPC_S_OUT_OF_MEMORY );
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
return(RPC_S_OK);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
RPC_STATUS
|
|||
|
DG_SCALL::GetAssociationContextCollection (
|
|||
|
OUT ContextCollection **CtxCollection
|
|||
|
)
|
|||
|
{
|
|||
|
RPC_STATUS Status = Connection->GetAssociationGroup(RemoteAddress);
|
|||
|
if (Status)
|
|||
|
{
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
ASSERT( Connection->pAssocGroup );
|
|||
|
|
|||
|
Status = Connection->pAssocGroup->GetAssociationContextCollection(CtxCollection);
|
|||
|
|
|||
|
return (Status);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
void
|
|||
|
DG_SCALL::InquireObjectUuid (
|
|||
|
OUT RPC_UUID * ObjectUuid
|
|||
|
)
|
|||
|
{
|
|||
|
ObjectUuid->CopyUuid(&pSavedPacket->Header.ObjectId);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
RPC_STATUS
|
|||
|
DG_SCALL::ToStringBinding (
|
|||
|
OUT RPC_CHAR * * 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 = Connection->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::ConvertToServerBinding (
|
|||
|
OUT RPC_BINDING_HANDLE * pServerBinding
|
|||
|
)
|
|||
|
{
|
|||
|
return CreateReverseBinding(pServerBinding, FALSE);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
RPC_STATUS
|
|||
|
DG_SCALL::CreateReverseBinding (
|
|||
|
OUT RPC_BINDING_HANDLE * pServerBinding,
|
|||
|
BOOL IncludeEndpoint
|
|||
|
)
|
|||
|
{
|
|||
|
RPC_STATUS Status;
|
|||
|
RPC_CHAR * ClientAddress;
|
|||
|
RPC_CHAR * ClientEndpoint;
|
|||
|
RPC_CHAR * StringBinding;
|
|||
|
|
|||
|
ClientAddress = (RPC_CHAR *) _alloca(sizeof(RPC_CHAR) * Connection->TransportInterface->AddressStringSize);
|
|||
|
if (!ClientAddress)
|
|||
|
{
|
|||
|
return RPC_S_OUT_OF_MEMORY;
|
|||
|
}
|
|||
|
|
|||
|
Status = Connection->TransportInterface->QueryAddress(RemoteAddress, ClientAddress);
|
|||
|
if ( Status != RPC_S_OK )
|
|||
|
{
|
|||
|
return(Status);
|
|||
|
}
|
|||
|
|
|||
|
if (IncludeEndpoint)
|
|||
|
{
|
|||
|
ClientEndpoint = (RPC_CHAR *) _alloca(sizeof(RPC_CHAR) * Connection->TransportInterface->EndpointStringSize);
|
|||
|
if (!ClientEndpoint)
|
|||
|
{
|
|||
|
return RPC_S_OUT_OF_MEMORY;
|
|||
|
}
|
|||
|
|
|||
|
Status = Connection->TransportInterface->QueryEndpoint(RemoteAddress, ClientEndpoint);
|
|||
|
if ( Status != RPC_S_OK )
|
|||
|
{
|
|||
|
return(Status);
|
|||
|
}
|
|||
|
|
|||
|
Status = RpcStringBindingCompose(0,
|
|||
|
Connection->pAddress->InqRpcProtocolSequence(),
|
|||
|
ClientAddress,
|
|||
|
ClientEndpoint,
|
|||
|
0,
|
|||
|
&StringBinding
|
|||
|
);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Status = RpcStringBindingCompose(0,
|
|||
|
Connection->pAddress->InqRpcProtocolSequence(),
|
|||
|
ClientAddress,
|
|||
|
0,
|
|||
|
0,
|
|||
|
&StringBinding
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
if ( Status != RPC_S_OK )
|
|||
|
{
|
|||
|
return(Status);
|
|||
|
}
|
|||
|
|
|||
|
Status = RpcBindingFromStringBinding(StringBinding, pServerBinding);
|
|||
|
if (RPC_S_OK == Status)
|
|||
|
{
|
|||
|
Status = RpcBindingSetObject(*pServerBinding,
|
|||
|
(UUID *) &pSavedPacket->Header.ObjectId
|
|||
|
);
|
|||
|
|
|||
|
RpcStringFree(&StringBinding);
|
|||
|
}
|
|||
|
|
|||
|
return(Status);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
RPC_STATUS
|
|||
|
DG_SCALL::ConvertSidToUserW(
|
|||
|
IN SID *pSid,
|
|||
|
OUT RPC_CHAR **ppwsPrincipal
|
|||
|
)
|
|||
|
{
|
|||
|
// Called by InquireAuthClient to convert the SID passed up by the
|
|||
|
// client to a "Domain\User" string. The DG_SCALL cashes the last
|
|||
|
// SID (pCachedSid). If the new SID to convert matches the cashed
|
|||
|
// SID then just return that one (don't have to hit the domain server).
|
|||
|
//
|
|||
|
#define MAX_USERNAME_SIZE 256
|
|||
|
#define MAX_DOMAIN_SIZE 256
|
|||
|
RPC_STATUS Status = RPC_S_OK;
|
|||
|
WCHAR wsName[MAX_USERNAME_SIZE];
|
|||
|
WCHAR wsDomain[MAX_DOMAIN_SIZE];
|
|||
|
DWORD dwNameSize;
|
|||
|
DWORD dwDomainSize;
|
|||
|
DWORD dwSize;
|
|||
|
SID_NAME_USE sidType;
|
|||
|
|
|||
|
|
|||
|
if ( (pCachedSid) && (pSid)
|
|||
|
&& (IsValidSid(pSid)) && (EqualSid(pCachedSid,pSid)) )
|
|||
|
{
|
|||
|
//
|
|||
|
// Great! SIDs match...
|
|||
|
//
|
|||
|
#ifdef DBG
|
|||
|
DbgPrint("DG_SCALL::ConvertSidToUserW(): Cash hit.\n");
|
|||
|
#endif
|
|||
|
*ppwsPrincipal = (RPC_CHAR*)I_RpcAllocate(dwCachedUserNameSize);
|
|||
|
if (*ppwsPrincipal)
|
|||
|
RpcpStringCopy(*ppwsPrincipal,pwsCachedUserName);
|
|||
|
else
|
|||
|
Status = RPC_S_OUT_OF_MEMORY;
|
|||
|
}
|
|||
|
else if ( (pSid) && (IsValidSid(pSid)) )
|
|||
|
{
|
|||
|
dwNameSize = sizeof(wsName);
|
|||
|
dwDomainSize = sizeof(wsDomain);
|
|||
|
|
|||
|
if (LookupAccountSidW( NULL,
|
|||
|
pSid,
|
|||
|
wsName,
|
|||
|
&dwNameSize,
|
|||
|
wsDomain,
|
|||
|
&dwDomainSize,
|
|||
|
&sidType) )
|
|||
|
{
|
|||
|
//
|
|||
|
// Got a new SID, remember it for next time.
|
|||
|
//
|
|||
|
*ppwsPrincipal = 0;
|
|||
|
|
|||
|
if (pCachedSid)
|
|||
|
{
|
|||
|
I_RpcFree(pCachedSid);
|
|||
|
pCachedSid = 0;
|
|||
|
}
|
|||
|
|
|||
|
if (pwsCachedUserName)
|
|||
|
{
|
|||
|
I_RpcFree(pwsCachedUserName);
|
|||
|
pwsCachedUserName = 0;
|
|||
|
}
|
|||
|
|
|||
|
dwCachedUserNameSize = 0;
|
|||
|
|
|||
|
dwSize = GetLengthSid(pSid);
|
|||
|
pCachedSid = (SID*)I_RpcAllocate(dwSize);
|
|||
|
if (pCachedSid == 0)
|
|||
|
{
|
|||
|
return RPC_S_OUT_OF_MEMORY;
|
|||
|
}
|
|||
|
|
|||
|
if (!CopySid(dwSize,pCachedSid,pSid))
|
|||
|
{
|
|||
|
return RPC_S_OUT_OF_MEMORY;
|
|||
|
}
|
|||
|
|
|||
|
dwSize = sizeof(RPC_CHAR) * (2 + wcslen(wsName) + wcslen(wsDomain));
|
|||
|
pwsCachedUserName = (RPC_CHAR*)I_RpcAllocate(dwSize);
|
|||
|
if (pwsCachedUserName == 0)
|
|||
|
{
|
|||
|
return RPC_S_OUT_OF_MEMORY;
|
|||
|
}
|
|||
|
|
|||
|
RpcpStringCopy(pwsCachedUserName,wsDomain);
|
|||
|
RpcpStringCat(pwsCachedUserName,RPC_CONST_STRING("\\"));
|
|||
|
RpcpStringCat(pwsCachedUserName,wsName);
|
|||
|
|
|||
|
*ppwsPrincipal = (RPC_CHAR*)I_RpcAllocate(dwSize);
|
|||
|
if (*ppwsPrincipal)
|
|||
|
{
|
|||
|
RpcpStringCopy(*ppwsPrincipal,pwsCachedUserName);
|
|||
|
dwCachedUserNameSize = dwSize;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
I_RpcFree(pCachedSid);
|
|||
|
pCachedSid = 0;
|
|||
|
dwCachedUserNameSize = 0;
|
|||
|
Status = RPC_S_OUT_OF_MEMORY;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//
|
|||
|
// LookupAccountSidW() failed. Lookup its error and return.
|
|||
|
//
|
|||
|
Status = GetLastError();
|
|||
|
*ppwsPrincipal = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
#ifdef DBG
|
|||
|
DbgPrint("DG_SCALL::ConvertSidToUserW(): No SID to convert.\n");
|
|||
|
#endif
|
|||
|
*ppwsPrincipal = 0;
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DG_SCALL *
|
|||
|
DG_SCONNECTION::RemoveIdleCalls(
|
|||
|
DG_SCALL * List,
|
|||
|
BOOL Aggressive,
|
|||
|
RPC_INTERFACE * Interface
|
|||
|
)
|
|||
|
{
|
|||
|
if (TRUE == Mutex.TryRequest())
|
|||
|
{
|
|||
|
if (BlockIdleCallRemoval)
|
|||
|
{
|
|||
|
Mutex.Clear();
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
long CurrentTime = GetTickCount();
|
|||
|
|
|||
|
PDG_SCALL Node = 0;
|
|||
|
|
|||
|
if (CachedCalls)
|
|||
|
{
|
|||
|
for (Node = CachedCalls->Next; Node; Node = Node->Next)
|
|||
|
{
|
|||
|
ASSERT( !Node->InvalidHandle(DG_SCALL_TYPE) );
|
|||
|
ASSERT(Node->TimeStamp != 0x31415926);
|
|||
|
|
|||
|
if (CurrentTime - Node->TimeStamp > IDLE_SCALL_LIFETIME)
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
ActiveCalls.RemoveIdleCalls(Aggressive, Interface);
|
|||
|
|
|||
|
if (Node)
|
|||
|
{
|
|||
|
PDG_SCALL Next = Node->Next;
|
|||
|
Node->Next = 0;
|
|||
|
Node = Next;
|
|||
|
}
|
|||
|
|
|||
|
Mutex.Clear();
|
|||
|
|
|||
|
while (Node)
|
|||
|
{
|
|||
|
ASSERT( !Node->InvalidHandle(DG_SCALL_TYPE) );
|
|||
|
ASSERT(Node->TimeStamp != 0x31415926);
|
|||
|
|
|||
|
PDG_SCALL Next = Node->Next;
|
|||
|
|
|||
|
Node->Next = List;
|
|||
|
List = Node;
|
|||
|
Node = Next;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return List;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
DG_SCONNECTION::HasExpired()
|
|||
|
{
|
|||
|
if (GetTickCount() - TimeStamp > FIVE_MINUTES_IN_MSEC &&
|
|||
|
0 == ReferenceCount)
|
|||
|
{
|
|||
|
CLAIM_MUTEX Lock(Mutex);
|
|||
|
|
|||
|
if (GetTickCount() - TimeStamp > FIVE_MINUTES_IN_MSEC &&
|
|||
|
0 == ReferenceCount)
|
|||
|
{
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
DG_SCALL::HasExpired(
|
|||
|
BOOL Aggressive,
|
|||
|
RPC_INTERFACE * ExpiringInterface
|
|||
|
)
|
|||
|
{
|
|||
|
long CurrentTime = CurrentTimeInMsec();
|
|||
|
|
|||
|
if (CurrentTime - TimeStamp < 40 * 1000 )
|
|||
|
{
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
AsyncStatus = RPC_S_CALL_FAILED;
|
|||
|
|
|||
|
if (PipeWaitType != PWT_NONE)
|
|||
|
{
|
|||
|
#ifdef DEBUGRPC
|
|||
|
DbgPrint("RPC DG: aborting an abandoned pipe operation on %x\n", this);
|
|||
|
#endif
|
|||
|
FinishSendOrReceive(TRUE);
|
|||
|
}
|
|||
|
else if (State == CallDispatched ||
|
|||
|
State == CallAfterDispatch )
|
|||
|
{
|
|||
|
#ifdef DEBUGRPC
|
|||
|
|
|||
|
DbgPrint("RPC DG: possible blockage: scall %lx has been dispatched for %u seconds\n",
|
|||
|
this, (CurrentTime - TimeStamp)/1000 );
|
|||
|
#endif
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
return Cleanup();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
PDG_SCONNECTION
|
|||
|
SERVER_ACTIVITY_TABLE::FindOrCreate(
|
|||
|
DG_ADDRESS * Address,
|
|||
|
DG_PACKET * Packet
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Looks for connection matching the packet's activity uuid. If the activity
|
|||
|
hint is not 0xffff, we search in that hash bucket, otherwise we create
|
|||
|
a hash value and look there.
|
|||
|
|
|||
|
If a connection is found or created, its mutex is taken.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pPacket - data to find call (activity uuid and activity hint)
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
the new call, or zero
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
unsigned Hash = Packet->Header.ActivityHint;
|
|||
|
|
|||
|
if (Hash == 0xffff)
|
|||
|
{
|
|||
|
Hash = MakeHash(&Packet->Header.ActivityId);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// This can happen only if someone is sending garbage.
|
|||
|
//
|
|||
|
if (Hash >= BUCKET_COUNT)
|
|||
|
{
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
RequestHashMutex(Hash);
|
|||
|
|
|||
|
PDG_SCONNECTION Connection = 0;
|
|||
|
UUID_HASH_TABLE_NODE * pNode = UUID_HASH_TABLE::Lookup(
|
|||
|
&Packet->Header.ActivityId,
|
|||
|
Hash
|
|||
|
);
|
|||
|
if (pNode)
|
|||
|
{
|
|||
|
Connection = DG_SCONNECTION::FromHashNode(pNode);
|
|||
|
|
|||
|
if (FALSE == Connection->Mutex.TryRequest())
|
|||
|
{
|
|||
|
Connection->TimeStamp = GetTickCount();
|
|||
|
ReleaseHashMutex(Hash);
|
|||
|
|
|||
|
// If the connection is busy more than five minutes,
|
|||
|
// then the connection will go away and this will crash.
|
|||
|
//
|
|||
|
Connection->Mutex.Request();
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
ReleaseHashMutex(Hash);
|
|||
|
}
|
|||
|
}
|
|||
|
else if (Packet->Header.PacketType == DG_REQUEST)
|
|||
|
{
|
|||
|
if ((Packet->Header.PacketFlags & DG_PF_FORWARDED) &&
|
|||
|
(Packet->Header.PacketFlags2 & DG_PF2_FORWARDED_2) == 0)
|
|||
|
{
|
|||
|
//
|
|||
|
// This packet doesn't have correct auth info. We can't
|
|||
|
// instantiate a connection without it.
|
|||
|
//
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Connection = Address->AllocateConnection();
|
|||
|
if (Connection)
|
|||
|
{
|
|||
|
ServerConnections->BeginIdlePruning();
|
|||
|
Connection->Activate(&Packet->Header, (unsigned short) Hash);
|
|||
|
Connection->Mutex.Request();
|
|||
|
UUID_HASH_TABLE::Add(&Connection->ActivityNode, Hash);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
ReleaseHashMutex(Hash);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
ReleaseHashMutex(Hash);
|
|||
|
}
|
|||
|
|
|||
|
return Connection;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
void SERVER_ACTIVITY_TABLE::Prune()
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Remove idle connections and calls from the activity table.
|
|||
|
Each call to this fn cleans one bucket.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
unsigned Bucket;
|
|||
|
long CurrentTime = GetTickCount();
|
|||
|
|
|||
|
//
|
|||
|
// Don't check the whole table too often.
|
|||
|
//
|
|||
|
if (CurrentTime - LastFinishTime < IDLE_SCONNECTION_SWEEP_INTERVAL)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
Bucket = InterlockedIncrement(&BucketCounter) % BUCKET_COUNT;
|
|||
|
|
|||
|
PruneSpecificBucket(Bucket, FALSE, 0);
|
|||
|
|
|||
|
if (Bucket == BUCKET_COUNT-1)
|
|||
|
{
|
|||
|
LastFinishTime = CurrentTime;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void
|
|||
|
SERVER_ACTIVITY_TABLE::BeginIdlePruning()
|
|||
|
{
|
|||
|
if (!fPruning)
|
|||
|
{
|
|||
|
if (InterlockedIncrement(&fPruning) > 1)
|
|||
|
{
|
|||
|
InterlockedDecrement(&fPruning);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
IdleScavengerTimer.Initialize( PruneWhileIdle, 0 );
|
|||
|
DelayedProcedures->Add(&IdleScavengerTimer, IDLE_SCONNECTION_SWEEP_INTERVAL, FALSE);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void
|
|||
|
SERVER_ACTIVITY_TABLE::PruneWhileIdle( PVOID unused )
|
|||
|
{
|
|||
|
if (GetTickCount() - ServerConnections->LastFinishTime > IDLE_SCONNECTION_SWEEP_INTERVAL)
|
|||
|
{
|
|||
|
BOOL fEmpty = ServerConnections->PruneEntireTable( 0 );
|
|||
|
if (fEmpty)
|
|||
|
{
|
|||
|
InterlockedDecrement(&fPruning);
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
DelayedProcedures->Add(&ServerConnections->IdleScavengerTimer, IDLE_SCONNECTION_SWEEP_INTERVAL+5000, FALSE);
|
|||
|
}
|
|||
|
|
|||
|
BOOL
|
|||
|
SERVER_ACTIVITY_TABLE::PruneEntireTable(
|
|||
|
RPC_INTERFACE * Interface
|
|||
|
)
|
|||
|
{
|
|||
|
BOOL Empty = TRUE;
|
|||
|
unsigned Bucket;
|
|||
|
|
|||
|
//
|
|||
|
// Make sure this thread has an associated THREAD.
|
|||
|
// This won't be true for the RpcServerListen thread.
|
|||
|
//
|
|||
|
ThreadSelf();
|
|||
|
|
|||
|
for (Bucket = 0; Bucket < BUCKET_COUNT; ++Bucket)
|
|||
|
{
|
|||
|
Empty &= PruneSpecificBucket( Bucket, TRUE, Interface );
|
|||
|
}
|
|||
|
|
|||
|
LastFinishTime = GetTickCount();
|
|||
|
|
|||
|
return Empty;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
SERVER_ACTIVITY_TABLE::PruneSpecificBucket(
|
|||
|
unsigned Bucket,
|
|||
|
BOOL Aggressive,
|
|||
|
RPC_INTERFACE * Interface
|
|||
|
)
|
|||
|
{
|
|||
|
if (!Buckets[Bucket])
|
|||
|
{
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Scan the bucket for idle calls and connections.
|
|||
|
// Remove them from the table, but don't delete them.
|
|||
|
//
|
|||
|
DG_SCALL * IdleCalls = 0;
|
|||
|
|
|||
|
UUID_HASH_TABLE_NODE * pNode;
|
|||
|
UUID_HASH_TABLE_NODE * IdleConnections = 0;
|
|||
|
|
|||
|
unsigned Inactive = 0;
|
|||
|
|
|||
|
RequestHashMutex(Bucket);
|
|||
|
|
|||
|
pNode = Buckets[Bucket];
|
|||
|
while (pNode)
|
|||
|
{
|
|||
|
UUID_HASH_TABLE_NODE * pNext = pNode->pNext;
|
|||
|
|
|||
|
DG_SCONNECTION * Connection = DG_SCONNECTION::FromHashNode(pNode);
|
|||
|
|
|||
|
IdleCalls = Connection->RemoveIdleCalls( IdleCalls, Aggressive, Interface );
|
|||
|
|
|||
|
if (Connection->HasExpired())
|
|||
|
{
|
|||
|
UUID_HASH_TABLE::Remove(pNode, Bucket);
|
|||
|
|
|||
|
pNode->pNext = IdleConnections;
|
|||
|
IdleConnections = pNode;
|
|||
|
}
|
|||
|
|
|||
|
pNode = pNext;
|
|||
|
}
|
|||
|
|
|||
|
BOOL Empty = TRUE;
|
|||
|
|
|||
|
if (Buckets[Bucket])
|
|||
|
{
|
|||
|
Empty = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
ReleaseHashMutex(Bucket);
|
|||
|
|
|||
|
//
|
|||
|
// Now delete the idle objects.
|
|||
|
//
|
|||
|
while (IdleCalls)
|
|||
|
{
|
|||
|
++Inactive;
|
|||
|
|
|||
|
DG_SCALL * Call = IdleCalls;
|
|||
|
IdleCalls = Call->Next;
|
|||
|
|
|||
|
delete Call;
|
|||
|
}
|
|||
|
|
|||
|
while (IdleConnections)
|
|||
|
{
|
|||
|
DG_SCONNECTION * Connection = DG_SCONNECTION::FromHashNode(IdleConnections);
|
|||
|
|
|||
|
IdleConnections = IdleConnections->pNext;
|
|||
|
|
|||
|
Connection->Deactivate();
|
|||
|
}
|
|||
|
|
|||
|
LogEvent(SU_STABLE, EV_PRUNE, IntToPtr(Bucket), 0, Inactive);
|
|||
|
|
|||
|
return Empty;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
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);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
inline RPC_STATUS
|
|||
|
DG_SCONNECTION::CreateCallbackBindingAndReleaseMutex(
|
|||
|
DG_TRANSPORT_ADDRESS RemoteAddress
|
|||
|
)
|
|||
|
{
|
|||
|
RPC_STATUS Status;
|
|||
|
|
|||
|
RPC_CHAR * StringBinding;
|
|||
|
RPC_CHAR * Address;
|
|||
|
RPC_CHAR * Endpoint;
|
|||
|
|
|||
|
unsigned Length;
|
|||
|
unsigned AddressLength;
|
|||
|
unsigned EndpointLength;
|
|||
|
unsigned ProtocolLength;
|
|||
|
|
|||
|
Callback.Binding = 0;
|
|||
|
|
|||
|
Address = (RPC_CHAR *) _alloca(sizeof(RPC_CHAR) * pAddress->Endpoint.TransportInterface->AddressStringSize);
|
|||
|
Endpoint = (RPC_CHAR *) _alloca(sizeof(RPC_CHAR) * pAddress->Endpoint.TransportInterface->EndpointStringSize);
|
|||
|
|
|||
|
if (!Address || !Endpoint)
|
|||
|
{
|
|||
|
Mutex.Clear();
|
|||
|
return RPC_S_OUT_OF_MEMORY;
|
|||
|
}
|
|||
|
|
|||
|
Status = pAddress->Endpoint.TransportInterface->QueryAddress(RemoteAddress, Address);
|
|||
|
if ( Status != RPC_S_OK )
|
|||
|
{
|
|||
|
Mutex.Clear();
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
Status = pAddress->Endpoint.TransportInterface->QueryEndpoint(RemoteAddress, Endpoint);
|
|||
|
if ( Status != RPC_S_OK )
|
|||
|
{
|
|||
|
Mutex.Clear();
|
|||
|
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)
|
|||
|
{
|
|||
|
Mutex.Clear();
|
|||
|
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.
|
|||
|
//
|
|||
|
IncrementRefCount();
|
|||
|
Mutex.Clear();
|
|||
|
|
|||
|
pAddress->CheckThreadPool();
|
|||
|
|
|||
|
//
|
|||
|
// 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.
|
|||
|
//
|
|||
|
Status = RpcBindingFromStringBinding(StringBinding, &Callback.Binding);
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
inline void
|
|||
|
DG_SCONNECTION::SubmitCallbackIfNecessary(
|
|||
|
PDG_SCALL Call,
|
|||
|
PDG_PACKET Packet,
|
|||
|
DG_TRANSPORT_ADDRESS RemoteAddress
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
doc:callback
|
|||
|
|
|||
|
This function determines whether the server should issue a conv callback
|
|||
|
to the client, and submits an async call if so.
|
|||
|
|
|||
|
- The first non-idempotent call on a given activity ID requires a callback
|
|||
|
in order to guarantee the call wasn't executed by a prior server
|
|||
|
instance. If the activity stays quiet for 5 minutes and the
|
|||
|
DG_SCONNECTION is thrown away, the next call will force another callback.
|
|||
|
|
|||
|
- Each security context requires a callback; this allows the cleint to
|
|||
|
transfer the session key securely to the server.
|
|||
|
|
|||
|
In NT 5.0 beta 1:
|
|||
|
|
|||
|
This function submits callbacks from the 'msconv' interface because that
|
|||
|
notifies an NT5 or better client that the server supports the
|
|||
|
DG_PF2_UNRELATED bit to overlap calls.
|
|||
|
|
|||
|
In NT 5 beta 2:
|
|||
|
|
|||
|
The callback request is sent with the DG_PF2_UNRELATED bit set, even
|
|||
|
though it doesn'nt need to be set. That tells teh client that overlapped
|
|||
|
calls are OK.
|
|||
|
|
|||
|
If ms_conv_who_are_you_auth fails, the server will try conv_who_are_you_auth.
|
|||
|
|
|||
|
If ms_conv_who_are_you2 fails, the server will try conv_who_are_you2, which
|
|||
|
is supported by NT 3.51 and NT 4.0, as well as all current DCE systems;
|
|||
|
if that fails, we try conv_who_are_you which is all that NT 3.5 and the old
|
|||
|
UNIX NCA servers support. I doubt that we have ever tested interop with NCA...
|
|||
|
|
|||
|
A connection is allowed only a single oustanding callback at a time. This
|
|||
|
should not be a significant restriction.
|
|||
|
|
|||
|
The rule is that a thread must hold the connection mutex if it wants to
|
|||
|
change Callback.State from NoCallbackAttempted to any other value. It can
|
|||
|
then release the mutex and party on the other members of Callback, since
|
|||
|
all other instances of this procedure will exit immediately
|
|||
|
if Callback.State != NoCallbackAttempted.
|
|||
|
|
|||
|
In a nutshell, this function
|
|||
|
- creates a binding handle to the client,
|
|||
|
- releases the connection mutex,
|
|||
|
- fills Callback.* with the info needed by FinishConvCallback(), and
|
|||
|
- calls an conv or msconv stub
|
|||
|
|
|||
|
When the async call is complete (or fails), some thread will call
|
|||
|
ConvNotificationRoutine() which will either submit another async call
|
|||
|
or call FinishConvCallback() to mop up. For a successful secure callback,
|
|||
|
FinishConvCallback() will also call AcceptThirdLeg and then set
|
|||
|
ActiveSeurityContext to be the newly created context. For a failed
|
|||
|
callback, FinishConvCallback will notify the DG_SCALL that started
|
|||
|
all this, so it can send a REJECT to the client.
|
|||
|
|
|||
|
A wrinkle is that two objects need access to Packet: the DG_SCALL
|
|||
|
is using it as [in] stub data, and this fn uses it for AcceptFirstTime.
|
|||
|
So future code shuffling needs to make sure the call doesn't die during
|
|||
|
a callback, and that the call doesn't delete the packet until
|
|||
|
AcceptFirstTime is complete.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
RPC_STATUS Status = RPC_S_OK;
|
|||
|
|
|||
|
//
|
|||
|
// Has a callback already been attempted?
|
|||
|
//
|
|||
|
if (Callback.State != NoCallbackAttempted)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Is a callback unnecessary?
|
|||
|
//
|
|||
|
DG_SECURITY_TRAILER * Verifier = (DG_SECURITY_TRAILER *)
|
|||
|
(Packet->Header.Data + Packet->GetPacketBodyLen());
|
|||
|
|
|||
|
unsigned char KeySequenceNumber = Verifier->key_vers_num;
|
|||
|
|
|||
|
if (Packet->Header.AuthProto == 0)
|
|||
|
{
|
|||
|
if (pAssocGroup ||
|
|||
|
(Packet->Header.PacketFlags & DG_PF_IDEMPOTENT))
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//
|
|||
|
// See if we have already established a security context
|
|||
|
// for this client.
|
|||
|
//
|
|||
|
SECURITY_CONTEXT * CurrentContext = SecurityContextDict.Find(KeySequenceNumber);
|
|||
|
if (CurrentContext)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// A callback is needed.
|
|||
|
//
|
|||
|
Callback.State = SetupInProgress;
|
|||
|
Callback.Call = Call;
|
|||
|
Callback.Binding = 0;
|
|||
|
Callback.Credentials = 0;
|
|||
|
Callback.TokenBuffer = 0;
|
|||
|
Callback.ResponseBuffer = 0;
|
|||
|
Callback.SecurityContext = 0;
|
|||
|
Callback.ThirdLegNeeded = FALSE;
|
|||
|
Callback.DataRep = 0x00ffffff & (*(unsigned long *) Packet->Header.DataRep);
|
|||
|
Callback.KeySequence = KeySequenceNumber;
|
|||
|
|
|||
|
Callback.Call->IncrementRefCount();
|
|||
|
|
|||
|
Status = CreateCallbackBindingAndReleaseMutex( RemoteAddress );
|
|||
|
|
|||
|
if (Status)
|
|||
|
{
|
|||
|
FinishConvCallback(Status);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
Status = RpcAsyncInitializeHandle(&Callback.AsyncState, sizeof(Callback.AsyncState));
|
|||
|
if (Status)
|
|||
|
{
|
|||
|
FinishConvCallback(Status);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
Callback.AsyncState.NotificationType = RpcNotificationTypeCallback;
|
|||
|
Callback.AsyncState.u.NotificationRoutine = ConvNotificationRoutine;
|
|||
|
Callback.AsyncState.UserInfo = this;
|
|||
|
|
|||
|
if (Packet->Header.AuthProto)
|
|||
|
{
|
|||
|
unsigned long AuthenticationService = Packet->Header.AuthProto;
|
|||
|
unsigned long AuthenticationLevel = Verifier->protection_level;
|
|||
|
|
|||
|
//
|
|||
|
// 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;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Create an empty security context.
|
|||
|
//
|
|||
|
CLIENT_AUTH_INFO Info;
|
|||
|
|
|||
|
Info.AuthenticationService = AuthenticationService;
|
|||
|
Info.AuthenticationLevel = AuthenticationLevel;
|
|||
|
Info.ServerPrincipalName = 0;
|
|||
|
Info.PacHandle = 0;
|
|||
|
Info.AuthorizationService= 0;
|
|||
|
|
|||
|
Callback.SecurityContext = new SECURITY_CONTEXT(&Info, KeySequenceNumber, TRUE, &Status);
|
|||
|
|
|||
|
if (!Callback.SecurityContext)
|
|||
|
{
|
|||
|
Status = RPC_S_OUT_OF_MEMORY;
|
|||
|
}
|
|||
|
|
|||
|
CallTestHook( TH_RPC_SECURITY_SERVER_CONTEXT_CREATED, Callback.SecurityContext, this );
|
|||
|
|
|||
|
if (Status)
|
|||
|
{
|
|||
|
FinishConvCallback(Status);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get my security credentials.
|
|||
|
//
|
|||
|
Status = pAddress->Server->AcquireCredentials(
|
|||
|
AuthenticationService,
|
|||
|
AuthenticationLevel,
|
|||
|
&Callback.Credentials
|
|||
|
);
|
|||
|
if (Status)
|
|||
|
{
|
|||
|
LogError(SU_SCONN, EV_STATUS, this, (void *) 100, Status );
|
|||
|
FinishConvCallback(Status);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Allocate challenge and response buffers.
|
|||
|
//
|
|||
|
Callback.TokenLength = Callback.Credentials->MaximumTokenLength();
|
|||
|
|
|||
|
Callback.TokenBuffer = new unsigned char [2 * Callback.TokenLength];
|
|||
|
Callback.ResponseBuffer = Callback.TokenBuffer + Callback.TokenLength;
|
|||
|
|
|||
|
if (!Callback.TokenBuffer)
|
|||
|
{
|
|||
|
FinishConvCallback(RPC_S_OUT_OF_MEMORY);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// 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 = &Packet->Header;
|
|||
|
InputBuffers[0].cbBuffer = sizeof(NCA_PACKET_HEADER);
|
|||
|
|
|||
|
InputBuffers[1].BufferType = SECBUFFER_DATA | SECBUFFER_READONLY;
|
|||
|
InputBuffers[1].pvBuffer = Packet->Header.Data;
|
|||
|
InputBuffers[1].cbBuffer = Packet->GetPacketBodyLen();
|
|||
|
|
|||
|
InputBuffers[2].BufferType = SECBUFFER_TOKEN;
|
|||
|
InputBuffers[2].pvBuffer = Verifier;
|
|||
|
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 = Callback.TokenBuffer;
|
|||
|
OutputBuffers[2].cbBuffer = Callback.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;
|
|||
|
DceInitSecurityInfo.DceSecurityInfo.AssociationUuid = Packet->Header.ActivityId;
|
|||
|
|
|||
|
Status = Callback.SecurityContext->AcceptFirstTime(
|
|||
|
Callback.Credentials,
|
|||
|
&InputBufferDescriptor,
|
|||
|
&OutputBufferDescriptor,
|
|||
|
AuthenticationLevel,
|
|||
|
Callback.DataRep,
|
|||
|
FALSE
|
|||
|
);
|
|||
|
LogEvent( SU_SCONN, EV_SEC_ACCEPT1, this, IntToPtr(Status), I_RpcGetExtendedError());
|
|||
|
|
|||
|
switch (Status)
|
|||
|
{
|
|||
|
case RPC_S_OK:
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case RPC_P_CONTINUE_NEEDED:
|
|||
|
{
|
|||
|
Callback.ThirdLegNeeded = TRUE;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case RPC_P_COMPLETE_NEEDED:
|
|||
|
{
|
|||
|
#ifdef DEBUGRPC
|
|||
|
DbgPrint("RPC: dg does not support RPC_P_COMPLETE_NEEDED from AcceptSecurityContext\n");
|
|||
|
#endif
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case RPC_P_COMPLETE_AND_CONTINUE:
|
|||
|
{
|
|||
|
Callback.ThirdLegNeeded = TRUE;
|
|||
|
#ifdef DEBUGRPC
|
|||
|
DbgPrint("RPC: dg does not support RPC_P_COMPLETE_AND_CONTINUE from AcceptSecurityContext\n");
|
|||
|
#endif
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
default:
|
|||
|
{
|
|||
|
LogError( SU_SCONN, EV_SEC_ACCEPT1, this, IntToPtr(Status), I_RpcGetExtendedError());
|
|||
|
FinishConvCallback(Status);
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Callback.TokenLength = OutputBuffers[2].cbBuffer;
|
|||
|
|
|||
|
Callback.State = ConvWayAuthInProgress;
|
|||
|
Callback.DataIndex = 0;
|
|||
|
Callback.Status = 0;
|
|||
|
|
|||
|
_conv_who_are_you_auth(
|
|||
|
&Callback.AsyncState,
|
|||
|
Callback.Binding,
|
|||
|
(UUID *) &ActivityNode.Uuid,
|
|||
|
ProcessStartTime,
|
|||
|
Callback.TokenBuffer,
|
|||
|
Callback.TokenLength,
|
|||
|
Callback.Credentials->MaximumTokenLength(),
|
|||
|
&Callback.ClientSequence,
|
|||
|
&Callback.CasUuid,
|
|||
|
Callback.ResponseBuffer,
|
|||
|
&Callback.ResponseLength,
|
|||
|
&Callback.Status
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// We have to take the mutex before leaving this fn, regardless of error.
|
|||
|
// Do it here so the check of Callback.State is protected.
|
|||
|
//
|
|||
|
Mutex.Request();
|
|||
|
|
|||
|
//
|
|||
|
// If there is an error, see whether it is caused a notification. If so,
|
|||
|
// ConvCallCompleted() has changed the callback state and this thread should do nothing.
|
|||
|
// If the state is unchanged, this thread must clean up.
|
|||
|
//
|
|||
|
if (Callback.Status && Callback.State == ConvWayAuthInProgress)
|
|||
|
{
|
|||
|
FinishConvCallback(Callback.Status);
|
|||
|
|
|||
|
//
|
|||
|
// The mutex was recursively claimed by FinishConvCallback().
|
|||
|
// Get rid of the recursive one.
|
|||
|
//
|
|||
|
Mutex.Clear();
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
BlockIdleCallRemoval = FALSE;
|
|||
|
|
|||
|
Callback.State = ConvWay2InProgress;
|
|||
|
Callback.Status = 0;
|
|||
|
|
|||
|
_conv_who_are_you2(
|
|||
|
&Callback.AsyncState,
|
|||
|
Callback.Binding,
|
|||
|
(UUID *) &ActivityNode.Uuid,
|
|||
|
ProcessStartTime,
|
|||
|
&Callback.ClientSequence,
|
|||
|
&Callback.CasUuid,
|
|||
|
&Callback.Status
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// We have to take the mutex before leaving this fn, regardless of error.
|
|||
|
// Do it here so the check of Callback.State is protected.
|
|||
|
//
|
|||
|
Mutex.Request();
|
|||
|
|
|||
|
//
|
|||
|
// If there is an error, see whether it is caused a notification. If so,
|
|||
|
// ConvCallCompleted() has changed the callback state and this thread should do nothing.
|
|||
|
// If the state is unchanged, this thread must clean up.
|
|||
|
//
|
|||
|
if (Callback.Status && Callback.State == ConvWay2InProgress)
|
|||
|
{
|
|||
|
FinishConvCallback(Callback.Status);
|
|||
|
|
|||
|
//
|
|||
|
// The mutex was recursively claimed by FinishConvCallback().
|
|||
|
// Get rid of the recursive one.
|
|||
|
//
|
|||
|
Mutex.Clear();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Mutex.VerifyOwned();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
RPC_STATUS
|
|||
|
DG_SCONNECTION::FinishConvCallback(
|
|||
|
RPC_STATUS Status
|
|||
|
)
|
|||
|
{
|
|||
|
BlockIdleCallRemoval = FALSE;
|
|||
|
|
|||
|
if (!Status && Callback.ThirdLegNeeded )
|
|||
|
{
|
|||
|
#ifdef TRY_MSCONV_INTERFACE
|
|||
|
|
|||
|
ASSERT( Callback.State == ConvWayAuthInProgress ||
|
|||
|
Callback.State == MsConvWayAuthInProgress );
|
|||
|
|
|||
|
#else
|
|||
|
|
|||
|
ASSERT( Callback.State == ConvWayAuthInProgress ||
|
|||
|
Callback.State == ConvWayAuthMoreInProgress );
|
|||
|
|
|||
|
#endif
|
|||
|
//
|
|||
|
// Give the challenge response to the security package.
|
|||
|
//
|
|||
|
DCE_INIT_SECURITY_INFO DceInitSecurityInfo;
|
|||
|
|
|||
|
SECURITY_BUFFER_DESCRIPTOR InputBufferDescriptor;
|
|||
|
SECURITY_BUFFER_DESCRIPTOR OutputBufferDescriptor;
|
|||
|
SECURITY_BUFFER InputBuffers[4];
|
|||
|
SECURITY_BUFFER OutputBuffers[4];
|
|||
|
|
|||
|
InputBufferDescriptor.ulVersion = 0;
|
|||
|
InputBufferDescriptor.cBuffers = 4;
|
|||
|
InputBufferDescriptor.pBuffers = InputBuffers;
|
|||
|
|
|||
|
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 = Callback.ResponseBuffer;
|
|||
|
InputBuffers[2].cbBuffer = Callback.DataIndex;
|
|||
|
|
|||
|
InputBuffers[3].BufferType = SECBUFFER_PKG_PARAMS | SECBUFFER_READONLY;
|
|||
|
InputBuffers[3].pvBuffer = &DceInitSecurityInfo;
|
|||
|
InputBuffers[3].cbBuffer = sizeof(DCE_INIT_SECURITY_INFO);
|
|||
|
|
|||
|
DceInitSecurityInfo.PacketType = ~0;
|
|||
|
DceInitSecurityInfo.AuthorizationService = ~0;
|
|||
|
DceInitSecurityInfo.DceSecurityInfo.SendSequenceNumber = ~0;
|
|||
|
DceInitSecurityInfo.DceSecurityInfo.ReceiveSequenceNumber = Callback.KeySequence;
|
|||
|
DceInitSecurityInfo.DceSecurityInfo.AssociationUuid = ActivityNode.Uuid;
|
|||
|
|
|||
|
OutputBufferDescriptor.ulVersion = 0;
|
|||
|
OutputBufferDescriptor.cBuffers = 0;
|
|||
|
OutputBufferDescriptor.pBuffers = 0;
|
|||
|
|
|||
|
Status = Callback.SecurityContext->AcceptThirdLeg(
|
|||
|
Callback.DataRep,
|
|||
|
&InputBufferDescriptor,
|
|||
|
&OutputBufferDescriptor
|
|||
|
);
|
|||
|
|
|||
|
LogEvent( SU_SCONN, EV_SEC_ACCEPT3, this, IntToPtr(Status), I_RpcGetExtendedError());
|
|||
|
}
|
|||
|
|
|||
|
if (Callback.Binding)
|
|||
|
{
|
|||
|
RpcBindingFree(&Callback.Binding);
|
|||
|
}
|
|||
|
|
|||
|
delete Callback.TokenBuffer;
|
|||
|
|
|||
|
if (Callback.Credentials)
|
|||
|
{
|
|||
|
Callback.Credentials->DereferenceCredentials();
|
|||
|
}
|
|||
|
|
|||
|
if (RPC_S_OK == Status)
|
|||
|
{
|
|||
|
Mutex.Request();
|
|||
|
DecrementRefCount();
|
|||
|
|
|||
|
Callback.Call->DecrementRefCount();
|
|||
|
|
|||
|
if (0 == pAssocGroup)
|
|||
|
{
|
|||
|
ASSOCIATION_GROUP * Cas;
|
|||
|
|
|||
|
Cas = AssociationGroups->FindOrCreate(&Callback.CasUuid,
|
|||
|
(unsigned short) pAddress->Endpoint.TransportInterface->BasePduSize);
|
|||
|
if (0 == Cas)
|
|||
|
{
|
|||
|
// We want to retry later.
|
|||
|
Callback.State = NoCallbackAttempted;
|
|||
|
delete Callback.SecurityContext;
|
|||
|
return RPC_S_OUT_OF_MEMORY;
|
|||
|
}
|
|||
|
|
|||
|
pAssocGroup = Cas;
|
|||
|
}
|
|||
|
|
|||
|
if (Callback.SecurityContext)
|
|||
|
{
|
|||
|
SecurityContextDict.Insert(
|
|||
|
Callback.SecurityContext->AuthContextId,
|
|||
|
Callback.SecurityContext
|
|||
|
);
|
|||
|
|
|||
|
if (MaxKeySeq <= Callback.KeySequence)
|
|||
|
{
|
|||
|
MaxKeySeq = Callback.KeySequence;
|
|||
|
ActiveSecurityContext = Callback.SecurityContext;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
ASSERT( Callback.State != ConvWayAuthInProgress &&
|
|||
|
Callback.State != MsConvWayAuthInProgress );
|
|||
|
}
|
|||
|
|
|||
|
if (Callback.ClientSequence == CurrentCall->GetSequenceNumber())
|
|||
|
{
|
|||
|
Callback.State = CallbackSucceeded;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Callback.State = CallbackFailed;
|
|||
|
Status = RPC_S_CALL_FAILED;
|
|||
|
|
|||
|
PDG_SCALL Call = Callback.Call;
|
|||
|
do
|
|||
|
{
|
|||
|
PDG_SCALL Next;
|
|||
|
|
|||
|
Next = ActiveCalls.Successor(Call);
|
|||
|
Call->ConvCallbackFailed(Status);
|
|||
|
Call = Next;
|
|||
|
}
|
|||
|
while ( Call );
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
LogError( SU_SCONN, EV_SEC_ACCEPT3, this, IntToPtr(Status), I_RpcGetExtendedError());
|
|||
|
|
|||
|
delete Callback.SecurityContext;
|
|||
|
|
|||
|
Mutex.Request();
|
|||
|
DecrementRefCount();
|
|||
|
|
|||
|
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 (Status == RPC_S_OUT_OF_MEMORY ||
|
|||
|
Status == RPC_S_OUT_OF_RESOURCES )
|
|||
|
{
|
|||
|
Callback.State = NoCallbackAttempted;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Callback.State = CallbackFailed;
|
|||
|
}
|
|||
|
|
|||
|
Callback.Call->DecrementRefCount();
|
|||
|
|
|||
|
//
|
|||
|
// Map SSPI errors to access-denied. SSPI errors are facility code 0009 (see windows.h),
|
|||
|
// therefore all security errors are of the form 0x8009xxxx. (Again, see wondows.h)
|
|||
|
//
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
PDG_SCALL Call = Callback.Call;
|
|||
|
do
|
|||
|
{
|
|||
|
PDG_SCALL Next;
|
|||
|
|
|||
|
Next = ActiveCalls.Successor(Call);
|
|||
|
Call->ConvCallbackFailed(Status);
|
|||
|
Call = Next;
|
|||
|
}
|
|||
|
while ( Call );
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
void
|
|||
|
ConvCallCompletedWrapper(
|
|||
|
PVOID Connection
|
|||
|
)
|
|||
|
{
|
|||
|
PDG_SCONNECTION(Connection)->ConvCallCompleted();
|
|||
|
}
|
|||
|
|
|||
|
// it's a static procedure
|
|||
|
|
|||
|
void RPC_ENTRY
|
|||
|
DG_SCONNECTION::ConvNotificationRoutine (
|
|||
|
RPC_ASYNC_STATE * pAsync,
|
|||
|
void * Reserved,
|
|||
|
RPC_ASYNC_EVENT Event
|
|||
|
)
|
|||
|
{
|
|||
|
//
|
|||
|
// This function is called when the callback completes. The current thread
|
|||
|
// is holding the connection mutex for the callback, so we can't linger
|
|||
|
// in this procedure. The PostEvent call will cause all the expensive
|
|||
|
// operations to occur in a separate thread.
|
|||
|
//
|
|||
|
|
|||
|
PDG_SCONNECTION Connection = PDG_SCONNECTION(pAsync->UserInfo);
|
|||
|
|
|||
|
ASSERT( Event == RpcCallComplete );
|
|||
|
ASSERT( Connection->Callback.AsyncState.UserInfo == Connection );
|
|||
|
|
|||
|
Connection->pAddress->Endpoint.TransportInterface->PostEvent( DG_EVENT_CALLBACK_COMPLETE, Connection);
|
|||
|
}
|
|||
|
|
|||
|
void
|
|||
|
DG_SCONNECTION::ConvCallCompleted()
|
|||
|
{
|
|||
|
RPC_STATUS Status;
|
|||
|
|
|||
|
//
|
|||
|
// Ensure a thread is listening for new packets. We are in an expensive function.
|
|||
|
//
|
|||
|
pAddress->CheckThreadPool();
|
|||
|
|
|||
|
do
|
|||
|
{
|
|||
|
Status = RpcAsyncCompleteCall( &Callback.AsyncState, 0 );
|
|||
|
if (RPC_S_INVALID_ASYNC_CALL != Status)
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
Sleep(10);
|
|||
|
}
|
|||
|
while ( 1 );
|
|||
|
|
|||
|
LogEvent(SU_SCONN, EV_STATUS, this, 0, Status);
|
|||
|
|
|||
|
ASSERT( Status != RPC_S_INVALID_ASYNC_CALL &&
|
|||
|
Status != RPC_S_INVALID_ASYNC_HANDLE );
|
|||
|
|
|||
|
if (!Status)
|
|||
|
{
|
|||
|
if (Callback.Status == ERROR_SHUTDOWN_IN_PROGRESS)
|
|||
|
{
|
|||
|
Callback.Status = RPC_P_CLIENT_SHUTDOWN_IN_PROGRESS;
|
|||
|
}
|
|||
|
|
|||
|
Status = Callback.Status;
|
|||
|
LogEvent(SU_SCONN, EV_STATUS, this, 0, Status);
|
|||
|
}
|
|||
|
|
|||
|
if (!Status)
|
|||
|
{
|
|||
|
if (Callback.State == ConvWayInProgress)
|
|||
|
{
|
|||
|
Status = UuidCreate((UUID *) &Callback.CasUuid);
|
|||
|
if (Status == RPC_S_UUID_LOCAL_ONLY)
|
|||
|
{
|
|||
|
Status = RPC_S_OK;
|
|||
|
}
|
|||
|
|
|||
|
if (Status)
|
|||
|
{
|
|||
|
FinishConvCallback(Status);
|
|||
|
Mutex.Clear();
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
else if (Callback.State == ConvWayAuthInProgress ||
|
|||
|
Callback.State == ConvWayAuthMoreInProgress )
|
|||
|
{
|
|||
|
Callback.DataIndex += Callback.ResponseLength;
|
|||
|
}
|
|||
|
|
|||
|
FinishConvCallback(Status);
|
|||
|
|
|||
|
CallDispatchLoop();
|
|||
|
Mutex.Clear();
|
|||
|
}
|
|||
|
else switch (Callback.State)
|
|||
|
{
|
|||
|
case ConvWayAuthInProgress:
|
|||
|
case ConvWayAuthMoreInProgress:
|
|||
|
{
|
|||
|
if (RPC_S_SERVER_UNAVAILABLE == Status ||
|
|||
|
RPC_S_CALL_FAILED_DNE == Status ||
|
|||
|
RPC_S_CALL_FAILED == Status ||
|
|||
|
RPC_S_PROTOCOL_ERROR == Status)
|
|||
|
{
|
|||
|
Callback.Status = NCA_STATUS_WHO_ARE_YOU_FAILED;
|
|||
|
}
|
|||
|
|
|||
|
Callback.DataIndex += Callback.ResponseLength;
|
|||
|
|
|||
|
if (NCA_STATUS_PARTIAL_CREDENTIALS == Status)
|
|||
|
{
|
|||
|
Callback.State = ConvWayAuthMoreInProgress;
|
|||
|
Callback.Status = 0;
|
|||
|
|
|||
|
Status = RpcAsyncInitializeHandle(&Callback.AsyncState, sizeof(Callback.AsyncState));
|
|||
|
if (Status)
|
|||
|
{
|
|||
|
FinishConvCallback(Status);
|
|||
|
Mutex.Clear();
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if (Callback.Credentials->MaximumTokenLength() <= Callback.DataIndex)
|
|||
|
{
|
|||
|
FinishConvCallback(RPC_S_PROTOCOL_ERROR);
|
|||
|
Mutex.Clear();
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
_conv_who_are_you_auth_more(
|
|||
|
&Callback.AsyncState,
|
|||
|
Callback.Binding,
|
|||
|
(UUID *) &ActivityNode.Uuid,
|
|||
|
ProcessStartTime,
|
|||
|
Callback.DataIndex,
|
|||
|
Callback.Credentials->MaximumTokenLength() - Callback.DataIndex,
|
|||
|
Callback.ResponseBuffer + Callback.DataIndex,
|
|||
|
&Callback.ResponseLength,
|
|||
|
&Callback.Status
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// If there is an error, see whether it is caused a notification. If so,
|
|||
|
// ConvCallCompleted() has changed the callback state and this thread should do nothing.
|
|||
|
// If the state is unchanged, this thread must clean up.
|
|||
|
//
|
|||
|
if (Callback.Status)
|
|||
|
{
|
|||
|
Mutex.Request();
|
|||
|
|
|||
|
if (Callback.State == ConvWayAuthMoreInProgress)
|
|||
|
{
|
|||
|
FinishConvCallback(Callback.Status);
|
|||
|
|
|||
|
//
|
|||
|
// The mutex was recursively claimed by FinishConvCallback().
|
|||
|
//
|
|||
|
Mutex.Clear();
|
|||
|
Mutex.Clear();
|
|||
|
return;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Mutex.Clear();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
FinishConvCallback(Callback.Status);
|
|||
|
Mutex.Clear();
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case ConvWay2InProgress:
|
|||
|
{
|
|||
|
Callback.State = ConvWayInProgress;
|
|||
|
Callback.Status = 0;
|
|||
|
|
|||
|
Status = RpcAsyncInitializeHandle(&Callback.AsyncState, sizeof(Callback.AsyncState));
|
|||
|
if (Status)
|
|||
|
{
|
|||
|
FinishConvCallback(Status);
|
|||
|
Mutex.Clear();
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
Callback.AsyncState.NotificationType = RpcNotificationTypeCallback;
|
|||
|
Callback.AsyncState.u.NotificationRoutine = ConvNotificationRoutine;
|
|||
|
Callback.AsyncState.UserInfo = this;
|
|||
|
|
|||
|
_conv_who_are_you(
|
|||
|
&Callback.AsyncState,
|
|||
|
Callback.Binding,
|
|||
|
(UUID *) &ActivityNode.Uuid,
|
|||
|
ProcessStartTime,
|
|||
|
&Callback.ClientSequence,
|
|||
|
&Callback.Status
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// If there is an error, see whether it is caused a notification. If so,
|
|||
|
// ConvCallCompleted() has changed the callback state and this thread should do nothing.
|
|||
|
// If the state is unchanged, this thread must clean up.
|
|||
|
//
|
|||
|
if (Callback.Status)
|
|||
|
{
|
|||
|
Mutex.Request();
|
|||
|
|
|||
|
if (Callback.State == ConvWayInProgress)
|
|||
|
{
|
|||
|
FinishConvCallback(Callback.Status);
|
|||
|
|
|||
|
//
|
|||
|
// The mutex was recursively claimed by FinishConvCallback().
|
|||
|
//
|
|||
|
Mutex.Clear();
|
|||
|
Mutex.Clear();
|
|||
|
return;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Mutex.Clear();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case ConvWayInProgress:
|
|||
|
{
|
|||
|
FinishConvCallback(Callback.Status);
|
|||
|
Mutex.Clear();
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
default:
|
|||
|
{
|
|||
|
#ifdef DBG
|
|||
|
DbgPrint("RPC dg: unexpected callback state %x in connection %x\n", Callback.State, this);
|
|||
|
ASSERT( 0 );
|
|||
|
break;
|
|||
|
#endif
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Mutex.VerifyNotOwned();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
RPC_STATUS
|
|||
|
DG_SCONNECTION::GetAssociationGroup(
|
|||
|
DG_TRANSPORT_ADDRESS RemoteAddress
|
|||
|
)
|
|||
|
{
|
|||
|
if (pAssocGroup)
|
|||
|
{
|
|||
|
return RPC_S_OK;
|
|||
|
}
|
|||
|
|
|||
|
Mutex.Request();
|
|||
|
|
|||
|
if (pAssocGroup)
|
|||
|
{
|
|||
|
Mutex.Clear();
|
|||
|
return RPC_S_OK;
|
|||
|
}
|
|||
|
|
|||
|
if (Callback.State == NoCallbackAttempted)
|
|||
|
{
|
|||
|
//
|
|||
|
// A callback is needed.
|
|||
|
//
|
|||
|
RPC_STATUS Status;
|
|||
|
|
|||
|
Callback.State = SetupInProgress;
|
|||
|
Callback.Binding = 0;
|
|||
|
Callback.Credentials = 0;
|
|||
|
Callback.TokenBuffer = 0;
|
|||
|
Callback.ResponseBuffer = 0;
|
|||
|
Callback.SecurityContext = 0;
|
|||
|
Callback.ThirdLegNeeded = FALSE;
|
|||
|
Callback.DataRep = 0; // used only by secure c/b
|
|||
|
Callback.KeySequence = 0; // used only by secure c/b
|
|||
|
|
|||
|
Callback.Call = (DG_SCALL *) RpcpGetThreadContext();
|
|||
|
|
|||
|
if (NULL == Callback.Call)
|
|||
|
{
|
|||
|
Callback.State = CallbackFailed;
|
|||
|
Mutex.Clear();
|
|||
|
return RPC_S_OUT_OF_MEMORY;
|
|||
|
}
|
|||
|
|
|||
|
Callback.Call->IncrementRefCount();
|
|||
|
|
|||
|
Status = CreateCallbackBindingAndReleaseMutex( RemoteAddress );
|
|||
|
|
|||
|
if (Status)
|
|||
|
{
|
|||
|
FinishConvCallback(Status);
|
|||
|
Mutex.Clear();
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
Status = RpcAsyncInitializeHandle(&Callback.AsyncState, sizeof(Callback.AsyncState));
|
|||
|
if (Status)
|
|||
|
{
|
|||
|
FinishConvCallback(Status);
|
|||
|
Mutex.Clear();
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
Callback.AsyncState.NotificationType = RpcNotificationTypeCallback;
|
|||
|
Callback.AsyncState.u.NotificationRoutine = ConvNotificationRoutine;
|
|||
|
Callback.AsyncState.UserInfo = this;
|
|||
|
|
|||
|
//
|
|||
|
// If this were a secure connection, we wouldn't get into the
|
|||
|
// unmarshalling routine w/o a callback, and we would be here now.
|
|||
|
// So use conv_who_are_you2.
|
|||
|
//
|
|||
|
Callback.State = ConvWay2InProgress;
|
|||
|
Callback.Status = 0;
|
|||
|
|
|||
|
_conv_who_are_you2(
|
|||
|
&Callback.AsyncState,
|
|||
|
Callback.Binding,
|
|||
|
(UUID *) &ActivityNode.Uuid,
|
|||
|
ProcessStartTime,
|
|||
|
&Callback.ClientSequence,
|
|||
|
&Callback.CasUuid,
|
|||
|
&Callback.Status
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// If there is an error, see whether it is caused a notification. If so,
|
|||
|
// ConvCallCompleted() has changed the callback state and this thread should do nothing.
|
|||
|
// If the state is unchanged, this thread must clean up.
|
|||
|
//
|
|||
|
if (Callback.Status)
|
|||
|
{
|
|||
|
Mutex.Request();
|
|||
|
|
|||
|
if (Callback.State == ConvWay2InProgress)
|
|||
|
{
|
|||
|
FinishConvCallback(Callback.Status);
|
|||
|
|
|||
|
//
|
|||
|
// The mutex was recursively claimed by FinishConvCallback().
|
|||
|
//
|
|||
|
Mutex.Clear();
|
|||
|
Mutex.Clear();
|
|||
|
return Callback.Status;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Mutex.Clear();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Mutex.Clear();
|
|||
|
}
|
|||
|
|
|||
|
Mutex.VerifyNotOwned();
|
|||
|
|
|||
|
//
|
|||
|
// Wait for the result. This is not quite as efficient as using event notification,
|
|||
|
// but it's a lot simpler, and that's important at this late stage of the ship process.
|
|||
|
//
|
|||
|
while (Callback.State != CallbackSucceeded &&
|
|||
|
Callback.State != CallbackFailed)
|
|||
|
{
|
|||
|
Sleep(500);
|
|||
|
}
|
|||
|
|
|||
|
return Callback.Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
RPC_STATUS
|
|||
|
DG_SCONNECTION::MakeApplicationSecurityCallback(
|
|||
|
RPC_INTERFACE * Interface,
|
|||
|
PDG_SCALL Call
|
|||
|
)
|
|||
|
{
|
|||
|
RPC_STATUS Status = RPC_S_OK;
|
|||
|
|
|||
|
if (!ActiveSecurityContext)
|
|||
|
{
|
|||
|
LogError(SU_SCONN, EV_STATUS, this, (void *) 112, RPC_S_ACCESS_DENIED );
|
|||
|
Status = RPC_S_ACCESS_DENIED;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
unsigned Info = InterfaceCallbackResults.Find(Interface);
|
|||
|
|
|||
|
if ((Info & CBI_VALID) == 0 ||
|
|||
|
(Info & CBI_CONTEXT_MASK) != ActiveSecurityContext->AuthContextId ||
|
|||
|
(Info & CBI_SEQUENCE_MASK) != (Interface->SequenceNumber << CBI_SEQUENCE_SHIFT))
|
|||
|
{
|
|||
|
RPC_STATUS MyStatus;
|
|||
|
|
|||
|
MyStatus = Interface->CheckSecurityIfNecessary(Call);
|
|||
|
|
|||
|
if (RPC_S_OK == MyStatus)
|
|||
|
{
|
|||
|
LogEvent(SU_SCONN, EV_STATUS, this, (void *) 114, MyStatus );
|
|||
|
Info = CBI_ALLOWED;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
LogError(SU_SCONN, EV_STATUS, this, (void *) 114, MyStatus );
|
|||
|
Info = 0;
|
|||
|
}
|
|||
|
|
|||
|
Info |= CBI_VALID;
|
|||
|
Info |= ActiveSecurityContext->AuthContextId;
|
|||
|
Info |= (Interface->SequenceNumber << CBI_SEQUENCE_SHIFT);
|
|||
|
|
|||
|
InterfaceCallbackResults.Update(Interface, Info);
|
|||
|
|
|||
|
//
|
|||
|
// If the callback routine impersonated the client,
|
|||
|
// restore the thread to its native security context.
|
|||
|
//
|
|||
|
RevertToSelf();
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
LogEvent(SU_SCONN, EV_STATUS, this, (void *)115, Info);
|
|||
|
}
|
|||
|
|
|||
|
if (0 == (Info & CBI_ALLOWED))
|
|||
|
{
|
|||
|
Status = RPC_S_ACCESS_DENIED;
|
|||
|
LogError(SU_SCONN, EV_STATUS, this, (void *) 113, Status );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
RPC_STATUS
|
|||
|
DG_SCONNECTION::SealAndSendPacket(
|
|||
|
IN DG_ENDPOINT * SourceEndpoint,
|
|||
|
IN DG_TRANSPORT_ADDRESS RemoteAddress,
|
|||
|
IN UNALIGNED NCA_PACKET_HEADER *Header,
|
|||
|
IN unsigned long DataOffset
|
|||
|
)
|
|||
|
{
|
|||
|
ASSERT( 0 == ActivityNode.CompareUuid(&Header->ActivityId) );
|
|||
|
|
|||
|
if (DG_REJECT == Header->PacketType)
|
|||
|
{
|
|||
|
return SendSecurePacket( SourceEndpoint,
|
|||
|
RemoteAddress,
|
|||
|
Header,
|
|||
|
DataOffset,
|
|||
|
0
|
|||
|
);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
return SendSecurePacket( SourceEndpoint,
|
|||
|
RemoteAddress,
|
|||
|
Header,
|
|||
|
DataOffset,
|
|||
|
ActiveSecurityContext
|
|||
|
);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
RPC_STATUS
|
|||
|
DG_SCALL::Cancel(
|
|||
|
void * ThreadHandle
|
|||
|
)
|
|||
|
{
|
|||
|
Connection->Mutex.Request();
|
|||
|
|
|||
|
++Cancelled;
|
|||
|
BasePacketFlags2 |= DG_PF2_CANCEL_PENDING;
|
|||
|
|
|||
|
Connection->Mutex.Clear();
|
|||
|
|
|||
|
return RPC_S_OK;
|
|||
|
}
|
|||
|
|
|||
|
unsigned
|
|||
|
DG_SCALL::TestCancel()
|
|||
|
{
|
|||
|
if (!Cancelled)
|
|||
|
{
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
unsigned CancelCount;
|
|||
|
|
|||
|
Connection->Mutex.Request();
|
|||
|
|
|||
|
CancelCount = Cancelled;
|
|||
|
Cancelled = 0;
|
|||
|
BasePacketFlags2 &= ~(DG_PF2_CANCEL_PENDING);
|
|||
|
|
|||
|
Connection->Mutex.Clear();
|
|||
|
|
|||
|
return CancelCount;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
RPC_STATUS
|
|||
|
DG_SCALL::WaitForPipeEvent()
|
|||
|
{
|
|||
|
RPC_STATUS Status = RPC_S_OK;
|
|||
|
unsigned Locus;
|
|||
|
|
|||
|
if (!PipeWaitEvent)
|
|||
|
{
|
|||
|
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)
|
|||
|
{
|
|||
|
PipeWaitType = PWT_NONE;
|
|||
|
PipeThreadId = 0;
|
|||
|
return Status;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
IncrementRefCount();
|
|||
|
Connection->Mutex.Clear();
|
|||
|
|
|||
|
PipeWaitEvent->Wait();
|
|||
|
|
|||
|
Connection->Mutex.Request();
|
|||
|
DecrementRefCount();
|
|||
|
|
|||
|
ASSERT(PipeThreadId == GetCurrentThreadId());
|
|||
|
PipeThreadId = 0;
|
|||
|
|
|||
|
if (TerminateWhenConvenient)
|
|||
|
{
|
|||
|
Status = RPC_S_CALL_FAILED;
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
DG_SCALL::FinishSendOrReceive(
|
|||
|
BOOL Abort
|
|||
|
)
|
|||
|
{
|
|||
|
RPC_ASYNC_EVENT Event;
|
|||
|
|
|||
|
if (Abort)
|
|||
|
{
|
|||
|
TerminateWhenConvenient = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
switch (PipeWaitType)
|
|||
|
{
|
|||
|
case PWT_NONE: return FALSE;
|
|||
|
case PWT_SEND: Event = RpcSendComplete; break;
|
|||
|
case PWT_RECEIVE: Event = RpcReceiveComplete; break;
|
|||
|
default: ASSERT(0 && "bad pipe wait type"); return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
ASSERT( PipeThreadId != 0 );
|
|||
|
|
|||
|
PipeWaitType = PWT_NONE;
|
|||
|
|
|||
|
if (!pAsync)
|
|||
|
{
|
|||
|
PipeWaitEvent->Raise();
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
if (Event == RpcSendComplete)
|
|||
|
{
|
|||
|
if (FinalSendBufferPresent)
|
|||
|
{
|
|||
|
PipeThreadId = 0;
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
IssueNotification( Event );
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
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.
|
|||
|
|
|||
|
Sync case: the fn waits until the requested buffer bytes are available,
|
|||
|
copies them to Message->Buffer, and returns.
|
|||
|
|
|||
|
Async case: the fn returns the data if available now, otherwise
|
|||
|
RPC_S_ASYNC_CALL_PENDING. The app will be notified when the
|
|||
|
data becomes available. It can also poll.
|
|||
|
|
|||
|
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
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
RPC_STATUS Status = RPC_S_OK;
|
|||
|
unsigned Locus;
|
|||
|
|
|||
|
if (Message->RpcFlags & RPC_BUFFER_ASYNC)
|
|||
|
{
|
|||
|
LogEvent(SU_SCALL, EV_PROC, this, Message, 'A' + (('R' + (('c' + ('v' << 8)) << 8)) << 8));
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
LogEvent(SU_SCALL, EV_PROC, this, Message, 'R' + (('e' + (('c' + ('v' << 8)) << 8)) << 8));
|
|||
|
}
|
|||
|
|
|||
|
LogEvent(SU_SCALL, EV_BUFFER_IN, this, Message->Buffer, (Message->RpcFlags << 4) | Size);
|
|||
|
|
|||
|
Connection->Mutex.Request();
|
|||
|
|
|||
|
if (TerminateWhenConvenient)
|
|||
|
{
|
|||
|
FreeBuffer(Message);
|
|||
|
|
|||
|
Status = RPC_S_CALL_FAILED;
|
|||
|
if (pAsync)
|
|||
|
{
|
|||
|
//
|
|||
|
// case c) from doc:appref
|
|||
|
//
|
|||
|
DecrementRefCount();
|
|||
|
Cleanup();
|
|||
|
}
|
|||
|
|
|||
|
Connection->Mutex.Clear();
|
|||
|
|
|||
|
LogError(SU_SCALL, EV_STATUS, this, 0, Status);
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// 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)
|
|||
|
{
|
|||
|
if (Message->RpcFlags & RPC_BUFFER_NONOTIFY)
|
|||
|
{
|
|||
|
Connection->Mutex.Clear();
|
|||
|
|
|||
|
LogEvent(SU_SCALL, EV_STATUS, this, 0, RPC_S_ASYNC_CALL_PENDING);
|
|||
|
return RPC_S_ASYNC_CALL_PENDING;
|
|||
|
}
|
|||
|
|
|||
|
if (Message->RpcFlags & RPC_BUFFER_ASYNC)
|
|||
|
{
|
|||
|
ASSERT((PWT_NONE == PipeWaitType && 0 == PipeThreadId) ||
|
|||
|
(PWT_RECEIVE == PipeWaitType && GetCurrentThreadId() == PipeThreadId));
|
|||
|
|
|||
|
PipeWaitType = PWT_RECEIVE;
|
|||
|
PipeThreadId = GetCurrentThreadId();
|
|||
|
|
|||
|
Connection->Mutex.Clear();
|
|||
|
|
|||
|
LogEvent(SU_SCALL, EV_STATUS, this, 0, RPC_S_ASYNC_CALL_PENDING);
|
|||
|
return RPC_S_ASYNC_CALL_PENDING;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
ASSERT(PWT_NONE == PipeWaitType);
|
|||
|
ASSERT(0 == PipeThreadId);
|
|||
|
|
|||
|
PipeWaitType = PWT_RECEIVE;
|
|||
|
PipeThreadId = GetCurrentThreadId();
|
|||
|
|
|||
|
Status = WaitForPipeEvent();
|
|||
|
if (Status)
|
|||
|
{
|
|||
|
//
|
|||
|
// I would like to delete the buffer here, but NDR has already saved Message->Buffer
|
|||
|
// somewhere else and will later ask that it be deleted again.
|
|||
|
//
|
|||
|
// FreeBuffer(Message);
|
|||
|
|
|||
|
if (pAsync)
|
|||
|
{
|
|||
|
//
|
|||
|
// case c) from doc:appref
|
|||
|
//
|
|||
|
InitErrorPacket(pSavedPacket, DG_FAULT, Status);
|
|||
|
SealAndSendPacket(&pSavedPacket->Header);
|
|||
|
DecrementRefCount();
|
|||
|
Cleanup();
|
|||
|
}
|
|||
|
|
|||
|
Connection->Mutex.Clear();
|
|||
|
|
|||
|
LogError(SU_SCALL, EV_STATUS, this, 0, Status);
|
|||
|
return Status;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// This is here only for async receive case, but the if() clause
|
|||
|
// would only slow us down.
|
|||
|
//
|
|||
|
ASSERT( PipeWaitType == PWT_NONE );
|
|||
|
PipeThreadId = 0;
|
|||
|
|
|||
|
//
|
|||
|
// For secure RPC, verify packet integrity.
|
|||
|
//
|
|||
|
if (Connection->AuthInfo.AuthenticationLevel > RPC_C_AUTHN_LEVEL_NONE)
|
|||
|
{
|
|||
|
PDG_PACKET pScan = pReceivedPackets;
|
|||
|
|
|||
|
do
|
|||
|
{
|
|||
|
Status = Connection->VerifyRequestPacket(pScan);
|
|||
|
pScan = pScan->pNext;
|
|||
|
}
|
|||
|
while (pScan && Status == RPC_S_OK);
|
|||
|
}
|
|||
|
|
|||
|
if (RPC_S_OK == Status)
|
|||
|
{
|
|||
|
Status = AssembleBufferFromPackets(Message);
|
|||
|
}
|
|||
|
|
|||
|
if (Status)
|
|||
|
{
|
|||
|
LogError(SU_SCALL, EV_STATUS, this, 0, Status);
|
|||
|
FreeBuffer(Message);
|
|||
|
}
|
|||
|
|
|||
|
Connection->Mutex.Clear();
|
|||
|
|
|||
|
LogEvent(SU_SCALL, EV_BUFFER_OUT, this, Message->Buffer, Message->BufferLength);
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
RPC_STATUS
|
|||
|
DG_SCALL::Send(
|
|||
|
PRPC_MESSAGE Message
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Transfers a pipe data buffer to the client.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Message - the usual data
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
the usual suspects
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
RPC_STATUS Status;
|
|||
|
|
|||
|
if (Message->RpcFlags & RPC_BUFFER_ASYNC)
|
|||
|
{
|
|||
|
LogEvent(SU_SCALL, EV_PROC, this, Message, 'A' + (('S' + (('n' + ('d' << 8)) << 8)) << 8));
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
LogEvent(SU_SCALL, EV_PROC, this, Message, 'S' + (('e' + (('n' + ('d' << 8)) << 8)) << 8));
|
|||
|
}
|
|||
|
|
|||
|
LogEvent(SU_SCALL, EV_BUFFER_IN, this, Message->Buffer, (Message->RpcFlags << 4) | Message->BufferLength);
|
|||
|
|
|||
|
//
|
|||
|
// No notification occurs on the buffer containing the [out] static args.
|
|||
|
// This is consistent with synchronous pipes.
|
|||
|
//
|
|||
|
if (!(Message->RpcFlags & RPC_BUFFER_PARTIAL))
|
|||
|
{
|
|||
|
FinalSendBufferPresent = TRUE;
|
|||
|
Message->RpcFlags |= RPC_BUFFER_NONOTIFY;
|
|||
|
}
|
|||
|
|
|||
|
Connection->Mutex.Request();
|
|||
|
|
|||
|
ASSERT(fReceivedAllFragments);
|
|||
|
|
|||
|
if (Message->RpcFlags & RPC_BUFFER_ASYNC)
|
|||
|
{
|
|||
|
ASSERT(pAsync);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
ASSERT(State == CallDispatched ||
|
|||
|
State == CallSendingResponse ||
|
|||
|
State == CallAfterDispatch );
|
|||
|
}
|
|||
|
|
|||
|
if (TerminateWhenConvenient)
|
|||
|
{
|
|||
|
FreeBuffer(Message);
|
|||
|
|
|||
|
SetState(CallSendingResponse);
|
|||
|
Status = RPC_S_CALL_FAILED;
|
|||
|
if (pAsync)
|
|||
|
{
|
|||
|
//
|
|||
|
// case c) from doc:appref
|
|||
|
//
|
|||
|
DecrementRefCount();
|
|||
|
Cleanup();
|
|||
|
}
|
|||
|
|
|||
|
Connection->Mutex.Clear();
|
|||
|
|
|||
|
LogError(SU_SCALL, EV_STATUS, this, (void *) 1 , Status);
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
SetFragmentLengths();
|
|||
|
|
|||
|
if ((Message->RpcFlags & RPC_BUFFER_PARTIAL) &&
|
|||
|
Message->BufferLength < (ULONG) MaxFragmentSize * SendWindowSize )
|
|||
|
{
|
|||
|
if (pAsync)
|
|||
|
{
|
|||
|
IssueNotification( RpcSendComplete );
|
|||
|
}
|
|||
|
|
|||
|
Connection->Mutex.Clear();
|
|||
|
|
|||
|
LogEvent(SU_SCALL, EV_STATUS, this, (void *) 2, RPC_S_SEND_INCOMPLETE);
|
|||
|
return RPC_S_SEND_INCOMPLETE;
|
|||
|
}
|
|||
|
|
|||
|
if (State != CallSendingResponse)
|
|||
|
{
|
|||
|
SetState(CallSendingResponse);
|
|||
|
CleanupReceiveWindow();
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the message buffer as the current send buffer.
|
|||
|
//
|
|||
|
Status = PushBuffer(Message);
|
|||
|
|
|||
|
//
|
|||
|
// Ignore normal send errors - they are usually transient and a retry fixes the problem.
|
|||
|
//
|
|||
|
if (Status == RPC_P_SEND_FAILED)
|
|||
|
{
|
|||
|
Status = 0;
|
|||
|
}
|
|||
|
|
|||
|
if (Status)
|
|||
|
{
|
|||
|
FreeBuffer(Message);
|
|||
|
|
|||
|
if (pAsync)
|
|||
|
{
|
|||
|
//
|
|||
|
// case c) from doc:appref
|
|||
|
//
|
|||
|
InitErrorPacket(pSavedPacket, DG_FAULT, Status);
|
|||
|
SealAndSendPacket(&pSavedPacket->Header);
|
|||
|
DecrementRefCount();
|
|||
|
Cleanup();
|
|||
|
}
|
|||
|
|
|||
|
Connection->Mutex.Clear();
|
|||
|
|
|||
|
LogError(SU_SCALL, EV_STATUS, this, (void *) 3, Status);
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
PipeWaitType = PWT_SEND;
|
|||
|
PipeThreadId = GetCurrentThreadId();
|
|||
|
|
|||
|
if (Message->RpcFlags & RPC_BUFFER_ASYNC)
|
|||
|
{
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Status = WaitForPipeEvent();
|
|||
|
if (Status)
|
|||
|
{
|
|||
|
if (pAsync)
|
|||
|
{
|
|||
|
//
|
|||
|
// case c) from doc:appref
|
|||
|
//
|
|||
|
InitErrorPacket(pSavedPacket, DG_FAULT, Status);
|
|||
|
SealAndSendPacket(&pSavedPacket->Header);
|
|||
|
DecrementRefCount();
|
|||
|
Cleanup();
|
|||
|
}
|
|||
|
|
|||
|
Connection->Mutex.Clear();
|
|||
|
|
|||
|
LogError(SU_SCALL, EV_STATUS, this, (void *) 4, Status);
|
|||
|
return Status;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (pAsync && !(Message->RpcFlags & RPC_BUFFER_PARTIAL))
|
|||
|
{
|
|||
|
//
|
|||
|
// case b) from doc:appref
|
|||
|
// Don't nuke the call, just remove the extra reference
|
|||
|
//
|
|||
|
DecrementRefCount();
|
|||
|
}
|
|||
|
|
|||
|
Connection->Mutex.Clear();
|
|||
|
|
|||
|
//
|
|||
|
// Be careful looking at member data from here on!
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// if this was a PARTIAL send and the buffer did not occupy an even
|
|||
|
// number of packets, the message buffer and length now reflect
|
|||
|
// the unsent portion.
|
|||
|
//
|
|||
|
if (!Status && Message->BufferLength)
|
|||
|
{
|
|||
|
Status = RPC_S_SEND_INCOMPLETE;
|
|||
|
}
|
|||
|
|
|||
|
if (Status)
|
|||
|
{
|
|||
|
LogEvent(SU_SCALL, EV_STATUS, this, 0, Status);
|
|||
|
}
|
|||
|
|
|||
|
LogEvent(SU_SCALL, EV_BUFFER_OUT, this, Message->Buffer, Message->BufferLength);
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
RPC_STATUS
|
|||
|
DG_SCALL::AsyncSend (
|
|||
|
IN OUT PRPC_MESSAGE Message
|
|||
|
)
|
|||
|
{
|
|||
|
return Send(Message);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
RPC_STATUS
|
|||
|
DG_SCALL::AsyncReceive (
|
|||
|
IN OUT PRPC_MESSAGE Message,
|
|||
|
IN unsigned int Size
|
|||
|
)
|
|||
|
{
|
|||
|
return Receive(Message, Size);
|
|||
|
}
|
|||
|
|
|||
|
RPC_STATUS
|
|||
|
DG_SCALL::InqLocalConnAddress (
|
|||
|
IN OUT void *Buffer,
|
|||
|
IN OUT unsigned long *BufferSize,
|
|||
|
OUT unsigned long *AddressFormat
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is used by a server application to inquire about the local
|
|||
|
address on which a call is made.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Buffer - The buffer that will receive the output address
|
|||
|
|
|||
|
BufferSize - the size of the supplied Buffer on input. On output the
|
|||
|
number of bytes written to the buffer. If the buffer is too small
|
|||
|
to receive all the output data, ERROR_MORE_DATA is returned,
|
|||
|
nothing is written to the buffer, and BufferSize is set to
|
|||
|
the size of the buffer needed to return all the data.
|
|||
|
|
|||
|
AddressFormat - a constant indicating the format of the returned address.
|
|||
|
Currently supported for datagrams is RPC_P_ADDR_FORMAT_TCP_IPV4.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
RPC_S_OK - success.
|
|||
|
|
|||
|
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to complete this
|
|||
|
operation.
|
|||
|
|
|||
|
RPC_S_INVALID_BINDING - The supplied client binding is invalid.
|
|||
|
|
|||
|
RPC_S_CANNOT_SUPPORT - The local address was inquired for a protocol
|
|||
|
sequence that doesn't support this type of functionality. Currently
|
|||
|
only ncacn_ip_tcp supports it.
|
|||
|
|
|||
|
RPC_S_* or Win32 error for other errors
|
|||
|
--*/
|
|||
|
{
|
|||
|
if (*BufferSize < sizeof(DWORD))
|
|||
|
{
|
|||
|
*BufferSize = sizeof(DWORD);
|
|||
|
return ERROR_MORE_DATA;
|
|||
|
}
|
|||
|
|
|||
|
*AddressFormat = RPC_P_ADDR_FORMAT_TCP_IPV4;
|
|||
|
*(DWORD *)Buffer = LocalAddress;
|
|||
|
|
|||
|
return RPC_S_OK;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
RPC_STATUS
|
|||
|
DG_SCALL::SetAsyncHandle (
|
|||
|
IN RPC_ASYNC_STATE * hAsync
|
|||
|
)
|
|||
|
{
|
|||
|
THREAD *MyThread = RpcpGetThreadPointer();
|
|||
|
|
|||
|
ASSERT(MyThread);
|
|||
|
LogEvent(SU_SCALL, EV_PROC, this, 0, 'S' + (('e' + (('t' + ('H' << 8)) << 8)) << 8));
|
|||
|
|
|||
|
MyThread->fAsync = TRUE;
|
|||
|
Connection->Mutex.Request();
|
|||
|
|
|||
|
ASSERT( State == CallDispatched );
|
|||
|
|
|||
|
ASSERT(0 == pAsync);
|
|||
|
|
|||
|
pAsync = hAsync;
|
|||
|
|
|||
|
IncrementRefCount();
|
|||
|
|
|||
|
Connection->Mutex.Clear();
|
|||
|
return RPC_S_OK;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
RPC_STATUS
|
|||
|
DG_SCALL::AbortAsyncCall (
|
|||
|
IN PRPC_ASYNC_STATE pAsync,
|
|||
|
IN unsigned long ExceptionCode
|
|||
|
)
|
|||
|
{
|
|||
|
LogEvent(SU_SCALL, EV_PROC, this, 0, 'A' + (('b' + (('t' + ('C' << 8)) << 8)) << 8));
|
|||
|
|
|||
|
Connection->Mutex.Request();
|
|||
|
|
|||
|
ASSERT( State == CallDispatched ||
|
|||
|
State == CallAfterDispatch );
|
|||
|
|
|||
|
if ( State != CallDispatched &&
|
|||
|
State != CallAfterDispatch )
|
|||
|
{
|
|||
|
Connection->Mutex.Clear();
|
|||
|
return RPC_S_INVALID_ASYNC_CALL;
|
|||
|
}
|
|||
|
|
|||
|
SetState(CallSendingResponse);
|
|||
|
|
|||
|
CleanupReceiveWindow();
|
|||
|
|
|||
|
// case d) from doc:appref
|
|||
|
//
|
|||
|
InitErrorPacket(pSavedPacket, DG_FAULT, ExceptionCode);
|
|||
|
SealAndSendPacket(&pSavedPacket->Header);
|
|||
|
DecrementRefCount();
|
|||
|
Cleanup();
|
|||
|
|
|||
|
Connection->Mutex.Clear();
|
|||
|
|
|||
|
return RPC_S_OK;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
DG_SCALL::IssueNotification (
|
|||
|
IN RPC_ASYNC_EVENT Event
|
|||
|
)
|
|||
|
{
|
|||
|
LogEvent(SU_SCALL, EV_NOTIFY, this, 0, Event);
|
|||
|
|
|||
|
if (Event == RpcSendComplete && !(pAsync->Flags & RPC_C_NOTIFY_ON_SEND_COMPLETE))
|
|||
|
{
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
if (pAsync->NotificationType == RpcNotificationTypeApc)
|
|||
|
{
|
|||
|
IncrementRefCount();
|
|||
|
}
|
|||
|
|
|||
|
int i;
|
|||
|
for (i=1; i < 3; ++i)
|
|||
|
{
|
|||
|
if (CALL::IssueNotification(Event))
|
|||
|
{
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
Sleep(200);
|
|||
|
}
|
|||
|
|
|||
|
DecrementRefCount();
|
|||
|
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
void
|
|||
|
DG_SCALL::FreeAPCInfo (
|
|||
|
IN RPC_APC_INFO *pAPCInfo
|
|||
|
)
|
|||
|
{
|
|||
|
LogEvent(SU_SCALL, EV_APC, this);
|
|||
|
|
|||
|
Connection->Mutex.Request();
|
|||
|
|
|||
|
DecrementRefCount();
|
|||
|
|
|||
|
BOOL Final = FALSE;
|
|||
|
|
|||
|
if (pAPCInfo->Event == RpcSendComplete &&
|
|||
|
!(BufferFlags & RPC_BUFFER_PARTIAL))
|
|||
|
{
|
|||
|
Final = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
CALL::FreeAPCInfo(pAPCInfo);
|
|||
|
|
|||
|
if (Final)
|
|||
|
{
|
|||
|
Cleanup();
|
|||
|
}
|
|||
|
|
|||
|
Connection->Mutex.Clear();
|
|||
|
}
|
|||
|
|
|||
|
void
|
|||
|
DG_SCALL::FreePipeBuffer (
|
|||
|
IN PRPC_MESSAGE Message
|
|||
|
)
|
|||
|
{
|
|||
|
FreeBuffer(Message);
|
|||
|
}
|
|||
|
|
|||
|
RPC_STATUS
|
|||
|
DG_SCALL::ReallocPipeBuffer (
|
|||
|
IN PRPC_MESSAGE Message,
|
|||
|
IN unsigned int NewSize
|
|||
|
)
|
|||
|
{
|
|||
|
LogEvent(SU_SCALL, EV_PROC, this, Message, 'R' + (('B' + (('u' + ('f' << 8)) << 8)) << 8));
|
|||
|
LogEvent(SU_SCALL, EV_BUFFER_IN, this, Message->Buffer, (Message->RpcFlags << 4) | Message->BufferLength);
|
|||
|
|
|||
|
Connection->Mutex.Request();
|
|||
|
|
|||
|
if (TerminateWhenConvenient)
|
|||
|
{
|
|||
|
Connection->Mutex.Clear();
|
|||
|
LogError(SU_SCALL, EV_STATUS, this, 0, RPC_S_CALL_FAILED);
|
|||
|
return RPC_S_CALL_FAILED;
|
|||
|
}
|
|||
|
|
|||
|
RPC_STATUS Status;
|
|||
|
|
|||
|
//
|
|||
|
// If we are updating the [in] buffer, we need to inform
|
|||
|
// DispatchToStubWorker of the new [in] buffer.
|
|||
|
//
|
|||
|
void * OldBuffer = Message->Buffer;
|
|||
|
|
|||
|
Status = CommonReallocBuffer(Message, NewSize);
|
|||
|
if (Status == RPC_S_OK)
|
|||
|
{
|
|||
|
if (OldBuffer == DispatchBuffer)
|
|||
|
{
|
|||
|
DispatchBuffer = Message->Buffer;
|
|||
|
}
|
|||
|
|
|||
|
// The ReservedForRuntime field is a local variable of ProcessRpcCall,
|
|||
|
// so it is valid only during dispatch.
|
|||
|
//
|
|||
|
if (State == CallDispatched)
|
|||
|
{
|
|||
|
PRPC_RUNTIME_INFO Info = (PRPC_RUNTIME_INFO) Message->ReservedForRuntime;
|
|||
|
if (OldBuffer == Info->OldBuffer)
|
|||
|
{
|
|||
|
Info->OldBuffer = Message->Buffer;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#ifdef MONITOR_SERVER_PACKET_COUNT
|
|||
|
if (Message->Buffer != OldBuffer)
|
|||
|
{
|
|||
|
ASSERT( DG_PACKET::FromStubData(Message->Buffer)->pCount == 0 );
|
|||
|
DG_PACKET::FromStubData(Message->Buffer)->pCount = &OutstandingPacketCount;
|
|||
|
InterlockedIncrement( &OutstandingPacketCount );
|
|||
|
LogEvent( SU_SCALL, '(', this, DG_PACKET::FromStubData(Message->Buffer), OutstandingPacketCount );
|
|||
|
}
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
Connection->Mutex.Clear();
|
|||
|
|
|||
|
LogEvent(SU_SCALL, EV_BUFFER_OUT, this, Message->Buffer, (Message->RpcFlags << 4) | Message->BufferLength);
|
|||
|
if (Status)
|
|||
|
{
|
|||
|
LogError(SU_SCALL, EV_STATUS, this, 0, Status);
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|