mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1428 lines
32 KiB
1428 lines
32 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Abstract:
|
|
|
|
Implementation of the RPC on LPC (LRPC) protocol engine for the server.
|
|
|
|
Author:
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include <sysinc.h>
|
|
#include <rpc.h>
|
|
#include <rpcdcep.h>
|
|
#include <rpcerrp.h>
|
|
#include <rpcssp.h>
|
|
#include <util.hxx>
|
|
#include <rpcuuid.hxx>
|
|
#include <mutex.hxx>
|
|
#include <threads.hxx>
|
|
#include <thrdctx.hxx>
|
|
#include <sdict.hxx>
|
|
#include <sdict2.hxx>
|
|
#include <interlck.hxx>
|
|
#include <binding.hxx>
|
|
#include <handle.hxx>
|
|
#include <secclnt.hxx>
|
|
#include <secsvr.hxx>
|
|
#include <hndlsvr.hxx>
|
|
#include <lpcsys.hxx>
|
|
#include <lrpcpack.hxx>
|
|
#include <lrpcsvr.hxx>
|
|
|
|
inline void
|
|
ListenForConnectsWrapper(
|
|
LRPC_ADDRESS * Address
|
|
)
|
|
{
|
|
Address->ListenForConnects();
|
|
}
|
|
|
|
inline void
|
|
ListenForRequestsWrapper(
|
|
LRPC_ASSOCIATION * Association
|
|
)
|
|
{
|
|
Association->ListenForRequests();
|
|
}
|
|
|
|
|
|
LRPC_ADDRESS::LRPC_ADDRESS (
|
|
OUT RPC_STATUS * RpcStatus
|
|
) : RPC_ADDRESS(RpcStatus)
|
|
/*++
|
|
|
|
--*/
|
|
{
|
|
LpcListenPort = NULL;
|
|
CallThreadCount = 0;
|
|
ActiveCallCount = 0;
|
|
ServerListeningFlag = 0;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
LRPC_ADDRESS::FireUpManager (
|
|
IN unsigned int MinimumConcurrentCalls
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
No Op.
|
|
|
|
Arguments:
|
|
|
|
MinimumConcurrentCalls - Unused.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK - We successfully fired up the manager.
|
|
|
|
RPC_S_OUT_OF_THREADS - We could not create one thread.
|
|
|
|
--*/
|
|
{
|
|
UNUSED(MinimumConcurrentCalls);
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
LRPC_ADDRESS::ServerStartingToListen (
|
|
IN unsigned int MinimumCallThreads,
|
|
IN unsigned int MaximumConcurrentCalls,
|
|
IN int ServerThreadsStarted
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine gets called when RpcServerListen is called by the application.
|
|
We need to create the threads we need to receive remote procedure calls.
|
|
|
|
Arguments:
|
|
|
|
MinimumCallThreads - Supplies the minimum number of threads which we
|
|
must create.
|
|
|
|
MaximumConcurrentCalls - Unused.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK - Ok, this address is all ready to start listening for remote
|
|
procedure calls.
|
|
|
|
RPC_S_OUT_OF_THREADS - We could not create enough threads so that we
|
|
have at least the minimum number of call threads required (as
|
|
specified by the MinimumCallThreads argument).
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS RpcStatus;
|
|
|
|
UNUSED(MaximumConcurrentCalls);
|
|
|
|
this->MinimumCallThreads = MinimumCallThreads;
|
|
AddressMutex.Request();
|
|
RpcStatus = Server->CreateThread(
|
|
(THREAD_PROC)&ListenForConnectsWrapper,
|
|
this
|
|
);
|
|
if ( RpcStatus != RPC_S_OK )
|
|
{
|
|
AddressMutex.Clear();
|
|
ASSERT( RpcStatus == RPC_S_OUT_OF_THREADS );
|
|
return(RpcStatus);
|
|
}
|
|
ServerListeningFlag = 1;
|
|
AddressMutex.Clear();
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
|
|
void
|
|
LRPC_ADDRESS::ServerStoppedListening (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We just need to indicate that the server is no longer listening, and
|
|
set the minimum call thread count to one.
|
|
|
|
--*/
|
|
{
|
|
ServerListeningFlag = 0;
|
|
MinimumCallThreads = 1;
|
|
}
|
|
|
|
|
|
unsigned int
|
|
LRPC_ADDRESS::InqNumberOfActiveCalls (
|
|
)
|
|
/*++
|
|
|
|
Return Value:
|
|
|
|
The number of active calls on this address will be returned.
|
|
|
|
--*/
|
|
{
|
|
return(ActiveCallCount);
|
|
}
|
|
|
|
void
|
|
LRPC_ADDRESS::BeginNewCall(
|
|
)
|
|
{
|
|
InterlockedIncrement(&ActiveCallCount);
|
|
}
|
|
|
|
void
|
|
LRPC_ADDRESS::EndCall(
|
|
)
|
|
{
|
|
InterlockedDecrement(&ActiveCallCount);
|
|
}
|
|
|
|
|
|
char* __strchr(char* string, char ch)
|
|
{
|
|
while(*string && *string != ch)
|
|
string++;
|
|
if(*string == ch)
|
|
return string;
|
|
return NULL;
|
|
}
|
|
|
|
RPC_STATUS
|
|
LRPC_ADDRESS::SetupAddressWithEndpoint (
|
|
IN RPC_CHAR PAPI * Endpoint,
|
|
OUT RPC_CHAR PAPI * PAPI * lNetworkAddress,
|
|
OUT unsigned int PAPI * NumNetworkAddress,
|
|
IN void PAPI * SecurityDescriptor, OPTIONAL
|
|
IN unsigned int PendingQueueSize,
|
|
IN RPC_CHAR PAPI * RpcProtocolSequence,
|
|
IN unsigned long EndpointFlags,
|
|
IN unsigned long NICFlags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We need to setup the connection port and get ready to receive remote
|
|
procedure calls. We will use the name of this machine as the network
|
|
address.
|
|
|
|
Arguments:
|
|
|
|
Endpoint - Supplies the endpoint to be used will this address.
|
|
|
|
NetworkAddress - Returns the network address for this server. The
|
|
ownership of the buffer allocated to contain the network address
|
|
passes to the caller.
|
|
|
|
SecurityDescriptor - Optionally supplies a security descriptor to
|
|
be placed on this address.
|
|
|
|
PendingQueueSize - Unused.
|
|
|
|
RpcProtocolSequence - Unused.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK - We successfully setup this address.
|
|
|
|
RPC_S_INVALID_SECURITY_DESC - The supplied security descriptor is
|
|
invalid.
|
|
|
|
RPC_S_CANT_CREATE_ENDPOINT - The endpoint format is correct, but
|
|
the endpoint can not be created.
|
|
|
|
RPC_S_INVALID_ENDPOINT_FORMAT - The endpoint is not a valid
|
|
endpoint for this particular transport interface.
|
|
|
|
RPC_S_OUT_OF_RESOURCES - Insufficient resources are available to
|
|
setup the address.
|
|
|
|
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to setup
|
|
the address.
|
|
|
|
--*/
|
|
{
|
|
BOOL Boolean;
|
|
DWORD NetworkAddressLength = MAX_COMPUTERNAME_LENGTH + 1;
|
|
RPC_STATUS RpcStatus;
|
|
LPC_PROC * Proc;
|
|
char * PAPI * tmpPtr;
|
|
|
|
UNUSED(PendingQueueSize);
|
|
UNUSED(RpcProtocolSequence);
|
|
|
|
if (Endpoint == NULL || Endpoint[0] == NULL) {
|
|
return (RPC_S_INVALID_ENDPOINT_FORMAT);
|
|
}
|
|
|
|
if (__strchr((char *)Endpoint, '\\') != NULL) {
|
|
return (RPC_S_INVALID_ENDPOINT_FORMAT);
|
|
}
|
|
|
|
*lNetworkAddress = new RPC_CHAR[MAX_COMPUTERNAME_LENGTH + 1 + sizeof(RPC_CHAR *)];
|
|
if ( *lNetworkAddress == 0 ) {
|
|
return(RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
|
|
*NumNetworkAddress = 1;
|
|
|
|
tmpPtr = (char * PAPI *) *lNetworkAddress;
|
|
|
|
tmpPtr[0] = (char *) *lNetworkAddress + sizeof(RPC_CHAR * ) ;
|
|
|
|
Boolean = GetComputerName((char *)tmpPtr[0], &NetworkAddressLength);
|
|
|
|
#ifdef DEBUGRPC
|
|
|
|
if ( Boolean != TRUE ) {
|
|
PrintToDebugger("LRPC-S : GetComputerName : %d\n", GetLastError());
|
|
}
|
|
|
|
#endif
|
|
|
|
ASSERT( Boolean == TRUE );
|
|
|
|
LpcListenPort = new LPC_CONNECT_PORT;
|
|
|
|
if (LpcListenPort == NULL) {
|
|
return (RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
|
|
RpcStatus = LpcListenPort->BindToName((LPCSTR)Endpoint);
|
|
if (RpcStatus != RPC_S_OK) {
|
|
delete LpcListenPort;
|
|
return (RpcStatus);
|
|
}
|
|
|
|
return (RPC_S_OK);
|
|
}
|
|
|
|
|
|
static RPC_CHAR HexDigits[] =
|
|
{
|
|
RPC_CONST_CHAR('0'),
|
|
RPC_CONST_CHAR('1'),
|
|
RPC_CONST_CHAR('2'),
|
|
RPC_CONST_CHAR('3'),
|
|
RPC_CONST_CHAR('4'),
|
|
RPC_CONST_CHAR('5'),
|
|
RPC_CONST_CHAR('6'),
|
|
RPC_CONST_CHAR('7'),
|
|
RPC_CONST_CHAR('8'),
|
|
RPC_CONST_CHAR('9'),
|
|
RPC_CONST_CHAR('A'),
|
|
RPC_CONST_CHAR('B'),
|
|
RPC_CONST_CHAR('C'),
|
|
RPC_CONST_CHAR('D'),
|
|
RPC_CONST_CHAR('E'),
|
|
RPC_CONST_CHAR('F')
|
|
};
|
|
|
|
|
|
static RPC_CHAR PAPI *
|
|
ULongToHexString (
|
|
IN RPC_CHAR PAPI * String,
|
|
IN unsigned long Number
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We convert an unsigned long into hex representation in the specified
|
|
string. The result is always eight characters long; zero padding is
|
|
done if necessary.
|
|
|
|
Arguments:
|
|
|
|
String - Supplies a buffer to put the hex representation into.
|
|
|
|
Number - Supplies the unsigned long to convert to hex.
|
|
|
|
Return Value:
|
|
|
|
A pointer to the end of the hex string is returned.
|
|
|
|
--*/
|
|
{
|
|
*String++ = HexDigits[(Number >> 28) & 0x0F];
|
|
*String++ = HexDigits[(Number >> 24) & 0x0F];
|
|
*String++ = HexDigits[(Number >> 20) & 0x0F];
|
|
*String++ = HexDigits[(Number >> 16) & 0x0F];
|
|
*String++ = HexDigits[(Number >> 12) & 0x0F];
|
|
*String++ = HexDigits[(Number >> 8) & 0x0F];
|
|
*String++ = HexDigits[(Number >> 4) & 0x0F];
|
|
*String++ = HexDigits[Number & 0x0F];
|
|
return(String);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
LRPC_ADDRESS::SetupAddressUnknownEndpoint (
|
|
OUT RPC_CHAR PAPI * PAPI * Endpoint,
|
|
OUT RPC_CHAR PAPI * PAPI * lNetworkAddress,
|
|
OUT unsigned int PAPI * NumNetworkAddress,
|
|
IN void PAPI * SecurityDescriptor, OPTIONAL
|
|
IN unsigned int PendingQueueSize,
|
|
IN RPC_CHAR PAPI * RpcProtocolSequence,
|
|
IN unsigned long EndpointFlags,
|
|
IN unsigned long NICFlags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is like LRPC_ADDRESS::SetupAddressWithEndpoint except we need to
|
|
make up the endpoint.
|
|
|
|
Arguments:
|
|
|
|
Endpoint - Returns the endpoint for this address. The ownership
|
|
of the buffer allocated to contain the endpoint passes to the
|
|
caller.
|
|
|
|
NetworkAddress - Returns the network address for this server. The
|
|
ownership of the buffer allocated to contain the network address
|
|
passes to the caller.
|
|
|
|
SecurityDescriptor - Optionally supplies a security descriptor to
|
|
be placed on this address.
|
|
|
|
PendingQueueSize - Unused.
|
|
|
|
RpcProtocolSequence - Unused.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK - We successfully setup this address.
|
|
|
|
RPC_S_INVALID_SECURITY_DESC - The supplied security descriptor is
|
|
invalid.
|
|
|
|
RPC_S_OUT_OF_RESOURCES - Insufficient resources are available to
|
|
setup the address.
|
|
|
|
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to setup
|
|
the address.
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS RpcStatus;
|
|
RPC_CHAR DynamicEndpoint[64];
|
|
RPC_CHAR * String;
|
|
|
|
UNUSED(PendingQueueSize);
|
|
UNUSED(RpcProtocolSequence);
|
|
|
|
for (;;)
|
|
{
|
|
String = DynamicEndpoint;
|
|
|
|
*String++ = RPC_CONST_CHAR('L');
|
|
*String++ = RPC_CONST_CHAR('R');
|
|
*String++ = RPC_CONST_CHAR('P');
|
|
*String++ = RPC_CONST_CHAR('C');
|
|
|
|
String = ULongToHexString(String, LpcSystemGetNextSequenceNumber());
|
|
*String++ = RPC_CONST_CHAR('.');
|
|
String = ULongToHexString(String, 1);
|
|
*String = 0;
|
|
|
|
RpcStatus = SetupAddressWithEndpoint(DynamicEndpoint, lNetworkAddress, NumNetworkAddress,
|
|
SecurityDescriptor, 0, 0, EndpointFlags, NICFlags);
|
|
|
|
if ( RpcStatus != RPC_S_DUPLICATE_ENDPOINT )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( RpcStatus == RPC_S_OK )
|
|
{
|
|
*Endpoint = DuplicateString(DynamicEndpoint);
|
|
if ( *Endpoint == 0 )
|
|
{
|
|
return(RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
#if DBG
|
|
|
|
if ( ( RpcStatus != RPC_S_INVALID_SECURITY_DESC )
|
|
&& ( RpcStatus != RPC_S_OUT_OF_RESOURCES )
|
|
&& ( RpcStatus != RPC_S_OUT_OF_MEMORY ) )
|
|
{
|
|
PrintToDebugger("LRPC-S : SetupAddressWithEndpoint : %d\n", RpcStatus);
|
|
}
|
|
|
|
#endif // DBG
|
|
|
|
ASSERT( ( RpcStatus == RPC_S_INVALID_SECURITY_DESC )
|
|
|| ( RpcStatus == RPC_S_OUT_OF_RESOURCES )
|
|
|| ( RpcStatus == RPC_S_OUT_OF_MEMORY ) );
|
|
|
|
return(RpcStatus);
|
|
}
|
|
|
|
|
|
void
|
|
LRPC_ADDRESS::ListenForConnects (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Here is where we receive remote procedure calls to this address. One
|
|
more threads will be executing this routine at once.
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS RpcStatus;
|
|
LPC_DATA_PORT * ClientPort;
|
|
LONG ClientId;
|
|
|
|
while (1) {
|
|
RpcStatus = LpcListenPort->Listen(&ClientPort);
|
|
if (RpcStatus != RPC_S_OK) {
|
|
LpcListenPort->Dereference();
|
|
LpcListenPort = NULL;
|
|
return;
|
|
}
|
|
DispatchConnectRequest(ClientPort);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
LRPC_ADDRESS::DispatchConnectRequest(
|
|
LPC_DATA_PORT * ClientPort
|
|
)
|
|
{
|
|
RPC_STATUS RpcStatus;
|
|
LPC_DATA_PORT * ServerPort;
|
|
LRPC_ASSOCIATION * Association;
|
|
LRPC_SASSOC_GROUP * AssocGroup;
|
|
|
|
ServerPort = LpcListenPort->Accept(ClientPort);
|
|
if (ServerPort == NULL) {
|
|
#ifdef DEBUGRPC
|
|
PrintToDebugger("LRPC-S: Accept failed %x\n", LpcListenPort);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
Association = new LRPC_ASSOCIATION();
|
|
if (Association == NULL) {
|
|
delete ServerPort;
|
|
return;
|
|
}
|
|
|
|
Association->LpcServerPort = ServerPort;
|
|
|
|
AssocGroup = FindAssocGroup(ClientPort->ProcessId);
|
|
if (AssocGroup == NULL) {
|
|
AssocGroup = new LRPC_SASSOC_GROUP(this, ClientPort->ProcessId);
|
|
if (AssocGroup == NULL) {
|
|
delete Association;
|
|
return;
|
|
}
|
|
}
|
|
|
|
Association->AssocGroup = AssocGroup;
|
|
AssocGroup->AddRef();
|
|
|
|
#ifdef DEBUGRPC_DETAIL
|
|
PrintToDebugger("LRPC-S: Association %x assigned to ag=%x addr=%x\n", Association, AssocGroup, this);
|
|
#endif
|
|
|
|
RpcStatus = Server->CreateThread(
|
|
(THREAD_PROC)&ListenForRequestsWrapper, Association);
|
|
if ( RpcStatus != RPC_S_OK ) {
|
|
delete Association;
|
|
return;
|
|
}
|
|
}
|
|
|
|
LRPC_SASSOC_GROUP *
|
|
LRPC_ADDRESS::FindAssocGroup(
|
|
LONG ClientId
|
|
)
|
|
{
|
|
LRPC_SASSOC_GROUP * AssocGroup;
|
|
|
|
CritSec.Enter();
|
|
|
|
AssocGroups.Reset();
|
|
while ( (AssocGroup = AssocGroups.Next()) != 0) {
|
|
if (AssocGroup->ReferenceCount > 0 && AssocGroup->ClientId == ClientId) {
|
|
CritSec.Leave();
|
|
return (AssocGroup);
|
|
}
|
|
}
|
|
|
|
CritSec.Leave();
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
void
|
|
LRPC_ADDRESS::InsertAssocGroup(
|
|
LRPC_SASSOC_GROUP * AssocGroup
|
|
)
|
|
{
|
|
CritSec.Enter();
|
|
|
|
AssocGroup->Key = AssocGroups.Insert(AssocGroup);
|
|
|
|
CritSec.Leave();
|
|
}
|
|
|
|
void
|
|
LRPC_ADDRESS::DeleteAssocGroup(
|
|
unsigned short Key
|
|
)
|
|
{
|
|
CritSec.Enter();
|
|
|
|
AssocGroups.Delete(Key);
|
|
|
|
CritSec.Leave();
|
|
}
|
|
|
|
|
|
LRPC_ASSOCIATION::LRPC_ASSOCIATION (
|
|
)
|
|
/*++
|
|
|
|
--*/
|
|
{
|
|
LpcServerPort = NULL;
|
|
|
|
AssocGroup = NULL;
|
|
|
|
#ifdef DEBUGRPC_DETAIL
|
|
PrintToDebugger("LRPC-S: Created Association %x\n", this);
|
|
#endif
|
|
}
|
|
|
|
|
|
LRPC_ASSOCIATION::~LRPC_ASSOCIATION (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We will call this routine when the client has notified us that this port
|
|
has closed, and there are no calls outstanding on it.
|
|
|
|
--*/
|
|
{
|
|
if (AssocGroup != NULL) {
|
|
AssocGroup->Dereference();
|
|
AssocGroup = NULL;
|
|
}
|
|
|
|
GlobalMutexRequest();
|
|
|
|
if (LpcServerPort) {
|
|
LpcServerPort->Disconnect();
|
|
LpcServerPort->Dereference();
|
|
LpcServerPort = NULL;
|
|
}
|
|
|
|
GlobalMutexClear();
|
|
|
|
#ifdef DEBUGRPC_DETAIL
|
|
PrintToDebugger("LRPC-S: Deleted Association %x\n", this);
|
|
#endif
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
LRPC_ASSOCIATION::AddBinding (
|
|
IN OUT LRPC_BIND_EXCHANGE * Bind
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We will attempt to add a new binding to this association.
|
|
|
|
Arguments:
|
|
|
|
BindExchange - Supplies a description of the interface to which the
|
|
client wish to bind.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS RpcStatus;
|
|
RPC_SYNTAX_IDENTIFIER TransferSyntax;
|
|
RPC_INTERFACE * RpcInterface;
|
|
LRPC_SBINDING * Binding;
|
|
|
|
RpcStatus = AssocGroup->Address->FindInterfaceTransfer(&Bind->InterfaceId,
|
|
&Bind->TransferSyntax, 1, &TransferSyntax, &RpcInterface);
|
|
if ( RpcStatus != RPC_S_OK )
|
|
{
|
|
return(RpcStatus);
|
|
}
|
|
|
|
Binding = new LRPC_SBINDING(RpcInterface, &Bind->TransferSyntax);
|
|
if ( Binding == 0 )
|
|
{
|
|
return(RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
Binding->PresentationContext = Bindings.Insert(Binding);
|
|
if ( Binding->PresentationContext == -1 )
|
|
{
|
|
delete Binding;
|
|
return(RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
Bind->PresentationContext = Binding->PresentationContext;
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
LRPC_ASSOCIATION::DispatchRequest (
|
|
LRPC_MESSAGE * Any,
|
|
LPVOID Buffer,
|
|
DWORD BufferSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We will process the original request message in this routine, dispatch
|
|
the remote procedure call to the stub, and then send the response
|
|
message.
|
|
|
|
Arguments:
|
|
|
|
Any - Supplies the request message which was received from
|
|
the client.
|
|
|
|
Return Value:
|
|
|
|
The reply message to be sent to the client will be returned.
|
|
|
|
--*/
|
|
{
|
|
RPC_MESSAGE Message;
|
|
LRPC_SBINDING * SBinding;
|
|
LRPC_SCALL SCall(this);
|
|
RPC_STATUS RpcStatus, ExceptionCode;
|
|
|
|
SBinding = Bindings.Find(Any->Rpc.RpcHeader.PresentationContext);
|
|
if ( SBinding == 0 )
|
|
{
|
|
return (SendFault(RPC_S_UNKNOWN_IF));
|
|
}
|
|
|
|
SCall.SBinding = SBinding;
|
|
|
|
Message.Buffer = Buffer;
|
|
Message.BufferLength = (unsigned int)BufferSize;
|
|
Message.TransferSyntax = &SBinding->TransferSyntax;
|
|
Message.ProcNum = Any->Rpc.RpcHeader.ProcedureNumber;
|
|
Message.Handle = &SCall;
|
|
|
|
// NDR_DREP_ASCII | NDR_DREP_LITTLE_ENDIAN | NDR_DREP_IEEE
|
|
|
|
Message.DataRepresentation = 0x00 | 0x10 | 0x0000;
|
|
|
|
if ( Any->Rpc.RpcHeader.ObjectUuidFlag != 0 )
|
|
{
|
|
SCall.ObjectUuidFlag = 1;
|
|
RpcpMemoryCopy(&SCall.ObjectUuid,
|
|
&Any->Rpc.RpcHeader.ObjectUuid, sizeof(UUID));
|
|
}
|
|
|
|
RpcpSetThreadContext(&SCall);
|
|
|
|
// pipes not supported on LRPC,
|
|
// the received buffer is always complete
|
|
Message.RpcFlags = RPC_BUFFER_COMPLETE ;
|
|
|
|
if ( SCall.ObjectUuidFlag != 0 )
|
|
{
|
|
RpcStatus = SBinding->RpcInterface->DispatchToStubWithObject(
|
|
&Message,
|
|
&SCall.ObjectUuid,
|
|
0,
|
|
&ExceptionCode);
|
|
}
|
|
else
|
|
{
|
|
RpcStatus = SBinding->RpcInterface->DispatchToStub(&Message, 0,
|
|
&ExceptionCode);
|
|
}
|
|
RpcpSetThreadContext(0);
|
|
|
|
if (RpcStatus != RPC_S_OK) {
|
|
if (RpcStatus == RPC_P_EXCEPTION_OCCURED) {
|
|
SendFault(ExceptionCode);
|
|
return (RPC_S_OK);
|
|
}
|
|
if (RpcStatus == RPC_S_NOT_LISTENING) {
|
|
RpcStatus = RPC_S_SERVER_TOO_BUSY;
|
|
}
|
|
SendFault(RpcStatus);
|
|
return (RpcStatus);
|
|
}
|
|
|
|
Any->Rpc.RpcHeader.MessageType = LRPC_MSG_RESPONSE;
|
|
if (LpcServerPort->Send(Any,
|
|
sizeof(LRPC_RPC_MESSAGE),
|
|
Message.Buffer,
|
|
Message.BufferLength) != RPC_S_OK) {
|
|
return (RPC_S_CALL_FAILED);
|
|
}
|
|
|
|
return (RPC_S_OK);
|
|
}
|
|
|
|
static unsigned char AlignFour[4] =
|
|
{
|
|
0,
|
|
3,
|
|
2,
|
|
1
|
|
};
|
|
|
|
void
|
|
LRPC_ASSOCIATION::ListenForRequests(
|
|
)
|
|
{
|
|
LRPC_MESSAGE Any;
|
|
LPVOID GlobalBuf;
|
|
DWORD GlobalBufSize;
|
|
DWORD ActualSize;
|
|
RPC_STATUS RpcStatus;
|
|
|
|
while (1) {
|
|
RpcStatus = LpcServerPort->Receive(&Any,
|
|
sizeof(Any),
|
|
&ActualSize,
|
|
&GlobalBuf,
|
|
&GlobalBufSize);
|
|
if (RpcStatus != RPC_S_OK) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
switch (Any.Bind.MessageType) {
|
|
case LRPC_MSG_REQUEST:
|
|
RpcStatus = DispatchRequest(&Any, GlobalBuf, GlobalBufSize);
|
|
break;
|
|
case LRPC_MSG_BIND:
|
|
Any.Bind.BindExchange.RpcStatus = AddBinding(&Any.Bind.BindExchange);
|
|
RpcStatus = LpcServerPort->Send(&Any, sizeof(LRPC_BIND_MESSAGE), 0, 0);
|
|
#ifdef DEBUGRPC
|
|
if (RpcStatus != RPC_S_OK) {
|
|
PrintToDebugger("LRPC-S: send LRPC_BIND_MESSAGE %d\n", RpcStatus);
|
|
}
|
|
#endif
|
|
break;
|
|
case LRPC_MSG_CLOSE:
|
|
goto Cleanup;
|
|
default:
|
|
#ifdef DEBUGRPC
|
|
PrintToDebugger("LRPC-S: invalid message %d\n", Any.Bind.MessageType);
|
|
#endif
|
|
SendFault(RPC_S_PROTOCOL_ERROR);
|
|
break;
|
|
}
|
|
}
|
|
Cleanup:
|
|
|
|
delete this;
|
|
}
|
|
|
|
RPC_STATUS
|
|
LRPC_ASSOCIATION::SendFault(
|
|
RPC_STATUS RpcStatus
|
|
)
|
|
{
|
|
LRPC_FAULT_MESSAGE Fault;
|
|
|
|
Fault.MessageType = LRPC_MSG_FAULT;
|
|
Fault.RpcStatus = RpcStatus;
|
|
|
|
return LpcServerPort->Send(&Fault, sizeof(Fault), 0, 0);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
LRPC_SCALL::GetBuffer (
|
|
IN OUT PRPC_MESSAGE Message
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We will allocate a buffer which will be used to either send a request
|
|
or receive a response.
|
|
|
|
Arguments:
|
|
|
|
Message - Supplies the length of the buffer that is needed. The buffer
|
|
will be returned.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK - A buffer has been successfully allocated. It will be of at
|
|
least the required length.
|
|
|
|
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to allocate that
|
|
large a buffer.
|
|
|
|
--*/
|
|
{
|
|
Message->Buffer = Association->LpcServerPort->GetBuffer(Message->BufferLength);
|
|
if (Message->Buffer == NULL) {
|
|
return (RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
|
|
ASSERT( ((unsigned long) Message->Buffer) % 8 == 0 );
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
LRPC_SCALL::SendReceive (
|
|
IN OUT PRPC_MESSAGE Message
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
Message - Supplies the request and returns the response of a remote
|
|
procedure call.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK - The remote procedure call completed successful.
|
|
|
|
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to perform the
|
|
remote procedure call.
|
|
|
|
RPC_S_OUT_OF_RESOURCES - Insufficient resources are available to complete
|
|
the remote procedure call.
|
|
|
|
--*/
|
|
{
|
|
DWORD ActualSize;
|
|
RPC_STATUS ExceptionCode, RpcStatus;
|
|
LPC_DATA_PORT * ServerPort;
|
|
LRPC_MESSAGE OutMsg;
|
|
LRPC_MESSAGE InMsg;
|
|
int OutMsgLen;
|
|
void * OutMsgBuf;
|
|
int OutMsgBufLen;
|
|
|
|
Association->AssocGroup->Address->Server->OutgoingCallback();
|
|
|
|
// NDR_DREP_ASCII | NDR_DREP_LITTLE_ENDIAN | NDR_DREP_IEEE
|
|
Message->DataRepresentation = 0x00 | 0x10 | 0x0000;
|
|
|
|
ServerPort = Association->LpcServerPort;
|
|
|
|
OutMsg.Rpc.RpcHeader.MessageType = LRPC_MSG_REQUEST;
|
|
OutMsg.Rpc.RpcHeader.ProcedureNumber = Message->ProcNum;
|
|
OutMsg.Rpc.RpcHeader.PresentationContext = SBinding->PresentationContext;
|
|
OutMsgLen = sizeof(LRPC_RPC_MESSAGE);
|
|
OutMsgBuf = Message->Buffer;
|
|
OutMsgBufLen = Message->BufferLength;
|
|
|
|
while (1) {
|
|
|
|
RpcStatus = ServerPort->Transceive(&OutMsg,
|
|
OutMsgLen,
|
|
OutMsgBuf,
|
|
OutMsgBufLen,
|
|
&InMsg,
|
|
sizeof(LRPC_MESSAGE),
|
|
&ActualSize,
|
|
&Message->Buffer,
|
|
(PDWORD)&Message->BufferLength);
|
|
|
|
if (RpcStatus != RPC_S_OK) {
|
|
return (RpcStatus != RPC_S_OUT_OF_MEMORY ? RPC_S_CALL_FAILED
|
|
: RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
|
|
if ( InMsg.Rpc.RpcHeader.MessageType == LRPC_MSG_FAULT ) {
|
|
return (InMsg.Fault.RpcStatus);
|
|
}
|
|
|
|
if ( InMsg.Rpc.RpcHeader.MessageType == LRPC_MSG_RESPONSE ) {
|
|
Message->Handle = (RPC_BINDING_HANDLE) this;
|
|
return (RPC_S_OK);
|
|
}
|
|
|
|
if ( InMsg.Rpc.RpcHeader.MessageType == LRPC_MSG_CLOSE ) {
|
|
return (RPC_S_CALL_FAILED);
|
|
}
|
|
|
|
ASSERT(InMsg.Rpc.RpcHeader.MessageType == LRPC_MSG_REQUEST );
|
|
|
|
Message->TransferSyntax = &SBinding->TransferSyntax;
|
|
Message->ProcNum = InMsg.Rpc.RpcHeader.ProcedureNumber;
|
|
|
|
// pipes not supported on LRPC,
|
|
// the received buffer is always complete
|
|
Message->RpcFlags = RPC_BUFFER_COMPLETE ;
|
|
|
|
if ( ObjectUuidFlag != 0 ) {
|
|
RpcStatus = SBinding->RpcInterface->DispatchToStubWithObject(Message, &ObjectUuid, 1, &ExceptionCode);
|
|
} else {
|
|
RpcStatus = SBinding->RpcInterface->DispatchToStub(Message,
|
|
1, &ExceptionCode);
|
|
}
|
|
|
|
if ( RpcStatus != RPC_S_OK ) {
|
|
|
|
ASSERT( RpcStatus == RPC_P_EXCEPTION_OCCURED
|
|
|| RpcStatus == RPC_S_PROCNUM_OUT_OF_RANGE );
|
|
|
|
OutMsg.Fault.MessageType = LRPC_MSG_FAULT;
|
|
OutMsg.Fault.RpcStatus = RpcStatus != RPC_S_PROCNUM_OUT_OF_RANGE ?
|
|
ExceptionCode : RPC_S_PROCNUM_OUT_OF_RANGE;
|
|
OutMsgLen = sizeof(LRPC_FAULT_MESSAGE);
|
|
OutMsgBuf = NULL;
|
|
OutMsgBufLen = 0;
|
|
|
|
} else {
|
|
|
|
OutMsg.Rpc.RpcHeader.MessageType = LRPC_MSG_RESPONSE;
|
|
OutMsgLen = sizeof(LRPC_RPC_MESSAGE);
|
|
OutMsgBuf = Message->Buffer;
|
|
OutMsgBufLen = Message->BufferLength;
|
|
|
|
}
|
|
}
|
|
|
|
// Never here
|
|
|
|
return (RPC_S_INTERNAL_ERROR);
|
|
}
|
|
|
|
|
|
void
|
|
LRPC_SCALL::FreeBuffer (
|
|
IN PRPC_MESSAGE Message
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We will free the supplied buffer.
|
|
|
|
Arguments:
|
|
|
|
Message - Supplies the buffer to be freed.
|
|
|
|
--*/
|
|
{
|
|
Association->LpcServerPort->FreeBuffer(Message->Buffer);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
LRPC_SCALL::ImpersonateClient (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We will impersonate the client which made the remote procedure call.
|
|
|
|
--*/
|
|
{
|
|
return (RPC_S_CANNOT_SUPPORT);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
LRPC_SCALL::RevertToSelf (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This reverts a server thread back to itself after impersonating a client.
|
|
We just check to see if the server thread is impersonating; this optimizes
|
|
the common case.
|
|
|
|
--*/
|
|
{
|
|
return (RPC_S_CANNOT_SUPPORT);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
LRPC_SCALL::IsClientLocal (
|
|
OUT unsigned int * ClientLocalFlag
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
A client using LRPC will always be local.
|
|
|
|
Arguments:
|
|
|
|
ClientLocalFlag - Returns a flag which will always be set to a non-zero
|
|
value indicating that the client is local.
|
|
|
|
--*/
|
|
{
|
|
UNUSED(this);
|
|
|
|
*ClientLocalFlag = 1;
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
LRPC_SCALL::ConvertToServerBinding (
|
|
OUT RPC_BINDING_HANDLE __RPC_FAR * ServerBinding
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
If possible, convert this call into a server binding, meaning a
|
|
binding handle pointing back to the client.
|
|
|
|
Arguments:
|
|
|
|
ServerBinding - Returns the server binding.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK - The server binding has successfully been created.
|
|
|
|
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to allocate
|
|
a new binding handle.
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS RpcStatus;
|
|
RPC_CHAR UuidString[37];
|
|
RPC_CHAR * StringBinding;
|
|
DWORD NetworkAddressLength = MAX_COMPUTERNAME_LENGTH + 1;
|
|
BOOL Boolean;
|
|
RPC_CHAR * NetworkAddress;
|
|
|
|
if ( ObjectUuidFlag != 0 ) {
|
|
ObjectUuid.ConvertToString(UuidString);
|
|
UuidString[36] = 0;
|
|
}
|
|
|
|
NetworkAddress = new RPC_CHAR[NetworkAddressLength];
|
|
if ( NetworkAddress == 0 ) {
|
|
return(RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
|
|
Boolean = GetComputerName((char *)NetworkAddress, &NetworkAddressLength);
|
|
|
|
#ifdef DEBUGRPC
|
|
|
|
if ( Boolean != TRUE ) {
|
|
PrintToDebugger("LRPC-S : GetComputerName : %d\n", GetLastError());
|
|
}
|
|
|
|
#endif
|
|
|
|
ASSERT( Boolean == TRUE );
|
|
|
|
RpcStatus = RpcStringBindingCompose((ObjectUuidFlag != 0 ? UuidString : 0),
|
|
RPC_CONST_STRING("ncalrpc"), NetworkAddress, 0, 0, &StringBinding);
|
|
delete NetworkAddress;
|
|
if ( RpcStatus != RPC_S_OK ) {
|
|
return(RpcStatus);
|
|
}
|
|
|
|
RpcStatus = RpcBindingFromStringBinding(StringBinding, ServerBinding);
|
|
RpcStringFree(&StringBinding);
|
|
return(RpcStatus);
|
|
}
|
|
|
|
|
|
void
|
|
LRPC_SCALL::InquireObjectUuid (
|
|
OUT RPC_UUID * ObjectUuid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine copies the object uuid from the call into the supplied
|
|
ObjectUuid argument.
|
|
|
|
Arguments:
|
|
|
|
ObjectUuid - Returns a copy of the object uuid passed by the client
|
|
in the remote procedure call.
|
|
|
|
--*/
|
|
{
|
|
if ( ObjectUuidFlag == 0 )
|
|
{
|
|
ObjectUuid->SetToNullUuid();
|
|
}
|
|
else
|
|
{
|
|
ObjectUuid->CopyUuid(&(this->ObjectUuid));
|
|
}
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
LRPC_SCALL::ToStringBinding (
|
|
OUT RPC_CHAR ** StringBinding
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We need to convert this call into a string binding. We will ask the
|
|
address for a binding handle which we can then convert into a string
|
|
binding.
|
|
|
|
Arguments:
|
|
|
|
StringBinding - Returns the string binding for this call.
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
BINDING_HANDLE * BindingHandle;
|
|
RPC_STATUS RpcStatus;
|
|
|
|
BindingHandle = Association->AssocGroup->Address->InquireBinding();
|
|
|
|
if ( BindingHandle == 0 ) {
|
|
return(RPC_S_OUT_OF_MEMORY);
|
|
}
|
|
|
|
RpcStatus = BindingHandle->ToStringBinding(StringBinding);
|
|
BindingHandle->BindingFree();
|
|
return(RpcStatus);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
LRPC_SCALL::MonitorAssociation (
|
|
IN PRPC_RUNDOWN RundownRoutine,
|
|
IN void * Context
|
|
)
|
|
{
|
|
ASSERT(Association != NULL);
|
|
|
|
ASSERT(Association->AssocGroup != NULL);
|
|
|
|
#ifdef DEBUGRPC_DETAIL
|
|
PrintToDebugger("LRPC-S: MonitorAssociation (%x) ass=%x, ag=%x\n", Context, Association, Association->AssocGroup);
|
|
#endif
|
|
|
|
return(Association->AssocGroup->MonitorAssociation(RundownRoutine, Context));
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
LRPC_SCALL::StopMonitorAssociation (
|
|
)
|
|
{
|
|
ASSERT(Association != NULL);
|
|
|
|
ASSERT(Association->AssocGroup != NULL);
|
|
|
|
#ifdef DEBUGRPC_DETAIL
|
|
PrintToDebugger("LRPC-S: StopMonitorAssociation ass=%x, ag=%x\n", Association, Association->AssocGroup);
|
|
#endif
|
|
|
|
return(Association->AssocGroup->StopMonitorAssociation());
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
LRPC_SCALL::GetAssociationContext (
|
|
OUT void ** AssociationContext
|
|
)
|
|
{
|
|
ASSERT(Association != NULL);
|
|
|
|
ASSERT(Association->AssocGroup != NULL);
|
|
|
|
*AssociationContext = Association->AssocGroup->AssociationContext();
|
|
|
|
#ifdef DEBUGRPC_DETAIL
|
|
PrintToDebugger("LRPC-S: GetAssociationContext got %x ass=%x, ag=%x\n", *AssociationContext, Association, Association->AssocGroup);
|
|
#endif
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
LRPC_SCALL::SetAssociationContext (
|
|
IN void * Context
|
|
)
|
|
{
|
|
ASSERT(Association != NULL);
|
|
|
|
ASSERT(Association->AssocGroup != NULL);
|
|
|
|
Association->AssocGroup->SetAssociationContext(Context);
|
|
|
|
#ifdef DEBUGRPC_DETAIL
|
|
PrintToDebugger("LRPC-S: SetAssociationContext(%x) ass=%x\n, ag=%x", Context, Association, Association->AssocGroup);
|
|
#endif
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
RPC_STATUS
|
|
LRPC_SCALL::InquireAuthClient (
|
|
OUT RPC_AUTHZ_HANDLE PAPI * Privileges,
|
|
OUT RPC_CHAR PAPI * PAPI * ServerPrincipalName, OPTIONAL
|
|
OUT unsigned long PAPI * AuthenticationLevel,
|
|
OUT unsigned long PAPI * AuthenticationService,
|
|
OUT unsigned long PAPI * AuthorizationService
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Each protocol module must define this routine: it is used to obtain
|
|
the authentication and authorization information about a client making
|
|
the remote procedure call represented by this.
|
|
|
|
Arguments:
|
|
|
|
Privileges - Returns a the privileges of the client.
|
|
|
|
ServerPrincipalName - Returns the server principal name which the client
|
|
specified.
|
|
|
|
AuthenticationLevel - Returns the authentication level requested by
|
|
the client.
|
|
|
|
AuthenticationService - Returns the authentication service requested by
|
|
the client.
|
|
|
|
AuthorizationService - Returns the authorization service requested by
|
|
the client.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_CANNOT_SUPPORT - This value will always be returned.
|
|
|
|
--*/
|
|
{
|
|
UNUSED(this);
|
|
UNUSED(Privileges);
|
|
UNUSED(ServerPrincipalName);
|
|
UNUSED(AuthenticationLevel);
|
|
UNUSED(AuthenticationService);
|
|
UNUSED(AuthorizationService);
|
|
|
|
return(RPC_S_CANNOT_SUPPORT);
|
|
}
|
|
|
|
LRPC_SASSOC_GROUP::LRPC_SASSOC_GROUP(
|
|
LRPC_ADDRESS * Address,
|
|
LONG ClientId
|
|
)
|
|
{
|
|
this->Address = Address;
|
|
|
|
this->ClientId = ClientId;
|
|
|
|
ReferenceCount = 0;
|
|
|
|
Address->InsertAssocGroup(this);
|
|
|
|
#ifdef DEBUGRPC_DETAIL
|
|
PrintToDebugger("LRPC-S: AssocGroup %x created, ClientId = %x\n", this, ClientId);
|
|
#endif
|
|
}
|
|
|
|
LRPC_SASSOC_GROUP::~LRPC_SASSOC_GROUP(
|
|
)
|
|
{
|
|
ASSERT(Address != NULL);
|
|
|
|
Address->DeleteAssocGroup(Key);
|
|
|
|
Address = NULL;
|
|
|
|
#ifdef DEBUGRPC_DETAIL
|
|
PrintToDebugger("LRPC-S: AssocGroup %x deleted\n");
|
|
#endif
|
|
}
|
|
|
|
void
|
|
LRPC_SASSOC_GROUP::AddRef(
|
|
)
|
|
{
|
|
InterlockedIncrement(&ReferenceCount);
|
|
}
|
|
|
|
void
|
|
LRPC_SASSOC_GROUP::Dereference(
|
|
)
|
|
{
|
|
if (InterlockedDecrement(&ReferenceCount) == 0) {
|
|
delete this;
|
|
}
|
|
}
|
|
|
|
|
|
RPC_ADDRESS *
|
|
SpcCreateRpcAddress (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We just to create a new LRPC_ADDRESS. This routine is a proxy for the
|
|
new constructor to isolate the other modules.
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS RpcStatus = RPC_S_OK;
|
|
RPC_ADDRESS * RpcAddress;
|
|
|
|
RpcAddress = new LRPC_ADDRESS(&RpcStatus);
|
|
if ( RpcStatus != RPC_S_OK )
|
|
{
|
|
return(0);
|
|
}
|
|
return(RpcAddress);
|
|
}
|