1273 lines
35 KiB
C
1273 lines
35 KiB
C
/*++
|
|
* File name:
|
|
* rclx.c
|
|
* Contents:
|
|
* A module for communicating with clxtshar via TCP/IP
|
|
* RCLX (Remote CLient eXecution) implemetation
|
|
*
|
|
* Copyright (C) 1998-1999 Microsoft Corp.
|
|
--*/
|
|
|
|
#include <windows.h>
|
|
#include <winsock.h>
|
|
#include <malloc.h>
|
|
#include <stdio.h>
|
|
|
|
#include "tclient.h"
|
|
|
|
#define PROTOCOLAPI __declspec(dllexport)
|
|
#include "protocol.h"
|
|
|
|
#include "gdata.h"
|
|
#include "queues.h"
|
|
#include "misc.h"
|
|
#include "bmpcache.h"
|
|
#include "rclx.h"
|
|
#include "extraexp.h"
|
|
|
|
/*
|
|
* Globals
|
|
*/
|
|
u_short g_nPortNumber = RCLX_DEFAULT_PORT; // Default port to listen
|
|
SOCKET g_hSrvSocket = INVALID_SOCKET; // Socket to listen to
|
|
PRCLXCONTEXT g_pRClxList = NULL; // Linked list of all connections
|
|
// which havn't receive
|
|
// client info
|
|
|
|
#ifndef SD_RECEIVE
|
|
#define SD_RECEIVE 0x00
|
|
#define SD_SEND 0x01
|
|
#define SD_BOTH 0x02
|
|
#endif // SD_RECEIVE
|
|
|
|
SOCKET RClx_Listen(u_short nPortNumber);
|
|
VOID RClx_CloseSocket(SOCKET hSocket);
|
|
VOID _RClx_RemoveContextFromGlobalQueue(PRCLXCONTEXT pContext);
|
|
|
|
|
|
/*++
|
|
* Function:
|
|
* RClx_Init
|
|
* Description:
|
|
* Module initialization. Calls WSAStartup, creates a listening
|
|
* on wich to listen to. Selects FD_ACCEPT as event passed to
|
|
* the feedback thread/window
|
|
* Return value:
|
|
* TRUE on success
|
|
* Called by:
|
|
* tclient.c:InitDone
|
|
--*/
|
|
BOOL RClx_Init(VOID)
|
|
{
|
|
WORD versionRequested;
|
|
WSADATA wsaData;
|
|
INT intRC;
|
|
BOOL rv = FALSE;
|
|
|
|
versionRequested = MAKEWORD(1, 1);
|
|
|
|
intRC = WSAStartup(versionRequested, &wsaData);
|
|
|
|
if (intRC != 0)
|
|
{
|
|
TRACE((ERROR_MESSAGE,
|
|
"Failed to initialize WinSock rc:%d\n",
|
|
intRC));
|
|
printf("Failed to initialize WinSock rc:%d\n", intRC);
|
|
goto exitpt;
|
|
}
|
|
|
|
g_hSrvSocket = RClx_Listen(g_nPortNumber);
|
|
if (g_hSrvSocket == INVALID_SOCKET)
|
|
{
|
|
TRACE((ERROR_MESSAGE,
|
|
"Can't bind on port: %d\n",
|
|
g_nPortNumber));
|
|
printf("Can't bind on port: %d\n",
|
|
g_nPortNumber);
|
|
goto exitpt;
|
|
}
|
|
|
|
rv = TRUE;
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
|
|
/*++
|
|
* Function:
|
|
* RClx_Done
|
|
* Description:
|
|
* Destruction of the module. Closes the listening socket and
|
|
* winsocket library
|
|
* Called by:
|
|
* tclient.c:InitDone
|
|
--*/
|
|
VOID RClx_Done(VOID)
|
|
{
|
|
PRCLXCONTEXT pRClxIter;
|
|
|
|
// g_pRClxList cleanup
|
|
pRClxIter = g_pRClxList;
|
|
while (pRClxIter)
|
|
{
|
|
RClx_EndRecv(pRClxIter);
|
|
pRClxIter = pRClxIter->pNext;
|
|
}
|
|
|
|
if (g_hSrvSocket)
|
|
closesocket(g_hSrvSocket);
|
|
WSACleanup();
|
|
}
|
|
|
|
|
|
/*++
|
|
* Function:
|
|
* RClx_Listen
|
|
* Description:
|
|
* Creates a socket on wich to listen for incoming connections from
|
|
* clxtshar.dll. Selects the feedback window/thread as a dispatcher
|
|
* of FD_ACCEPT events, i.e RClx_DispatchWSockEvent will be called
|
|
* when winsock event occured
|
|
* Arguments:
|
|
* nPortNumber - port to listen to
|
|
* Return value:
|
|
* INVALID_SOCKET on error, or valid SOCKET of the listening port
|
|
* Called by:
|
|
* RClx_Init
|
|
--*/
|
|
SOCKET RClx_Listen(u_short nPortNumber)
|
|
{
|
|
struct sockaddr_in sin;
|
|
INT optval = 1;
|
|
SOCKET hSocket;
|
|
|
|
ASSERT(g_nPortNumber);
|
|
|
|
hSocket = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
if (hSocket == INVALID_SOCKET)
|
|
{
|
|
TRACE((ERROR_MESSAGE, "Can't create socket: %d\n", WSAGetLastError()));
|
|
printf("Can't create socket: %d\n", WSAGetLastError());
|
|
goto exitpt;
|
|
}
|
|
|
|
setsockopt(hSocket, // Don't linger on socket close
|
|
IPPROTO_TCP,
|
|
TCP_NODELAY,
|
|
(const char *)&optval,
|
|
sizeof(optval));
|
|
setsockopt(hSocket,
|
|
SOL_SOCKET,
|
|
SO_DONTLINGER,
|
|
(const char *)&optval,
|
|
sizeof(optval));
|
|
|
|
memset(&sin, 0, sizeof(sin));
|
|
sin.sin_family = PF_INET;
|
|
sin.sin_port = htons(nPortNumber);
|
|
sin.sin_addr.s_addr = INADDR_ANY;
|
|
|
|
if (bind(hSocket, (SOCKADDR *)&sin, sizeof(sin)) == SOCKET_ERROR)
|
|
{
|
|
TRACE((ERROR_MESSAGE, "Can't bind: %d\n", WSAGetLastError()));
|
|
printf("Can't bind: %d\n", WSAGetLastError());
|
|
closesocket(hSocket);
|
|
hSocket = INVALID_SOCKET;
|
|
goto exitpt;
|
|
}
|
|
if (listen(hSocket, 5) == SOCKET_ERROR)
|
|
{
|
|
TRACE((ERROR_MESSAGE, "Can't listen: %d\n", WSAGetLastError()));
|
|
printf("Can't listen: %d\n", WSAGetLastError());
|
|
closesocket(hSocket);
|
|
hSocket = INVALID_SOCKET;
|
|
goto exitpt;
|
|
}
|
|
|
|
while(!g_hWindow)
|
|
{
|
|
Sleep(500); // Window is not created yet. Wait !
|
|
}
|
|
if (WSAAsyncSelect(hSocket, g_hWindow, WM_WSOCK, FD_ACCEPT) ==
|
|
SOCKET_ERROR)
|
|
{
|
|
TRACE((ERROR_MESSAGE,
|
|
"Can't \"select\" FD_ACCEPT on listening socket: %d\n",
|
|
WSAGetLastError()));
|
|
printf("Can't \"select\" FD_ACCEPT on listening socket: %d\n",
|
|
WSAGetLastError());
|
|
RClx_CloseSocket(hSocket);
|
|
hSocket = INVALID_SOCKET;
|
|
goto exitpt;
|
|
}
|
|
|
|
exitpt:
|
|
return hSocket;
|
|
}
|
|
|
|
|
|
/*++
|
|
* Function:
|
|
* RClx_Accept
|
|
* Description:
|
|
* Accepts next connection from g_hSrvSocket
|
|
* Return value:
|
|
* INVALID_SOCKET on error, or valid SOCKET of the accepted connection
|
|
* Called by:
|
|
* RClx_DispatchWSockEvent upon FD_ACCEPT event
|
|
--*/
|
|
SOCKET RClx_Accept(VOID)
|
|
{
|
|
struct sockaddr_in sin;
|
|
INT addrlen;
|
|
SOCKET hClient;
|
|
|
|
ASSERT(g_hSrvSocket != INVALID_SOCKET);
|
|
|
|
addrlen = sizeof(sin);
|
|
|
|
if ((hClient = accept(g_hSrvSocket,
|
|
(struct sockaddr *)&sin,
|
|
&addrlen)) ==
|
|
INVALID_SOCKET)
|
|
{
|
|
if (WSAGetLastError() != WSAEWOULDBLOCK)
|
|
TRACE((ERROR_MESSAGE,
|
|
"Accept failed: %d\n",
|
|
WSAGetLastError()));
|
|
goto exitpt;
|
|
}
|
|
|
|
exitpt:
|
|
return hClient;
|
|
}
|
|
|
|
|
|
/*++
|
|
* Function:
|
|
* RClx_StartRecv
|
|
* Description:
|
|
* Allocates and initializes context for a connection
|
|
* Return value:
|
|
* PRCLXCONTEXT - pointer to valid RCLX context or NULL on error
|
|
* Called by:
|
|
* RClx_DispatchWSockEvent upon FD_ACCEPT event
|
|
--*/
|
|
PRCLXCONTEXT RClx_StartRecv(VOID)
|
|
{
|
|
PRCLXCONTEXT pContext;
|
|
|
|
pContext = malloc(sizeof(*pContext));
|
|
|
|
if (!pContext)
|
|
goto exitpt;
|
|
|
|
memset(pContext, 0, sizeof(*pContext));
|
|
|
|
exitpt:
|
|
return pContext;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* RClx_EndRecv
|
|
* Description:
|
|
* Frees all resources allocated within an RCLX context
|
|
* and the context itself.
|
|
* Becaus the RCLX context is kept in hClient member of CONNECTINFO
|
|
* structure, the caller of this function have to zero pCI->hClient
|
|
* Arguments:
|
|
* pContext - an RCLX context
|
|
* Called by:
|
|
* RClx_DispatchWSockEvent upon FD_ACCEPT event when fail to
|
|
* find waiting worker or
|
|
* scfuncs.c:_CloseConnectionInfo
|
|
--*/
|
|
VOID RClx_EndRecv(PRCLXCONTEXT pContext)
|
|
{
|
|
|
|
ASSERT(pContext);
|
|
|
|
if (pContext->pHead)
|
|
{
|
|
free(pContext->pHead);
|
|
pContext->pHead = NULL;
|
|
}
|
|
|
|
if (pContext->pTail)
|
|
{
|
|
free(pContext->pTail);
|
|
pContext->pTail = NULL;
|
|
}
|
|
|
|
if (pContext->hSocket && pContext->hSocket != INVALID_SOCKET)
|
|
{
|
|
RClx_CloseSocket(pContext->hSocket);
|
|
}
|
|
|
|
free(pContext);
|
|
|
|
}
|
|
|
|
|
|
/*++
|
|
* Function:
|
|
* RClx_Receive
|
|
* Description:
|
|
* RCLXCONTEXT contains a buffer for incoming message
|
|
* this function trys to receive it all. If the socket blocks
|
|
* the function exits with OK and next time FD_READ is received will
|
|
* be called again. If a whole message is received pContext->bRecvDone
|
|
* is set to TRUE. This is an indication that message arrived
|
|
* Arguments:
|
|
* pContext - RCLX context
|
|
* Return value:
|
|
* TRUE if everithing went OK. FALSE if socket must be closed
|
|
* Called by:
|
|
* RClx_DispatchWSockEvent upon FD_READ event
|
|
--*/
|
|
BOOL RClx_Receive(PRCLXCONTEXT pContext)
|
|
{
|
|
INT result = 0;
|
|
SOCKET hSocket;
|
|
|
|
ASSERT(pContext);
|
|
hSocket = pContext->hSocket;
|
|
ASSERT(hSocket != INVALID_SOCKET);
|
|
|
|
do {
|
|
if (!pContext->bPrologReceived)
|
|
{
|
|
// Receive the prolog
|
|
result = recv(hSocket,
|
|
((BYTE *)&(pContext->Prolog)) +
|
|
sizeof(pContext->Prolog) - pContext->nBytesToReceive,
|
|
pContext->nBytesToReceive,
|
|
0);
|
|
if (result != SOCKET_ERROR)
|
|
{
|
|
pContext->nBytesToReceive -= result;
|
|
if (!pContext->nBytesToReceive)
|
|
{
|
|
pContext->bPrologReceived = TRUE;
|
|
|
|
result = 1; // Hack, otherwise the loop can exit
|
|
|
|
pContext->nBytesToReceive = pContext->Prolog.HeadSize;
|
|
|
|
// Check if we have proper buffers allocated
|
|
alloc_retry:
|
|
if (pContext->Prolog.HeadSize)
|
|
if (!pContext->pHead)
|
|
pContext->pHead = malloc(pContext->Prolog.HeadSize);
|
|
|
|
else if (pContext->Prolog.HeadSize >
|
|
pContext->nHeadAllocated)
|
|
|
|
pContext->pHead = realloc(pContext->pHead,
|
|
pContext->Prolog.HeadSize);
|
|
|
|
if (pContext->Prolog.TailSize)
|
|
if (!pContext->pTail)
|
|
pContext->pTail = malloc(pContext->Prolog.TailSize);
|
|
|
|
else if (pContext->Prolog.TailSize >
|
|
pContext->nTailAllocated)
|
|
pContext->pTail = realloc(pContext->pTail,
|
|
pContext->Prolog.TailSize);
|
|
|
|
if ((pContext->Prolog.HeadSize && !pContext->pHead)
|
|
||
|
|
(pContext->Prolog.TailSize && !pContext->pTail))
|
|
{
|
|
TRACE((WARNING_MESSAGE,
|
|
"Can't (re)allocate memory. Sleep for a minute"));
|
|
Sleep(60000);
|
|
goto alloc_retry;
|
|
} else {
|
|
pContext->nHeadAllocated = pContext->Prolog.HeadSize;
|
|
pContext->nTailAllocated = pContext->Prolog.TailSize;
|
|
}
|
|
}
|
|
}
|
|
} else if (!pContext->bHeadReceived)
|
|
{
|
|
// Receive the message head
|
|
if (pContext->nBytesToReceive)
|
|
result = recv(hSocket,
|
|
((BYTE *)pContext->pHead) +
|
|
pContext->Prolog.HeadSize - pContext->nBytesToReceive,
|
|
pContext->nBytesToReceive,
|
|
0);
|
|
else
|
|
result = 0;
|
|
|
|
if (result != SOCKET_ERROR)
|
|
{
|
|
pContext->nBytesToReceive -= result;
|
|
if (!pContext->nBytesToReceive)
|
|
{
|
|
pContext->bHeadReceived = TRUE;
|
|
pContext->nBytesToReceive = pContext->Prolog.TailSize;
|
|
result = 1; // Hack, otherwise the loop can exit
|
|
}
|
|
}
|
|
} else {
|
|
// Receive the message tail (actual info)
|
|
if (pContext->nBytesToReceive)
|
|
result = recv(hSocket,
|
|
((BYTE *)pContext->pTail) +
|
|
pContext->Prolog.TailSize - pContext->nBytesToReceive,
|
|
pContext->nBytesToReceive,
|
|
0);
|
|
else
|
|
result = 0;
|
|
|
|
if (result != SOCKET_ERROR)
|
|
{
|
|
pContext->nBytesToReceive -= result;
|
|
if (!pContext->nBytesToReceive)
|
|
{
|
|
// Cool, we have the whole message
|
|
pContext->bRecvDone = TRUE;
|
|
pContext->bPrologReceived = FALSE;
|
|
pContext->bHeadReceived = FALSE;
|
|
pContext->nBytesToReceive = sizeof(pContext->Prolog);
|
|
result = 1; // Hack, otherwise the loop can exit
|
|
}
|
|
}
|
|
}
|
|
|
|
} while (result != 0 && !pContext->bRecvDone &&
|
|
result != SOCKET_ERROR);
|
|
|
|
// At this point the message is not 100%
|
|
// received
|
|
// Only pContext->bRecvDone indicates this
|
|
|
|
// return FALSE if error is occured
|
|
|
|
if (!result)
|
|
return FALSE; // connection was gracefully closed
|
|
|
|
if (result == SOCKET_ERROR)
|
|
{
|
|
if (WSAGetLastError() == WSAEWOULDBLOCK)
|
|
return TRUE; // the call will block, but ok
|
|
else
|
|
return FALSE; // other SOCKET_ERROR
|
|
}
|
|
|
|
return TRUE; // it is ok
|
|
}
|
|
|
|
VOID
|
|
_RClx_Bitmap(PRCLXCONTEXT pContext)
|
|
{
|
|
WCHAR wszFeed[MAX_STRING_LENGTH];
|
|
PBMPFEEDBACK pBmpFeed;
|
|
PRCLXBITMAPFEED pRClxFeed = pContext->pHead;
|
|
|
|
// If BitmapInfo is empty (glyph) it's not sent
|
|
// Hence the HeadSize is smaller
|
|
if (pContext->Prolog.HeadSize > sizeof(*pRClxFeed))
|
|
{
|
|
TRACE((WARNING_MESSAGE,
|
|
"BitmapOut: Received header is larger than expected\n"));
|
|
ASSERT(0);
|
|
goto exitpt;
|
|
}
|
|
|
|
if (pContext->Prolog.TailSize != pRClxFeed->bmpsize)
|
|
{
|
|
TRACE((WARNING_MESSAGE,
|
|
"BitmapOut: Received bits are not equal to expected."
|
|
"Expect:%d, read:%d\n",
|
|
pRClxFeed->bmpsize, pContext->Prolog.TailSize));
|
|
ASSERT(0);
|
|
goto exitpt;
|
|
}
|
|
|
|
pBmpFeed = _alloca(sizeof(*pBmpFeed) + pRClxFeed->bmisize + pRClxFeed->bmpsize);
|
|
if (!pBmpFeed)
|
|
{
|
|
TRACE((WARNING_MESSAGE,
|
|
"BitmapOut:alloca failed to allocate 0x%x bytes\n",
|
|
sizeof(*pBmpFeed) + pRClxFeed->bmpsize));
|
|
goto exitpt;
|
|
}
|
|
|
|
pBmpFeed->lProcessId = pContext->hSocket;
|
|
pBmpFeed->bmpsize = pRClxFeed->bmpsize;
|
|
pBmpFeed->bmiSize = pRClxFeed->bmisize;
|
|
pBmpFeed->xSize = pRClxFeed->xSize;
|
|
pBmpFeed->ySize = pRClxFeed->ySize;
|
|
|
|
// Check and copy bitmapinfo
|
|
if (pBmpFeed->bmiSize > sizeof(pBmpFeed->BitmapInfo))
|
|
{
|
|
TRACE((WARNING_MESSAGE,
|
|
"BitmapOut:BITMAPINFO is more than expected. Rejecting\n"));
|
|
goto exitpt;
|
|
}
|
|
memcpy(&(pBmpFeed->BitmapInfo),
|
|
&(pRClxFeed->BitmapInfo),
|
|
pBmpFeed->bmiSize);
|
|
|
|
// Copy the bits after the structure
|
|
memcpy(((BYTE *)(&pBmpFeed->BitmapInfo)) + pBmpFeed->bmiSize,
|
|
pContext->pTail,
|
|
pContext->Prolog.TailSize);
|
|
|
|
// Convert the glyph
|
|
if (!Glyph2String(pBmpFeed, wszFeed, sizeof(wszFeed)/sizeof(WCHAR)))
|
|
{
|
|
goto exitpt;
|
|
}
|
|
|
|
_CheckForWaitingWorker(wszFeed, (DWORD)(pContext->hSocket));
|
|
|
|
exitpt:
|
|
;
|
|
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* RClx_MsgReceived
|
|
* Description:
|
|
* Dispatches a received message. Converts it in proper internal
|
|
* format and passes it to some queues.c function to find a waiting
|
|
* worker
|
|
* The message is in the RCLX context.
|
|
* Arguments:
|
|
* pContext - RCLX context
|
|
* Called by:
|
|
* RClx_DispatchWSockEvent upon FD_READ event
|
|
--*/
|
|
VOID RClx_MsgReceived(PRCLXCONTEXT pContext)
|
|
{
|
|
UINT32 *puSessionID;
|
|
|
|
ASSERT(pContext);
|
|
ASSERT(pContext->pOwner);
|
|
|
|
switch(pContext->Prolog.FeedType)
|
|
{
|
|
case FEED_CONNECT:
|
|
_CheckForWorkerWaitingConnect((HWND)pContext, pContext->hSocket);
|
|
break;
|
|
case FEED_LOGON:
|
|
puSessionID = (UINT *)pContext->pHead;
|
|
|
|
ASSERT(puSessionID);
|
|
_SetSessionID(pContext->hSocket, *puSessionID);
|
|
break;
|
|
case FEED_DISCONNECT:
|
|
_SetClientDead(pContext->hSocket);
|
|
_CheckForWorkerWaitingDisconnect(pContext->hSocket);
|
|
_CancelWaitingWorker(pContext->hSocket);
|
|
break;
|
|
case FEED_BITMAP:
|
|
_RClx_Bitmap(pContext);
|
|
break;
|
|
case FEED_TEXTOUT:
|
|
//TRACE((WARNING_MESSAGE, "TEXTOUT order is not implemented\n"));
|
|
// pContext->pHead - unused
|
|
_CheckForWaitingWorker(
|
|
(LPCWSTR)(pContext->pTail),
|
|
(DWORD)(pContext->hSocket));
|
|
break;
|
|
case FEED_TEXTOUTA:
|
|
TRACE((WARNING_MESSAGE, "TEXTOUTA order not implemented\n"));
|
|
break;
|
|
case FEED_CLIPBOARD:
|
|
{
|
|
PRCLXCLIPBOARDFEED pRClxClipFeed = pContext->pHead;
|
|
|
|
_CheckForWorkerWaitingClipboard(
|
|
pContext->pOwner,
|
|
pRClxClipFeed->uiFormat,
|
|
pRClxClipFeed->nClipBoardSize,
|
|
pContext->pTail,
|
|
(DWORD)(pContext->hSocket));
|
|
}
|
|
break;
|
|
case FEED_CLIENTINFO:
|
|
ASSERT(0); // Shouldn't appear here
|
|
break;
|
|
case FEED_WILLCALLAGAIN:
|
|
TRACE((INFO_MESSAGE, "WILLCALLAGAIN received, disconnecting the client\n"));
|
|
ASSERT(pContext->pOwner);
|
|
|
|
EnterCriticalSection(g_lpcsGuardWaitQueue);
|
|
pContext->pOwner->bWillCallAgain = TRUE;
|
|
pContext->pOwner->dead = TRUE;
|
|
_CancelWaitingWorker(pContext->hSocket);
|
|
pContext->pOwner->hClient = NULL;
|
|
LeaveCriticalSection(g_lpcsGuardWaitQueue);
|
|
|
|
_RClx_RemoveContextFromGlobalQueue(pContext);
|
|
RClx_EndRecv(pContext);
|
|
break;
|
|
case FEED_DATA:
|
|
{
|
|
PCONNECTINFO pCI;
|
|
PRCLXDATA pRClxData;
|
|
PRCLXDATACHAIN pNewEntry;
|
|
PWAIT4STRING pWait;
|
|
|
|
TRACE((ALIVE_MESSAGE, "RClx data arrived\n"));
|
|
|
|
pCI = pContext->pOwner;
|
|
ASSERT(pCI);
|
|
pRClxData = (PRCLXDATA)pContext->pHead;
|
|
ASSERT(pRClxData);
|
|
ASSERT(pRClxData->uiSize + sizeof(*pRClxData) ==
|
|
(pContext->Prolog.HeadSize));
|
|
|
|
pWait = _RetrieveFromWaitQByOwner(pCI);
|
|
|
|
pNewEntry = malloc(sizeof(*pNewEntry) + pRClxData->uiSize);
|
|
|
|
if (!pNewEntry)
|
|
{
|
|
// trash out the received data
|
|
TRACE((WARNING_MESSAGE,
|
|
"Can't allocate %d bytes for RCLXDATACHAIN\n",
|
|
pContext->Prolog.HeadSize));
|
|
break;
|
|
}
|
|
|
|
pNewEntry->uiOffset = 0;
|
|
pNewEntry->pNext = NULL;
|
|
memcpy(&pNewEntry->RClxData,
|
|
pRClxData,
|
|
pContext->Prolog.HeadSize);
|
|
|
|
EnterCriticalSection(g_lpcsGuardWaitQueue);
|
|
if (!pCI->pRClxDataChain)
|
|
pCI->pRClxDataChain = pCI->pRClxLastDataChain = pNewEntry;
|
|
else
|
|
{
|
|
ASSERT(pCI->pRClxLastDataChain);
|
|
pCI->pRClxLastDataChain->pNext = pNewEntry;
|
|
pCI->pRClxLastDataChain = pNewEntry;
|
|
}
|
|
|
|
// signal the worker
|
|
if (pWait &&
|
|
pWait->WaitType == WAIT_DATA)
|
|
SetEvent(pWait->evWait);
|
|
else
|
|
TRACE((WARNING_MESSAGE, "no event to signal\n"));
|
|
|
|
LeaveCriticalSection(g_lpcsGuardWaitQueue);
|
|
|
|
break;
|
|
}
|
|
default:
|
|
ASSERT(0);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*++
|
|
* Function:
|
|
* RClx_SendBuffer
|
|
* Description:
|
|
* Sends a buffer thru socket. The socket must be BLOCKING
|
|
* so, all the buffer is send after this function exits
|
|
* Arguments:
|
|
* hSocket - the socket
|
|
* pBuffer - the buffer
|
|
* nSize - buffer size
|
|
* Return value:
|
|
* TRUE on success, FALSE if the connection failed
|
|
* Called by:
|
|
* RClx_SendMessage, RClx_SendConnectInfo
|
|
--*/
|
|
BOOL RClx_SendBuffer(SOCKET hSocket, PVOID pBuffer, UINT nSize)
|
|
{
|
|
INT result = 0;
|
|
UINT nBytesToSend = nSize;
|
|
|
|
ASSERT(hSocket != INVALID_SOCKET);
|
|
ASSERT(pBuffer);
|
|
|
|
if (!nSize)
|
|
goto exitpt;
|
|
|
|
do {
|
|
result = send(hSocket, pBuffer, nBytesToSend, 0);
|
|
|
|
if (result != SOCKET_ERROR)
|
|
{
|
|
nBytesToSend -= result;
|
|
(BYTE *)pBuffer += result;
|
|
} else
|
|
if (WSAGetLastError() == WSAEWOULDBLOCK)
|
|
{
|
|
// The socket is blocked, wait on select until it's writable
|
|
FD_SET fd;
|
|
|
|
FD_ZERO(&fd);
|
|
FD_SET(hSocket, &fd);
|
|
|
|
result = select(-1, NULL, &fd, NULL, NULL);
|
|
}
|
|
} while (result != SOCKET_ERROR && nBytesToSend);
|
|
|
|
exitpt:
|
|
return (result != SOCKET_ERROR);
|
|
}
|
|
|
|
|
|
/*++
|
|
* Function:
|
|
* RClx_SendMessage
|
|
* Description:
|
|
* Sends an window message to the client
|
|
* Arguments:
|
|
* pContext - RCLX context
|
|
* uiMessage - message Id
|
|
* wParam - word parameter
|
|
* lParam - long parameter
|
|
* Return value:
|
|
* TRUE on success
|
|
* Called by:
|
|
* scfuncs.c:SCSenddata
|
|
--*/
|
|
BOOL RClx_SendMessage(PRCLXCONTEXT pContext,
|
|
UINT uiMessage,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
RCLXMSG ClxMsg;
|
|
RCLXREQPROLOG ReqProlog;
|
|
SOCKET hSocket;
|
|
BOOL rv = TRUE;
|
|
|
|
ASSERT(pContext);
|
|
hSocket = pContext->hSocket;
|
|
ASSERT(hSocket != INVALID_SOCKET);
|
|
|
|
ReqProlog.ReqType = REQ_MESSAGE;
|
|
ReqProlog.ReqSize = sizeof(ClxMsg);
|
|
ClxMsg.message = uiMessage;
|
|
ClxMsg.wParam = (UINT32)wParam;
|
|
ClxMsg.lParam = (UINT32)lParam;
|
|
|
|
// Send the request prolog
|
|
rv = RClx_SendBuffer(pContext->hSocket, &ReqProlog, sizeof(ReqProlog));
|
|
if (!rv)
|
|
{
|
|
TRACE((ERROR_MESSAGE, "Can't send: %d\n", WSAGetLastError()));
|
|
goto exitpt;
|
|
}
|
|
|
|
// Try to send the whole message
|
|
rv = RClx_SendBuffer(pContext->hSocket, &ClxMsg, sizeof(ClxMsg));
|
|
if (!rv)
|
|
{
|
|
TRACE((ERROR_MESSAGE, "Can't send: %d\n", WSAGetLastError()));
|
|
goto exitpt;
|
|
}
|
|
|
|
if (strstr(pContext->pOwner->szClientType, "WIN16") != NULL)
|
|
Sleep(100); // Don't send very fast to WIN16
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
|
|
/*++
|
|
* Function:
|
|
* RClx_SendConnectInfo
|
|
* Description:
|
|
* Sends the information to the client about Hydra server to connect to
|
|
* like, server name, resolution etc
|
|
* Arguments:
|
|
* pContext - RCLX context
|
|
* xRes, yRes - resolution
|
|
* ConnectionFlags -
|
|
* - the "client/hydra server" connection
|
|
* will be compressed
|
|
* -
|
|
* the bitmaps received by the client will be saved
|
|
* on the disc
|
|
* Return value:
|
|
* TRUE on success
|
|
* Called by:
|
|
* scfuncs.c:SCConnect
|
|
--*/
|
|
BOOL RClx_SendConnectInfo(PRCLXCONTEXT pContext,
|
|
LPCWSTR wszHydraServer,
|
|
INT xRes,
|
|
INT yRes,
|
|
INT ConnectionFlags)
|
|
{
|
|
RCLXREQPROLOG ReqProlog;
|
|
SOCKET hSocket;
|
|
RCLXCONNECTINFO ClxInfo;
|
|
BOOL rv;
|
|
|
|
ASSERT(pContext);
|
|
ASSERT(pContext->pOwner);
|
|
|
|
hSocket = pContext->hSocket;
|
|
ASSERT(hSocket != INVALID_SOCKET);
|
|
|
|
ReqProlog.ReqType = REQ_CONNECTINFO;
|
|
ReqProlog.ReqSize = sizeof(ClxInfo);
|
|
|
|
ClxInfo.YourID = pContext->pOwner->dwThreadId;
|
|
ClxInfo.xResolution = xRes;
|
|
ClxInfo.yResolution = yRes;
|
|
ClxInfo.bLowSpeed = (ConnectionFlags & TSFLAG_COMPRESSION)?TRUE:FALSE;
|
|
ClxInfo.bPersistentCache = (ConnectionFlags & TSFLAG_BITMAPCACHE)?TRUE:FALSE;
|
|
WideCharToMultiByte(
|
|
CP_ACP,
|
|
0,
|
|
wszHydraServer,
|
|
-1,
|
|
ClxInfo.szHydraServer,
|
|
sizeof(ClxInfo.szHydraServer),
|
|
NULL, NULL);
|
|
|
|
// Send the request prolog
|
|
rv = RClx_SendBuffer(pContext->hSocket, &ReqProlog, sizeof(ReqProlog));
|
|
if (!rv)
|
|
{
|
|
TRACE((ERROR_MESSAGE, "Can't send: %d\n", WSAGetLastError()));
|
|
goto exitpt;
|
|
}
|
|
|
|
// Try to send the whole message
|
|
rv = RClx_SendBuffer(pContext->hSocket, &ClxInfo, sizeof(ClxInfo));
|
|
if (!rv)
|
|
{
|
|
TRACE((ERROR_MESSAGE, "Can't send: %d\n", WSAGetLastError()));
|
|
goto exitpt;
|
|
}
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* RClx_SendClipboard
|
|
* Description:
|
|
* Sends a new clipboard content for the client machine
|
|
* Arguments:
|
|
* pContext - RCLX context
|
|
* pClipboard - clipboard content
|
|
* nDataLength - data length
|
|
* uiFormat - the clipboard format
|
|
* Return value:
|
|
* TRUE on success
|
|
* Called by:
|
|
* scfuncs.c:SCClipboard
|
|
--*/
|
|
BOOL RClx_SendClipboard(
|
|
PRCLXCONTEXT pContext,
|
|
PVOID pClipboard,
|
|
UINT nDataLength,
|
|
UINT uiFormat)
|
|
{
|
|
RCLXREQPROLOG ReqProlog;
|
|
SOCKET hSocket;
|
|
RCLXCLIPBOARD SetClipReq;
|
|
BOOL rv = FALSE;
|
|
|
|
ASSERT(pContext);
|
|
ASSERT((pClipboard && nDataLength) || (!pClipboard && !nDataLength));
|
|
|
|
hSocket = pContext->hSocket;
|
|
ASSERT(hSocket != INVALID_SOCKET);
|
|
|
|
ReqProlog.ReqType = REQ_SETCLIPBOARD;
|
|
ReqProlog.ReqSize = sizeof(SetClipReq) + nDataLength;
|
|
|
|
SetClipReq.uiFormat = uiFormat;
|
|
|
|
// Send the request prolog
|
|
rv = RClx_SendBuffer(pContext->hSocket, &ReqProlog, sizeof(ReqProlog));
|
|
if (!rv)
|
|
{
|
|
TRACE((ERROR_MESSAGE, "Can't send: %d\n", WSAGetLastError()));
|
|
goto exitpt;
|
|
}
|
|
|
|
rv = RClx_SendBuffer(pContext->hSocket, &SetClipReq, sizeof(SetClipReq));
|
|
if (!rv)
|
|
{
|
|
TRACE((ERROR_MESSAGE, "Can't send: %d\n", WSAGetLastError()));
|
|
goto exitpt;
|
|
}
|
|
|
|
// Send the data after all (if any)
|
|
if (pClipboard)
|
|
{
|
|
rv = RClx_SendBuffer(pContext->hSocket, pClipboard, nDataLength);
|
|
if (!rv)
|
|
{
|
|
TRACE((ERROR_MESSAGE, "Can't send: %d\n", WSAGetLastError()));
|
|
goto exitpt;
|
|
}
|
|
}
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* RClx_SendClipboardRequest
|
|
* Description:
|
|
* Request the clipboard content from the client machine
|
|
* Arguments:
|
|
* pContext - RCLX context
|
|
* uiFormat - the desired clipboard format
|
|
* Return value:
|
|
* TRUE on success
|
|
* Called by:
|
|
* scfuncs.c:SCClipboard
|
|
--*/
|
|
BOOL RClx_SendClipboardRequest(
|
|
PRCLXCONTEXT pContext,
|
|
UINT uiFormat)
|
|
{
|
|
RCLXREQPROLOG ReqProlog;
|
|
SOCKET hSocket;
|
|
RCLXCLIPBOARD GetClipReq;
|
|
BOOL rv = FALSE;
|
|
|
|
ASSERT(pContext);
|
|
ASSERT(pContext->pOwner);
|
|
hSocket = pContext->hSocket;
|
|
ASSERT(hSocket != INVALID_SOCKET);
|
|
|
|
pContext->pOwner->bRClxClipboardReceived = FALSE;
|
|
|
|
ReqProlog.ReqType = REQ_GETCLIPBOARD;
|
|
ReqProlog.ReqSize = sizeof(GetClipReq);
|
|
|
|
GetClipReq.uiFormat = uiFormat;
|
|
|
|
// Send the request prolog
|
|
rv = RClx_SendBuffer(pContext->hSocket, &ReqProlog, sizeof(ReqProlog));
|
|
if (!rv)
|
|
{
|
|
TRACE((ERROR_MESSAGE, "Can't send: %d\n", WSAGetLastError()));
|
|
goto exitpt;
|
|
}
|
|
|
|
rv = RClx_SendBuffer(pContext->hSocket, &GetClipReq, sizeof(GetClipReq));
|
|
if (!rv)
|
|
{
|
|
TRACE((ERROR_MESSAGE, "Can't send: %d\n", WSAGetLastError()));
|
|
goto exitpt;
|
|
}
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* RClx_CloseSocket
|
|
* Description:
|
|
* Gracefully closes a socket
|
|
* Arguments:
|
|
* hSocket - socket for closing
|
|
* Called by:
|
|
* RClx_EndRecv
|
|
* RClx_DispatchWSockEvent
|
|
* RClx_Listen
|
|
--*/
|
|
VOID RClx_CloseSocket(SOCKET hSocket)
|
|
{
|
|
BYTE tBuf[128];
|
|
INT recvresult;
|
|
|
|
ASSERT(hSocket != INVALID_SOCKET);
|
|
|
|
WSAAsyncSelect(hSocket, g_hWindow, 0, 0);
|
|
|
|
shutdown(hSocket, SD_SEND);
|
|
do {
|
|
recvresult = recv(hSocket, tBuf, sizeof(tBuf), 0);
|
|
} while (recvresult && recvresult != SOCKET_ERROR);
|
|
|
|
closesocket(hSocket);
|
|
}
|
|
|
|
//
|
|
// search & destroy from g_pRClxList
|
|
//
|
|
VOID _RClx_RemoveContextFromGlobalQueue(PRCLXCONTEXT pContext)
|
|
{
|
|
PRCLXCONTEXT pRClxIter = g_pRClxList;
|
|
PRCLXCONTEXT pRClxPrev = NULL;
|
|
|
|
while (pRClxIter && pRClxIter != pContext)
|
|
{
|
|
pRClxPrev = pRClxIter;
|
|
pRClxIter = pRClxIter->pNext;
|
|
}
|
|
|
|
if (!pRClxIter)
|
|
goto exitpt;
|
|
|
|
if (!pRClxPrev)
|
|
g_pRClxList = pContext->pNext;
|
|
else
|
|
pRClxPrev->pNext = pContext->pNext;
|
|
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
/*+++
|
|
* Function:
|
|
* _AddRClxContextToClientConnection
|
|
* Description:
|
|
* FEED_CLIENTINFO is received, now find a proper thread to assign the
|
|
* RCLX context
|
|
* Argument:
|
|
* pRClxCtx - RCLX context
|
|
* Called by:
|
|
* RClx_DispatchWSockEvent
|
|
*
|
|
--*/
|
|
VOID
|
|
_AddRClxContextToClientConnection(PRCLXCONTEXT pRClxCtx)
|
|
{
|
|
PRCLXCLIENTINFOFEED pClntInfo;
|
|
PCONNECTINFO pCI;
|
|
|
|
ASSERT(pRClxCtx);
|
|
ASSERT(pRClxCtx->Prolog.FeedType == FEED_CLIENTINFO);
|
|
|
|
EnterCriticalSection(g_lpcsGuardWaitQueue);
|
|
|
|
pClntInfo = (PRCLXCLIENTINFOFEED)(pRClxCtx->pHead);
|
|
|
|
ASSERT(pClntInfo);
|
|
|
|
TRACE((ALIVE_MESSAGE,
|
|
"CLIENTINFO received, recon=%d info: %s\n",
|
|
pClntInfo->nReconnectAct,
|
|
pClntInfo->szClientInfo));
|
|
|
|
// If nReconnectAct is non zero then lookup for thread which waits
|
|
// reconnection
|
|
if (pClntInfo->nReconnectAct)
|
|
{
|
|
ASSERT(pClntInfo->ReconnectID);
|
|
// Identify by dwProcessId
|
|
pCI = _CheckForWorkerWaitingReconnectAndSetNewId(
|
|
(HWND)pRClxCtx,
|
|
(DWORD)pClntInfo->ReconnectID,
|
|
(DWORD)pRClxCtx->hSocket);
|
|
|
|
if (!pCI)
|
|
{
|
|
TRACE((WARNING_MESSAGE,
|
|
"Nobody is waiting for REconnect."
|
|
" Disconnecting the socket\n"));
|
|
_RClx_RemoveContextFromGlobalQueue(pRClxCtx);
|
|
RClx_EndRecv(pRClxCtx);
|
|
} else {
|
|
_snprintf(pCI->szClientType, sizeof(pCI->szClientType),
|
|
"%s",
|
|
pClntInfo->szClientInfo);
|
|
pRClxCtx->pOwner = pCI;
|
|
}
|
|
goto exitpt;
|
|
}
|
|
|
|
// Get the first waiting for connect from remote clx
|
|
// accepted socket goes into dwProcessId
|
|
// pRClxCtx goes int hClient member of CONNECTINFO
|
|
// structure. In order to determine the different IDs
|
|
// (one is process Id, and second one is socket)
|
|
// a member bRClxMode is used
|
|
pCI = _CheckForWorkerWaitingConnectAndSetId((HWND)pRClxCtx,
|
|
(DWORD)pRClxCtx->hSocket);
|
|
if (!pCI)
|
|
{
|
|
TRACE((WARNING_MESSAGE,
|
|
"Nobody is waiting for connect."
|
|
" Disconnecting the socket\n"));
|
|
_RClx_RemoveContextFromGlobalQueue(pRClxCtx);
|
|
RClx_EndRecv(pRClxCtx);
|
|
goto exitpt;
|
|
} else {
|
|
_snprintf(pCI->szClientType, sizeof(pCI->szClientType),
|
|
"%s",
|
|
pClntInfo->szClientInfo);
|
|
pRClxCtx->pOwner = pCI;
|
|
}
|
|
|
|
exitpt:
|
|
|
|
LeaveCriticalSection(g_lpcsGuardWaitQueue);
|
|
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* RClx_DispatchWSockEvent
|
|
* Description:
|
|
* Dispatches winsock events: FD_ACCEPT, FD_READ and FD_CLOSE
|
|
* The event is passed by the feedback window/thread
|
|
* Arguments:
|
|
* hSocket - the socket for which the event is
|
|
* lEvent - actualy lParam of the winsock message
|
|
* contains the event and error (if any)
|
|
* Called by:
|
|
* tclient.c:_FeedbackWndProc
|
|
--*/
|
|
VOID RClx_DispatchWSockEvent(SOCKET hSocket, LPARAM lEvent)
|
|
{
|
|
SOCKET hClient;
|
|
PCONNECTINFO pCI;
|
|
PRCLXCONTEXT pRClxCtx;
|
|
|
|
if (WSAGETSELECTERROR(lEvent))
|
|
{
|
|
TRACE((ERROR_MESSAGE,
|
|
"Select error: %d\n",
|
|
WSAGETSELECTERROR(lEvent)));
|
|
goto perform_close; // On error
|
|
// behave like the socket is closed
|
|
}
|
|
|
|
switch(WSAGETSELECTEVENT(lEvent))
|
|
{
|
|
case FD_ACCEPT:
|
|
// Accept all incoming sockets
|
|
// look in our WaitQ for a free worker waiting connection
|
|
// if there's no free worker disconnect the socket
|
|
// so the remote side knows that connection is unwanted
|
|
|
|
ASSERT(hSocket == g_hSrvSocket);
|
|
// Accept all incoming connections
|
|
while ((hClient = RClx_Accept()) != INVALID_SOCKET)
|
|
{
|
|
|
|
// Put this socket in Async receive mode
|
|
// The first operation is send, so we well not lose
|
|
// FD_READ message
|
|
if (WSAAsyncSelect(hClient,
|
|
g_hWindow,
|
|
WM_WSOCK,
|
|
FD_READ|FD_CLOSE) == SOCKET_ERROR)
|
|
{
|
|
TRACE((ERROR_MESSAGE,
|
|
"Can't \"select\" client socket: %d."
|
|
"Disconnecting the socket\n",
|
|
WSAGetLastError()));
|
|
RClx_CloseSocket(hClient);
|
|
goto exitpt;
|
|
}
|
|
|
|
// Allocate our context
|
|
pRClxCtx = RClx_StartRecv();
|
|
if (!pRClxCtx)
|
|
{
|
|
TRACE((WARNING_MESSAGE,
|
|
"Can't allocate memmory. Disconnecting the socket\n"));
|
|
RClx_CloseSocket(hClient);
|
|
goto exitpt;
|
|
}
|
|
pRClxCtx->hSocket = hClient;
|
|
pRClxCtx->nBytesToReceive = sizeof(pRClxCtx->Prolog);
|
|
|
|
// Add this context to the list of connections which didn't
|
|
// receive their FEED_CLIENTINFO yet
|
|
// we don't need crit sect, 'cause this is single thread op
|
|
pRClxCtx->pNext = g_pRClxList;
|
|
g_pRClxList = pRClxCtx;
|
|
PostMessage(g_hWindow, WM_WSOCK, hClient, FD_READ);
|
|
}
|
|
|
|
break;
|
|
|
|
case FD_READ:
|
|
// Check first if this comes from RClxList socket
|
|
pRClxCtx = g_pRClxList;
|
|
while (pRClxCtx && pRClxCtx->hSocket != hSocket)
|
|
pRClxCtx = pRClxCtx->pNext;
|
|
|
|
if (pRClxCtx)
|
|
{
|
|
RClx_Receive(pRClxCtx);
|
|
if (pRClxCtx->bRecvDone)
|
|
{
|
|
pRClxCtx->bRecvDone = FALSE;
|
|
if (pRClxCtx->Prolog.FeedType == FEED_CLIENTINFO)
|
|
{
|
|
|
|
_RClx_RemoveContextFromGlobalQueue(pRClxCtx);
|
|
|
|
pRClxCtx->pNext = NULL;
|
|
|
|
//
|
|
// Don't use pRClxCtx past this point
|
|
// this function could call RClx_EndRecv
|
|
//
|
|
_AddRClxContextToClientConnection(pRClxCtx);
|
|
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// What if the connection is closed here ?!
|
|
// Solution: use the same critical section as the queue manager
|
|
EnterCriticalSection(g_lpcsGuardWaitQueue);
|
|
pCI = _CheckIsAcceptable((DWORD)hSocket, TRUE);
|
|
|
|
if (pCI && pCI->hClient)
|
|
{
|
|
pRClxCtx = (PRCLXCONTEXT)pCI->hClient;
|
|
|
|
RClx_Receive(pRClxCtx);
|
|
if (pRClxCtx->bRecvDone)
|
|
{
|
|
pRClxCtx->bRecvDone = FALSE;
|
|
RClx_MsgReceived(pRClxCtx);
|
|
}
|
|
}
|
|
LeaveCriticalSection(g_lpcsGuardWaitQueue);
|
|
|
|
break;
|
|
|
|
case FD_CLOSE:
|
|
perform_close:
|
|
// The socket closes, "last call" for the worker
|
|
pCI = _CheckIsAcceptable((DWORD)hSocket, TRUE);
|
|
|
|
if (pCI)
|
|
{
|
|
_SetClientDead((DWORD)hSocket);
|
|
_CheckForWorkerWaitingDisconnect((DWORD)hSocket);
|
|
_CancelWaitingWorker((DWORD)hSocket);
|
|
}
|
|
break;
|
|
default:
|
|
ASSERT(0);
|
|
}
|
|
|
|
exitpt:
|
|
;
|
|
}
|