22 #include <winpr/assert.h>
23 #include <freerdp/config.h>
29 #include <winpr/crt.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;
62 static UINT echo_server_open_channel(echo_server* echo)
67 DWORD BytesReturned = 0;
68 PULONG pSessionId = NULL;
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 == NULL)
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;
123 static DWORD WINAPI echo_server_thread_func(LPVOID arg)
130 HANDLE ChannelEvent = NULL;
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
"",
154 if (WTSVirtualChannelQuery(echo->echo_channel, WTSVirtualEventHandle, &buffer,
155 &BytesReturned) == TRUE)
157 if (BytesReturned ==
sizeof(HANDLE))
158 CopyMemory(&ChannelEvent, buffer,
sizeof(HANDLE));
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(NULL, 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_SetPosition(s, 0);
242 if (!WTSVirtualChannelRead(echo->echo_channel, 0, NULL, 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 = NULL;
281 if (error && echo->context.rdpcontext)
282 setChannelError(echo->context.rdpcontext, error,
283 "echo_server_thread_func reported an error");
294 static UINT echo_server_open(echo_server_context* context)
296 echo_server* echo = (echo_server*)context;
298 if (echo->thread == NULL)
300 if (!(echo->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
302 WLog_ERR(TAG,
"CreateEvent failed!");
303 return ERROR_INTERNAL_ERROR;
306 if (!(echo->thread = CreateThread(NULL, 0, echo_server_thread_func, (
void*)echo, 0, NULL)))
308 WLog_ERR(TAG,
"CreateEvent failed!");
309 (void)CloseHandle(echo->stopEvent);
310 echo->stopEvent = NULL;
311 return ERROR_INTERNAL_ERROR;
315 return CHANNEL_RC_OK;
323 static UINT echo_server_close(echo_server_context* context)
325 UINT error = CHANNEL_RC_OK;
326 echo_server* echo = (echo_server*)context;
330 (void)SetEvent(echo->stopEvent);
332 if (WaitForSingleObject(echo->thread, INFINITE) == WAIT_FAILED)
334 error = GetLastError();
335 WLog_ERR(TAG,
"WaitForSingleObject failed with error %" PRIu32
"", error);
339 (void)CloseHandle(echo->thread);
340 (void)CloseHandle(echo->stopEvent);
342 echo->stopEvent = NULL;
348 static BOOL echo_server_request(echo_server_context* context,
const BYTE* buffer, UINT32 length)
356 echo_server* echo = (echo_server*)context;
359 return WTSVirtualChannelWrite(echo->echo_channel, cnv.pv, length, NULL);
362 echo_server_context* echo_server_context_new(HANDLE vcm)
364 echo_server* echo = NULL;
365 echo = (echo_server*)calloc(1,
sizeof(echo_server));
369 echo->context.vcm = vcm;
370 echo->context.Open = echo_server_open;
371 echo->context.Close = echo_server_close;
372 echo->context.Request = echo_server_request;
375 WLog_ERR(TAG,
"calloc failed!");
377 return (echo_server_context*)echo;
380 void echo_server_context_free(echo_server_context* context)
382 echo_server* echo = (echo_server*)context;
383 echo_server_close(context);