|
|
/*++
* 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: ; }
|