22#include <winpr/assert.h>
23#include <freerdp/config.h>
30#include <winpr/synch.h>
31#include <winpr/thread.h>
32#include <winpr/stream.h>
33#include <winpr/sysinfo.h>
35#include <freerdp/freerdp.h>
36#include <freerdp/server/echo.h>
37#include <freerdp/channels/echo.h>
38#include <freerdp/channels/log.h>
40#define TAG CHANNELS_TAG("echo.server")
44 echo_server_context context;
62static UINT echo_server_open_channel(echo_server* echo)
65 HANDLE hEvent =
nullptr;
67 DWORD BytesReturned = 0;
68 PULONG pSessionId =
nullptr;
70 if (WTSQuerySessionInformationA(echo->context.vcm, WTS_CURRENT_SESSION, WTSSessionId,
71 (LPSTR*)&pSessionId, &BytesReturned) == FALSE)
73 WLog_ERR(TAG,
"WTSQuerySessionInformationA failed!");
74 return ERROR_INTERNAL_ERROR;
77 echo->SessionId = (DWORD)*pSessionId;
78 WTSFreeMemory(pSessionId);
79 hEvent = WTSVirtualChannelManagerGetEventHandle(echo->context.vcm);
80 StartTick = GetTickCount();
82 while (echo->echo_channel ==
nullptr)
84 if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
86 Error = GetLastError();
87 WLog_ERR(TAG,
"WaitForSingleObject failed with error %" PRIu32
"!", Error);
91 echo->echo_channel = WTSVirtualChannelOpenEx(echo->SessionId, ECHO_DVC_CHANNEL_NAME,
92 WTS_CHANNEL_OPTION_DYNAMIC);
94 if (echo->echo_channel)
99 channelId = WTSChannelGetIdByHandle(echo->echo_channel);
101 IFCALLRET(echo->context.ChannelIdAssigned, status, &echo->context, channelId);
104 WLog_ERR(TAG,
"context->ChannelIdAssigned failed!");
105 return ERROR_INTERNAL_ERROR;
111 Error = GetLastError();
113 if (Error == ERROR_NOT_FOUND)
116 if (GetTickCount() - StartTick > 5000)
120 return echo->echo_channel ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
123static DWORD WINAPI echo_server_thread_func(LPVOID arg)
126 void* buffer =
nullptr;
130 HANDLE ChannelEvent =
nullptr;
131 DWORD BytesReturned = 0;
132 echo_server* echo = (echo_server*)arg;
136 if ((error = echo_server_open_channel(echo)))
139 WLog_ERR(TAG,
"echo_server_open_channel failed with error %" PRIu32
"!", error);
140 IFCALLRET(echo->context.OpenResult, error2, &echo->context,
141 ECHO_SERVER_OPEN_RESULT_NOTSUPPORTED);
144 WLog_ERR(TAG,
"echo server's OpenResult callback failed with error %" PRIu32
"",
152 ChannelEvent =
nullptr;
154 if (WTSVirtualChannelQuery(echo->echo_channel, WTSVirtualEventHandle, &buffer,
155 &BytesReturned) == TRUE)
157 if (BytesReturned ==
sizeof(HANDLE))
158 ChannelEvent = *(HANDLE*)buffer;
160 WTSFreeMemory(buffer);
164 events[nCount++] = echo->stopEvent;
165 events[nCount++] = ChannelEvent;
171 status = WaitForMultipleObjects(nCount, events, FALSE, 100);
173 if (status == WAIT_FAILED)
175 error = GetLastError();
176 WLog_ERR(TAG,
"WaitForMultipleObjects failed with error %" PRIu32
"", error);
180 if (status == WAIT_OBJECT_0)
182 IFCALLRET(echo->context.OpenResult, error, &echo->context,
183 ECHO_SERVER_OPEN_RESULT_CLOSED);
186 WLog_ERR(TAG,
"OpenResult failed with error %" PRIu32
"!", error);
191 if (WTSVirtualChannelQuery(echo->echo_channel, WTSVirtualChannelReady, &buffer,
192 &BytesReturned) == FALSE)
194 IFCALLRET(echo->context.OpenResult, error, &echo->context,
195 ECHO_SERVER_OPEN_RESULT_ERROR);
198 WLog_ERR(TAG,
"OpenResult failed with error %" PRIu32
"!", error);
203 ready = *((BOOL*)buffer);
204 WTSFreeMemory(buffer);
208 IFCALLRET(echo->context.OpenResult, error, &echo->context, ECHO_SERVER_OPEN_RESULT_OK);
211 WLog_ERR(TAG,
"OpenResult failed with error %" PRIu32
"!", error);
217 s = Stream_New(
nullptr, 4096);
221 WLog_ERR(TAG,
"Stream_New failed!");
222 (void)WTSVirtualChannelClose(echo->echo_channel);
223 ExitThread(ERROR_NOT_ENOUGH_MEMORY);
224 return ERROR_NOT_ENOUGH_MEMORY;
229 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
231 if (status == WAIT_FAILED)
233 error = GetLastError();
234 WLog_ERR(TAG,
"WaitForMultipleObjects failed with error %" PRIu32
"", error);
238 if (status == WAIT_OBJECT_0)
241 Stream_ResetPosition(s);
242 if (!WTSVirtualChannelRead(echo->echo_channel, 0,
nullptr, 0, &BytesReturned))
244 WLog_ERR(TAG,
"WTSVirtualChannelRead failed!");
245 error = ERROR_INTERNAL_ERROR;
249 if (BytesReturned < 1)
252 if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
254 WLog_ERR(TAG,
"Stream_EnsureRemainingCapacity failed!");
255 error = CHANNEL_RC_NO_MEMORY;
259 if (WTSVirtualChannelRead(echo->echo_channel, 0, Stream_BufferAs(s,
char),
260 (ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
262 WLog_ERR(TAG,
"WTSVirtualChannelRead failed!");
263 error = ERROR_INTERNAL_ERROR;
267 IFCALLRET(echo->context.Response, error, &echo->context, Stream_Buffer(s), BytesReturned);
271 WLog_ERR(TAG,
"Response failed with error %" PRIu32
"!", error);
276 Stream_Free(s, TRUE);
277 (void)WTSVirtualChannelClose(echo->echo_channel);
278 echo->echo_channel =
nullptr;
281 if (error && echo->context.rdpcontext)
282 setChannelError(echo->context.rdpcontext, error,
283 "echo_server_thread_func reported an error");
294static UINT echo_server_open(echo_server_context* context)
296 echo_server* echo = (echo_server*)context;
298 if (echo->thread ==
nullptr)
300 if (!(echo->stopEvent = CreateEvent(
nullptr, TRUE, FALSE,
nullptr)))
302 WLog_ERR(TAG,
"CreateEvent failed!");
303 return ERROR_INTERNAL_ERROR;
307 CreateThread(
nullptr, 0, echo_server_thread_func, (
void*)echo, 0,
nullptr)))
309 WLog_ERR(TAG,
"CreateEvent failed!");
310 (void)CloseHandle(echo->stopEvent);
311 echo->stopEvent =
nullptr;
312 return ERROR_INTERNAL_ERROR;
316 return CHANNEL_RC_OK;
324static UINT echo_server_close(echo_server_context* context)
326 UINT error = CHANNEL_RC_OK;
327 echo_server* echo = (echo_server*)context;
331 (void)SetEvent(echo->stopEvent);
333 if (WaitForSingleObject(echo->thread, INFINITE) == WAIT_FAILED)
335 error = GetLastError();
336 WLog_ERR(TAG,
"WaitForSingleObject failed with error %" PRIu32
"", error);
340 (void)CloseHandle(echo->thread);
341 (void)CloseHandle(echo->stopEvent);
342 echo->thread =
nullptr;
343 echo->stopEvent =
nullptr;
349static BOOL echo_server_request(echo_server_context* context,
const BYTE* buffer, UINT32 length)
357 echo_server* echo = (echo_server*)context;
360 return WTSVirtualChannelWrite(echo->echo_channel, cnv.pv, length,
nullptr);
363echo_server_context* echo_server_context_new(HANDLE vcm)
365 echo_server* echo =
nullptr;
366 echo = (echo_server*)calloc(1,
sizeof(echo_server));
370 echo->context.vcm = vcm;
371 echo->context.Open = echo_server_open;
372 echo->context.Close = echo_server_close;
373 echo->context.Request = echo_server_request;
376 WLog_ERR(TAG,
"calloc failed!");
378 return (echo_server_context*)echo;
381void echo_server_context_free(echo_server_context* context)
383 echo_server* echo = (echo_server*)context;
384 echo_server_close(context);