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

6451 lines
168 KiB

/*++
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 "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)
// Used to test races between conv callback sync and async completion paths.
//#define INDUCE_CONV_CALLBACK_RACES
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
)
{
RPC_STATUS Status;
Status = Endpoint.TransportInterface->Listen(
Endpoint.TransportEndpoint,
NetworkAddress,
pEndpointString,
SecurityDescriptor,
EndpointFlags,
NICFlags
);
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
--*/
{
CORRUPTION_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();
}
Connection->pAddress->DecrementActiveCallCount();
if (pAsync)
{
Interface->EndCall(0, 1);
if (Interface->IsAutoListenInterface())
{
// This is the path where async calls complete.
// We need to decrement CallNumber.
Interface->EndAutoListenCall(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.
//
DictionaryCursor 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;
// Initialize the fields that may remain unused so that we do not send
// back stale data. Even though these values will be ignored, revealing
// the contents of server's memory present a security threat.
RpcpMemorySet(&pSavedPacket->Header.InterfaceId, 0, sizeof(RPC_UUID));
RpcpMemorySet(&pSavedPacket->Header.InterfaceVersion, 0, sizeof(RPC_VERSION));
pSavedPacket->Header.OperationNumber = 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, Message->BufferLength);
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] and [broadcast] calls should be [idempotent].
//
if (((pHeader->PacketFlags & DG_PF_MAYBE) || (pHeader->PacketFlags & DG_PF_BROADCAST)) &&
!(pHeader->PacketFlags & DG_PF_IDEMPOTENT))
{
Mutex.Clear();
LogEvent(SU_SCONN, EV_PKT_IN, this, (void *) 0, (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();
// After we are done processing the call we should
// clear EEInfo record before moving on to the next one.
// This prevent eeinfo from building up.
RpcpPurgeEEInfo();
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())
{
// The AutoListen and NullManager call counts for the interface
// will be added on dispatch so we do not need to add it here.
// 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
)
{
RPC_STATUS Status = RPC_S_OK;
if (0 == Connection->ActiveSecurityContext)
{
if ( (Connection->TransportInterface->InquireAuthClient) )
{
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)
{
Status = Connection->ActiveSecurityContext->GetDceInfo(&Privileges, &AuthorizationService);
}
if (Privs)
{
*Privs = Privileges;
}
if (pAuthorizationService)
{
*pAuthorizationService = AuthorizationService;
}
if (Status != RPC_S_OK)
{
return Status;
}
}
if (ARGUMENT_PRESENT(ServerPrincipalName))
{
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.
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
);
#ifdef INDUCE_CONV_CALLBACK_RACES
Sleep(1000);
#endif
//
// 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
);
#ifdef INDUCE_CONV_CALLBACK_RACES
Sleep(1000);
#endif
//
// 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 the state advanced past ConvWay2InProgress, then async completion must have
// already been processed so we should let the other thread do the job.
//
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;
// Force this function to be idempotent.
// The sync comple path races with the async complete path.
// We avoid the race by updating the state of the callback
// to let the subsequent callers know that it has been adjusted.
Mutex.Request();
if (Status != RPC_S_OK)
{
// If the function has been called in the past, this is a noop.
if (Callback.State == NoCallbackAttempted
|| Callback.State == CallbackFailed
)
{
// Do not clear the mutex since the callers expect it
// to be claimed recursively by FinishConvCallback.
return Status;
}
// If this is the first call, start by marking callback as completed.
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;
}
}
Mutex.Clear();
if (Status == RPC_S_OK && 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();
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 );
#ifdef INDUCE_CONV_CALLBACK_RACES
Callback.Status = 0xbaad;
#endif
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:
{
#ifdef INDUCE_CONV_CALLBACK_RACES
Sleep(2000);
#endif
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:
{
#ifdef INDUCE_CONV_CALLBACK_RACES
Sleep(2000);
#endif
//
// 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.
//
Mutex.Request();
if (Callback.Status)
{
FinishConvCallback(Callback.Status);
// The mutex was recursively claimed by FinishConvCallback().
Mutex.Clear();
Mutex.Clear();
return;
}
else
{
Callback.State = ConvWayInProgress;
Callback.Status = 0;
}
Mutex.Clear();
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;
}
// The Callback.State will be set to CallbackFailed if
// FinishConvCallback has already been called. This is possible
// if the callback has failed before the client side of the call
// returned from the conv_ routine. In this case FinishConvCallback
// will be called in DG_SCONNECTION::SubmitCallbackIfNecessary(), but also
// a callback completion event will be queued, and we will process it here.
case CallbackFailed:
{
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;
// Check whether security callback is required and security is being used.
if (!ActiveSecurityContext
&& !Interface->IsAllowingUnsecureCallbacks())
{
LogError(SU_SCONN, EV_STATUS, this, (void *) 112, RPC_S_ACCESS_DENIED );
Status = RPC_S_ACCESS_DENIED;
}
// Handle the case when RPC security is being used and we can cache callbacks.
else if (ActiveSecurityContext)
{
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 );
}
}
// Handle the case with no security and no caching.
// This is only possible if the interface has been registered with
// RPC_IF_ALLOW_CALLBACKS_WITH_NO_AUTH flag.
// Every callback is dispatched.
else
{
ASSERT(Interface->IsAllowingUnsecureCallbacks());
Status = Interface->CheckSecurityIfNecessary(Call);
if (RPC_S_OK == Status)
LogEvent(SU_SCONN, EV_STATUS, this, (void *) 114, Status );
else
LogError(SU_SCONN, EV_STATUS, this, (void *) 114, Status );
RevertToSelf();
}
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;
if (Interface->IsAutoListenInterface())
{
Interface->BeginAutoListenCall();
}
Interface->BeginNullManagerCall();
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;
}