Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

530 lines
11 KiB

Overview
--------
This design is based on the NT LPC design. It is simplified, only
using those facilties needed for RPC. The assumption is that all objects
of the implementation reside in a shared heap are synchronized using
a interprocess Mutex, and that the heap resides in the same virtual
address space between the client and server process.
Implementation Plan
-------------------
The first step will be to understand and verify the operation of the Heap
API extention which Chicago apparently supports which allows interprocess
shared heaps. I plan on implementing LPC (details below) first on NT
as an interthread LPC prototype, then will go interprocess using the
Heap API extensions of Chicago. Following that, I will add a "NT-Like"
LPC C Interface layer which will emulate a subset of NT LPC.
Once the core LPC mechanism is working correctly, I will then integrate it
into the RPC Runtime using the existing NT LRPC (SPC) modules as a basis.
As a goal, I will attempt to leave the LRPC code unmodified an emulate the
NT LPC APIs so that the LRPC code can be shared between Chicago and NT.
But if I determine this effort hampers the development, I will modify the
LRPC code to work with the new Chicago LPC module.
The PORT Object
---------------
The primary object is the Port. Generally, there are two types of ports: A
Connection Port, and a Communication Port. A Client Communication Port is
a more specific case of a Communication Port which supports operations which
are only apporopriate for clients. Connection ports are only
created by a server and exist to facilitate connection establishment.
Two Communication ports exist for each connection between a client and server.
Data is exchanged between the client and server by enqueuing messages on
Communication Ports.
Client/Server Connection Establishment
--------------------------------------
The server creates a Named Connection Port, and waits for connect requests
enqueued on the port by clients. The client creates a Client Communication
Port, and enqueues a Connect Request Message on the Server Connection port
with a reference to the client port, and waits for a event to be signalled.
The server dequeues the Connect Request, examines the request, and if it wishes
to accept it, creates a Server Communication Port, and signals the event
which the client is waiting on. The Client Communication Port points to the
Server Communication port and visa-versa--the connection is established.
Throughout the lifetime of the connection, Clients enqueue Messages on the
Server Communication Port, and the Server enqueues Messages on the Client
Communication Port.
Disconnection
-------------
Disconnecting an already established connection is simply a matter of clearing
the PeerPort's PeerPort field, Signalling the peer semaphore and deleting one
end of the connection (The client would delete the Client end of the
connection).
The Connection Port List
------------------------
The client determines which Server Connection Port to enqueue the
Connect Request Message on by searching a "Connection Port List"
for a Connection Port Name which matches the name the client desires.
The Server adds the Connection Port to the Connection Port list
when the Connection Port is created.
The MESSAGE Object
------------------
A Message is enqueued on a Port's MESSAGE_QUEUE (see below).
There are two types of Messages:
A Connect Request Message which is only enqueued on a Connection Port
and a Data Message which is only enqueued on a Communication Port
(Client and Server).
The MESSAGE_QUEUE Object
------------------------
Each Port contains a MESSAGE_QUEUE. MESSAGEs are enqueued
on MESSAGE_QUEUEs. A Message Queue is simply a linked list of messages,
and a counted semaphore with methods for enqueuing/dequeuing MESSAGEs. The
counted semaphore is incremented when a messages is enqueued, and decremented
when a message is dequeued. If the semaphore is zero, the dequeuing operation
blocks until the semaphore is non-zero.
A sketchy basis for a prototype:
--------------------------------
class SHARED_HEAP {
HANDLE hHeap;
SHARED_HEAP(
);
~SHARED_HEAP();
void *
Alloc(
int Size
);
void
Free(
void * Buffer
);
}
SHARED_HEAP::SHARED_HEAP()
{
hHeap = HeapCreate();
}
SHARED_HEAP::~SHARED_HEAP()
{
HeapDestroy(hHeap);
}
void *
SHARED_HEAP::Alloc(
int Size
)
{
return (HeapAlloc());
}
void
SHARED_HEAP::Free(
void * Buffer
)
{
HeapFree(Buffer);
}
class MESSAGE {
MESSAGE * Next;
PORT * Port;
int DataSize;
void * Data;
MESSAGE(
PORT * Port,
void * Buffer,
int Size
);
#ifdef USE_SHARED_HEAP
void *
operator new(size_t Size) { return (lpc_shared_heap.Alloc(Size)) }
void
operator delete(void * Buffer) { return (lpc_shared_heap.Free(Buffer)) }
#endif
}
MESSAGE::MESSAGE(
PORT * OwnerPort,
void * Buffer,
int Size
)
{
Next = NULL;
Port = OwnerPort;
Data = Buffer;
DataSize = Size;
}
class MESSAGE_QUEUE {
MESSAGE * Head;
MESSAGE * Tail;
HANDLE hSemaphore;
MESSAGE_QUEUE();
~MESSAGE_QUEUE();
void
Enqueue(
MESSAGE * Message
);
MESSAGE *
Dequeue(
);
#ifdef USE_SHARED_HEAP
void *
operator new(size_t Size) { return (lpc_shared_heap.Alloc(Size)) }
void
operator delete(void * Buffer) { return (lpc_shared_heap.Free(Buffer)) }
#endif
}
MESSAGE_QUEUE::MESSAGE_QUEUE()
{
Head = Tail = NULL;
hSemaphore = CreateSemaphore();
}
MESSAGE_QUEUE::~MESSAGE_QUEUE()
{
CloseHandle(hSemaphore);
}
MESSAGE_QUEUE::Enqueue(
MESSAGE * Message
)
{
Message->Next = NULL;
ENTER_CRITICAL_SECTION();
if (Tail == NULL) {
Head = Message;
} else {
Tail->Next = Message;
}
Tail = Message;
EXIT_CRITICAL_SECTION();
ReleaseSemaphore(hSemaphore, 1, NULL);
}
MESSAGE *
MESSAGE_QUEUE::Dequeue()
{
MESSAGE * Message;
ENTER_CRITICAL_SECTION();
if (Head == NULL) {
EXIT_CRITICAL_SECTION();
WaitForSingleObject(hSemaphore, INFINITE);
ENTER_CRITICAL_SECTION();
}
ASSERT(Head);
Message = Head;
if ((Head = Head->Next) == NULL) {
Tail = NULL;
}
EXIT_CRITICAL_SECTION();
return (Message);
}
class PORT {
HANDLE hConnectedEvent;
MESSAGE_QUEUE MessageQueue;
Wait(
);
Signal(
);
#ifdef USE_SHARED_HEAP
void *
operator new(size_t Size) { return (lpc_shared_heap.Alloc(Size)) }
void
operator delete(void * Buffer) { return (lpc_shared_heap.Free(Buffer)) }
#endif
}
PORT::PORT()
{
hConnectedEvent = CreateEvent();
}
PORT::~PORT()
{
CloseHandle(hConnectedEvent);
}
PORT::Wait()
{
WaitForSingleObject(hConnectedEvent);
}
PORT::Signal()
{
SetEvent(hConnectedEvent);
}
class CONNECTION_PORT : PORT {
MESSAGE_QUEUE ConnectQueue;
char
PortName[PORTNAME_SIZE];
CONNECTION_PORT();
~CONNECTION_PORT();
MESSAGE *
Listen();
COMMUNICATION_PORT *
Accept(
MESSAGE * ConnectRequest
);
void
Reject(
MESSAGE * ConnectRequest
);
#ifdef USE_SHARED_HEAP
void *
operator new(size_t Size) { return (lpc_shared_heap.Alloc(Size)) }
void
operator delete(void * Buffer) { return (lpc_shared_heap.Free(Buffer)) }
#endif
}
CONNECTION_PORT::CONNECTION_PORT()
{
put this on CONNECTION_PORT_LIST;
}
CONNECTION_PORT::~CONNECTION_PORT()
{
remove this from CONNECTION_PORT_LIST;
}
MESSAGE *
CONNECTION_PORT::Listen()
{
return (ConnectQueue.Dequeue());
}
COMMUNICATION_PORT *
CONNECTION_PORT::Accept(
MESSAGE * ConnectRequest
)
{
COMMUNICATION_PORT * ServerCommunicationPort;
COMMUNICATION_PORT * ClientPort;
ServerCommunicationPort = new COMMUNICATION_PORT();
ClientPort = ConnectRequest->ClientPort;
ServerCommunicationPort->PeerPort = ClientPort;
ClientPort->PeerPort = ServerCommunicationPort;
ClientPort->Signal();
return (ServerCommunicationPort);
}
void
CONNECTION_PORT::Reject(
MESSAGE * ConnectRequest
)
{
ConnectRequest->ClientPort->Signal();
return (SUCCESS);
}
class CONNECTION_PORT_LIST {
CONNECTION_PORT *
Head;
void
InsertInList(CONNECTION_PORT *);
CONNECTION_PORT *
FindNamedPort(
char * PortName
);
#ifdef USE_SHARED_HEAP
void *
operator new(size_t Size) { return (lpc_shared_heap.Alloc(Size)) }
void
operator delete(void * Buffer) { return (lpc_shared_heap.Free(Buffer)) }
#endif
}
class COMMUNICATION_PORT : PORT {
PORT * PeerPort; // Client points to server, visa-versa.
COMMUNICATION_PORT();
~COMMUNICATION_PORT();
STATUS
Send(
void * Buffer,
int BufferSize
);
STATUS
Receive(
void * * Buffer,
int * BufferSize
);
#ifdef USE_SHARED_HEAP
void *
operator new(size_t Size) { return (lpc_shared_heap.Alloc(Size)) }
void
operator delete(void * Buffer) { return (lpc_shared_heap.Free(Buffer)) }
#endif
}
COMMUNICATION_PORT::~COMMUNICATION_PORT()
{
if (PeerPort) {
ENTER_CRITICAL_SECTION();
PeerPort->PeerPort = NULL;
EXIT_CRITICAL_SECTION();
PeerPort->Signal();
}
Delete all messages enqueued on port.
}
STATUS
COMMUNICATION_PORT::Send(
void * Buffer,
int BufferSize
)
{
DWORD ThreadExitCode;
MESSAGE * Message;
if (PeerPort == NULL) {
return (ERROR);
}
Message = new MESSAGE(this, Buffer, BufferSize);
PeerPort->MessageQueue.Enqueue(Message);
return (SUCCESS);
}
STATUS
COMMUNICATION_PORT::Receive(
void * * Buffer,
int * BufferSize
)
{
MESSAGE * Message;
Wait();
if ((Message = Dequeue()) == NULL) {
return (ERROR);
}
*Buffer = Message->Data;
*BufferSize = Message->DataSize;
delete(Message);
return (SUCCESS);
}
class CLIENT_COMMUNICATION_PORT : COMMUNICATION_PORT {
STATUS
ConnectToServer(
char * PortName,
char * Buffer,
int BufferSize
);
#ifdef USE_SHARED_HEAP
void *
operator new(size_t Size) { return (lpc_shared_heap.Alloc(Size)) }
void
operator delete(void * Buffer) { return (lpc_shared_heap.Free(Buffer)) }
#endif
}
STATUS
CLIENT_COMMUNICATION_PORT::ConnectToServer(
char * PortName,
void * Buffer,
int BufferSize
)
{
MESSAGE * Message;
CONNECTION_PORT * ConnectionPort;
ConnectionPort = ConnectionPortList.FindNamedPort(PortName);
if (ConnectionPort == NULL) {
return (ERROR);
}
Message = new MESSAGE(this, Buffer, BufferSize);
ConnectionPort->ConnectQueue.Enqueue(Message).
Wait();
if (PeerPort == NULL) {
return (ERROR);
}
return (SUCCESS);
}