windows-nt/Source/XPSP1/NT/com/rpc/runtime/mtrt/hndlsvr.cxx
2020-09-26 16:20:57 +08:00

5405 lines
144 KiB
C++
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1990 - 1999
//
// File: hndlsvr.cxx
//
//--------------------------------------------------------------------------
/* --------------------------------------------------------------------
Microsoft OS/2 LAN Manager
Copyright(c) Microsoft Corp., 1990
-------------------------------------------------------------------- */
/* --------------------------------------------------------------------
File : hndlsvr.cxx
Description :
This file contains the implementations of the classes defined in hndlsvr.hxx.
These routines are independent of the actual RPC protocol / transport layer.
In addition, these routines are also independent of the specific operating
system in use.
History :
mikemon ??-??-?? Beginning of recorded history.
mikemon 10-15-90 Changed the shutdown functionality to PauseExecution
rather than suspending and resuming a thread.
mikemon 12-28-90 Updated the comments to match reality.
connieh 17-Feb-94 Created RPC_SERVER::RegisterRpcForwardFunction
Kamen Moutafov (KamenM) Dec 99 - Feb 2000 - Support for cell debugging stuff
-------------------------------------------------------------------- */
#include <precomp.hxx>
#include <queue.hxx>
#include <hndlsvr.hxx>
#include <svrbind.hxx>
#include <thrdctx.hxx>
#include <rpcobj.hxx>
#include <rpccfg.h>
#include <sdict2.hxx>
#include <dispatch.h>
#include <queue.hxx>
#include <lpcpack.hxx>
#include <lpcsvr.hxx>
extern LRPC_SERVER *GlobalLrpcServer ;
#include <ProtBind.hxx>
#include <osfpcket.hxx>
#include <bitset.hxx>
#include <osfclnt.hxx>
#include <secsvr.hxx>
#include <osfsvr.hxx>
#include <dgpkt.hxx>
#include <dgsvr.hxx>
RPC_STATUS RPC_ENTRY
DefaultCallbackFn (
IN RPC_IF_HANDLE InterfaceUuid,
IN void *Context
)
/*++
Function Name:DefaultCallbackFn
Parameters:
Description:
Returns:
RPC_S_OK: Access is allowed
other failures: Access is denied
--*/
{
RPC_CALL_ATTRIBUTES CallAttributes;
RPC_STATUS Status;
CallAttributes.Version = RPC_CALL_ATTRIBUTES_VERSION;
CallAttributes.Flags = 0;
Status = RpcServerInqCallAttributesW(Context,
&CallAttributes);
if (Status != RPC_S_OK)
return Status;
if ((CallAttributes.AuthenticationService == RPC_C_AUTHN_NONE)
|| (CallAttributes.AuthenticationLevel == RPC_C_AUTHN_LEVEL_NONE)
|| (CallAttributes.NullSession)
)
{
return RPC_S_ACCESS_DENIED;
}
return RPC_S_OK;
}
RPC_INTERFACE::RPC_INTERFACE (
IN RPC_SERVER_INTERFACE PAPI * RpcInterfaceInformation,
IN RPC_SERVER * Server,
IN unsigned int Flags,
IN unsigned int MaxCalls,
IN unsigned int MaxRpcSize,
IN RPC_IF_CALLBACK_FN PAPI *IfCallbackFn,
OUT RPC_STATUS *Status
) : NullManagerActiveCallCount(0), AutoListenCallCount(0)
/*++
Routine Description:
This method will get called to construct an instance of the
RPC_INTERFACE class. We have got to make a copy of the rpc interface
information supplied. The copy is necessary because we do not delete
interfaces when they are unregistered. We just mark them as being
inactive. In addition, we need to set the NullManagerFlag
to zero, since this is used as the flag indicating whether we have
got a manager for the NULL type UUID.
Arguments:
RpcInterfaceInformation - Supplies the rpc interface information
which describes this interface.
Server - Supplies the rpc server which owns this rpc interface.
--*/
{
ALLOCATE_THIS(RPC_INTERFACE);
unsigned int Length;
PipeInterfaceFlag = 0;
SequenceNumber = 1;
#if DBG
Strict = iuschDontKnow;
#endif
if (RpcInterfaceInformation->Length > sizeof(RPC_SERVER_INTERFACE) )
{
Length = sizeof(RPC_SERVER_INTERFACE);
}
else
{
Length = RpcInterfaceInformation->Length;
}
if ((RpcInterfaceInformation->Length > NT351_INTERFACE_SIZE)
&& (RpcInterfaceInformation->Flags & RPC_INTERFACE_HAS_PIPES))
{
PipeInterfaceFlag = 1;
}
RpcpMemoryCopy(&(this->RpcInterfaceInformation), RpcInterfaceInformation, Length);
NullManagerFlag = 0;
ManagerCount = 0;
this->Server = Server;
this->Flags = Flags ;
this->MaxCalls = MaxCalls ;
this->MaxRpcSize = MaxRpcSize;
fBindingsExported = 0;
UuidVector = NULL;
if (Flags & RPC_IF_ALLOW_SECURE_ONLY
&& IfCallbackFn == NULL)
{
this->CallbackFn = DefaultCallbackFn;
}
else
{
this->CallbackFn = IfCallbackFn ;
}
if (DoesInterfaceSupportMultipleTransferSyntaxes(RpcInterfaceInformation))
{
*Status = NdrServerGetSupportedSyntaxes(RpcInterfaceInformation,
&NumberOfSupportedTransferSyntaxes,
&TransferSyntaxesArray, &PreferredTransferSyntax);
if (*Status != RPC_S_OK)
return;
}
else
{
NumberOfSupportedTransferSyntaxes = 0;
}
*Status = RPC_S_OK;
}
RPC_STATUS
RPC_INTERFACE::RegisterTypeManager (
IN RPC_UUID PAPI * ManagerTypeUuid OPTIONAL,
IN RPC_MGR_EPV PAPI * ManagerEpv OPTIONAL
)
/*++
Routine Description:
This method is used to register a type manager with this interface.
If no type UUID is specified, or it is the NULL type UUID, we
stick the manager entry point vector right in this instance (assuming
that there is not already one), otherwise, we put it into the
dictionary of interface manager objects.
Arguments:
ManagerTypeUuid - Optionally supplies the type UUID for the manager
we want to register with this rpc interface. If no type UUID
is supplied then the NULL type UUID is assumed.
ManagerEpv - Supplies then entry point vector for this manager. This
vector is used to dispatch from the stub to the application
code.
Return Values:
RPC_S_OK - The type manager has been successfully added to this
rpc interface.
RPC_S_OUT_OF_MEMORY - Insufficient memory is availabe to add the
type manager to the rpc interface.
RPC_S_TYPE_ALREADY_REGISTERED - A manager entry point vector has
already been registered for the this interface under the
specified manager type UUID.
--*/
{
RPC_INTERFACE_MANAGER * InterfaceManager;
// First we need to check if the null UUID is being specified as
// the type UUID; either, explicit or implicit by not specifying
// a type UUID argument.
RequestGlobalMutex();
if ( (ARGUMENT_PRESENT(ManagerTypeUuid) == 0)
|| ( (ARGUMENT_PRESENT(ManagerTypeUuid) != 0)
&& (ManagerTypeUuid->IsNullUuid() != 0)))
{
if (NullManagerFlag != 0)
{
ClearGlobalMutex();
return(RPC_S_TYPE_ALREADY_REGISTERED);
}
NullManagerEpv = ManagerEpv;
NullManagerFlag = 1;
ManagerCount += 1;
ClearGlobalMutex();
return(RPC_S_OK);
}
// If we reach here, a non-NULL type UUID is specified.
InterfaceManager = FindInterfaceManager(ManagerTypeUuid);
if (InterfaceManager == 0)
{
InterfaceManager = new RPC_INTERFACE_MANAGER(ManagerTypeUuid,
ManagerEpv);
if (InterfaceManager == 0)
{
ClearGlobalMutex();
return(RPC_S_OUT_OF_MEMORY);
}
if (InterfaceManagerDictionary.Insert(InterfaceManager) == -1)
{
ClearGlobalMutex();
delete InterfaceManager;
return(RPC_S_OUT_OF_MEMORY);
}
ManagerCount += 1;
ClearGlobalMutex();
return(RPC_S_OK);
}
if (InterfaceManager->ValidManager() == 0)
{
InterfaceManager->SetManagerEpv(ManagerEpv);
ManagerCount += 1;
ClearGlobalMutex();
return(RPC_S_OK);
}
ClearGlobalMutex();
return(RPC_S_TYPE_ALREADY_REGISTERED);
}
RPC_INTERFACE_MANAGER *
RPC_INTERFACE::FindInterfaceManager (
IN RPC_UUID PAPI * ManagerTypeUuid
)
/*++
Routine Description:
This method is used to obtain the interface manager corresponding to
the specified type UUID. The type UUID must not be the null UUID.
Arguments:
ManagerTypeUuid - Supplies the type UUID for which we are trying to
find the interface manager.
Return Value:
If a interface manager for this type UUID is found, a pointer to it
will be returned; otherwise, zero will be returned.
--*/
{
RPC_INTERFACE_MANAGER * InterfaceManager;
DictionaryCursor cursor;
InterfaceManagerDictionary.Reset(cursor);
while ((InterfaceManager = InterfaceManagerDictionary.Next(cursor)) != 0)
{
if (InterfaceManager->MatchTypeUuid(ManagerTypeUuid) == 0)
return(InterfaceManager);
}
return(0);
}
RPC_STATUS
RPC_INTERFACE::DispatchToStub (
IN OUT PRPC_MESSAGE Message,
IN unsigned int CallbackFlag,
IN PRPC_DISPATCH_TABLE DispatchTableToUse,
OUT RPC_STATUS PAPI * ExceptionCode
)
/*++
Routine Description:
This method is used to dispatch remote procedure calls to the
appropriate stub and hence to the appropriate manager entry point.
This routine is used for calls having a null UUID (implicit or
explicit). We go to great pains to insure that we do not grab
a mutex.
Arguments:
Message - Supplies the response message and returns the reply
message.
CallbackFlag - Supplies a flag indicating whether this is a callback
or not. The argument will be zero if this is an original call,
and non-zero if it is a callback.
ExceptionCode - Returns the exact exception code if an exception
occurs.
Return Value:
RPC_S_OK - This value will be returned if the operation completed
successfully.
RPC_S_PROCNUM_OUT_OF_RANGE - If the procedure number for this call is
too large, this value will be returned.
RPC_S_UNKNOWN_IF - If this interface does not exist, you will get this
value back.
RPC_S_NOT_LISTENING - The rpc server which owns this rpc interface
is not listening for remote procedure calls right now.
RPC_S_SERVER_TOO_BUSY - This call will cause there to be too many
concurrent remote procedure calls for the rpc server which owns
this interface.
RPC_P_EXCEPTION_OCCURED - A fault occured, and we need to remote it. The
ExceptionCode argument will contain the exception code for the
fault.
RPC_S_UNSUPPORTED_TYPE - This interface exists, but does not have a manager
for the null type.
--*/
{
RPC_STATUS RpcStatus = RPC_S_OK;
if ( CallbackFlag == 0 )
{
NullManagerActiveCallCount.Increment();
if ( NullManagerFlag == 0 )
{
NullManagerActiveCallCount.Decrement();
RpcStatus = RPC_S_UNSUPPORTED_TYPE;
RpcpErrorAddRecord(EEInfoGCRuntime,
RpcStatus,
EEInfoDLDispatchToStub10);
if ( ManagerCount == 0 )
{
RpcStatus = RPC_S_UNKNOWN_IF;
RpcpErrorAddRecord(EEInfoGCRuntime,
RpcStatus,
EEInfoDLDispatchToStub20);
}
}
}
if (RpcStatus != RPC_S_OK)
{
((MESSAGE_OBJECT *) Message->Handle)->FreeBuffer(Message);
return RpcStatus;
}
Message->ManagerEpv = NullManagerEpv;
RpcStatus = DispatchToStubWorker(Message, CallbackFlag, DispatchTableToUse, ExceptionCode);
if ( RpcStatus != RPC_S_OK
|| (((MESSAGE_OBJECT *) Message->Handle)->IsSyncCall()
&& CallbackFlag == 0 ))
{
NullManagerActiveCallCount.Decrement();
}
//
// DispatchToStubWorker freed Message.Buffer if an error occurred.
//
return(RpcStatus);
}
RPC_STATUS
RPC_INTERFACE::DispatchToStubWorker (
IN OUT PRPC_MESSAGE Message,
IN unsigned int CallbackFlag,
IN PRPC_DISPATCH_TABLE DispatchTableToUse,
OUT RPC_STATUS PAPI * ExceptionCode
)
/*++
Routine Description:
This method is used to dispatch remote procedure calls to the
appropriate stub and hence to the appropriate manager entry point.
It will be used for calls with and without objects specified.
We go to great pains to insure that we do not grab a mutex.
Arguments:
Message - Supplies the response message and returns the reply
message. If this routine returns anything other than RPC_S_OK
Message->Buffer has already been freed.
CallbackFlag - Supplies a flag indicating whether this is a callback
or not. The argument will be zero if this is an original call,
and non-zero if it is a callback.
DispatchTableToUse - a pointer to the dispatch table to use. This is
used to select b/n stubs for NDR20 and NDR64 transfer syntaxes
ExceptionCode - Returns the exact exception code if an exception
occurs.
Return Value:
RPC_S_OK - This value will be returned if the operation completed
successfully.
RPC_S_PROCNUM_OUT_OF_RANGE - If the procedure number for this call is
too large, this value will be returned.
RPC_S_NOT_LISTENING - The rpc server which owns this rpc interface
is not listening for remote procedure calls right now.
RPC_S_SERVER_TOO_BUSY - This call will cause there to be too many
concurrent remote procedure calls for the rpc server which owns
this interface.
RPC_P_EXCEPTION_OCCURED - A fault occured, and we need to remote it. The
ExceptionCode argument will contain the exception code for the
fault.
--*/
{
RPC_STATUS RpcStatus = RPC_S_OK;
void * OldServerContextList;
unsigned int procnum ;
THREAD *Self = RpcpGetThreadPointer() ;
ASSERT(Self);
if (Flags & RPC_IF_OLE)
{
procnum = 0 ;
}
else
{
procnum = Message->ProcNum ;
}
if (CallbackFlag == 0)
{
if (IsAutoListenInterface())
{
if (AutoListenCallCount.GetInteger() >= (long) MaxCalls)
{
RpcStatus = RPC_S_SERVER_TOO_BUSY ;
RpcpErrorAddRecord(EEInfoGCRuntime,
RpcStatus,
EEInfoDLDispatchToStubWorker10,
(ULONG)AutoListenCallCount.GetInteger(),
(ULONG)MaxCalls);
}
}
else
{
if (Server->IsServerListening() == 0)
{
RpcStatus = RPC_S_NOT_LISTENING;
RpcpErrorAddRecord(EEInfoGCRuntime,
RpcStatus,
EEInfoDLDispatchToStubWorker20);
}
else if (Server->fAccountForMaxCalls && Server->CallBeginning() == 0)
{
RpcStatus = RPC_S_SERVER_TOO_BUSY;
RpcpErrorAddRecord(EEInfoGCRuntime,
RpcStatus,
EEInfoDLDispatchToStubWorker30);
}
}
}
if (procnum >=
DispatchTableToUse->DispatchTableCount)
{
if (RpcStatus != RPC_S_SERVER_TOO_BUSY)
{
EndCall(CallbackFlag) ;
}
RpcStatus = RPC_S_PROCNUM_OUT_OF_RANGE;
RpcpErrorAddRecord(EEInfoGCRuntime,
RpcStatus,
EEInfoDLDispatchToStubWorker40);
}
if (RpcStatus != RPC_S_OK)
{
MO(Message)->FreeBuffer(Message);
return RpcStatus;
}
Server->IncomingCall();
((PRPC_RUNTIME_INFO) Message->ReservedForRuntime)->OldBuffer =
Message->Buffer ;
Message->RpcInterfaceInformation = &RpcInterfaceInformation;
SCALL(Message)->DoPreDispatchProcessing(Message, CallbackFlag);
if ( DispatchToStubInC(DispatchTableToUse->DispatchTable[procnum], Message, ExceptionCode) != 0 )
{
LogEvent(SU_EXCEPT, EV_STATUS, LongToPtr(*ExceptionCode),
DispatchTableToUse->DispatchTable[procnum], (ULONG_PTR)Message, 1, 1);
#if defined(DBG) && defined(i386)
#if 1
RtlCheckForOrphanedCriticalSections(NtCurrentThread());
#endif
#endif
RpcStatus = RPC_P_EXCEPTION_OCCURED;
RpcpErrorAddRecord(EEInfoGCApplication,
*ExceptionCode,
EEInfoDLRaiseExc,
GetInterfaceFirstDWORD(),
(short)procnum,
Message->RpcFlags,
GetCurrentThreadId());
}
RPC_MESSAGE OriginalMessage ;
OriginalMessage.ReservedForRuntime = 0;
OriginalMessage.Buffer =
((PRPC_RUNTIME_INFO) Message->ReservedForRuntime)->OldBuffer;
Self->ResetYield();
if (Self->IsSyncCall())
{
//
// Since this is a sync call, we know that it has
// not been freed yet. So we can safely touch it.
//
SCALL(Message)->DoPostDispatchProcessing();
//
// The dispatched call was a sync call
//
if (RPC_S_OK == RpcStatus)
{
//
// If the stub didn't allocate an output buffer, do so now.
//
if (OriginalMessage.Buffer == Message->Buffer)
{
Message->RpcFlags = 0;
Message->BufferLength = 0;
MO(Message)->GetBuffer(Message, 0);
}
//
// Free the [in] buffer that we saved.
//
MO(Message)->FreeBuffer(&OriginalMessage);
EndCall(CallbackFlag) ;
}
else
{
ASSERT(RpcStatus == RPC_P_EXCEPTION_OCCURED) ;
//
// Free the buffer in the caller's message; this can be either
// the [in] buffer or the [out] buffer, depending upon which
// line of the stub caused the error.
//
// If the exception occurred after allocating the [out] buffer,
// also free the [in] buffer.
//
if (OriginalMessage.Buffer != Message->Buffer)
{
MO(Message)->FreeBuffer(&OriginalMessage);
}
if (Message->Buffer)
{
MO(Message)->FreeBuffer(Message);
}
EndCall(CallbackFlag) ;
}
}
else
{
//
// The dispatched call was an async call
//
if (RpcStatus != RPC_S_OK
&& OriginalMessage.Buffer != Message->Buffer)
{
//
// The dispatch buffer will be freed during cleanup
// of the async call
//
MO(Message)->FreeBuffer(Message);
}
}
return(RpcStatus);
}
void
RPC_INTERFACE::EndCall(
IN unsigned int CallbackFlag,
BOOL fAsync
)
{
if (fAsync)
{
NullManagerActiveCallCount.Decrement();
}
if (CallbackFlag == 0)
{
if (!(Flags & RPC_IF_AUTOLISTEN) && Server->fAccountForMaxCalls)
{
Server->CallEnding();
}
}
}
RPC_STATUS
RPC_INTERFACE::DispatchToStubWithObject (
IN OUT PRPC_MESSAGE Message,
IN RPC_UUID * ObjectUuid,
IN unsigned int CallbackFlag,
IN PRPC_DISPATCH_TABLE DispatchTableToUse,
OUT RPC_STATUS PAPI * ExceptionCode
)
/*++
Routine Description:
This method is used to dispatch remote procedure calls to the
appropriate stub and hence to the appropriate manager entry point.
This routine is used for calls which have an associated object.
Arguments:
Message - Supplies the response message and returns the reply
message.
ObjectUuid - Supplies the object uuid to map into the manager entry
point for this call.
CallbackFlag - Supplies a flag indicating whether this is a callback
or not. The argument will be zero if this is an original call,
and non-zero if it is a callback.
ExceptionCode - Returns the exact exception code if an exception
occurs.
Return Value:
RPC_S_OK - The operation completed successfully.
RPC_S_PROCNUM_OUT_OF_RANGE - If the procedure number for this call is
too large, this value will be returned.
RPC_S_UNKNOWN_IF - If the specified manager is no longer
valid, you will get this value back.
RPC_S_NOT_LISTENING - The rpc server which owns this rpc interface
is not listening for remote procedure calls right now.
RPC_S_SERVER_TOO_BUSY - This call will cause there to be too many
concurrent remote procedure calls for the rpc server which owns
this interface.
RPC_P_EXCEPTION_OCCURED - A fault occured, and we need to remote it. The
ExceptionCode argument will contain the exception code for the
fault.
RPC_S_UNSUPPORTED_TYPE - There is no type manager for the object's type
for this interface.
--*/
{
RPC_UUID TypeUuid;
RPC_STATUS RpcStatus;
RPC_INTERFACE_MANAGER * RpcInterfaceManager;
RpcStatus = ObjectInqType(ObjectUuid, &TypeUuid);
VALIDATE(RpcStatus)
{
RPC_S_OK,
RPC_S_OBJECT_NOT_FOUND
} END_VALIDATE;
if ( RpcStatus == RPC_S_OK )
{
RpcInterfaceManager = FindInterfaceManager(&TypeUuid);
if ( ( RpcInterfaceManager != 0 )
&& ( ( CallbackFlag != 0 )
|| ( RpcInterfaceManager->ValidManager() != 0 ) ) )
{
Message->ManagerEpv = RpcInterfaceManager->QueryManagerEpv();
if ( CallbackFlag == 0 )
{
RpcInterfaceManager->CallBeginning();
}
RpcStatus = DispatchToStubWorker(Message, CallbackFlag, DispatchTableToUse,
ExceptionCode);
if ( CallbackFlag == 0 )
{
RpcInterfaceManager->CallEnding();
}
return(RpcStatus);
}
if (this != GlobalManagementInterface)
{
// There is a type for this object, but no type manager for
// this interface.
RpcStatus = RPC_S_UNSUPPORTED_TYPE;
if ( ManagerCount == 0 )
{
RpcStatus = RPC_S_UNKNOWN_IF;
}
((MESSAGE_OBJECT *) Message->Handle)->FreeBuffer(Message);
return RpcStatus;
}
}
// There has not been a type registered for this object, so we will
// just go ahead and try and use the NULL type manager.
return(DispatchToStub(Message, CallbackFlag, DispatchTableToUse, ExceptionCode));
}
BOOL
RPC_INTERFACE::IsObjectSupported (
IN RPC_UUID * ObjectUuid
)
/*++
Routine Description:
Determines whether the manager for the given object UUID is registered.
Arguments:
ObjectUuid - the client's object UUID
Return Value:
RPC_S_OK if it is OK to dispatch
RPC_S_UNKNOWN_IF if the interface is not registered
RPC_S_UNSUPPORTED_TYPE if this particular object's type is not registered
--*/
{
RPC_STATUS Status = RPC_S_OK;
if (ObjectUuid->IsNullUuid() )
{
if ( NullManagerFlag == 0 )
{
Status = RPC_S_UNSUPPORTED_TYPE;
if ( ManagerCount == 0 )
{
Status = RPC_S_UNKNOWN_IF;
}
}
}
else
{
RPC_UUID TypeUuid;
Status = ObjectInqType(ObjectUuid, &TypeUuid);
if ( Status == RPC_S_OK )
{
RPC_INTERFACE_MANAGER * RpcInterfaceManager = 0;
RpcInterfaceManager = FindInterfaceManager(&TypeUuid);
if (!RpcInterfaceManager ||
!RpcInterfaceManager->ValidManager())
{
Status = RPC_S_UNSUPPORTED_TYPE;
if ( ManagerCount == 0 )
{
Status = RPC_S_UNKNOWN_IF;
}
}
}
else
{
Status = RPC_S_OK;
if ( NullManagerFlag == 0 )
{
Status = RPC_S_UNSUPPORTED_TYPE;
if ( ManagerCount == 0 )
{
Status = RPC_S_UNKNOWN_IF;
}
}
}
}
return Status;
}
RPC_STATUS
RPC_INTERFACE::UpdateBindings (
IN RPC_BINDING_VECTOR *BindingVector
)
/*++
Function Name:UpdateEpMapperBindings
Parameters:
Description:
Returns:
--*/
{
RPC_STATUS Status;
unsigned int Length;
#if !defined(NO_LOCATOR_CODE)
NS_ENTRY *NsEntry;
#endif
DictionaryCursor cursor;
if (fBindingsExported)
{
Status = RegisterEntries(&RpcInterfaceInformation,
BindingVector,
UuidVector,
(unsigned char *) Annotation,
fReplace);
if (Status != RPC_S_OK)
{
return Status;
}
}
#if !defined(NO_LOCATOR_CODE)
// shortcut the common path and avoid taking and holding
// unnecessarily the high contention global mutex
if (NsEntries.Size() == 0)
return RPC_S_OK;
RequestGlobalMutex();
NsEntries.Reset(cursor);
while ((NsEntry = NsEntries.Next(cursor)) != 0)
{
//
// Actually update the locator bindings
//
Status = GlobalRpcServer->NsBindingUnexport(
NsEntry->EntryNameSyntax,
NsEntry->EntryName,
&RpcInterfaceInformation);
if (Status == RPC_S_OK)
{
Status = GlobalRpcServer->NsBindingExport(
NsEntry->EntryNameSyntax,
NsEntry->EntryName,
&RpcInterfaceInformation,
BindingVector);
#if DBG
if (Status != RPC_S_OK)
{
PrintToDebugger("RPC: Bindings were unexported, but could not re-export\n");
}
#endif
}
}
ClearGlobalMutex();
#endif
return RPC_S_OK;
}
RPC_STATUS
RPC_INTERFACE::InterfaceExported (
IN UUID_VECTOR *MyObjectUuidVector,
IN unsigned char *MyAnnotation,
IN BOOL MyfReplace
)
/*++
Function Name:InterfaceExported
Parameters:
Description:
RpcEpRegister was called on this interface. We need to keep track
of the parameters, so that if we get a PNP notification, we can update
the bindings using there params
Returns:
RPC_S_OK: things went fine
RPC_S_OUT_OF_MEMORY: ran out of memory
--*/
{
RequestGlobalMutex();
if (UuidVector
&& UuidVector != MyObjectUuidVector)
{
RpcpFarFree(UuidVector);
UuidVector = 0;
}
if (MyObjectUuidVector)
{
if (UuidVector != MyObjectUuidVector)
{
int Size = MyObjectUuidVector->Count*(sizeof(UUID)+sizeof(UUID *))
+sizeof(unsigned long);
UUID *Uuids;
unsigned i;
UuidVector = (UUID_VECTOR *) RpcpFarAllocate(Size);
if (UuidVector == 0)
{
ClearGlobalMutex();
return RPC_S_OUT_OF_MEMORY;
}
Uuids = (UUID *) ((char *) UuidVector + sizeof(unsigned long)
+(sizeof(UUID *) * MyObjectUuidVector->Count));
UuidVector->Count = MyObjectUuidVector->Count;
for (i = 0; i < UuidVector->Count; i++)
{
Uuids[i] = *(MyObjectUuidVector->Uuid[i]);
UuidVector->Uuid[i] = &Uuids[i];
}
}
}
else
{
UuidVector = 0;
}
if (MyAnnotation)
{
strncpy((char *) Annotation, (char *) MyAnnotation, 63);
Annotation[63]=0;
}
else
{
Annotation[0] = 0;
}
fReplace = MyfReplace;
fBindingsExported = 1;
ClearGlobalMutex();
return RPC_S_OK;
}
#if !defined(NO_LOCATOR_CODE)
NS_ENTRY *
RPC_INTERFACE::FindEntry (
IN unsigned long EntryNameSyntax,
IN RPC_CHAR *EntryName
)
{
NS_ENTRY *NsEntry;
DictionaryCursor cursor;
//
// This function will always be called with the mutex held
//
NsEntries.Reset(cursor);
while ((NsEntry = NsEntries.Next(cursor)) != 0)
{
if (NsEntry->Match(EntryNameSyntax, EntryName))
{
return NsEntry;
}
}
return 0;
}
RPC_STATUS
RPC_INTERFACE::NsInterfaceUnexported (
IN unsigned long EntryNameSyntax,
IN RPC_CHAR *EntryName
)
{
NS_ENTRY *NsEntry;
RequestGlobalMutex();
NsEntry = FindEntry(EntryNameSyntax, EntryName);
if (NsEntry == 0)
{
ClearGlobalMutex();
#if DBG
PrintToDebugger("RPC: No corresponding exported entry\n");
#endif
return RPC_S_ENTRY_NOT_FOUND;
}
NsEntries.Delete(NsEntry->Key);
ClearGlobalMutex();
return RPC_S_OK;
}
RPC_STATUS
RPC_INTERFACE::NsInterfaceExported (
IN unsigned long EntryNameSyntax,
IN RPC_CHAR *EntryName
)
{
RPC_STATUS Status = RPC_S_OK;
NS_ENTRY *NsEntry;
int retval;
RequestGlobalMutex();
NsEntry = FindEntry(EntryNameSyntax, EntryName);
ClearGlobalMutex();
if (NsEntry)
{
return RPC_S_OK;
}
NsEntry = new NS_ENTRY(
EntryNameSyntax,
EntryName,
&Status);
if (NsEntry == 0)
{
return RPC_S_OUT_OF_MEMORY;
}
if (Status != RPC_S_OK)
{
delete NsEntry;
return Status;
}
RequestGlobalMutex();
NsEntry->Key = NsEntries.Insert(NsEntry);
ClearGlobalMutex();
if (NsEntry->Key == -1)
{
delete NsEntry;
return RPC_S_OUT_OF_MEMORY;
}
return RPC_S_OK;
}
#endif
static unsigned int
MatchSyntaxIdentifiers (
IN PRPC_SYNTAX_IDENTIFIER ServerSyntax,
IN PRPC_SYNTAX_IDENTIFIER ClientSyntax
)
/*++
Routine Description:
This method compares two syntax identifiers (which consist of a
uuid, a major version number, and a minor version number). In
order for the syntax identifiers to match, the uuids must be the
same, the major version numbers must be the same, and the client
minor version number must be less than or equal to the server
minor version number.
Arguments:
ServerSyntax - Supplies the server syntax identifier.
ClientSyntax - Supplies the client syntax identifer.
Return Value:
Zero will be returned if the client syntax identifier matches the
server syntax identifier; otherwise, non-zero will be returned.
--*/
{
if (RpcpMemoryCompare(&(ServerSyntax->SyntaxGUID),
&(ClientSyntax->SyntaxGUID), sizeof(UUID)) != 0)
return(1);
if (ServerSyntax->SyntaxVersion.MajorVersion
!= ClientSyntax->SyntaxVersion.MajorVersion)
return(1);
if (ServerSyntax->SyntaxVersion.MinorVersion
< ClientSyntax->SyntaxVersion.MinorVersion)
return(1);
return(0);
}
unsigned int
RPC_INTERFACE::MatchInterfaceIdentifier (
IN PRPC_SYNTAX_IDENTIFIER InterfaceIdentifier
)
/*++
Routine Description:
This method compares the supplied interface identifier (which consists
of the interface uuid and interface version) against that contained
in this rpc interface. In order for this rpc interface to match,
the interface uuids must be the same, the interface major versions
must be the same, and the supplied interface minor version must be
less than or equal to the interface minor version contained in this
rpc interface.
Arguments:
InterfaceIdentifier - Supplies the interface identifier to compare
against that contained in this rpc interface.
Return Value:
Zero will be returned if the supplied interface identifer matches
(according to the rules described above) the interface identifier
contained in this rpc interface; otherwise, non-zero will be returned.
--*/
{
if (ManagerCount == 0)
return(1);
return(MatchSyntaxIdentifiers(&(RpcInterfaceInformation.InterfaceId),
InterfaceIdentifier));
}
unsigned int
RPC_INTERFACE::SelectTransferSyntax (
IN PRPC_SYNTAX_IDENTIFIER ProposedTransferSyntaxes,
IN unsigned int NumberOfTransferSyntaxes,
OUT PRPC_SYNTAX_IDENTIFIER AcceptedTransferSyntax,
OUT BOOL *fIsInterfaceTransferPreferred,
OUT int *ProposedTransferSyntaxIndex,
OUT int *AvailableTransferSyntaxIndex
)
/*++
Routine Description:
This method is used to select a transfer syntax from a list of one
or more transfer syntaxes. If a transfer syntax is selected, then
it will be returned in one of the arguments.
Arguments:
ProposedTransferSyntaxes - Supplies a list of one or more transfer
syntaxes from which this interface should select one which it
supports if possible.
NumberOfTransferSyntaxes - Supplies the number of transfer syntaxes
in the proposed transfer syntaxes argument.
AcceptedTransferSyntax - Returns the selected transfer syntax, if
one is selected.
fIsInterfaceTransferPreferred - true if the selected transfer syntax
is preferred by the server
ProposedTransferSyntaxIndex - the index of the transfer syntax that is
chosen from the proposed transfer syntaxes array. Zero based.
AvailableTransferSyntaxIndex - the index of the transfer syntax that is
chosen from the available transfer syntaxes in the interface. This
value must be stored in the binding and retrieved when asking for the
transfer syntax and dispatch table. Zero based.
Return Value:
Zero will be returned if a transfer syntax is selected; otherwise,
non-zero will be returned.
--*/
{
unsigned int ProposedIndex;
unsigned int AvailableIndex;
unsigned int NumberOfAvailableSyntaxes;
BOOL fMultipleTranfserSyntaxesSelected;
RPC_SYNTAX_IDENTIFIER *CurrentTransferSyntax;
RPC_SYNTAX_IDENTIFIER *BackupTransferSyntax = NULL;
int BackupProposedTransferSyntaxIndex;
int BackupAvailableTransferSyntaxIndex;
fMultipleTranfserSyntaxesSelected = AreMultipleTransferSyntaxesSupported();
if (fMultipleTranfserSyntaxesSelected)
NumberOfAvailableSyntaxes = NumberOfSupportedTransferSyntaxes;
else
NumberOfAvailableSyntaxes = 1;
for (AvailableIndex = 0; AvailableIndex < NumberOfAvailableSyntaxes; AvailableIndex ++)
{
if (fMultipleTranfserSyntaxesSelected)
CurrentTransferSyntax = &(TransferSyntaxesArray[AvailableIndex].TransferSyntax);
else
CurrentTransferSyntax = &RpcInterfaceInformation.TransferSyntax;
for (ProposedIndex = 0; ProposedIndex < NumberOfTransferSyntaxes;
ProposedIndex++)
{
if (MatchSyntaxIdentifiers(CurrentTransferSyntax,
&(ProposedTransferSyntaxes[ProposedIndex])) == 0)
{
// is this the preferred transfer syntax for the server?
if (AvailableIndex == PreferredTransferSyntax)
{
// this is the preferred transfer syntax - just
// copy it and return
RpcpMemoryCopy(AcceptedTransferSyntax,
&(ProposedTransferSyntaxes[ProposedIndex]),
sizeof(RPC_SYNTAX_IDENTIFIER));
*fIsInterfaceTransferPreferred = TRUE;
*ProposedTransferSyntaxIndex = ProposedIndex;
*AvailableTransferSyntaxIndex = AvailableIndex;
return(0);
}
else
{
// this is not the preferred syntax - just remeber this
// one (if no previous match was found) and continue
if (BackupTransferSyntax == NULL)
{
BackupTransferSyntax = &(ProposedTransferSyntaxes[ProposedIndex]);
BackupProposedTransferSyntaxIndex = ProposedIndex;
BackupAvailableTransferSyntaxIndex = AvailableIndex;
}
}
}
}
}
// if we're here, this means we didn't find the preferred transfer syntax
// check whether there is a backup syntax
if (BackupTransferSyntax)
{
RpcpMemoryCopy(AcceptedTransferSyntax, BackupTransferSyntax,
sizeof(RPC_SYNTAX_IDENTIFIER));
*fIsInterfaceTransferPreferred = FALSE;
*ProposedTransferSyntaxIndex = BackupProposedTransferSyntaxIndex;
*AvailableTransferSyntaxIndex = BackupAvailableTransferSyntaxIndex;
return(0);
}
// nada - no transfer syntax matches
return(1);
}
void RPC_INTERFACE::GetSelectedTransferSyntaxAndDispatchTable(IN int SelectedTransferSyntaxIndex,
OUT RPC_SYNTAX_IDENTIFIER **SelectedTransferSyntax,
OUT PRPC_DISPATCH_TABLE *SelectedDispatchTable)
{
MIDL_SYNTAX_INFO *SelectedSyntaxInfo;
if (DoesInterfaceSupportMultipleTransferSyntaxes(&RpcInterfaceInformation))
{
ASSERT((unsigned int)SelectedTransferSyntaxIndex <= NumberOfSupportedTransferSyntaxes);
SelectedSyntaxInfo = &TransferSyntaxesArray[SelectedTransferSyntaxIndex];
*SelectedTransferSyntax = &SelectedSyntaxInfo->TransferSyntax;
// DCOM has only one dispatch table - they change the dispatch target
// internally. They will define only the dispatch table in the
// interface
if (SelectedSyntaxInfo->DispatchTable)
*SelectedDispatchTable = SelectedSyntaxInfo->DispatchTable;
else
*SelectedDispatchTable = GetDefaultDispatchTable();
}
else
{
*SelectedTransferSyntax = &RpcInterfaceInformation.TransferSyntax;
*SelectedDispatchTable = GetDefaultDispatchTable();
}
}
RPC_STATUS
RPC_INTERFACE::UnregisterManagerEpv (
IN RPC_UUID PAPI * ManagerTypeUuid, OPTIONAL
IN unsigned int WaitForCallsToComplete
)
/*++
Routine Description:
In this method, we unregister one or all of the manager entry point
vectors for this interface, depending on what, if anything, is
specified for the manager type uuid argument.
Arguments:
ManagerTypeUuid - Optionally supplies the type uuid of the manager
entry point vector to be removed. If this argument is not supplied,
then all manager entry point vectors for this interface will
be removed.
WaitForCallsToComplete - Supplies a flag indicating whether or not
this routine should wait for all calls to complete using the
interface and manager being unregistered. A non-zero value
indicates to wait.
Return Value:
RPC_S_OK - The manager entry point vector(s) are(were) successfully
removed from the this interface.
RPC_S_UNKNOWN_MGR_TYPE - The specified type uuid is not registered
with this interface.
RPC_S_UNKNOWN_IF - The specified interface is not registered with
the rpc server.
--*/
{
RPC_INTERFACE_MANAGER * InterfaceManager;
DictionaryCursor cursor;
RequestGlobalMutex();
if (ManagerCount == 0)
{
ClearGlobalMutex();
return(RPC_S_UNKNOWN_MGR_TYPE);
}
if (ARGUMENT_PRESENT(ManagerTypeUuid) == 0)
{
InterfaceManagerDictionary.Reset(cursor);
while ((InterfaceManager = InterfaceManagerDictionary.Next(cursor)) != 0)
{
InterfaceManager->InvalidateManager();
}
ManagerCount = 0;
NullManagerFlag = 0;
ClearGlobalMutex();
if ( WaitForCallsToComplete != 0 )
{
while ( NullManagerActiveCallCount.GetInteger() > 0 )
{
PauseExecution(500L);
}
InterfaceManagerDictionary.Reset(cursor);
while ((InterfaceManager = InterfaceManagerDictionary.Next(cursor)) != 0)
{
while ( InterfaceManager->InquireActiveCallCount() > 0 )
{
PauseExecution(500L);
}
}
}
return(RPC_S_OK);
}
if (ManagerTypeUuid->IsNullUuid() != 0)
{
if (NullManagerFlag == 0)
{
ClearGlobalMutex();
return(RPC_S_UNKNOWN_MGR_TYPE);
}
ManagerCount -= 1;
NullManagerFlag = 0;
ClearGlobalMutex();
if ( WaitForCallsToComplete != 0 )
{
while ( NullManagerActiveCallCount.GetInteger() > 0 )
{
PauseExecution(500L);
}
}
return(RPC_S_OK);
}
InterfaceManager = FindInterfaceManager(ManagerTypeUuid);
if ( (InterfaceManager == 0)
|| (InterfaceManager->ValidManager() == 0))
{
ClearGlobalMutex();
return(RPC_S_UNKNOWN_MGR_TYPE);
}
InterfaceManager->InvalidateManager();
ManagerCount -= 1;
ClearGlobalMutex();
if ( WaitForCallsToComplete != 0 )
{
while ( InterfaceManager->InquireActiveCallCount() > 0 )
{
PauseExecution(500L);
}
}
return(RPC_S_OK);
}
RPC_STATUS
RPC_INTERFACE::InquireManagerEpv (
IN RPC_UUID PAPI * ManagerTypeUuid, OPTIONAL
OUT RPC_MGR_EPV PAPI * PAPI * ManagerEpv
)
/*++
Routine Description:
This method is used to obtain the manager entry point vector
with the specified type uuid supported by this interface.
Arguments:
ManagerTypeUuid - Optionally supplies the type uuid of the manager
entry point vector we want returned. If no manager type uuid
is specified, then the null uuid is assumed.
ManagerEpv - Returns the manager entry point vector.
Return Value:
RPC_S_OK - The manager entry point vector has successfully been
returned.
RPC_S_UNKNOWN_MGR_TYPE - The specified type uuid is not registered
with the interface.
RPC_S_UNKNOWN_IF - The specified interface is not registered with
the rpc server.
--*/
{
RPC_INTERFACE_MANAGER * InterfaceManager;
RequestGlobalMutex();
if (ManagerCount == 0)
{
ClearGlobalMutex();
return(RPC_S_UNKNOWN_IF);
}
if ( (ARGUMENT_PRESENT(ManagerTypeUuid) == 0)
|| (ManagerTypeUuid->IsNullUuid() != 0))
{
if (NullManagerFlag == 0)
{
ClearGlobalMutex();
return(RPC_S_UNKNOWN_MGR_TYPE);
}
*ManagerEpv = NullManagerEpv;
ClearGlobalMutex();
return(RPC_S_OK);
}
InterfaceManager = FindInterfaceManager(ManagerTypeUuid);
if ( (InterfaceManager == 0)
|| (InterfaceManager->ValidManager() == 0))
{
ClearGlobalMutex();
return(RPC_S_UNKNOWN_MGR_TYPE);
}
*ManagerEpv = InterfaceManager->QueryManagerEpv();
ClearGlobalMutex();
return(RPC_S_OK);
}
RPC_STATUS
RPC_INTERFACE::UpdateRpcInterfaceInformation (
IN RPC_SERVER_INTERFACE PAPI * RpcInterfaceInformation,
IN unsigned int Flags,
IN unsigned int MaxCalls,
IN unsigned int MaxRpcSize,
IN RPC_IF_CALLBACK_FN PAPI *IfCallbackFn
)
/*++
Routine Description:
We never delete the interface objects from a server; we just invalidate
them. This means that if an interface has been complete unregistered
(ie. it has no managers), we need to update the interface information
again.
Arguments:
RpcInterfaceInformation - Supplies the interface information which this
interface should be using.
--*/
{
unsigned int Length;
RPC_STATUS Status;
Length = RpcInterfaceInformation->Length;
ASSERT((Length == sizeof(RPC_SERVER_INTERFACE)) ||
(Length == NT351_INTERFACE_SIZE));
// make it stick on free builds as well
if ((Length != sizeof(RPC_SERVER_INTERFACE)) &&
(Length != NT351_INTERFACE_SIZE))
return RPC_S_UNKNOWN_IF;
if ( ManagerCount == 0 )
{
if (DoesInterfaceSupportMultipleTransferSyntaxes(RpcInterfaceInformation))
{
Status = NdrServerGetSupportedSyntaxes(RpcInterfaceInformation,
&NumberOfSupportedTransferSyntaxes,
&TransferSyntaxesArray, &PreferredTransferSyntax);
if (Status != RPC_S_OK)
return Status;
}
else
{
NumberOfSupportedTransferSyntaxes = 0;
}
RpcpMemoryCopy(&(this->RpcInterfaceInformation),
RpcInterfaceInformation, Length);
}
if (Flags & RPC_IF_AUTOLISTEN
&& (this->Flags & RPC_IF_AUTOLISTEN) == 0)
{
GlobalRpcServer->IncrementAutoListenInterfaceCount() ;
}
this->Flags = Flags ;
this->MaxCalls = MaxCalls ;
this->MaxRpcSize = MaxRpcSize;
SequenceNumber++ ;
if (Flags & RPC_IF_ALLOW_SECURE_ONLY
&& IfCallbackFn == NULL)
{
this->CallbackFn = DefaultCallbackFn;
}
else
{
this->CallbackFn = IfCallbackFn ;
}
return RPC_S_OK;
}
RPC_IF_ID __RPC_FAR *
RPC_INTERFACE::InquireInterfaceId (
)
/*++
Return Value:
If this interface is active, its interface id will be returned in a
newly allocated chunk of memory; otherwise, zero will be returned.
--*/
{
RPC_IF_ID __RPC_FAR * RpcIfId;
if ( ManagerCount == 0 )
{
return(0);
}
RpcIfId = (RPC_IF_ID __RPC_FAR *) RpcpFarAllocate(sizeof(RPC_IF_ID));
if ( RpcIfId == 0 )
{
return(0);
}
RpcIfId->Uuid = RpcInterfaceInformation.InterfaceId.SyntaxGUID;
RpcIfId->VersMajor =
RpcInterfaceInformation.InterfaceId.SyntaxVersion.MajorVersion;
RpcIfId->VersMinor =
RpcInterfaceInformation.InterfaceId.SyntaxVersion.MinorVersion;
return(RpcIfId);
}
RPC_STATUS
RPC_INTERFACE::CheckSecurityIfNecessary(
IN void * Context
)
{
//
// If manager count in non-zero, this interface is still registered
// If it has been registered with a call back function, invoke the callback
// function, else return success....
RPC_IF_ID RpcIfId;
RPC_STATUS RpcStatus = RPC_S_OK;
if (CallbackFn != 0)
{
RpcIfId.Uuid = RpcInterfaceInformation.InterfaceId.SyntaxGUID;
RpcIfId.VersMajor =
RpcInterfaceInformation.InterfaceId.SyntaxVersion.MajorVersion;
RpcIfId.VersMinor =
RpcInterfaceInformation.InterfaceId.SyntaxVersion.MinorVersion;
BeginAutoListenCall();
if (ManagerCount == 0)
{
EndAutoListenCall();
return (RPC_S_UNKNOWN_IF);
}
RpcTryExcept
{
RpcStatus = CallbackFn(&RpcIfId, Context);
}
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
{
RpcStatus = RPC_S_ACCESS_DENIED;
}
RpcEndExcept
EndAutoListenCall();
}
return(RpcStatus);
}
void
RPC_INTERFACE::WaitForCalls(
)
/*++
Routine Description:
Waits for the completion of all the calls on a given interface.
--*/
{
DictionaryCursor cursor;
RPC_INTERFACE_MANAGER * InterfaceManager;
while ( NullManagerActiveCallCount.GetInteger() > 0 )
{
PauseExecution(500L);
}
InterfaceManagerDictionary.Reset(cursor);
while ((InterfaceManager = InterfaceManagerDictionary.Next(cursor)) != 0)
{
while ( InterfaceManager->InquireActiveCallCount() > 0 )
{
PauseExecution(500L);
}
}
}
RPC_SERVER::RPC_SERVER (
IN OUT RPC_STATUS PAPI * RpcStatus
) : AvailableCallCount(0),
ServerMutex(RpcStatus,
TRUE // pre-allocate semaphore
),
StopListeningEvent(RpcStatus),
ThreadCacheMutex(RpcStatus,
TRUE, // pre-allocate semaphore
100
),
NumAutoListenInterfaces(0)
/*++
Routine Description:
This routine will get called to construct an instance of the
RPC_SERVER class.
--*/
{
ALLOCATE_THIS(RPC_SERVER);
ServerListeningFlag = 0;
ListeningThreadFlag = 0;
WaitingThreadFlag = 0;
MinimumCallThreads = 1;
MaximumConcurrentCalls = 1;
IncomingRpcCount = 0;
OutgoingRpcCount = 0;
ReceivedPacketCount = 0;
SentPacketCount = 0;
ThreadCache = 0;
ListenStatusCode = RPC_S_OK;
fAccountForMaxCalls = TRUE;
pRpcForwardFunction = (RPC_FORWARD_FUNCTION *)0;
#if !defined(NO_LOCATOR_CODE)
pNsBindingExport = 0;
pNsBindingUnexport = 0;
#endif
}
RPC_INTERFACE *
RPC_SERVER::FindInterface (
IN RPC_SERVER_INTERFACE PAPI * RpcInterfaceInformation
)
/*++
Routine Description:
This method is used to find the rpc interface registered with this
server which matches the supplied rpc interface information.
Arguments:
RpcInterfaceInformation - Supplies the rpc interface information
identifying the rpc interface we are looking for.
Return Value:
The rpc interface matching the supplied rpc interface information
will be returned if it is found; otherwise, zero will be returned.
--*/
{
RPC_INTERFACE * RpcInterface;
DictionaryCursor cursor;
ServerMutex.VerifyOwned();
RpcInterfaceDictionary.Reset(cursor);
while ((RpcInterface = RpcInterfaceDictionary.Next(cursor)) != 0)
{
if (RpcInterface->MatchRpcInterfaceInformation(
RpcInterfaceInformation) == 0)
{
return(RpcInterface);
}
}
// The management interface is implicitly registered in all servers.
if ( (GlobalManagementInterface)
&& (GlobalManagementInterface->MatchRpcInterfaceInformation(
RpcInterfaceInformation) == 0) )
{
return(GlobalManagementInterface);
}
return(0);
}
int
RPC_SERVER::AddInterface (
IN RPC_INTERFACE * RpcInterface
)
/*++
Routine Description:
This method will be used to all an rpc interface to the set of
interfaces known about by this server.
Arguments:
RpcInterface - Supplies the rpc interface to add to the set of
interfaces.
Return Value:
Zero will be returned if the interface is successfully added to
the set; otherwise, non-zero will be returned indicating that
insufficient memory is available to complete the operation.
--*/
{
if (RpcInterfaceDictionary.Insert(RpcInterface) == -1)
{
ServerMutex.Clear();
return(-1);
}
return(0);
}
RPC_STATUS
RPC_SERVER::FindInterfaceTransfer (
IN PRPC_SYNTAX_IDENTIFIER InterfaceIdentifier,
IN PRPC_SYNTAX_IDENTIFIER ProposedTransferSyntaxes,
IN unsigned int NumberOfTransferSyntaxes,
OUT PRPC_SYNTAX_IDENTIFIER AcceptedTransferSyntax,
OUT RPC_INTERFACE ** AcceptingRpcInterface,
OUT BOOL *fInterfaceTransferIsPreferred,
OUT int *ProposedTransferSyntaxIndex,
OUT int *AvailableTransferSyntaxIndex
)
/*++
Routine Description:
This method is used to determine if a client bind request can be
accepted or not. All we have got to do here is hand off to the
server which owns this address.
Arguments:
InterfaceIdentifier - Supplies the syntax identifier for the
interface; this is the interface uuid and version.
ProposedTransferSyntaxes - Supplies a list of one or more transfer
syntaxes which the client initiating the binding supports. The
server should pick one of these which is supported by the
interface.
NumberOfTransferSyntaxes - Supplies the number of transfer syntaxes
specified in the proposed transfer syntaxes argument.
AcceptedTransferSyntax - Returns the transfer syntax which the
server accepted.
AcceptingRpcInterface - Returns a pointer to the rpc interface found
which supports the requested interface and one of the requested
transfer syntaxes.
fInterfaceTransferIsPreferred - non zero if the interface transfer
returned is preferred.
TransferSyntaxIndex - the index of the chosen transfer syntax in the
ProposedTransferSyntaxesArray
Return Value:
RPC_S_OK - The requested interface exists and it supports at least
one of the proposed transfer syntaxes. We are all set, now we
can make remote procedure calls.
RPC_S_UNSUPPORTED_TRANSFER_SYNTAX - The requested interface exists,
but it does not support any of the proposed transfer syntaxes.
RPC_S_UNKNOWN_IF - The requested interface is not supported
by this rpc server.
--*/
{
RPC_INTERFACE * RpcInterface;
unsigned int InterfaceFound = 0;
DictionaryCursor cursor;
ServerMutex.Request();
RpcInterfaceDictionary.Reset(cursor);
while ((RpcInterface = RpcInterfaceDictionary.Next(cursor)) != 0)
{
if (RpcInterface->MatchInterfaceIdentifier(InterfaceIdentifier) == 0)
{
InterfaceFound = 1;
if (RpcInterface->SelectTransferSyntax(ProposedTransferSyntaxes,
NumberOfTransferSyntaxes, AcceptedTransferSyntax,
fInterfaceTransferIsPreferred, ProposedTransferSyntaxIndex,
AvailableTransferSyntaxIndex) == 0)
{
ServerMutex.Clear();
*AcceptingRpcInterface = RpcInterface;
return(RPC_S_OK);
}
}
}
ServerMutex.Clear();
// The management interface is implicitly registered in all servers.
if ( (GlobalManagementInterface)
&& (GlobalManagementInterface->MatchInterfaceIdentifier(
InterfaceIdentifier) == 0 ) )
{
InterfaceFound = 1;
if (GlobalManagementInterface->SelectTransferSyntax(
ProposedTransferSyntaxes, NumberOfTransferSyntaxes,
AcceptedTransferSyntax, fInterfaceTransferIsPreferred,
ProposedTransferSyntaxIndex, AvailableTransferSyntaxIndex) == 0)
{
*AcceptingRpcInterface = GlobalManagementInterface;
return(RPC_S_OK);
}
}
if (InterfaceFound == 0)
return(RPC_S_UNKNOWN_IF);
return(RPC_S_UNSUPPORTED_TRANS_SYN);
}
RPC_INTERFACE *
RPC_SERVER::FindInterface (
IN PRPC_SYNTAX_IDENTIFIER InterfaceIdentifier
)
/*++
Routine Description:
The datagram protocol module will use this routine to find the interface
with out worrying about the transfer syntax. Datagram RPC does not support
more than a single transfer syntax.
Arguments:
InterfaceIdentifier - Supplies the identifier (UUID and version) of the
interface we are trying to find.
Return Value:
If the interface is found it will be returned; otherwise, zero will be
returned.
--*/
{
RPC_INTERFACE * RpcInterface;
DictionaryCursor cursor;
ServerMutex.Request();
RpcInterfaceDictionary.Reset(cursor);
while ( (RpcInterface = RpcInterfaceDictionary.Next(cursor)) != 0)
{
if ( RpcInterface->MatchInterfaceIdentifier(InterfaceIdentifier)
== 0 )
{
ServerMutex.Clear();
return(RpcInterface);
}
}
ServerMutex.Clear();
// The management interface is implicitly registered in all servers.
if ( (GlobalManagementInterface)
&& (GlobalManagementInterface->MatchInterfaceIdentifier(
InterfaceIdentifier) == 0) )
{
return(GlobalManagementInterface);
}
return(0);
}
RPC_STATUS
RPC_SERVER::ServerListen (
IN unsigned int MinimumCallThreads,
IN unsigned int MaximumConcurrentCalls,
IN unsigned int DontWait
)
/*++
Routine Description:
This method is called to start this rpc server listening for remote
procedure calls. We do not return until after StopServerListening
has been called and all active calls complete, or an error occurs.
Arguments:
MinimumCallThreads - Supplies the minimum number of call threads
which should be created to service remote procedure calls.
MaximumConcurrentCalls - Supplies the maximum concurrent calls this
rpc server is willing to accept at one time.
DontWait - Supplies a flag indicating whether or not to wait until
RpcMgmtStopServerListening has been called and all calls have
completed. A non-zero value indicates not to wait.
Return Value:
RPC_S_OK - Everything worked out in the end. All active calls
completed successfully after RPC_SERVER::StopServerListening
was called. No errors occured in the transports.
RPC_S_ALREADY_LISTENING - This rpc server is already listening.
RPC_S_NO_PROTSEQS_REGISTERED - No protocol sequences have been
registered with this rpc server. As a consequence it is
impossible for this rpc server to receive any remote procedure
calls, hence, the error code.
RPC_S_MAX_CALLS_TOO_SMALL - MaximumConcurrentCalls is smaller than
MinimumCallThreads or MaximumConcurrentCalls is zero.
--*/
{
RPC_ADDRESS * RpcAddress;
RPC_STATUS Status;
DictionaryCursor cursor;
if ( ( MaximumConcurrentCalls < MinimumCallThreads )
|| ( MaximumConcurrentCalls == 0 ) )
{
return(RPC_S_MAX_CALLS_TOO_SMALL);
}
if ( MaximumConcurrentCalls > 0x7FFFFFFF )
{
MaximumConcurrentCalls = 0x7FFFFFFF;
}
ServerMutex.Request();
if ( ListeningThreadFlag != 0 )
{
ServerMutex.Clear();
return(RPC_S_ALREADY_LISTENING);
}
if ( RpcAddressDictionary.Size() == 0
&& RpcDormantAddresses.IsQueueEmpty())
{
ServerMutex.Clear();
return(RPC_S_NO_PROTSEQS_REGISTERED);
}
this->MaximumConcurrentCalls = MaximumConcurrentCalls;
// if we are provided the default number, then we don't really care -
// play for optimal performance
if (MaximumConcurrentCalls == RPC_C_LISTEN_MAX_CALLS_DEFAULT)
fAccountForMaxCalls = FALSE;
this->MinimumCallThreads = MinimumCallThreads;
AvailableCallCount.SetInteger( MaximumConcurrentCalls );
RpcAddressDictionary.Reset(cursor);
while ( (RpcAddress = RpcAddressDictionary.Next(cursor)) != 0 )
{
Status = RpcAddress->ServerStartingToListen(
MinimumCallThreads,
MaximumConcurrentCalls);
if (Status)
{
ServerMutex.Clear();
return(Status);
}
}
StopListeningEvent.Lower();
ServerListeningFlag = 1;
ListeningThreadFlag = 1;
if ( DontWait != 0 )
{
ServerMutex.Clear();
return(RPC_S_OK);
}
WaitingThreadFlag = 1;
ServerMutex.Clear();
return(WaitForStopServerListening());
}
RPC_STATUS
RPC_SERVER::WaitForStopServerListening (
)
/*++
Routine Description:
We wait for StopServerListening to be called and then for all active
remote procedure calls to complete before returning.
Return Value:
RPC_S_OK - Everything worked out in the end. All active calls
completed successfully after RPC_SERVER::StopServerListening
was called. No errors occured in the transports.
--*/
{
RPC_ADDRESS * RpcAddress;
DictionaryCursor cursor;
RPC_INTERFACE * RpcInterface;
StopListeningEvent.Wait();
if ( ListenStatusCode != RPC_S_OK )
{
ListeningThreadFlag = 0;
return(ListenStatusCode);
}
RpcAddressDictionary.Reset(cursor);
while ( (RpcAddress = RpcAddressDictionary.Next(cursor)) != 0 )
{
RpcAddress->ServerStoppedListening();
}
RpcAddressDictionary.Reset(cursor);
while ( (RpcAddress = RpcAddressDictionary.Next(cursor)) != 0 )
{
RpcAddress->WaitForCalls();
}
// Wait for calls on all interfaces to complete
RpcInterfaceDictionary.Reset(cursor);
while ((RpcInterface = RpcInterfaceDictionary.Next(cursor)) != 0)
{
RpcInterface->WaitForCalls();
}
ServerMutex.Request();
WaitingThreadFlag = 0;
ListeningThreadFlag = 0;
ServerMutex.Clear();
return(RPC_S_OK);
}
RPC_STATUS
RPC_SERVER::WaitServerListen (
)
/*++
Routine Description:
This routine performs the wait that ServerListen normally performs
when the DontWait flag is not set. An application must call this
routine only after RpcServerListen has been called with the DontWait
flag set. We do not return until RpcMgmtStopServerListening is called
and all active remote procedure calls complete, or a fatal error occurs
in the runtime.
Return Value:
RPC_S_OK - Everything worked as expected. All active remote procedure
calls have completed. It is now safe to exit this process.
RPC_S_ALREADY_LISTENING - Another thread has already called
WaitServerListen and has not yet returned.
RPC_S_NOT_LISTENING - ServerListen has not yet been called.
--*/
{
ServerMutex.Request();
if ( ListeningThreadFlag == 0 )
{
ServerMutex.Clear();
return(RPC_S_NOT_LISTENING);
}
if ( WaitingThreadFlag != 0 )
{
ServerMutex.Clear();
return(RPC_S_ALREADY_LISTENING);
}
WaitingThreadFlag = 1;
ServerMutex.Clear();
return(WaitForStopServerListening());
}
void
RPC_SERVER::InquireStatistics (
OUT RPC_STATS_VECTOR * Statistics
)
/*++
Routine Description:
This method is used to obtain the statistics for this rpc server.
Arguments:
Statistics - Returns the statistics for this rpc server.
--*/
{
Statistics->Stats[RPC_C_STATS_CALLS_IN] = IncomingRpcCount;
Statistics->Stats[RPC_C_STATS_CALLS_OUT] = OutgoingRpcCount;
Statistics->Stats[RPC_C_STATS_PKTS_IN] = ReceivedPacketCount;
Statistics->Stats[RPC_C_STATS_PKTS_OUT] = SentPacketCount;
}
RPC_STATUS
RPC_SERVER::StopServerListening (
)
/*++
Routine Description:
This method is called to stop this rpc server from listening for
more remote procedure calls. Active calls are allowed to complete
(including callbacks). The thread which called ServerListen will
return when all active calls complete.
Return Value:
RPC_S_OK - The thread that called ServerListen has successfully been
notified that it should shutdown.
RPC_S_NOT_LISTENING - There is no thread currently listening.
--*/
{
if (ListeningThreadFlag == 0)
return(RPC_S_NOT_LISTENING);
ListenStatusCode = RPC_S_OK;
ServerListeningFlag = 0;
StopListeningEvent.Raise();
return(RPC_S_OK);
}
RPC_STATUS
RPC_SERVER::UseRpcProtocolSequence (
IN RPC_CHAR PAPI * NetworkAddress,
IN RPC_CHAR PAPI * RpcProtocolSequence,
IN unsigned int PendingQueueSize,
IN RPC_CHAR PAPI *Endpoint,
IN void PAPI * SecurityDescriptor,
IN unsigned long EndpointFlags,
IN unsigned long NICFlags
)
/*++
Routine Description:
This method is who does the work of creating new address (they
are called protocol sequences in the DCE lingo) and adding them to
this rpc server.
Arguments:
RpcProtocolSequence - Supplies the rpc protocol sequence we wish
to add to this rpc server.
PendingQueueSize - Supplies the size of the queue of pending
requests which should be created by the transport. Some transports
will not be able to make use of this value, while others will.
Endpoint - Optionally supplies an endpoint to be used for the new
address. If an endpoint is not specified, then we will let
the transport specify the endpoint.
SecurityDescriptor - Optionally supplies a security descriptor to
be placed on the rpc protocol sequence (address) we are adding
to this rpc server.
Return Value:
RPC_S_OK - The requested rpc protocol sequence has been added to
this rpc server.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to add the
requested rpc protocol sequence to this rpc server.
RPC_S_PROTSEQ_NOT_SUPPORTED - The specified rpc protocol sequence
is not supported (but it appears to be valid).
RPC_S_INVALID_RPC_PROTSEQ - The specified rpc protocol sequence is
syntactically invalid.
RPC_S_DUPLICATE_ENDPOINT - The supplied endpoint has already been
added to this rpc server.
RPC_S_INVALID_SECURITY_DESC - The supplied security descriptor is
invalid.
--*/
{
TRANS_INFO *ServerTransInfo ;
RPC_STATUS Status;
RPC_ADDRESS * RpcAddress;
RPC_ADDRESS * Address;
NETWORK_ADDRESS_VECTOR *pNetworkAddressVector;
unsigned int StaticEndpointFlag;
int Key;
DictionaryCursor cursor;
THREAD *ThisThread;
ThisThread = ThreadSelf();
if (ThisThread == NULL)
return RPC_S_OUT_OF_MEMORY;
// remove old EEInfo
RpcpPurgeEEInfo();
if (IsServerSideDebugInfoEnabled())
{
Status = InitializeServerSideCellHeapIfNecessary();
if (Status != RPC_S_OK)
return Status;
}
if ( RpcpStringCompare(RpcProtocolSequence,
RPC_CONST_STRING("ncalrpc")) == 0 )
{
RpcAddress = LrpcCreateRpcAddress();
}
else if ( RpcpStringNCompare(
RPC_CONST_STRING("ncadg_"),
RpcProtocolSequence,
6) == 0)
{
//
// Just use the osf mapping...it simply calls the
// protocol-independent ones.
//
Status = OsfMapRpcProtocolSequence(1,
RpcProtocolSequence,
&ServerTransInfo);
if (Status != RPC_S_OK)
{
return Status;
}
RpcAddress = DgCreateRpcAddress(ServerTransInfo);
}
else if ( RpcpStringNCompare(
RPC_CONST_STRING("ncacn_"),
RpcProtocolSequence,
6) == 0)
{
Status = OsfMapRpcProtocolSequence(1,
RpcProtocolSequence,
&ServerTransInfo);
if (Status != RPC_S_OK)
{
return(Status);
}
RpcAddress = OsfCreateRpcAddress(ServerTransInfo);
}
else
{
return(RPC_S_PROTSEQ_NOT_SUPPORTED);
}
if (RpcAddress == 0)
{
return(RPC_S_OUT_OF_MEMORY);
}
if (ARGUMENT_PRESENT(Endpoint))
{
ServerMutex.Request();
RpcAddressDictionary.Reset(cursor);
while ((Address = RpcAddressDictionary.Next(cursor)) != 0)
{
if ( Address->SameEndpointAndProtocolSequence(
NetworkAddress,
RpcProtocolSequence,
Endpoint) != 0 )
{
ServerMutex.Clear();
delete RpcAddress;
return(RPC_S_DUPLICATE_ENDPOINT);
}
}
ServerMutex.Clear();
Endpoint = DuplicateString(Endpoint);
if (Endpoint == 0)
{
delete RpcAddress;
return(RPC_S_OUT_OF_MEMORY);
}
StaticEndpointFlag = 1;
}
else
{
//
// MACBUG:
// We need to include this for Macintosh/Win95 also...
//
ServerMutex.Request() ;
RpcAddressDictionary.Reset(cursor) ;
while ((Address = RpcAddressDictionary.Next(cursor)) != 0)
{
if ( Address->SameProtocolSequence(NetworkAddress,
RpcProtocolSequence) != 0 )
{
ServerMutex.Clear();
delete RpcAddress;
return(RPC_S_OK);
}
}
ServerMutex.Clear();
StaticEndpointFlag = 0;
} // else
if (EndpointFlags & RPC_C_DONT_FAIL)
{
RpcAddress->PnpNotify();
}
Status = RpcAddress->ServerSetupAddress(
NetworkAddress,
&Endpoint,
PendingQueueSize,
SecurityDescriptor,
EndpointFlags,
NICFlags,
&pNetworkAddressVector);
if (Status == RPC_S_OK)
{
RPC_CHAR *MyNetworkAddress = NULL;
RpcProtocolSequence = DuplicateString(RpcProtocolSequence);
if (RpcProtocolSequence == 0)
{
delete Endpoint;
delete RpcAddress;
return(RPC_S_OUT_OF_MEMORY);
}
if (ARGUMENT_PRESENT(NetworkAddress))
{
MyNetworkAddress = DuplicateString(NetworkAddress);
if (MyNetworkAddress == 0)
{
delete Endpoint;
delete RpcAddress;
delete RpcProtocolSequence;
return(RPC_S_OUT_OF_MEMORY);
}
}
Status = RpcAddress->SetEndpointAndStuff(
MyNetworkAddress,
Endpoint,
RpcProtocolSequence,
this,
StaticEndpointFlag,
PendingQueueSize,
SecurityDescriptor,
EndpointFlags,
NICFlags,
pNetworkAddressVector);
if (Status != RPC_S_OK)
{
delete RpcAddress;
return Status;
}
}
else
{
if (EndpointFlags & RPC_C_DONT_FAIL)
{
int retval;
RPC_CHAR *MyNetworkAddress = NULL;
RpcProtocolSequence = DuplicateString(RpcProtocolSequence);
if (RpcProtocolSequence == 0)
{
delete Endpoint;
delete RpcAddress;
return(RPC_S_OUT_OF_MEMORY);
}
if (ARGUMENT_PRESENT(NetworkAddress))
{
MyNetworkAddress = DuplicateString(NetworkAddress);
if (MyNetworkAddress == 0)
{
delete Endpoint;
delete RpcAddress;
delete RpcProtocolSequence;
return(RPC_S_OUT_OF_MEMORY);
}
}
Status = RpcAddress->SetEndpointAndStuff(
MyNetworkAddress,
Endpoint,
RpcProtocolSequence,
this,
StaticEndpointFlag,
PendingQueueSize,
SecurityDescriptor,
EndpointFlags,
NICFlags,
NULL);
if (Status != RPC_S_OK)
{
delete Endpoint;
delete RpcAddress;
return Status;
}
ServerMutex.Request();
retval = RpcDormantAddresses.PutOnQueue(RpcAddress, 0);
ServerMutex.Clear();
if (retval == 1)
{
delete Endpoint;
delete RpcAddress;
return RPC_S_OUT_OF_MEMORY;
}
ServerMutex.Request();
Status = RpcAddress->ServerStartingToListen(
MinimumCallThreads,
MaximumConcurrentCalls);
ServerMutex.Clear();
if (Status)
{
return(Status);
}
return RPC_S_OK;
}
else
{
delete RpcAddress;
return(Status);
}
}
Key = AddAddress(RpcAddress);
if (Key == -1)
{
delete RpcAddress;
return(RPC_S_OUT_OF_MEMORY);
}
RpcAddress->DictKey = Key;
ServerMutex.Request();
Status = RpcAddress->ServerStartingToListen(
MinimumCallThreads,
MaximumConcurrentCalls);
ServerMutex.Clear();
if (Status)
{
return(Status);
}
//
// Inform the transport that it can start.
//
RpcAddress->CompleteListen() ;
return(RPC_S_OK);
}
int
RPC_SERVER::AddAddress (
IN RPC_ADDRESS * RpcAddress
)
/*++
Routine Description:
This method is used to add an rpc address to the dictionary of
rpc addresses know about by this rpc server.
Arguments:
RpcAddress - Supplies the rpc address to be inserted into the
dictionary of rpc addresses.
Return Value:
RPC_S_OK - The supplied rpc address has been successfully added
to the dictionary.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to insert
the rpc address into the dictionary.
--*/
{
int Key;
ServerMutex.Request();
Key = RpcAddressDictionary.Insert(RpcAddress);
ServerMutex.Clear();
return(Key);
}
RPC_STATUS
RPC_SERVER::UnregisterIf (
IN RPC_SERVER_INTERFACE PAPI * RpcInterfaceInformation OPTIONAL,
IN RPC_UUID PAPI * ManagerTypeUuid OPTIONAL,
IN unsigned int WaitForCallsToComplete
)
/*++
Routine Description:
This method does the work of unregistering an interface from this
rpc server. We actually do not remove the interface; what we do
is to one or all of the manager entry point vector depending upon
the type uuid argument supplied.
Arguments:
RpcInterfaceInformation - Supplies a description of the interface
for which we want to unregister one or all manager entry point
vectors.
ManagerTypeUuid - Optionally supplies the type uuid of the manager
entry point vector to be removed. If this argument is not supplied,
then all manager entry point vectors for this interface will
be removed.
WaitForCallsToComplete - Supplies a flag indicating whether or not
this routine should wait for all calls to complete using the
interface and manager being unregistered. A non-zero value
indicates to wait.
Return Value:
RPC_S_OK - The manager entry point vector(s) are(were) successfully
removed from the specified interface.
RPC_S_UNKNOWN_MGR_TYPE - The specified type uuid is not registered
with the interface.
RPC_S_UNKNOWN_IF - The specified interface is not registered with
the rpc server.
--*/
{
RPC_INTERFACE * RpcInterface;
RPC_STATUS RpcStatus;
RPC_STATUS Status;
int i;
DictionaryCursor cursor;
UNUSED(WaitForCallsToComplete);
if (ARGUMENT_PRESENT(RpcInterfaceInformation))
{
ServerMutex.Request();
RpcInterface = FindInterface(RpcInterfaceInformation);
ServerMutex.Clear();
if (RpcInterface == 0)
return(RPC_S_UNKNOWN_IF);
if (RpcInterface->IsAutoListenInterface())
{
GlobalRpcServer->DecrementAutoListenInterfaceCount() ;
while ( RpcInterface->InqAutoListenCallCount() )
{
RPC_ADDRESS * Address;
ServerMutex.Request();
RpcAddressDictionary.Reset(cursor);
while (0 != (Address = RpcAddressDictionary.Next(cursor)))
{
Address->EncourageCallCleanup(RpcInterface);
}
ServerMutex.Clear();
PauseExecution(500);
}
}
return(RpcInterface->UnregisterManagerEpv(ManagerTypeUuid,
WaitForCallsToComplete));
}
Status = RPC_S_UNKNOWN_MGR_TYPE;
ServerMutex.Request();
RpcInterfaceDictionary.Reset(cursor);
while ((RpcInterface = RpcInterfaceDictionary.Next(cursor)) != 0)
{
// auto-listen intefaces have to be individually unregistered
if (RpcInterface->IsAutoListenInterface())
{
continue;
}
RpcStatus = RpcInterface->UnregisterManagerEpv(ManagerTypeUuid,
WaitForCallsToComplete);
if (RpcStatus == RPC_S_OK)
{
Status = RPC_S_OK;
}
}
ServerMutex.Clear();
return(Status);
}
RPC_STATUS
RPC_SERVER::InquireManagerEpv (
IN RPC_SERVER_INTERFACE PAPI * RpcInterfaceInformation,
IN RPC_UUID PAPI * ManagerTypeUuid, OPTIONAL
OUT RPC_MGR_EPV PAPI * PAPI * ManagerEpv
)
/*++
Routine Description:
This method is used to obtain the manager entry point vector for
an interface supported by this rpc server. A type uuid argument
specifies which manager entry point vector is to be obtained.
Arguments:
RpcInterfaceInformation - Supplies a description of the interface.
ManagerTypeUuid - Optionally supplies the type uuid of the manager
entry point vector we want returned. If no manager type uuid
is specified, then the null uuid is assumed.
ManagerEpv - Returns the manager entry point vector.
Return Value:
RPC_S_OK - The manager entry point vector has successfully been
returned.
RPC_S_UNKNOWN_MGR_TYPE - The specified type uuid is not registered
with the interface.
RPC_S_UNKNOWN_IF - The specified interface is not registered with
the rpc server.
--*/
{
RPC_INTERFACE * RpcInterface;
ServerMutex.Request();
RpcInterface = FindInterface(RpcInterfaceInformation);
ServerMutex.Clear();
if (RpcInterface == 0)
return(RPC_S_UNKNOWN_IF);
return(RpcInterface->InquireManagerEpv(ManagerTypeUuid, ManagerEpv));
}
RPC_STATUS
RPC_SERVER::InquireBindings (
OUT RPC_BINDING_VECTOR PAPI * PAPI * BindingVector
)
/*++
Routine Description:
For each rpc protocol sequence registered with this rpc server
we want to create a binding handle which can be used to make
remote procedure calls using the registered rpc protocol sequence.
We return a vector of these binding handles.
Arguments:
BindingVector - Returns the vector of binding handles.
Return Value:
RPC_S_OK - At least one rpc protocol sequence has been registered
with this rpc server, and the operation completed successfully.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to complete
the operation.
RPC_S_NO_BINDINGS - No rpc protocol sequences have been successfully
registered with this rpc server.
--*/
{
RPC_BINDING_VECTOR PAPI * RpcBindingVector;
unsigned int Index, RpcAddressIndex;
RPC_ADDRESS * RpcAddress;
BINDING_HANDLE * BindingHandle;
int i ;
RPC_CHAR PAPI * LocalNetworkAddress;
int count = 0 ;
DictionaryCursor cursor;
ServerMutex.Request();
if (RpcAddressDictionary.Size() == 0
&& RpcDormantAddresses.IsQueueEmpty())
{
ServerMutex.Clear();
return(RPC_S_NO_BINDINGS);
}
RpcAddressDictionary.Reset(cursor);
while ((RpcAddress = RpcAddressDictionary.Next(cursor)) != 0)
{
count += RpcAddress->InqNumNetworkAddress();
}
RpcBindingVector = (RPC_BINDING_VECTOR PAPI *) RpcpFarAllocate(
sizeof(RPC_BINDING_VECTOR) + (count -1 )
* sizeof(RPC_BINDING_HANDLE) );
if (RpcBindingVector == 0)
{
ServerMutex.Clear();
return(RPC_S_OUT_OF_MEMORY);
}
RpcBindingVector->Count = count;
for (Index = 0; Index < RpcBindingVector->Count; Index++)
RpcBindingVector->BindingH[Index] = 0;
Index = 0;
RpcAddressDictionary.Reset(cursor);
while ((RpcAddress = RpcAddressDictionary.Next(cursor)) != 0)
{
RpcAddressIndex = 0;
LocalNetworkAddress = RpcAddress->
GetListNetworkAddress(RpcAddressIndex) ;
while(LocalNetworkAddress != NULL)
{
BindingHandle = RpcAddress->
InquireBinding(LocalNetworkAddress);
if (BindingHandle == 0)
{
ServerMutex.Clear();
RpcBindingVectorFree(&RpcBindingVector);
return(RPC_S_OUT_OF_MEMORY);
}
RpcBindingVector->BindingH[Index] = BindingHandle;
Index += 1;
RpcAddressIndex += 1;
LocalNetworkAddress = RpcAddress->
GetListNetworkAddress(RpcAddressIndex) ;
}
}
ServerMutex.Clear();
ASSERT(Index == RpcBindingVector->Count);
*BindingVector = RpcBindingVector;
return(RPC_S_OK);
}
RPC_STATUS
RPC_SERVER::RegisterAuthInfoHelper (
IN RPC_CHAR PAPI * ServerPrincipalName,
IN unsigned long AuthenticationService,
IN RPC_AUTH_KEY_RETRIEVAL_FN GetKeyFunction, OPTIONAL
IN void PAPI * Argument OPTIONAL
)
{
RPC_AUTHENTICATION * Service;
RPC_STATUS RpcStatus;
RPC_CHAR __RPC_FAR * PrincipalName;
DictionaryCursor cursor;
if (ServerPrincipalName == NULL)
{
ServerPrincipalName = new RPC_CHAR[1];
if (ServerPrincipalName == NULL)
{
return (RPC_S_OUT_OF_MEMORY);
}
ServerPrincipalName[0] = '\0';
}
if ( AuthenticationService == 0 )
{
return(RPC_S_UNKNOWN_AUTHN_SERVICE);
}
if (AuthenticationService == RPC_C_AUTHN_DEFAULT)
{
RpcpGetDefaultSecurityProviderInfo();
AuthenticationService = DefaultProviderId;
}
ServerMutex.Request();
AuthenticationDictionary.Reset(cursor);
while ( (Service = AuthenticationDictionary.Next(cursor)) != 0 )
{
if ( Service->AuthenticationService == AuthenticationService &&
0 == RpcpStringCompare(Service->ServerPrincipalName, ServerPrincipalName))
{
Service->GetKeyFunction = GetKeyFunction;
Service->Argument = Argument;
ServerMutex.Clear();
// Flush the server-credentials cache
RpcStatus = RemoveCredentialsFromCache(AuthenticationService);
return (RpcStatus);
}
}
RpcStatus = IsAuthenticationServiceSupported(AuthenticationService);
if ( RpcStatus != RPC_S_OK )
{
ServerMutex.Clear();
if ( (RpcStatus == RPC_S_UNKNOWN_AUTHN_SERVICE) ||
(RpcStatus == RPC_S_UNKNOWN_AUTHN_LEVEL) )
{
return (RPC_S_UNKNOWN_AUTHN_SERVICE);
}
else
{
return (RPC_S_OUT_OF_MEMORY);
}
}
Service = new RPC_AUTHENTICATION;
if ( Service == 0 )
{
ServerMutex.Clear();
return(RPC_S_OUT_OF_MEMORY);
}
Service->AuthenticationService = AuthenticationService;
Service->ServerPrincipalName = DuplicateString(ServerPrincipalName);
Service->GetKeyFunction = GetKeyFunction;
Service->Argument = Argument;
if ( Service->ServerPrincipalName == 0 )
{
ServerMutex.Clear();
delete Service;
return(RPC_S_OUT_OF_MEMORY);
}
if ( AuthenticationDictionary.Insert(Service) == -1 )
{
ServerMutex.Clear();
delete Service;
return(RPC_S_OUT_OF_MEMORY);
}
ServerMutex.Clear();
return(RPC_S_OK);
}
RPC_STATUS
RPC_SERVER::AutoRegisterAuthSvc(
IN RPC_CHAR * ServerPrincipalName,
IN unsigned long AuthenticationService
)
{
RPC_STATUS Status;
DictionaryCursor cursor;
RPC_AUTHENTICATION * Service;
//
// Don't need to auto-register the provider if it is already registered
//
ServerMutex.Request();
AuthenticationDictionary.Reset(cursor);
while ( (Service = AuthenticationDictionary.Next(cursor)) != 0 )
{
if ( Service->AuthenticationService == AuthenticationService)
{
ServerMutex.Clear();
return (RPC_S_OK);
}
}
ServerMutex.Clear();
Status = RegisterAuthInfoHelper(ServerPrincipalName,
AuthenticationService,
NULL,
NULL);
if (Status == RPC_S_UNKNOWN_AUTHN_SERVICE)
{
//
// Ok to not register provider if it is disabled
//
return RPC_S_OK;
}
return Status;
}
RPC_STATUS
RPC_SERVER::RegisterAuthInformation (
IN RPC_CHAR PAPI * ServerPrincipalName,
IN unsigned long AuthenticationService,
IN RPC_AUTH_KEY_RETRIEVAL_FN GetKeyFunction, OPTIONAL
IN void PAPI * Argument OPTIONAL
)
/*++
Routine Description:
This method is used to register authentication, authorization, and
a server principal name to be used for security for this server. We
will use this information to authenticate remote procedure calls.
Arguments:
ServerPrincipalName - Supplies the principal name for the server.
AuthenticationService - Supplies an authentication service to use when
the server receives a remote procedure call.
GetKeyFunction - Optionally supplies a routine to be used when the runtime
needs an encryption key.
Argument - Optionally supplies an argument to be passed to the routine used
to get keys each time it is called.
Return Value:
RPC_S_OK - The authentication service and server principal name have
been registered with this RPC server.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to complete the
operation.
RPC_S_UNKNOWN_AUTHN_SERVICE - The specified authentication service is
not supported.
--*/
{
RPC_STATUS Status;
RPC_CHAR *PrincName;
Status = RegisterAuthInfoHelper(ServerPrincipalName,
AuthenticationService,
GetKeyFunction,
Argument);
if (Status != RPC_S_OK)
{
return Status;
}
if (AuthenticationService != RPC_C_AUTHN_GSS_NEGOTIATE)
{
return RPC_S_OK;
}
Status = AutoRegisterAuthSvc(ServerPrincipalName, RPC_C_AUTHN_WINNT);
if (Status != RPC_S_OK)
{
return Status;
}
Status = AutoRegisterAuthSvc(ServerPrincipalName, RPC_C_AUTHN_GSS_KERBEROS);
return Status;
}
RPC_STATUS
RPC_SERVER::AcquireCredentials (
IN unsigned long AuthenticationService,
IN unsigned long AuthenticationLevel,
OUT SECURITY_CREDENTIALS ** SecurityCredentials
)
/*++
Routine Description:
The protocol modules will use this to obtain a set of credentials
for the specified authentication service, assuming that the server
supports them.
Arguments:
AuthenticationService - Supplies the authentication service for which
we hope to obtain credentials.
AuthenticationLevel - Supplies the authentication level to be used.
SecurityCredentials - Returns the security credentials.
Return Value:
RPC_S_OK - You have been given some security credentials, which you need
to free using RPC_SERVER::FreeCredentials when you are done with
them.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to complete the
operation.
RPC_S_UNKNOWN_AUTHN_SERVICE - The specified authentication service is
not supported by the current configuration.
--*/
{
RPC_AUTHENTICATION * Service;
RPC_STATUS RpcStatus = RPC_S_OK;
DictionaryCursor cursor;
ServerMutex.Request();
AuthenticationDictionary.Reset(cursor);
while ( (Service = AuthenticationDictionary.Next(cursor)) != 0 )
{
if ( Service->AuthenticationService == AuthenticationService )
{
ServerMutex.Clear();
RpcStatus = FindServerCredentials(
Service->GetKeyFunction,
Service->Argument,
AuthenticationService,
AuthenticationLevel,
Service->ServerPrincipalName,
SecurityCredentials
);
VALIDATE(RpcStatus)
{
RPC_S_OK,
RPC_S_SEC_PKG_ERROR,
RPC_S_OUT_OF_MEMORY,
RPC_S_INVALID_AUTH_IDENTITY,
ERROR_SHUTDOWN_IN_PROGRESS,
RPC_S_UNKNOWN_AUTHN_SERVICE
} END_VALIDATE;
return(RpcStatus);
}
}
ServerMutex.Clear();
return(RPC_S_UNKNOWN_AUTHN_SERVICE);
}
void
RPC_SERVER::FreeCredentials (
IN SECURITY_CREDENTIALS * SecurityCredentials
)
/*++
Routine Description:
A protocol module will indicate that it is through using a set of
security credentials, obtained from RPC_SERVER::AcquireCredentials,
using this routine.
Arguments:
SecurityCredentials - Supplies the security credentials to be freed.
--*/
{
SecurityCredentials->FreeCredentials();
delete SecurityCredentials;
}
RPC_STATUS
RPC_SERVER::RegisterInterface (
IN RPC_SERVER_INTERFACE PAPI * RpcInterfaceInformation,
IN RPC_UUID PAPI * ManagerTypeUuid,
IN RPC_MGR_EPV PAPI * ManagerEpv,
IN unsigned int Flags,
IN unsigned int MaxCalls,
IN unsigned int MaxRpcSize,
IN RPC_IF_CALLBACK_FN PAPI *IfCallbackFn
)
/*++
Routine Description:
This routine is used by server application to register a manager
entry point vector and optionally an interface. If the interface
has not been registered, then it will be registered. If it has
already been registered, the manager entry point vector will be
added to it under the specified type uuid.
Arguments:
RpcInterfaceInformation - Supplies a description of the interface to
be registered. We will make a copy of this information.
ManagerTypeUuid - Optionally supplies the type uuid for the specified
manager entry point vector. If no type uuid is supplied, then
the null uuid will be used as the type uuid.
ManagerEpv - Optionally supplies a manager entry point vector corresponding
to the type uuid. If a manager entry point vector is not supplied,
then the manager entry point vector in the interface will be
used.
Return Value:
RPC_S_OK - The specified rpc interface has been successfully
registered with the rpc server. It is now ready to accept
remote procedure calls.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to register
the rpc interface with the rpc server.
RPC_S_TYPE_ALREADY_REGISTERED - A manager entry point vector has
already been registered for the supplied rpc interface and
manager type UUID.
--*/
{
RPC_STATUS RpcStatus;
RPC_INTERFACE * RpcInterface;
RPC_ADDRESS *RpcAddress ;
DictionaryCursor cursor;
BOOL fInterfaceFound;
if ( ManagerEpv == 0 )
{
ManagerEpv = RpcInterfaceInformation->DefaultManagerEpv;
if ( (PtrToUlong(ManagerEpv)) == 0xFFFFFFFF)
{
// Stub compiled with -no_default_epv.
return (RPC_S_INVALID_ARG);
}
}
ServerMutex.Request();
RpcInterface = FindOrCreateInterfaceInternal(RpcInterfaceInformation, Flags, MaxCalls,
MaxRpcSize, IfCallbackFn, &RpcStatus, &fInterfaceFound);
if (RpcInterface == NULL)
{
ServerMutex.Clear();
return RpcStatus;
}
if (fInterfaceFound)
{
// if it was found, update the information
RpcStatus = RpcInterface->UpdateRpcInterfaceInformation(RpcInterfaceInformation,
Flags, MaxCalls, MaxRpcSize, IfCallbackFn);
if (RpcStatus != RPC_S_OK)
{
ServerMutex.Clear();
return RpcStatus;
}
}
RpcStatus = RpcInterface->RegisterTypeManager(ManagerTypeUuid, ManagerEpv);
if (Flags & RPC_IF_AUTOLISTEN)
{
RpcAddressDictionary.Reset(cursor);
while ( (RpcAddress = RpcAddressDictionary.Next(cursor)) != 0 )
{
RpcStatus = RpcAddress->ServerStartingToListen(
this->MinimumCallThreads,
MaxCalls);
if (RpcStatus != RPC_S_OK)
{
break;
}
}
}
ServerMutex.Clear();
return(RpcStatus);
}
void RPC_SERVER::CreateOrUpdateAddresses (void)
/*++
Function Name: CreateOrUpdateAddresses
Parameters:
Description:
The runtime just received a notification that a new protocol was loaded. We need
to create an ADDRESS object, listen on it and update the RPCSS bindings
appropriately.
Returns:
--*/
{
RPC_STATUS Status;
RPC_BINDING_VECTOR *BindingVector = 0;
RPC_INTERFACE * RpcInterface;
BOOL fChanged = 0;
RPC_ADDRESS *Address;
QUEUE tempQueue;
BOOL fTempQueueHasContents = FALSE;
int i;
DictionaryCursor cursor;
while (1)
{
unsigned int Length;
ServerMutex.Request();
Address = (RPC_ADDRESS *) RpcDormantAddresses.TakeOffQueue(&Length);
ServerMutex.Clear();
if (Address == 0)
{
break;
}
ASSERT(Length == 0);
if (Address->RestartAddress(MinimumCallThreads,
MaximumConcurrentCalls) != RPC_S_OK)
{
fTempQueueHasContents = TRUE;
if (tempQueue.PutOnQueue(Address, 0) != 0)
{
// putting on the temporary queue failed - out of memory
// in this case we'd rather cut the PnP process for now, and we'll
// go with what we have
// return the address
ServerMutex.Request();
// guaranteed to succeed
RpcDormantAddresses.PutOnQueue(Address, 0);
ServerMutex.Clear();
// break into merging the temp queue with the permanent one
break;
}
}
else
{
fChanged = 1;
}
}
if (fTempQueueHasContents)
{
ServerMutex.Request();
// merge back the queues - this should succeed if we have only added protocols. If we have
// removed protocols, this will fail, and we'll have a bug. Nothing we can do about it here.
RpcDormantAddresses.MergeWithQueue(&tempQueue);
ServerMutex.Clear();
}
if (fChanged)
{
ServerMutex.Request();
//
// Inquire the new bindings, and update them in the endpoint mapper
//
Status = InquireBindings(&BindingVector);
if (Status != RPC_S_OK)
{
goto Cleanup;
}
RpcInterfaceDictionary.Reset(cursor);
while ((RpcInterface = RpcInterfaceDictionary.Next(cursor)) != 0)
{
if (RpcInterface->NeedToUpdateBindings())
{
// we know an interface never gets deleted, only marked as
// unregistered. Therefore, it is safe to release the mutex
// and do the slow UpdateBindings outside the mutex
ServerMutex.Clear();
if ((Status = RpcInterface->UpdateBindings(BindingVector))
!= RPC_S_OK)
{
goto Cleanup;
}
ServerMutex.Request();
}
}
ServerMutex.Clear();
Status = RpcBindingVectorFree(&BindingVector);
ASSERT(Status == RPC_S_OK);
}
return;
Cleanup:
if (BindingVector)
{
Status = RpcBindingVectorFree(&BindingVector);
ASSERT(Status == RPC_S_OK);
}
}
RPC_INTERFACE *
RPC_SERVER::FindOrCreateInterfaceInternal (
IN RPC_SERVER_INTERFACE PAPI * RpcInterfaceInformation,
IN unsigned int Flags,
IN unsigned int MaxCalls,
IN unsigned int MaxRpcSize,
IN RPC_IF_CALLBACK_FN PAPI *IfCallbackFn,
OUT RPC_STATUS *Status,
OUT BOOL *fInterfaceFound
)
/*++
Function Name: FindOrCreateInterfaceInternal
Parameters:
RpcInterfaceInformation
Flags
MaxCalls
MaxRpcSize
IfCallbackFn
Status - meaningless if the return value is not NULL.
fInterfaceFound - TRUE if the interface was found, FALSE if it was created
Description:
Find or creates an interface with the appropriate parameters
Returns:
--*/
{
RPC_INTERFACE * RpcInterface;
ServerMutex.VerifyOwned();
RpcInterface = FindInterface(RpcInterfaceInformation);
if ( RpcInterface == 0 )
{
RpcInterface = new RPC_INTERFACE(RpcInterfaceInformation,
this, Flags, MaxCalls, MaxRpcSize, IfCallbackFn, Status);
if ( RpcInterface == 0 )
{
*Status = RPC_S_OUT_OF_MEMORY;
return NULL;
}
if ( AddInterface(RpcInterface) != 0 )
{
delete RpcInterface;
*Status = RPC_S_OUT_OF_MEMORY;
return NULL;
}
if (Flags & RPC_IF_AUTOLISTEN)
{
GlobalRpcServer->IncrementAutoListenInterfaceCount() ;
}
*fInterfaceFound = FALSE;
}
else
{
*fInterfaceFound = TRUE;
}
*Status = RPC_S_OK;
return RpcInterface;
}
RPC_INTERFACE *
RPC_SERVER::FindOrCreateInterface (
IN RPC_SERVER_INTERFACE PAPI * RpcInterfaceInformation,
OUT RPC_STATUS *Status
)
{
RPC_INTERFACE * RpcInterface;
BOOL fIgnored;
ServerMutex.Request();
RpcInterface = FindOrCreateInterfaceInternal(RpcInterfaceInformation,
RPC_IF_ALLOW_SECURE_ONLY, 0, gMaxRpcSize, NULL, Status, &fIgnored);
ServerMutex.Clear();
return RpcInterface;
}
RPC_STATUS
RPC_SERVER::InterfaceExported (
IN RPC_SERVER_INTERFACE PAPI * RpcInterfaceInformation,
IN UUID_VECTOR *MyObjectUuidVector,
IN unsigned char *MyAnnotation,
IN BOOL MyfReplace
)
/*++
Function Name:InterfaceExported
Parameters:
Description:
RpcEpRegister was called on this interface. We need to keep track
of the parameters, so that if we get a PNP notification, we can update
the bindings using there params
Returns:
--*/
{
RPC_INTERFACE * RpcInterface;
RPC_STATUS Status;
RpcInterface = FindOrCreateInterface (RpcInterfaceInformation, &Status);
if (RpcInterface == NULL)
{
return Status;
}
return RpcInterface->InterfaceExported(
MyObjectUuidVector,
MyAnnotation,
MyfReplace);
}
#if !defined(NO_LOCATOR_CODE)
RPC_STATUS
RPC_SERVER::NsInterfaceExported (
IN unsigned long EntryNameSyntax,
IN RPC_CHAR *EntryName,
IN RPC_SERVER_INTERFACE PAPI * RpcInterfaceInformation,
IN BOOL fUnexport
)
{
RPC_INTERFACE * RpcInterface;
RPC_STATUS Status;
HMODULE hLocator;
if (pNsBindingExport == 0)
{
hLocator = LoadLibrary ((const RPC_SCHAR *)RPC_CONST_STRING("rpcns4.dll"));
if (hLocator == 0)
{
return RPC_S_OUT_OF_MEMORY;
}
pNsBindingExport = (NS_EXPORT_FUNC) GetProcAddress(hLocator,
"RpcNsBindingExportW");
if (pNsBindingExport == 0)
{
return RPC_S_OUT_OF_MEMORY;
}
pNsBindingUnexport = (NS_UNEXPORT_FUNC) GetProcAddress(hLocator,
"RpcNsBindingUnexportW");
if (pNsBindingUnexport == 0)
{
pNsBindingExport = 0;
return RPC_S_OUT_OF_MEMORY;
}
}
RpcInterface = FindOrCreateInterface (RpcInterfaceInformation, &Status);
if (RpcInterface == NULL)
{
return Status;
}
if (fUnexport == 0)
{
return RpcInterface->NsInterfaceExported(
EntryNameSyntax,
EntryName);
}
else
{
return RpcInterface->NsInterfaceUnexported(
EntryNameSyntax,
EntryName);
}
}
#endif
RPC_STATUS
RPC_SERVER::EnumerateAndCallEachAddress (
IN AddressCallbackType actType,
IN OUT void *Context OPTIONAL
)
/*++
Function Name: DestroyContextHandlesForInterface
Parameters:
actType - the type of callback.
Context - opaque memory block to be passed to the callback.
Description:
This function is called when we want to invoke a specific method
on each address.
Returns:
RPC_S_OK for success or RPC_S_* for error
--*/
{
RPC_ADDRESS_DICT AddressDict;
RPC_ADDRESS_DICT *AddressDictToUse;
RPC_ADDRESS *CurrentAddress;
BOOL UsingAddressDictionaryCopy;
int Result;
DictionaryCursor cursor;
DestroyContextHandleCallbackContext *ContextHandleContext;
// try to copy all entries in the address dictionary to the local
// dictionary. We will walk the address list from there to avoid
// holding the server mutex while walking potentially large tree.
// If we do that, only the page faults will be enough to kill the
// server. On the other hand, we can't rely that the memory will be
// there. Therefore, we attempt to copy the dictionary under the
// mutex, but if this fails, we will retain the mutex and go ahead
// with the cleanup. The logic behind this is that if we don't have
// the few bytes to copy the dictionary, probably the server isn't
// doing anything, and holding the mutex won't hurt it anymore
ServerMutex.Request();
#if DBG
if (actType == actDestroyContextHandle)
{
RPC_INTERFACE *Interface;
ContextHandleContext = (DestroyContextHandleCallbackContext *)Context;
Interface = FindInterface(ContextHandleContext->RpcInterfaceInformation);
ASSERT(Interface);
// the interface must use strict context handles
ASSERT(Interface->DoesInterfaceUseNonStrict() == FALSE);
}
#endif
UsingAddressDictionaryCopy = AddressDict.ExpandToSize(RpcAddressDictionary.Size());
if (UsingAddressDictionaryCopy)
{
AddressDictToUse = &AddressDict;
RpcAddressDictionary.Reset(cursor);
while ( (CurrentAddress = RpcAddressDictionary.Next(cursor)) != 0 )
{
// we never destroy addresses. Therefore, we don't need to mark
// those addresses as used in any way
Result = AddressDict.Insert(CurrentAddress);
// this must succeed as we have reserved enough size
ASSERT(Result != -1);
}
ServerMutex.Clear();
}
else
{
AddressDictToUse = &RpcAddressDictionary;
}
// N.B. We may, or may not have the ServerMutex here - depending on how
// we were doing with memory
AddressDictToUse->Reset(cursor);
while ( (CurrentAddress = AddressDictToUse->Next(cursor)) != 0 )
{
switch (actType)
{
case actDestroyContextHandle:
ContextHandleContext = (DestroyContextHandleCallbackContext *)Context;
CurrentAddress->DestroyContextHandlesForInterface(
ContextHandleContext->RpcInterfaceInformation,
ContextHandleContext->RundownContextHandles
);
break;
case actCleanupIdleSContext:
CurrentAddress->CleanupIdleSContexts();
break;
default:
ASSERT(0);
}
}
if (!UsingAddressDictionaryCopy)
{
ServerMutex.Clear();
}
return RPC_S_OK;
}
RPC_STATUS
RPC_ENTRY
I_RpcNsInterfaceExported (
IN unsigned long EntryNameSyntax,
IN unsigned short *EntryName,
IN RPC_SERVER_INTERFACE PAPI * RpcInterfaceInformation
)
{
#if !defined(NO_LOCATOR_CODE)
InitializeIfNecessary();
return (GlobalRpcServer->NsInterfaceExported(
EntryNameSyntax,
EntryName,
RpcInterfaceInformation, 0));
#else
return RPC_S_CANNOT_SUPPORT;
#endif
}
RPC_STATUS
RPC_ENTRY
I_RpcNsInterfaceUnexported (
IN unsigned long EntryNameSyntax,
IN unsigned short *EntryName,
IN RPC_SERVER_INTERFACE PAPI * RpcInterfaceInformation
)
{
#if !defined(NO_LOCATOR_CODE)
InitializeIfNecessary();
return (GlobalRpcServer->NsInterfaceExported(
EntryNameSyntax,
EntryName,
RpcInterfaceInformation, 1));
#else
return RPC_S_CANNOT_SUPPORT;
#endif
}
#define MAXIMUM_CACHED_THREAD_TIMEOUT (1000L * 60L * 60L)
void
BaseCachedThreadRoutine (
IN CACHED_THREAD * CachedThread
)
/*++
Routine Description:
Each thread will execute this routine. When it first gets called, it
will immediately call the procedure and parameter specified in the
cached thread object. After that it will wait on its event and then
execute the specified routine everytime it gets woken up. If the wait
on the event times out, the thread will exit unless it has been protected.
Arguments:
CachedThread - Supplies the cached thread object to be used by this
thread.
--*/
{
RPC_SERVER * RpcServer = CachedThread->OwningRpcServer;
THREAD *pThread = RpcpGetThreadPointer();
long WaitTimeout;
ASSERT(pThread);
while (pThread->ThreadHandle() == (void *) -1)
{
Sleep(1);
}
ASSERT(pThread->ThreadHandle());
CachedThread->SetThread(pThread);
if (pThread->DebugCell)
{
pThread->DebugCell->TID = (unsigned short)GetCurrentThreadId();
pThread->DebugCell->LastUpdateTime = NtGetTickCount();
}
for(;;)
{
if (CachedThread->CallProcedure())
{
#ifdef RPC_OLD_IO_PROTECTION
// This thread has already timed-out waiting on
// a transport level cache. Let it go now...
ASSERT(pThread->InqProtectCount() == 1);
#endif
if (pThread->IsIOPending() == FALSE)
{
delete CachedThread;
return;
}
// we're a cached IOCP thread - we need to unjoin the IOCP
UnjoinCompletionPort();
}
WaitTimeout = gThreadTimeout;
// Now we cache this thread. This consists of clearing the
// work available flag and inserting the thread cache object into
// the list thread cache objects.
CachedThread->WorkAvailableFlag = WorkIsNotAvailable;
RpcServer->ThreadCacheMutex.Request();
RpcServer->InsertIntoFreeList(CachedThread);
RpcServer->ThreadCacheMutex.Clear();
// Now we loop waiting for work. We get out of the loop in three
// ways: (1) a timeout occurs and there is work to do, (2) the
// event gets kicked because there is work to do, (3) a timeout
// occurs, there is no work to do, and the thread is not protected.
for (;;)
{
// Ignore spurious signals.
while( (CachedThread->WaitForWorkEvent.Wait(WaitTimeout) == 0)
&& (CachedThread->WorkAvailableFlag == WorkIsNotAvailable) )
;
if (CachedThread->WorkAvailableFlag == WorkIsAvailable)
{
break;
}
// We must take the lock to avoid a race condition where another
// thread is trying to signal us right now.
RpcServer->ThreadCacheMutex.Request();
if (CachedThread->WorkAvailableFlag)
{
RpcServer->ThreadCacheMutex.Clear();
break;
}
if (pThread->IsIOPending())
{
// If we reach here, there is no work available, and the thread
// is protected. We just need to wait again. There is no need to
// busy wait if the thread is protected and it keeps timing out.
if (WaitTimeout < MAXIMUM_CACHED_THREAD_TIMEOUT/2)
{
WaitTimeout = WaitTimeout * 2;
}
// Since this thread can't exit anyway, move it to the front of the
// free list so it will be reused first.
RpcServer->RemoveFromFreeList(CachedThread);
RpcServer->InsertIntoFreeList(CachedThread);
RpcServer->ThreadCacheMutex.Clear();
continue;
}
// No work available.
#ifdef RPC_OLD_IO_PROTECTION
ASSERT(pThread->InqProtectCount() == 1);
#endif
// There is no work available, and this thread is not
// protected, so we can safely let it commit suicide.
RpcServer->RemoveFromFreeList(CachedThread);
RpcServer->ThreadCacheMutex.Clear();
delete CachedThread;
return;
}
ASSERT(CachedThread->WorkAvailableFlag == WorkIsAvailable);
}
NO_RETURN;
}
RPC_STATUS
RPC_SERVER::CreateThread (
IN THREAD_PROC Procedure,
IN void * Parameter
)
/*++
Routine Description:
This routine is used to create a new thread which will begin
executing the specified procedure. The procedure will be passed
parameter as the argument.
Arguments:
Procedure - Supplies the procedure which the new thread should
begin executing.
Parameter - Supplies the argument to be passed to the procedure.
Return Value:
RPC_S_OK - We successfully created a new thread and started it
executing the supplied procedure.
RPC_S_OUT_OF_THREADS - We could not create another thread.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to allocate
the data structures we need to complete this operation.
--*/
{
THREAD * Thread;
CACHED_THREAD * CachedThread;
RPC_STATUS RpcStatus = RPC_S_OK;
ThreadCacheMutex.Request();
CachedThread = RemoveHeadFromFreeList();
if (CachedThread)
{
// set all parameters within the mutex to avoid races
CachedThread->SetWakeUpThreadParams(Procedure, Parameter);
ThreadCacheMutex.Clear();
CachedThread->WakeUpThread();
return(RPC_S_OK);
}
ThreadCacheMutex.Clear();
if (IsServerSideDebugInfoEnabled())
{
RpcStatus = InitializeServerSideCellHeapIfNecessary();
if (RpcStatus != RPC_S_OK)
return RpcStatus;
}
CachedThread = new CACHED_THREAD(Procedure, Parameter, this, &RpcStatus);
if ( CachedThread == 0 )
{
return(RPC_S_OUT_OF_MEMORY);
}
if ( RpcStatus != RPC_S_OK )
{
delete CachedThread;
return(RpcStatus);
}
ASSERT( RpcStatus == RPC_S_OK );
Thread = new THREAD((THREAD_PROC) BaseCachedThreadRoutine, CachedThread,
&RpcStatus);
if (Thread == 0)
{
delete CachedThread;
return(RPC_S_OUT_OF_THREADS);
}
if (RpcStatus != RPC_S_OK)
{
delete CachedThread;
delete Thread;
if (RpcStatus == ERROR_MAX_THRDS_REACHED)
RpcStatus = RPC_S_OUT_OF_THREADS;
}
return(RpcStatus);
}
RPC_STATUS
RPC_SERVER::InquireInterfaceIds (
OUT RPC_IF_ID_VECTOR __RPC_FAR * __RPC_FAR * InterfaceIdVector
)
/*++
Routine Description:
This routine is used to obtain a vector of the interface identifiers of
the interfaces supported by this server.
Arguments:
IfIdVector - Returns a vector of the interfaces supported by this server.
Return Value:
RPC_S_OK - Everything worked out just great.
RPC_S_NO_INTERFACES - No interfaces have been registered with the runtime.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to allocate
the interface id vector.
--*/
{
DictionaryCursor cursor;
ServerMutex.Request();
if (RpcInterfaceDictionary.Size() == 0)
{
ServerMutex.Clear();
*InterfaceIdVector = 0;
return RPC_S_NO_INTERFACES;
}
*InterfaceIdVector = (RPC_IF_ID_VECTOR __RPC_FAR *) RpcpFarAllocate(
sizeof(RPC_IF_ID_VECTOR) + (RpcInterfaceDictionary.Size() - 1)
* sizeof(RPC_IF_ID __RPC_FAR *));
if ( *InterfaceIdVector == 0 )
{
ServerMutex.Clear();
return(RPC_S_OUT_OF_MEMORY);
}
(*InterfaceIdVector)->Count = 0;
(*InterfaceIdVector)->IfId[0] = (RPC_IF_ID __RPC_FAR *) RpcpFarAllocate(
sizeof(RPC_IF_ID));
RpcInterfaceDictionary.Reset(cursor);
RPC_INTERFACE * RpcInterface;
while ((RpcInterface = RpcInterfaceDictionary.Next(cursor)) != 0)
{
(*InterfaceIdVector)->IfId[(*InterfaceIdVector)->Count] =
RpcInterface->InquireInterfaceId();
if ( (*InterfaceIdVector)->IfId[(*InterfaceIdVector)->Count] != 0 )
{
RPC_IF_ID * Interface = (*InterfaceIdVector)->IfId[(*InterfaceIdVector)->Count];
(*InterfaceIdVector)->Count += 1;
}
}
ServerMutex.Clear();
if (0 == (*InterfaceIdVector)->Count)
{
RpcpFarFree(*InterfaceIdVector);
*InterfaceIdVector = 0;
return RPC_S_NO_INTERFACES;
}
return(RPC_S_OK);
}
RPC_STATUS
RPC_SERVER::InquirePrincipalName (
IN unsigned long AuthenticationService,
OUT RPC_CHAR __RPC_FAR * __RPC_FAR * ServerPrincipalName
)
/*++
Routine Description:
Arguments:
Return Value:
RPC_S_OK - The operation completed successfully.
RPC_S_OUT_OF_MEMORY - Insufficient memory is available to complete the
operation.
RPC_S_UNKNOWN_AUTHN_SERVICE - The specified authentication service is
not supported.
--*/
{
RPC_AUTHENTICATION * Service;
DictionaryCursor cursor;
ServerMutex.Request();
AuthenticationDictionary.Reset(cursor);
while ( (Service = AuthenticationDictionary.Next(cursor)) != 0 )
{
if ( Service->AuthenticationService == AuthenticationService )
{
ServerMutex.Clear();
*ServerPrincipalName = DuplicateString(
Service->ServerPrincipalName);
if ( *ServerPrincipalName == 0 )
{
return(RPC_S_OUT_OF_MEMORY);
}
return(RPC_S_OK);
}
}
ServerMutex.Clear();
return(RPC_S_UNKNOWN_AUTHN_SERVICE);
}
void
RPC_SERVER::RegisterRpcForwardFunction (
RPC_FORWARD_FUNCTION * pForwardFunction
)
/*++
Routine Description:
Sets the RPC_SERVER pEpmapperForwardFunction. (The pEpmapperForwardFunction
is the function the runtime can call when it receives a pkt for a
dynamic endpoint. pEpmapperForwardFunction will return endpoint of
the server to forward the pkt to).
Arguments:
pForwardFunction - pointer to the epmapper forward function.
Return Value:
none
--*/
{
pRpcForwardFunction = pForwardFunction;
}
RPC_STATUS
RPC_SERVER::UnregisterEndpoint (
IN RPC_CHAR __RPC_FAR * Protseq,
IN RPC_CHAR __RPC_FAR * Endpoint
)
{
return (RPC_S_CANNOT_SUPPORT);
}
RPC_ADDRESS::RPC_ADDRESS (
IN OUT RPC_STATUS PAPI * RpcStatus
) : AddressMutex(RpcStatus,
TRUE // pre-allocate semaphore
)
/*++
Routine Description:
We just need to initialization some stuff to zero. That way if we
later have to delete this address because of an error in initialization
we can tell what instance variables need to be freed.
--*/
{
NetworkAddress = 0;
Endpoint = 0;
RpcProtocolSequence = 0;
}
RPC_ADDRESS::~RPC_ADDRESS (
)
/*++
Routine Description:
This routine will only get called if part way through initialization
an error occurs. We just need to free up any memory used by instance
variables. Once FireUpManager has been called and succeeds, the
address will never be destroyed.
--*/
{
if (Endpoint != 0)
delete Endpoint;
if (RpcProtocolSequence != 0)
delete RpcProtocolSequence;
}
RPC_CHAR *
RPC_ADDRESS::GetListNetworkAddress (
IN unsigned int Index
)
/*++
Routine Description:
A pointer to the network address for this address is returned.
--*/
{
if (Index >= pNetworkAddressVector->Count)
{
return (NULL);
}
return(pNetworkAddressVector->NetworkAddresses[Index]);
}
RPC_STATUS
RPC_ADDRESS::CopyDescriptor (
IN void *SecurityDescriptor,
OUT void **OutDescriptor
)
{
BOOL b;
SECURITY_DESCRIPTOR_CONTROL Control;
DWORD Revision;
DWORD BufferLength;
if ( IsValidSecurityDescriptor(SecurityDescriptor) == FALSE )
{
return(RPC_S_INVALID_SECURITY_DESC);
}
if (FALSE == GetSecurityDescriptorControl(SecurityDescriptor, &Control, &Revision))
{
return(RPC_S_INVALID_SECURITY_DESC);
}
if (Control & SE_SELF_RELATIVE)
{
//
// Already self-relative, just copy it.
//
BufferLength = GetSecurityDescriptorLength(SecurityDescriptor);
ASSERT(BufferLength >= sizeof(SECURITY_DESCRIPTOR));
*OutDescriptor = RpcpFarAllocate(BufferLength);
if (*OutDescriptor == 0)
{
return(RPC_S_OUT_OF_MEMORY);
}
memcpy(*OutDescriptor, SecurityDescriptor, BufferLength);
return(RPC_S_OK);
}
//
// Make self-relative and copy it.
//
BufferLength = 0;
b = MakeSelfRelativeSD(SecurityDescriptor, 0, &BufferLength);
ASSERT(b == FALSE);
if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER )
{
return(RPC_S_INVALID_SECURITY_DESC);
}
ASSERT(BufferLength >= sizeof(SECURITY_DESCRIPTOR_RELATIVE));
*OutDescriptor = RpcpFarAllocate(BufferLength);
if (*OutDescriptor == 0)
{
return(RPC_S_OUT_OF_MEMORY);
}
b = MakeSelfRelativeSD(SecurityDescriptor,
*OutDescriptor,
&BufferLength);
if (b == FALSE)
{
ASSERT(GetLastError() != ERROR_INSUFFICIENT_BUFFER);
delete *OutDescriptor;
return(RPC_S_OUT_OF_MEMORY);
}
return(RPC_S_OK);
}
RPC_STATUS
RPC_ADDRESS::SetEndpointAndStuff (
IN RPC_CHAR PAPI * NetworkAddress,
IN RPC_CHAR PAPI * Endpoint,
IN RPC_CHAR PAPI * RpcProtocolSequence,
IN RPC_SERVER * Server,
IN unsigned int StaticEndpointFlag,
IN unsigned int PendingQueueSize,
IN void PAPI *SecurityDescriptor,
IN unsigned long EndpointFlags,
IN unsigned long NICFlags,
IN NETWORK_ADDRESS_VECTOR *pNetworkAddressVector
)
/*++
Routine Description:
We just need to set some instance variables of this rpc address.
Arguments:
Endpoint - Supplies the endpoint for this rpc address.
RpcProtocolSequence - Supplies the rpc protocol sequence for this
rpc address.
Server - Supplies the rpc server which owns this rpc address.
StaticEndpointFlag - Supplies a flag which specifies whether this
address has a static endpoint or a dynamic endpoint.
--*/
{
RPC_STATUS Status;
this->NetworkAddress = NetworkAddress;
this->Endpoint = Endpoint;
this->RpcProtocolSequence = RpcProtocolSequence;
this->pNetworkAddressVector = pNetworkAddressVector;
this->Server = Server;
this->StaticEndpointFlag = StaticEndpointFlag;
this->PendingQueueSize = PendingQueueSize;
this->EndpointFlags = EndpointFlags;
this->NICFlags = NICFlags;
if (SecurityDescriptor)
{
Status = CopyDescriptor(SecurityDescriptor,
&this->SecurityDescriptor);
if (Status != RPC_S_OK)
{
return Status;
}
}
return RPC_S_OK;
}
RPC_STATUS
RPC_ADDRESS::FindInterfaceTransfer (
IN PRPC_SYNTAX_IDENTIFIER InterfaceIdentifier,
IN PRPC_SYNTAX_IDENTIFIER ProposedTransferSyntaxes,
IN unsigned int NumberOfTransferSyntaxes,
OUT PRPC_SYNTAX_IDENTIFIER AcceptedTransferSyntax,
OUT RPC_INTERFACE ** RpcInterface,
OUT BOOL *fIsInterfaceTransferPreferred,
OUT int *ProposedTransferSyntaxIndex,
OUT int *AvailableTransferSyntaxIndex
)
/*++
Routine Description:
This method is used to determine if a client bind request can be
accepted or not. All we have got to do here is hand off to the
server which owns this address.
Arguments:
InterfaceIdentifier - Supplies the syntax identifier for the
interface; this is the interface uuid and version.
ProposedTransferSyntaxes - Supplies a list of one or more transfer
syntaxes which the client initiating the binding supports. The
server should pick one of these which is supported by the
interface.
NumberOfTransferSyntaxes - Supplies the number of transfer syntaxes
specified in the proposed transfer syntaxes argument.
AcceptedTransferSyntax - Returns the transfer syntax which the
server accepted.
RpcInterface - Returns a pointer to the rpc interface found which
supports the requested interface and one of the requested transfer
syntaxes.
Return Value:
RPC_S_OK - The requested interface exists and it supports at least
one of the proposed transfer syntaxes. We are all set, now we
can make remote procedure calls.
RPC_P_UNSUPPORTED_TRANSFER_SYNTAX - The requested interface exists,
but it does not support any of the proposed transfer syntaxes.
RPC_P_UNSUPPORTED_INTERFACE - The requested interface is not supported
by this rpc server.
--*/
{
return Server->FindInterfaceTransfer(
InterfaceIdentifier,
ProposedTransferSyntaxes,
NumberOfTransferSyntaxes,
AcceptedTransferSyntax,
RpcInterface,
fIsInterfaceTransferPreferred,
ProposedTransferSyntaxIndex,
AvailableTransferSyntaxIndex);
}
BINDING_HANDLE *
RPC_ADDRESS::InquireBinding (
RPC_CHAR * LocalNetworkAddress
)
/*++
Routine Description:
We need to return create and return a binding handle which can
be used by a client to make remote procedure calls to this rpc
address.
Return Value:
A newly created binding handle will be returned, inless an out
of memory error occurs, in which case zero will be returned.
--*/
{
RPC_STATUS Status;
DCE_BINDING * DceBinding;
BINDING_HANDLE * BindingHandle;
RPC_CHAR * DynamicEndpoint = 0;
RPC_CHAR * PAPI * tmpPtr;
if(LocalNetworkAddress == 0)
{
LocalNetworkAddress = pNetworkAddressVector->NetworkAddresses[0];
}
DceBinding = new DCE_BINDING(
0,
RpcProtocolSequence,
LocalNetworkAddress,
(StaticEndpointFlag != 0 ? Endpoint : 0),
0,
&Status);
if ((DceBinding == 0)
|| (Status != RPC_S_OK))
{
delete DceBinding;
return(0);
}
if (StaticEndpointFlag == 0)
{
DynamicEndpoint = DuplicateString(Endpoint);
if (DynamicEndpoint == 0)
{
delete DceBinding;
return(0);
}
}
BindingHandle = new SVR_BINDING_HANDLE(DceBinding, DynamicEndpoint, &Status);
if (BindingHandle == 0)
{
delete DceBinding;
}
return(BindingHandle);
}
RPC_STATUS
RPC_ADDRESS::ServerStartingToListen (
IN unsigned int MinimumCallThreads,
IN unsigned int MaximumConcurrentCalls
)
/*++
Routine Description:
This method will be called for each address when the server starts
listening. In addition, if an address is added while the server is
listening, then this method will be called. The purpose of the method
is to notify the address about the minimum number of call threads
required; the maximum concurrent calls can safely be ignored, but it
can be used to set an upper bound on the number of call threads.
Arguments:
MinimumCallThreads - Supplies a number indicating the minimum number
of call threads which should be created for this address.
MaximumConcurrentCalls - Supplies the maximum number of concurrent
calls which this server will support.
Return Value:
RPC_S_OK - This routine will always return this value. Protocol
support modules may return other values.
--*/
{
UNUSED(MinimumCallThreads);
UNUSED(MaximumConcurrentCalls);
return(RPC_S_OK);
}
void
RPC_ADDRESS::ServerStoppedListening (
)
/*++
Routine Description:
This routine will be called to notify an address that the server has
stopped listening for remote procedure calls. Each protocol module
may override this routine; it is safe not too, but not as efficient.
Note that this routine will be called before all calls using the
server have been allowed to complete.
--*/
{
}
long
RPC_ADDRESS::InqNumberOfActiveCalls (
)
/*++
Return Value:
Each protocol module will define this routine. We will use this
functionality when the server has stopped listening and is waiting
for all remote procedure calls to complete. The number of active calls
for the address will be returned.
--*/
{
return(ActiveCallCount);
}
RPC_STATUS
RPC_ADDRESS::RestartAddress (
IN unsigned int MinThreads,
IN unsigned int MaxCalls
)
{
RPC_STATUS Status;
int Key;
Status = ServerSetupAddress(
NetworkAddress,
&Endpoint,
PendingQueueSize,
SecurityDescriptor,
EndpointFlags,
NICFlags,
&pNetworkAddressVector);
if (Status != RPC_S_OK)
{
pNetworkAddressVector = NULL;
return Status;
}
Key = Server->AddAddress(this);
if (Key == -1)
{
return RPC_S_OUT_OF_MEMORY;
}
Server->ServerMutex.Request();
Status = ServerStartingToListen(MinThreads, MaxCalls);
Server->ServerMutex.Clear();
if (Status != RPC_S_OK)
{
return Status;
}
CompleteListen();
return RPC_S_OK;
}
void
RPC_ADDRESS::DestroyContextHandlesForInterface (
IN RPC_SERVER_INTERFACE PAPI * ,
IN BOOL
)
/*++
Function Name: DestroyContextHandlesForInterface
Parameters:
RpcInterfaceInformation - the interface for which context handles
are to be unregistered
RundownContextHandles - if non-zero, rundown the context handles. If
FALSE, destroy the runtime portion of the context handle resource,
but don't call the user rundown routine.
Description:
Each protocol engine will implement its own version of this routine,
if it supports the feature. For those who don't, this routine provides
default no-op behaviour
Returns:
--*/
{
}
void
RPC_ADDRESS::CleanupIdleSContexts (
void
)
/*++
Function Name: CleanupIdleSContexts
Parameters:
Description:
Each protocol engine will implement its own version of this routine,
if it supports the feature. For those who don't, this routine provides
default no-op behaviour
Returns:
--*/
{
}
/*====================================================================
SCONNECTION
==================================================================== */
RPC_STATUS
SetThreadSecurityContext(
SECURITY_CONTEXT * Context
)
/*++
Routine Description:
RpcImpersonateClient() takes a handle_t, so many threads can impersonate
the client of a single SCONNECTION. RPC needs to record which context
each thread is using. It is logical to place this in the TLS, but threads
not created by RPC lack the THREAD structure in their TLS. This wrapper
function will store the security context in the TLS if available, or
place the context in a dictionary if not.
Arguments:
Context - the security context to associate with this thread
Return Value:
RPC_S_OK if successful
RPC_S_OUT_OF_MEMORY if the dictionary insertion failed
--*/
{
THREAD * ThreadInfo = ThreadSelf();
if (ThreadInfo)
{
ThreadInfo->SecurityContext = Context;
return RPC_S_OK;
}
return RPC_S_OUT_OF_MEMORY;
}
SECURITY_CONTEXT *
QueryThreadSecurityContext(
)
/*++
Routine Description:
Fetches the security context associated with this thread for this
connection. We check the TLS if available; if nothing is there
then we scan the connection's dictionary.
Arguments:
none
Return Value:
the associated security context, or zero if none is found
--*/
{
THREAD * ThreadInfo = ThreadSelf();
if (ThreadInfo)
{
if (ThreadInfo->SecurityContext)
{
return (SECURITY_CONTEXT *) ThreadInfo->SecurityContext;
}
}
return 0;
}
SECURITY_CONTEXT *
ClearThreadSecurityContext(
)
/*++
Routine Description:
Clears the association between this thread and its security context
for this connection.
Arguments:
none
Return Value:
the formerly associated security context, or zero if none was found
--*/
{
THREAD * ThreadInfo = ThreadSelf();
if (ThreadInfo)
{
SECURITY_CONTEXT * Context =
(SECURITY_CONTEXT *) ThreadInfo->SecurityContext;
if (Context)
{
ThreadInfo->SecurityContext = 0;
return Context;
}
}
return 0;
}
RPC_STATUS
SCALL::ImpersonateClient (
)
// This routine just returns RPC_CANNOT_SUPPORT indicating that this
// particular connection does not support impersonation.
{
ASSERT(0 && "improper SCALL member called\n");
return(RPC_S_CANNOT_SUPPORT);
}
RPC_STATUS
SCALL::RevertToSelf (
)
// We always return RPC_CANNOT_SUPPORT indicating that the particular
// connection does not support impersonation.
{
ASSERT(0 && "improper SCALL member called\n");
return(RPC_S_CANNOT_SUPPORT);
}
void
NDRSContextHandlePostDispatchProcessing (
IN SCALL *SCall,
ServerContextHandle *CtxHandle
);
void
SCALL::DoPostDispatchProcessing (
void
)
{
DictionaryCursor cursor;
ServerContextHandle *CtxHandle;
ServerContextHandle *RetrievedCtxHandle;
int Key;
// the list will contain all in-only context
// handles, as they don't get marshalled. It will also
// contain the marshalling buffers for the newly
// created context handles
if (ActiveContextHandles.Size() > 0)
{
// no need to synchronize access to the
// dictionary - only this call will be
// touching it
ActiveContextHandles.Reset(cursor);
while ((CtxHandle = ActiveContextHandles.NextWithKey(cursor, &Key)) != 0)
{
// ignore buffers
if ((ULONG_PTR)CtxHandle & SCALL::DictionaryEntryIsBuffer)
{
RetrievedCtxHandle = ActiveContextHandles.Delete(Key);
ASSERT(RetrievedCtxHandle == CtxHandle);
continue;
}
// NDRSContextHandlePostDispatchProcessing will remove the context handle
// from the dictionary - this doesn't interfere with our
// enumeration
NDRSContextHandlePostDispatchProcessing(this,
CtxHandle
);
}
}
}
RPC_STATUS
SCONNECTION::IsClientLocal (
OUT unsigned int PAPI * ClientLocalFlag
)
/*++
Routine Description:
The connection oriented protocol module will override this method;
all other protocol modules should just use this routine. We need this
support so that the security system can tell if a client is local or
remote.
Arguments:
ClientLocalFlag - Returns an indication of whether or not the client is
local (ie. on the same machine as the server). This field will be
set to a non-zero value to indicate that the client is local;
otherwise, the client is remote.
Return Value:
RPC_S_CANNOT_SUPPORT - This will always be used.
--*/
{
UNUSED(ClientLocalFlag);
ASSERT(0 && "improper SCALL member called\n");
return(RPC_S_CANNOT_SUPPORT);
}
RPC_STATUS
SCALL::CreateAndSaveAuthzContextFromToken (
IN OUT PAUTHZ_CLIENT_CONTEXT_HANDLE pAuthzClientContextPlaceholder OPTIONAL,
IN HANDLE ImpersonationToken,
IN AUTHZ_RESOURCE_MANAGER_HANDLE AuthzResourceManager,
IN PLARGE_INTEGER pExpirationTime OPTIONAL,
IN LUID Identifier,
IN DWORD Flags,
IN PVOID DynamicGroupArgs OPTIONAL,
OUT PAUTHZ_CLIENT_CONTEXT_HANDLE pAuthzClientContext
)
/*++
Routine Description:
Creates an Authz context from token. If saving is requested, it
tries to save it in a thread-safe manner and duplicates it before
returning. If saving is not requested, the resulting authz context
is simply returned. In both cases caller owns the returned auth
context.
Arguments:
pAuthzClientContextPlaceholder - contains a pointer to an authz placeholder.
If NULL, out of the ImpersonationToken an authz context will
be made and will be returned. If non-NULL, it must contain
NULL. In this case an authz context will be created from the token,
and it will be stored in the placeholder in a thread safe manner and a
duplicate will be made and returned in pAuthzClientContext.
ImpersonationToken - the impersonation token to use.
AuthzResourceManager - authz parameters (not interpreted)
pExpirationTime - authz parameters (not interpreted)
Identifier - authz parameters (not interpreted)
Flags - authz parameters (not interpreted)
DynamicGroupArgs - authz parameters (not interpreted)
pAuthzClientContext - contains the output authz context on success
Return Value:
Win32 error code. EEInfo has been added.
--*/
{
RPC_STATUS Status = RPC_S_OK;
BOOL Result;
AUTHZ_CLIENT_CONTEXT_HANDLE AuthzClientContext;
Result = AuthzInitializeContextFromTokenFn(
Flags,
ImpersonationToken,
AuthzResourceManager,
pExpirationTime,
Identifier,
DynamicGroupArgs,
&AuthzClientContext);
if (!Result)
{
Status = GetLastError();
RpcpErrorAddRecord(EEInfoGCAuthz,
Status,
EEInfoDLSCALL__CreateAndSaveAuthzContextFromToken10,
GetCurrentThreadId(),
(ULONGLONG)AuthzResourceManager);
return Status;
}
if (pAuthzClientContextPlaceholder)
{
if (InterlockedCompareExchangePointer((PVOID *)pAuthzClientContextPlaceholder,
AuthzClientContext,
NULL) != NULL)
{
// somebody beat us to the punch - free the context we obtained
AuthzFreeContextFn(AuthzClientContext);
// use the context that has been provided
AuthzClientContext = *pAuthzClientContextPlaceholder;
}
Status = DuplicateAuthzContext(AuthzClientContext,
pExpirationTime,
Identifier,
Flags,
DynamicGroupArgs,
pAuthzClientContext);
if (Status)
{
// EEInfo has already been added
return Status;
}
}
else
{
*pAuthzClientContext = AuthzClientContext;
}
return Status;
}
RPC_STATUS
SCALL::DuplicateAuthzContext (
IN AUTHZ_CLIENT_CONTEXT_HANDLE AuthzClientContext,
IN PLARGE_INTEGER pExpirationTime OPTIONAL,
IN LUID Identifier,
IN DWORD Flags,
IN PVOID DynamicGroupArgs OPTIONAL,
OUT PAUTHZ_CLIENT_CONTEXT_HANDLE pAuthzClientContext
)
/*++
Routine Description:
Take an Authz context, and make a duplicate of it, using the
specified parameters. This method is a wrapper for
AuthzInitializeContextFromContext, mainly adding error handling.
Arguments:
AuthzClientContext - source authz context
pExpirationTime - authz parameters (not interpreted)
Identifier - authz parameters (not interpreted)
Flags - authz parameters (not interpreted)
DynamicGroupArgs - authz parameters (not interpreted)
pAuthzClientContext - target authz context pointer
Return Value:
Win32 error code.
--*/
{
RPC_STATUS Status;
BOOL Result;
// Copy the authz context. We must do a copy,
// to avoid lifetime issues b/n our copy
// and the client copy
Result = AuthzInitializeContextFromAuthzContextFn(
Flags,
AuthzClientContext,
pExpirationTime,
Identifier,
DynamicGroupArgs,
pAuthzClientContext);
if (!Result)
{
Status = GetLastError();
RpcpErrorAddRecord(EEInfoGCAuthz,
Status,
EEInfoDLSCALL__DuplicateAuthzContext10,
GetCurrentThreadId(),
(ULONGLONG)AuthzClientContext);
}
else
Status = RPC_S_OK;
return Status;
}
/* ====================================================================
ASSOCIATION_HANDLE :
==================================================================== */
static long AssociationIdCount = 0L;
void
DestroyContextCollection (
IN ContextCollection *CtxCollection
);
ASSOCIATION_HANDLE::ASSOCIATION_HANDLE (
void
)
{
CtxCollection = NULL;
AssociationID = InterlockedIncrement(&AssociationIdCount);
}
ASSOCIATION_HANDLE::~ASSOCIATION_HANDLE (
)
// We finally get to use the rundown routines for somethings. The association
// is being deleted which is the event that the rundown routines were waiting
// for.
{
FireRundown();
}
// Returns the context handle collection for this association.
RPC_STATUS
ASSOCIATION_HANDLE::GetAssociationContextCollection (
ContextCollection **CtxCollectionPlaceholder
)
/*++
Function Name: GetAssociationContextCollection
Parameters:
CtxCollectionPlaceholder - a placeholder where to put the pointer to
the context collection.
Description:
The context handle code will call the SCALL to get the collection
of context handles for this association. The SCALL method will
simply delegate to this.
This routine will check if the context handle collection was created
and if so, it will just return it. If it wasn't created, it will try
to create it.
Returns:
RPC_S_OK for success or RPC_S_* for error.
--*/
{
RPC_STATUS RpcStatus;
if (CtxCollection)
{
*CtxCollectionPlaceholder = CtxCollection;
return RPC_S_OK;
}
RpcStatus = NDRSContextInitializeCollection(&CtxCollection);
if (RpcStatus != RPC_S_OK)
return RpcStatus;
*CtxCollectionPlaceholder = CtxCollection;
return RpcStatus;
}
void
ASSOCIATION_HANDLE::FireRundown (
void
)
{
int nRetries = 20;
RPC_STATUS status;
if (CtxCollection)
{
// make a best effort to make sure there is another listening thread
// besides this one. If we repeatedly fail, we fire the rundown
// anyway - currently few servers use outgoing RPC callbacks into the
// same process, so we'd rather risk an unlikely deadlock than cause
// a sure leak
while (nRetries > 0)
{
status = CreateThread();
if (status == RPC_S_OK)
break;
Sleep(10);
nRetries --;
}
DestroyContextCollection(CtxCollection);
if (status == RPC_S_OK)
RundownNotificationCompleted();
}
}
// do nothing in the base case
RPC_STATUS ASSOCIATION_HANDLE::CreateThread(void)
{
return RPC_S_OK;
}
void ASSOCIATION_HANDLE::RundownNotificationCompleted(void)
{
}
void
ASSOCIATION_HANDLE::DestroyContextHandlesForInterface (
IN RPC_SERVER_INTERFACE PAPI * RpcInterfaceInformation,
IN BOOL RundownContextHandles
)
/*++
Function Name: DestroyContextHandlesForInterface
Parameters:
RpcInterfaceInformation - the interface for which context handles
are to be unregistered
RundownContextHandles - if non-zero, rundown the context handles. If
FALSE, destroy the runtime portion of the context handle resource,
but don't call the user rundown routine.
Description:
The association will call into NDR to destroy the specified context
handles. It will either have a reference on the association, or the
association mutex. Both ways, we're safe from destruction, and NDR
will synchronize access to the list internally. The address has made
a best effort not to hold the association mutex. If memory is low,
it may end up doing so, however.
Returns:
--*/
{
ContextCollection *LocalCtxCollection;
void *pGuard;
// N.B. An association mutex may be held on entry for this
// function. The server mutex may be held as well
LocalCtxCollection = CtxCollection;
// shortcut the common path
if (!LocalCtxCollection)
return;
pGuard = &RpcInterfaceInformation->InterfaceId;
// call into NDR to destroy the context handles
DestroyContextHandlesForGuard(LocalCtxCollection,
RundownContextHandles,
pGuard);
}
/* ====================================================================
Routine to initialize the server DLL.
==================================================================== */
int
InitializeServerDLL (
)
{
GetMaxRpcSizeAndThreadPoolParameters();
if (InitializeClientDLL() != 0)
return(1);
#if 0
if (InitializeSTransports() != 0)
return(1);
#endif
if (InitializeObjectDictionary() != 0)
return(1);
if (InitializeRpcServer() != 0)
return(1);
if (InitializeRpcProtocolLrpc() != 0)
return(1);
return(0);
}
#if DBG
void
RpcpInterfaceForCallDoesNotUseStrict (
IN RPC_BINDING_HANDLE BindingHandle
)
{
SCALL *SCall;
if (((MESSAGE_OBJECT *)BindingHandle)->Type(SCALL_TYPE))
{
SCall = (SCALL *)BindingHandle;
SCall->InterfaceForCallDoesNotUseStrict();
}
}
#endif
RPC_STATUS
InqLocalConnAddress (
IN SCALL *SCall,
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:
Binding - Supplies a valid server binding. The binding must have been
verified to be an SCALL by the caller.
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 are RPC_P_ADDR_FORMAT_TCP_IPV4 and
RPC_P_ADDR_FORMAT_TCP_IPV6.
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
--*/
{
// is this an osf scall?
if (!SCall->InvalidHandle(OSF_SCALL_TYPE))
{
OSF_SCALL *OsfSCall;
OsfSCall = (OSF_SCALL *)SCall;
return OsfSCall->InqLocalConnAddress(
Buffer,
BufferSize,
AddressFormat);
}
else if (!SCall->InvalidHandle(DG_SCALL_TYPE))
{
// this is a dg call
DG_SCALL *DgSCall;
DgSCall = (DG_SCALL *)SCall;
return DgSCall->InqLocalConnAddress(
Buffer,
BufferSize,
AddressFormat);
}
else
{
// the others don't support it
return RPC_S_CANNOT_SUPPORT;
}
}