windows-nt/Source/XPSP1/NT/sdktools/debuggers/dbgrpc/dbgrpc.cpp
2020-09-26 16:20:57 +08:00

1366 lines
34 KiB
C++

//----------------------------------------------------------------------------
//
// Remoting support.
//
// Copyright (C) Microsoft Corporation, 1999-2001.
//
//----------------------------------------------------------------------------
#include "pch.hpp"
#include <lmcons.h>
#define DBGRPC_SIGNATURE 'CPRD'
#define DBGRPC_PROTOCOL_VERSION 2
enum
{
SEQ_HANDSHAKE = 0xffff0000,
SEQ_IDENTITY,
SEQ_PASSWORD,
SEQ_CALL_HEADER,
};
#define DBGRPC_SHAKE_FULL_REMOTE_UNKNOWN 0x00000001
struct DbgRpcHandshake
{
ULONG Signature;
ULONG ProtocolVersion;
GUID DesiredObject;
DbgRpcObjectId RemoteObject;
ULONG IdentityLength;
ULONG PasswordLength;
ULONG Flags;
ULONG Reserved1;
ULONG64 Reserved2[10];
};
ULONG g_DbgRpcCallSequence;
CRITICAL_SECTION g_DbgRpcLock;
#define CreateUserThread(Start, Param, Tid) \
CreateThread(NULL, 0, Start, Param, 0, Tid)
#ifdef NT_NATIVE
#define ExitUserThread(Code) RtlExitUserThread(Code)
#else
#define ExitUserThread(Code) return Code
#endif
//----------------------------------------------------------------------------
//
// DbgRpcReceiveCalls.
//
//----------------------------------------------------------------------------
HRESULT
DbgRpcReceiveCalls(DbgRpcConnection* Conn, DbgRpcCall* Call, PUCHAR* InOutData)
{
HRESULT Status;
ULONG RetSeq = Call->Sequence;
DBG_ASSERT((Call->Flags & DBGRPC_RETURN) == 0 &&
*InOutData == NULL);
// If this thread isn't the owner of the connection we
// cannot read the socket as that could create a
// race condition with the owner thread reading
// the socket.
// If this is a locked call, where a higher-level lock
// prevents socket contention, we can allow it.
if ((Call->Flags & DBGRPC_LOCKED) == 0 &&
Conn->m_ThreadId != GetCurrentThreadId())
{
return RPC_E_WRONG_THREAD;
}
for (;;)
{
DbgRpcCall ReadCall;
if (Conn->m_Trans->Read(SEQ_CALL_HEADER, &ReadCall,
sizeof(ReadCall)) != sizeof(ReadCall))
{
DRPC_ERR(("%X: Unable to receive call header\n",
GetCurrentThreadId()));
return RPC_E_CLIENT_DIED;
}
ULONG Size;
PUCHAR Data;
if (ReadCall.Flags & DBGRPC_RETURN)
{
Size = ReadCall.OutSize;
}
else
{
Size = ReadCall.InSize;
ReadCall.Status = S_OK;
}
if (Size > 0)
{
Data = (PUCHAR)Conn->Alloc(Size);
if (Data == NULL)
{
DRPC_ERR(("%X: Unable to allocate call body\n",
GetCurrentThreadId()));
return E_OUTOFMEMORY;
}
if (Conn->m_Trans->Read(ReadCall.Sequence, Data, Size) != Size)
{
DRPC_ERR(("%X: Unable to receive call body\n",
GetCurrentThreadId()));
Conn->Free(Data);
return RPC_E_CLIENT_DIED;
}
}
else
{
Data = NULL;
}
#ifdef DBG_RPC
if (ReadCall.Flags & DBGRPC_RETURN)
{
DRPC(("%X: %X: Return %s (%X), ret 0x%X, out %d\n",
GetCurrentThreadId(), ReadCall.Sequence,
DbgRpcGetStubName(ReadCall.StubIndex),
ReadCall.StubIndex, ReadCall.Status, ReadCall.OutSize));
}
else
{
DRPC(("%X: %X: Request %s (%X), fl %X, in %d\n",
GetCurrentThreadId(), ReadCall.Sequence,
DbgRpcGetStubName(ReadCall.StubIndex),
ReadCall.StubIndex, ReadCall.Flags, ReadCall.InSize));
}
#endif
if (ReadCall.Flags & DBGRPC_RETURN)
{
if (ReadCall.Sequence != RetSeq)
{
#if DBG
DRPC_ERR(("%X: %X: Non-seq ret 0x%X for %s (%X)\n",
GetCurrentThreadId(), ReadCall.Sequence,
ReadCall.Status,
DbgRpcGetStubName(ReadCall.StubIndex),
ReadCall.StubIndex));
#else
DRPC_ERR(("%X: %X: Non-seq ret 0x%X for (%X)\n",
GetCurrentThreadId(), ReadCall.Sequence,
ReadCall.Status, ReadCall.StubIndex));
#endif
// This return is for some call other than the current
// call, which means that RPC is messed up.
// Discard the return and hope for the best.
Conn->FreeData(Data);
continue;
}
*Call = ReadCall;
*InOutData = Data;
return Call->Status;
}
PUCHAR OutData;
if (ReadCall.OutSize > 0)
{
DBG_ASSERT((ReadCall.Flags & DBGRPC_NO_RETURN) == 0);
OutData = (PUCHAR)Conn->Alloc(ReadCall.OutSize);
if (OutData == NULL)
{
if (Data)
{
Conn->Free(Data);
}
return E_OUTOFMEMORY;
}
}
else
{
OutData = NULL;
}
if (ReadCall.Flags & DBGRPC_NO_RETURN)
{
Conn->m_Flags |= DBGRPC_IN_ASYNC_CALL;
}
DbgRpcStubFunction StubFn = DbgRpcGetStub(ReadCall.StubIndex);
if (StubFn != NULL)
{
ReadCall.Status = StubFn((IUnknown*)ReadCall.ObjectId,
Conn, &ReadCall, Data, OutData);
}
else
{
ReadCall.Status = RPC_E_INVALIDMETHOD;
}
Conn->m_Flags &= ~DBGRPC_IN_ASYNC_CALL;
DRPC(("%X: %X: Called %s (%X), ret 0x%X, out %d\n",
GetCurrentThreadId(), ReadCall.Sequence,
DbgRpcGetStubName(ReadCall.StubIndex),
ReadCall.StubIndex, ReadCall.Status, ReadCall.OutSize));
Status = S_OK;
if ((ReadCall.Flags & DBGRPC_NO_RETURN) == 0)
{
ReadCall.Flags |= DBGRPC_RETURN;
// Take a lock here to make sure that the header
// and body are sequential in the stream.
EnterCriticalSection(&g_DbgRpcLock);
if (Conn->m_Trans->Write(ReadCall.Sequence,
&ReadCall, sizeof(ReadCall)) !=
sizeof(ReadCall) ||
(ReadCall.OutSize > 0 &&
Conn->m_Trans->Write(ReadCall.Sequence,
OutData, ReadCall.OutSize) !=
ReadCall.OutSize))
{
Status = RPC_E_CANTTRANSMIT_CALL;
}
LeaveCriticalSection(&g_DbgRpcLock);
}
if (OutData)
{
Conn->FreeData(OutData);
}
if (Data)
{
Conn->FreeData(Data);
}
if (Status != S_OK)
{
return Status;
}
}
}
//----------------------------------------------------------------------------
//
// DbgRpcConnection.
//
//----------------------------------------------------------------------------
DbgRpcConnection* g_DbgRpcConns;
DbgRpcConnection::DbgRpcConnection(DbgRpcTransport* Trans)
{
m_Trans = Trans;
m_Next = NULL;
m_ThreadId = GetCurrentThreadId();
m_Buffer = PTR_ALIGN2(PUCHAR, m_UnalignedBuffer,
DBGRPC_CONN_BUFFER_ALIGN);
m_BufferUsed = 0;
m_Flags = 0;
m_Objects = 0;
}
DbgRpcConnection::~DbgRpcConnection(void)
{
Disconnect();
}
PUCHAR
DbgRpcConnection::StartCall(DbgRpcCall* Call, DbgRpcObjectId ObjectId,
ULONG StubIndex, ULONG InSize, ULONG OutSize)
{
PUCHAR Data;
if (InSize > 0)
{
Data = (PUCHAR)Alloc(InSize);
if (Data == NULL)
{
return NULL;
}
}
else
{
// Have to return a non-zero pointer but
// it doesn't need to be valid since it should
// never be used.
Data = DBGRPC_NO_DATA;
}
Call->ObjectId = ObjectId;
DBG_ASSERT(StubIndex < 0x10000);
Call->StubIndex = (USHORT)StubIndex;
Call->Flags = 0;
Call->InSize = InSize;
Call->OutSize = OutSize;
Call->Status = S_OK;
Call->Sequence = InterlockedIncrement((PLONG)&g_DbgRpcCallSequence);
Call->Reserved1 = 0;
return Data;
}
HRESULT
DbgRpcConnection::SendReceive(DbgRpcCall* Call, PUCHAR* InOutData)
{
//
// Send call and in-parameter data.
//
DRPC(("%X: %X: Calling %s (%X), in %d, out %d\n",
GetCurrentThreadId(), Call->Sequence,
DbgRpcGetStubName(Call->StubIndex),
Call->StubIndex, Call->InSize, Call->OutSize));
if (m_Flags & DBGRPC_IN_ASYNC_CALL)
{
return RPC_E_CANTCALLOUT_INASYNCCALL;
}
// Take a lock here to make sure that the header
// and body are sequential in the stream.
EnterCriticalSection(&g_DbgRpcLock);
if (m_Trans->Write(SEQ_CALL_HEADER, Call, sizeof(*Call)) != sizeof(*Call))
{
LeaveCriticalSection(&g_DbgRpcLock);
return RPC_E_CANTTRANSMIT_CALL;
}
if (Call->InSize > 0)
{
if (m_Trans->Write(Call->Sequence, *InOutData, Call->InSize) !=
Call->InSize)
{
LeaveCriticalSection(&g_DbgRpcLock);
return RPC_E_CANTTRANSMIT_CALL;
}
// In data is no longer necessary.
Free(*InOutData);
}
LeaveCriticalSection(&g_DbgRpcLock);
// Clear out data pointer in case of later failures.
*InOutData = NULL;
HRESULT Status;
if (Call->Flags & DBGRPC_NO_RETURN)
{
Status = S_OK;
}
else
{
USHORT StubIndex = Call->StubIndex;
Status = DbgRpcReceiveCalls(this, Call, InOutData);
if (Call->StubIndex != StubIndex)
{
#if DBG
DRPC_ERR(("%X: %X: Call to %s (%X) returned from %s (%d)\n",
GetCurrentThreadId(), Call->Sequence,
DbgRpcGetStubName(StubIndex), StubIndex,
DbgRpcGetStubName(Call->StubIndex),
Call->StubIndex));
#else
DRPC_ERR(("%X: %X: Mismatched call return\n",
GetCurrentThreadId(), Call->Sequence));
#endif
Status = RPC_E_INVALID_DATAPACKET;
}
}
return Status;
}
PVOID
DbgRpcConnection::MallocAligned(ULONG Size)
{
PVOID Data, Align;
// Not enough buffer space left so allocate. malloc
// only gives out 8-byte-aligned memory so tweak things
// to get it aligned.
Data = malloc(Size + DBGRPC_CONN_BUFFER_ALIGN);
if (Data != NULL)
{
if ((ULONG_PTR)Data & (DBGRPC_CONN_BUFFER_ALIGN - 1))
{
Align = PTR_ALIGN2(PVOID, Data, DBGRPC_CONN_BUFFER_ALIGN);
}
else
{
Align = (PVOID)((PUCHAR)Data + DBGRPC_CONN_BUFFER_ALIGN);
}
*((PVOID*)Align - 1) = Data;
}
else
{
Align = NULL;
}
return Align;
}
void
DbgRpcConnection::FreeAligned(PVOID Ptr)
{
free(*((PVOID*)Ptr - 1));
}
PVOID
DbgRpcConnection::Alloc(ULONG Size)
{
PVOID Data = NULL;
// Keep every allocated chunk aligned.
Size = INT_ALIGN2(Size, DBGRPC_CONN_BUFFER_ALIGN);
// Don't burn up large parts of the buffer on big chunks
// as that may force many smaller chunks into dynamic
// allocations because the buffer is full.
if (Size <= DBGRPC_CONN_BUFFER_DYNAMIC_LIMIT)
{
EnterCriticalSection(&g_DbgRpcLock);
if (m_BufferUsed + Size <= DBGRPC_CONN_BUFFER_SIZE)
{
// Data is allocated in strict LIFO order so
// we just need to mark the end of the buffer as used.
Data = &m_Buffer[m_BufferUsed];
m_BufferUsed += Size;
}
LeaveCriticalSection(&g_DbgRpcLock);
}
if (Data == NULL)
{
Data = MallocAligned(Size);
}
return Data;
}
void
DbgRpcConnection::Free(PVOID Ptr)
{
if (Ptr >= m_Buffer && Ptr < m_Buffer + DBGRPC_CONN_BUFFER_SIZE)
{
EnterCriticalSection(&g_DbgRpcLock);
// Data was allocated in the connection buffer.
// Data is allocated in strict LIFO order so
// we just need to back up prior to the data.
m_BufferUsed = (ULONG)((PUCHAR)Ptr - m_Buffer);
LeaveCriticalSection(&g_DbgRpcLock);
}
else
{
// Data was dynamically allocated.
FreeAligned(Ptr);
}
}
void
DbgRpcConnection::Disconnect(void)
{
delete m_Trans;
m_Trans = NULL;
}
DbgRpcConnection*
DbgRpcGetConnection(ULONG Tid)
{
DbgRpcConnection* Conn;
EnterCriticalSection(&g_DbgRpcLock);
for (Conn = g_DbgRpcConns; Conn != NULL; Conn = Conn->m_Next)
{
if (Conn->m_ThreadId == Tid)
{
break;
}
}
LeaveCriticalSection(&g_DbgRpcLock);
return Conn;
}
void
DbgRpcAddConnection(DbgRpcConnection* Conn)
{
EnterCriticalSection(&g_DbgRpcLock);
Conn->m_Next = g_DbgRpcConns;
g_DbgRpcConns = Conn;
LeaveCriticalSection(&g_DbgRpcLock);
}
void
DbgRpcRemoveConnection(DbgRpcConnection* Conn)
{
EnterCriticalSection(&g_DbgRpcLock);
DbgRpcConnection* Prev = NULL;
DbgRpcConnection* Cur;
for (Cur = g_DbgRpcConns; Cur != NULL; Cur = Cur->m_Next)
{
if (Cur == Conn)
{
break;
}
Prev = Cur;
}
DBG_ASSERT(Cur != NULL);
if (Prev == NULL)
{
g_DbgRpcConns = Conn->m_Next;
}
else
{
Prev->m_Next = Conn->m_Next;
}
LeaveCriticalSection(&g_DbgRpcLock);
}
void
DbgRpcDeleteConnection(DbgRpcConnection* Conn)
{
DbgRpcRemoveConnection(Conn);
// It's possible that another thread is in the middle
// of using the connection for an async send. Disconnect
// the connection to force any pending calls to fail.
// The connection is already removed from the list
// so there shouldn't be any further usage.
Conn->Disconnect();
// Give up some time to let things fail. This
// could be made more deterministic by tracking
// connection usage but it doesn't seem necessary.
Sleep(1000);
delete Conn;
}
//----------------------------------------------------------------------------
//
// DbgRpcProxy.
//
//----------------------------------------------------------------------------
DbgRpcProxy::DbgRpcProxy(ULONG InterfaceIndex)
{
m_InterfaceIndex = InterfaceIndex;
m_OwningThread = ::GetCurrentThreadId();
m_LocalRefs = 0;
m_RemoteRefs = 1;
m_ObjectId = 0;
}
DbgRpcProxy::~DbgRpcProxy(void)
{
// If this proxy was attached to a connection detach it.
if (m_ObjectId)
{
DbgRpcConnection* Conn = DbgRpcGetConnection(m_OwningThread);
if (Conn != NULL)
{
DRPC_REF(("Conn %p obj %2d proxy %p\n",
Conn, Conn->m_Objects - 1, this));
if (InterlockedDecrement((PLONG)&Conn->m_Objects) == 0)
{
DbgRpcDeleteConnection(Conn);
}
}
}
}
IUnknown*
DbgRpcProxy::InitializeProxy(DbgRpcObjectId ObjectId,
IUnknown* ExistingProxy)
{
//
// The current debugger remoting does not preserve
// object identity as this simplifies proxy
// management. Nobody currently needs it, so
// we're not bothering with it. If object identity
// becomes important this routine is the place
// to implement proxy lookup and sharing.
//
// Handle NULL object case where proxy is unnecessary.
if (ObjectId == 0)
{
// Proxies all have the same basic layout so this
// cast works for any interface-specific proxy.
DbgRpcDeleteProxy(this);
return NULL;
}
DbgRpcConnection* Conn = DbgRpcGetConnection(m_OwningThread);
if (Conn != NULL)
{
InterlockedIncrement((PLONG)&Conn->m_Objects);
DRPC_REF(("Conn %p obj %2d proxy %p\n",
Conn, Conn->m_Objects, this));
}
m_ObjectId = ObjectId;
return ExistingProxy;
}
//----------------------------------------------------------------------------
//
// DbgRpcClientObject.
//
//----------------------------------------------------------------------------
void
DbgRpcClientObject::Finalize(void)
{
// Do-nothing convenience implementation.
}
//----------------------------------------------------------------------------
//
// Registration functions.
//
//----------------------------------------------------------------------------
#define DBGRPC_MAX_REG_SERVERS 16
ULONG g_DbgRpcRegServers[DBGRPC_MAX_REG_SERVERS][2];
void
DbgRpcRegisterServer(DbgRpcTransport* Trans,
DbgRpcClientObjectFactory* Factory)
{
#ifndef NT_NATIVE
char Desc[2 * MAX_PARAM_VALUE];
PSTR Tail;
Factory->GetServerTypeName(Desc);
Tail = Desc + strlen(Desc);
DBG_ASSERT(Tail < Desc + 32);
*Tail++ = ' ';
*Tail++ = '-';
*Tail++ = ' ';
Trans->GetParameters(Tail, sizeof(Desc) - (ULONG)(Tail - Desc));
HKEY Key;
LONG Status;
char ValName[32];
ULONG Index;
// No servers will survive a reboot so create a volatile
// key to ensure that even if the key isn't cleaned up
// at process exit it'll go away at the next reboot.
if ((Status = RegCreateKeyEx(HKEY_LOCAL_MACHINE, DEBUG_SERVER_KEY,
0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS,
NULL, &Key, NULL)) != ERROR_SUCCESS)
{
DRPC_ERR(("%X: Unable to register server '%s'\n",
GetCurrentThreadId(), Desc));
return;
}
// Prefix the value name with the thread ID to ensure that
// every thread currently running has its own namespace. This
// makes it impossible for two threads to attempt to write
// the same value at the same time.
sprintf(ValName, "%08X.", GetCurrentThreadId());
// Find an unused value and store the server information.
Index = 0;
for (;;)
{
DWORD Len;
sprintf(ValName + 9, "%08X", Index);
if (RegQueryValueEx(Key, ValName, NULL, NULL, NULL,
&Len) != ERROR_SUCCESS)
{
break;
}
Index++;
}
if ((Status = RegSetValueEx(Key, ValName, 0, REG_SZ, (LPBYTE)Desc,
strlen(Desc) + 1)) != ERROR_SUCCESS)
{
DRPC_ERR(("%X: Unable to register server '%s'\n",
GetCurrentThreadId(), Desc));
}
else
{
ULONG i;
// Remember the value name used so that it can be
// removed later. This is done with a simple
// static array since there shouldn't be that many
// servers in a process and they don't die until
// the process exits.
for (i = 0; i < DBGRPC_MAX_REG_SERVERS; i++)
{
if (g_DbgRpcRegServers[i][0] == 0)
{
g_DbgRpcRegServers[i][0] = GetCurrentThreadId();
g_DbgRpcRegServers[i][1] = Index;
break;
}
}
}
RegCloseKey(Key);
#endif // #ifndef NT_NATIVE
}
void
DbgRpcDeregisterServers(void)
{
#ifndef NT_NATIVE
HKEY Key;
LONG Status;
if ((Status = RegCreateKeyEx(HKEY_LOCAL_MACHINE, DEBUG_SERVER_KEY,
0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS,
NULL, &Key, NULL)) != ERROR_SUCCESS)
{
return;
}
ULONG i;
for (i = 0; i < DBGRPC_MAX_REG_SERVERS; i++)
{
if (g_DbgRpcRegServers[i][0] == 0)
{
continue;
}
char ValName[32];
sprintf(ValName, "%08X.%08X", g_DbgRpcRegServers[i][0],
g_DbgRpcRegServers[i][1]);
RegDeleteValue(Key, ValName);
g_DbgRpcRegServers[i][0] = 0;
g_DbgRpcRegServers[i][1] = 0;
}
RegCloseKey(Key);
#endif // #ifndef NT_NATIVE
}
//----------------------------------------------------------------------------
//
// Initialization functions.
//
//----------------------------------------------------------------------------
BOOL
DbgRpcOneTimeInitialization(void)
{
static BOOL s_Initialized = FALSE;
if (s_Initialized)
{
return TRUE;
}
#ifndef NT_NATIVE
WSADATA WsData;
if (WSAStartup(MAKEWORD(2, 0), &WsData) != 0)
{
return FALSE;
}
#endif
if (InitializeAllAccessSecObj() != S_OK)
{
return FALSE;
}
__try
{
InitializeCriticalSection(&g_DbgRpcLock);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
return FALSE;
}
DbgRpcInitializeClient();
return TRUE;
}
DbgRpcConnection*
DbgRpcCreateClientObject(DbgRpcTransport* Trans,
DbgRpcClientObjectFactory* Factory,
PSTR TransIdentity,
DbgRpcClientObject** ClientObject)
{
if (strlen(TransIdentity) >= DBGRPC_MAX_IDENTITY - 16)
{
// This check is really just to placate PREfix,
// as transport identities are always much shorter
// than this.
DRPC_ERR(("%X: Invalid transport identity\n",
GetCurrentThreadId()));
delete Trans;
return NULL;
}
DbgRpcConnection* Conn = new DbgRpcConnection(Trans);
if (Conn == NULL)
{
DRPC_ERR(("%X: Unable to allocate client connection\n",
GetCurrentThreadId()));
delete Trans;
return NULL;
}
DRPC(("%X: Read handshake\n",
GetCurrentThreadId()));
DbgRpcHandshake Shake;
if (Trans->Read(SEQ_HANDSHAKE, &Shake, sizeof(Shake)) != sizeof(Shake))
{
DRPC_ERR(("%X: Unable to read handshake from remote client\n",
GetCurrentThreadId()));
goto EH_Conn;
}
DRPC(("%X: Read handshake, sig %X, ver %X, obj %I64X, id %d, pwd %d\n",
GetCurrentThreadId(), Shake.Signature, Shake.ProtocolVersion,
Shake.RemoteObject, Shake.IdentityLength, Shake.PasswordLength));
if (Shake.Signature != DBGRPC_SIGNATURE ||
Shake.ProtocolVersion != DBGRPC_PROTOCOL_VERSION ||
Shake.RemoteObject != 0 ||
Shake.IdentityLength > DBGRPC_MAX_IDENTITY ||
(Shake.PasswordLength != 0 &&
Shake.PasswordLength != MAX_PASSWORD_BUFFER))
{
DRPC_ERR(("%X: Invalid handshake from remote client\n",
GetCurrentThreadId()));
goto EH_Conn;
}
char Identity[DBGRPC_MAX_IDENTITY];
if (Shake.IdentityLength > 0)
{
if (Trans->Read(SEQ_IDENTITY, Identity, Shake.IdentityLength) !=
Shake.IdentityLength)
{
DRPC_ERR(("%X: Unable to read identity from remote client\n",
GetCurrentThreadId()));
goto EH_Conn;
}
Identity[Shake.IdentityLength - 1] = 0;
}
else
{
strcpy(Identity, "OldRpc\\NoIdentity");
}
//
// Format the raw transport identity into something
// that'll look better appended to the reported identity.
//
char TransIdentityFmt[DBGRPC_MAX_IDENTITY];
sprintf(TransIdentityFmt, " (%s)", TransIdentity);
strncat(Identity, TransIdentityFmt,
DBGRPC_MAX_IDENTITY - strlen(Identity) - 1);
if (Shake.PasswordLength > 0)
{
if (!Trans->m_PasswordGiven)
{
DRPC_ERR(("%X: Password not given but client sent one\n",
GetCurrentThreadId()));
goto EH_Conn;
}
UCHAR Pwd[MAX_PASSWORD_BUFFER];
if (Trans->Read(SEQ_PASSWORD, Pwd, Shake.PasswordLength) !=
Shake.PasswordLength)
{
DRPC_ERR(("%X: Unable to read password from remote client\n",
GetCurrentThreadId()));
goto EH_Conn;
}
if (memcmp(Pwd, Trans->m_HashedPassword, MAX_PASSWORD_BUFFER) != 0)
{
DRPC_ERR(("%X: Client sent incorrect password\n",
GetCurrentThreadId()));
goto EH_Conn;
}
}
else if (Trans->m_PasswordGiven)
{
DRPC_ERR(("%X: Password given but client didn't send one\n",
GetCurrentThreadId()));
goto EH_Conn;
}
if (Shake.Flags & DBGRPC_SHAKE_FULL_REMOTE_UNKNOWN)
{
Conn->m_Flags |= DBGRPC_FULL_REMOTE_UNKNOWN;
}
DbgRpcClientObject* Object;
PVOID ObjInterface;
if (Factory->CreateInstance(&Shake.DesiredObject, &Object) != S_OK)
{
DRPC_ERR(("%X: Unable to create client object instance\n",
GetCurrentThreadId()));
goto EH_Conn;
}
if (Object->Initialize(Identity, &ObjInterface) != S_OK)
{
DRPC_ERR(("%X: Unable to initialize client object\n",
GetCurrentThreadId()));
goto EH_Object;
}
ZeroMemory(&Shake, sizeof(Shake));
Shake.Signature = DBGRPC_SIGNATURE;
Shake.ProtocolVersion = DBGRPC_PROTOCOL_VERSION;
Shake.RemoteObject = (DbgRpcObjectId)ObjInterface;
Shake.Flags = DBGRPC_SHAKE_FULL_REMOTE_UNKNOWN;
if (Trans->Write(SEQ_HANDSHAKE,
&Shake, sizeof(Shake)) != sizeof(Shake))
{
DRPC_ERR(("%X: Unable to write handshake to remote client\n",
GetCurrentThreadId()));
goto EH_Object;
}
DRPC(("%X: Object %p created\n",
GetCurrentThreadId(), Object));
Object->Finalize();
*ClientObject = Object;
DbgRpcAddConnection(Conn);
return Conn;
EH_Object:
Object->Uninitialize();
EH_Conn:
delete Conn;
return NULL;
}
struct ClientThreadData
{
DbgRpcTransport* Trans;
DbgRpcClientObjectFactory* Factory;
char Identity[DBGRPC_MAX_IDENTITY];
};
DWORD WINAPI
DbgRpcClientThread(PVOID ThreadParam)
{
DbgRpcClientObject* Object;
ClientThreadData* ThreadData = (ClientThreadData*)ThreadParam;
DbgRpcTransport* Trans = ThreadData->Trans;
DbgRpcClientObjectFactory* Factory = ThreadData->Factory;
DbgRpcConnection* Conn =
DbgRpcCreateClientObject(Trans, Factory,
ThreadData->Identity, &Object);
// Don't need this information any more.
delete ThreadParam;
if (Conn == NULL)
{
ExitUserThread(0);
}
if (DbgRpcServerThreadInitialize() != S_OK)
{
ExitUserThread(0);
}
DRPC(("%X: Created connection %p\n",
GetCurrentThreadId(), Conn));
DbgRpcCall Call;
PUCHAR Data;
HRESULT Status;
// Take a reference on the connection to ensure that
// it stays alive as long as this thread does.
Conn->m_Objects++;
for (;;)
{
Data = NULL;
ZeroMemory(&Call, sizeof(Call));
Status = DbgRpcReceiveCalls(Conn, &Call, &Data);
Conn->FreeData(Data);
if (Status != S_OK)
{
DRPC_ERR(("%X: Client thread call receive failed, 0x%X\n",
GetCurrentThreadId(), Status));
if (Status == RPC_E_CLIENT_DIED)
{
break;
}
}
}
DRPC(("%X: Removing connection %p\n",
GetCurrentThreadId(), Conn));
DbgRpcDeleteConnection(Conn);
Object->Uninitialize();
DbgRpcServerThreadUninitialize();
ExitUserThread(0);
}
#if _MSC_FULL_VER >= 13008827
#pragma warning(push)
#pragma warning(disable:4715) // Not all control paths return (due to infinite loop)
#endif
struct ServerThreadData
{
DbgRpcTransport* Trans;
DbgRpcClientObjectFactory* Factory;
};
DWORD WINAPI
DbgRpcServerThread(PVOID ThreadParam)
{
ServerThreadData* ServerData = (ServerThreadData*)ThreadParam;
DbgRpcTransport* ServerTrans = ServerData->Trans;
DbgRpcClientObjectFactory* Factory = ServerData->Factory;
// Values are now cached locally so free passed-in data.
delete ServerData;
HRESULT Status;
ClientThreadData* ClientData = NULL;
// Register this server for people browsing for servers.
DbgRpcRegisterServer(ServerTrans, Factory);
for (;;)
{
if (ClientData == NULL)
{
ClientData = new ClientThreadData;
if (ClientData == NULL)
{
DRPC_ERR(("%X: Unable to allocate ClientThreadData\n",
GetCurrentThreadId()));
Sleep(100);
continue;
}
}
Status = ServerTrans->AcceptConnection(&ClientData->Trans,
ClientData->Identity);
if (Status == S_OK)
{
DWORD Tid;
ClientData->Factory = Factory;
HANDLE Thread = CreateUserThread(DbgRpcClientThread,
ClientData, &Tid);
if (Thread == NULL)
{
DRPC_ERR(("%X: Client thread create failed, %d\n",
GetCurrentThreadId(), GetLastError()));
Sleep(100);
}
else
{
CloseHandle(Thread);
ClientData = NULL;
}
}
else
{
DRPC_ERR(("%X: Accept failed, %X\n",
GetCurrentThreadId(), Status));
Sleep(100);
}
}
ExitUserThread(0);
}
#if _MSC_FULL_VER >= 13008827
#pragma warning(pop)
#endif
HRESULT
DbgRpcCreateServer(PCSTR Options, DbgRpcClientObjectFactory* Factory)
{
DbgRpcTransport* Trans;
HRESULT Status;
if (!DbgRpcOneTimeInitialization())
{
return E_FAIL;
}
Trans = DbgRpcInitializeTransport(Options);
if (Trans == NULL)
{
return E_INVALIDARG;
}
Status = Trans->CreateServer();
if (Status != S_OK)
{
goto EH_Trans;
}
ServerThreadData* ThreadData;
ThreadData = new ServerThreadData;
if (ThreadData == NULL)
{
Status = E_OUTOFMEMORY;
goto EH_Trans;
}
ThreadData->Trans = Trans;
ThreadData->Factory = Factory;
DWORD Tid;
HANDLE Thread;
Thread = CreateUserThread(DbgRpcServerThread, ThreadData, &Tid);
if (Thread == NULL)
{
Status = WIN32_LAST_STATUS();
delete ThreadData;
goto EH_Trans;
}
CloseHandle(Thread);
return S_OK;
EH_Trans:
delete Trans;
return Status;
}
#define MIN_CLIENT_IDENTITY (DBGRPC_MAX_IDENTITY * 3 / 4)
void
GetClientIdentity(PSTR Identity)
{
#ifndef NT_NATIVE
char CompName[MAX_COMPUTERNAME_LENGTH + 1];
ULONG CompSize;
char UserName[UNLEN + 1];
ULONG UserSize;
CompSize = sizeof(CompName);
if (!GetComputerName(CompName, &CompSize))
{
sprintf(CompName, "CErr%d", GetLastError());
CompSize = strlen(CompName);
}
else if (CompSize == 0)
{
strcpy(CompName, "NoComp");
CompSize = 6;
}
if (CompSize > DBGRPC_MAX_IDENTITY - MIN_CLIENT_IDENTITY - 1)
{
CompSize = DBGRPC_MAX_IDENTITY - MIN_CLIENT_IDENTITY - 1;
}
CompName[CompSize] = 0;
UserSize = sizeof(UserName);
if (!GetUserName(UserName, &UserSize))
{
sprintf(UserName, "UErr%d", GetLastError());
UserSize = strlen(UserName);
}
else if (UserSize == 0)
{
strcpy(UserName, "NoUser");
UserSize = 6;
}
if (UserSize > DBGRPC_MAX_IDENTITY - MIN_CLIENT_IDENTITY - 1)
{
UserSize = DBGRPC_MAX_IDENTITY - MIN_CLIENT_IDENTITY - 1;
}
UserName[UserSize] = 0;
memcpy(Identity, CompName, CompSize);
Identity[CompSize] = '\\';
Identity[CompSize + 1] = 0;
strncat(Identity + CompSize + 1, UserName,
DBGRPC_MAX_IDENTITY - CompSize - 2);
#else // #ifndef NT_NATIVE
strcpy(Identity, "NtNative");
#endif // #ifndef NT_NATIVE
}
HRESULT
DbgRpcCreateServerConnection(DbgRpcTransport* Trans,
const GUID* DesiredObject,
IUnknown** ClientObject)
{
HRESULT Status;
DbgRpcConnection* Conn = new DbgRpcConnection(Trans);
if (Conn == NULL)
{
delete Trans;
return E_OUTOFMEMORY;
}
IUnknown* Object;
DbgRpcProxy* Proxy;
ULONG IfUnique;
Status = DbgRpcPreallocProxy(*DesiredObject, (void **)&Object,
&Proxy, &IfUnique);
if (Status != S_OK)
{
goto EH_Conn;
}
Status = Trans->ConnectServer();
if (Status != S_OK)
{
goto EH_Proxy;
}
char Identity[DBGRPC_MAX_IDENTITY];
GetClientIdentity(Identity);
DbgRpcHandshake Shake;
ZeroMemory(&Shake, sizeof(Shake));
Shake.Signature = DBGRPC_SIGNATURE;
Shake.ProtocolVersion = DBGRPC_PROTOCOL_VERSION;
Shake.DesiredObject = *DesiredObject;
Shake.IdentityLength = sizeof(Identity);
Shake.PasswordLength = Trans->m_PasswordGiven ? MAX_PASSWORD_BUFFER : 0;
Shake.Flags = DBGRPC_SHAKE_FULL_REMOTE_UNKNOWN;
if (Trans->Write(SEQ_HANDSHAKE, &Shake, sizeof(Shake)) != sizeof(Shake))
{
Status = E_FAIL;
goto EH_Proxy;
}
if (Trans->Write(SEQ_IDENTITY, Identity, Shake.IdentityLength) !=
Shake.IdentityLength)
{
Status = E_FAIL;
goto EH_Proxy;
}
if (Trans->m_PasswordGiven &&
Trans->Write(SEQ_PASSWORD,
Trans->m_HashedPassword, Shake.PasswordLength) !=
Shake.PasswordLength)
{
Status = E_FAIL;
goto EH_Proxy;
}
if (Trans->Read(SEQ_HANDSHAKE, &Shake, sizeof(Shake)) != sizeof(Shake))
{
Status = E_FAIL;
goto EH_Proxy;
}
DRPC(("%X: Read handshake, sig %X, ver %X\n",
GetCurrentThreadId(), Shake.Signature, Shake.ProtocolVersion));
if (Shake.Signature != DBGRPC_SIGNATURE ||
Shake.ProtocolVersion != DBGRPC_PROTOCOL_VERSION ||
Shake.RemoteObject == 0)
{
Status = RPC_E_VERSION_MISMATCH;
goto EH_Proxy;
}
if (Shake.Flags & DBGRPC_SHAKE_FULL_REMOTE_UNKNOWN)
{
Conn->m_Flags |= DBGRPC_FULL_REMOTE_UNKNOWN;
}
// Connection must be added first so it's looked up
// by InitializeProxy.
DbgRpcAddConnection(Conn);
*ClientObject = Proxy->InitializeProxy(Shake.RemoteObject, Object);
DRPC(("%X: Object %I64X proxied by %p\n",
GetCurrentThreadId(), Shake.RemoteObject, *ClientObject));
return S_OK;
EH_Proxy:
DbgRpcDeleteProxy(Proxy);
EH_Conn:
delete Conn;
return Status;
}
HRESULT
DbgRpcConnectServer(PCSTR Options, const GUID* DesiredObject,
IUnknown** ClientObject)
{
DbgRpcTransport* Trans;
HRESULT Status;
if (!DbgRpcOneTimeInitialization())
{
return E_FAIL;
}
Trans = DbgRpcInitializeTransport(Options);
if (Trans == NULL)
{
return E_INVALIDARG;
}
return DbgRpcCreateServerConnection(Trans, DesiredObject, ClientObject);
}