FreeRDP
Loading...
Searching...
No Matches
server/echo_main.c
1
22#include <winpr/assert.h>
23#include <freerdp/config.h>
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28
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>
34
35#include <freerdp/freerdp.h>
36#include <freerdp/server/echo.h>
37#include <freerdp/channels/echo.h>
38#include <freerdp/channels/log.h>
39
40#define TAG CHANNELS_TAG("echo.server")
41
42typedef struct
43{
44 echo_server_context context;
45
46 BOOL opened;
47
48 HANDLE stopEvent;
49
50 HANDLE thread;
51 void* echo_channel;
52
53 DWORD SessionId;
54
55} echo_server;
56
62static UINT echo_server_open_channel(echo_server* echo)
63{
64 DWORD Error = 0;
65 HANDLE hEvent = NULL;
66 DWORD StartTick = 0;
67 DWORD BytesReturned = 0;
68 PULONG pSessionId = NULL;
69
70 if (WTSQuerySessionInformationA(echo->context.vcm, WTS_CURRENT_SESSION, WTSSessionId,
71 (LPSTR*)&pSessionId, &BytesReturned) == FALSE)
72 {
73 WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
74 return ERROR_INTERNAL_ERROR;
75 }
76
77 echo->SessionId = (DWORD)*pSessionId;
78 WTSFreeMemory(pSessionId);
79 hEvent = WTSVirtualChannelManagerGetEventHandle(echo->context.vcm);
80 StartTick = GetTickCount();
81
82 while (echo->echo_channel == NULL)
83 {
84 if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
85 {
86 Error = GetLastError();
87 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error);
88 return Error;
89 }
90
91 echo->echo_channel = WTSVirtualChannelOpenEx(echo->SessionId, ECHO_DVC_CHANNEL_NAME,
92 WTS_CHANNEL_OPTION_DYNAMIC);
93
94 if (echo->echo_channel)
95 {
96 UINT32 channelId = 0;
97 BOOL status = TRUE;
98
99 channelId = WTSChannelGetIdByHandle(echo->echo_channel);
100
101 IFCALLRET(echo->context.ChannelIdAssigned, status, &echo->context, channelId);
102 if (!status)
103 {
104 WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
105 return ERROR_INTERNAL_ERROR;
106 }
107
108 break;
109 }
110
111 Error = GetLastError();
112
113 if (Error == ERROR_NOT_FOUND)
114 break;
115
116 if (GetTickCount() - StartTick > 5000)
117 break;
118 }
119
120 return echo->echo_channel ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
121}
122
123static DWORD WINAPI echo_server_thread_func(LPVOID arg)
124{
125 wStream* s = NULL;
126 void* buffer = NULL;
127 DWORD nCount = 0;
128 HANDLE events[8];
129 BOOL ready = FALSE;
130 HANDLE ChannelEvent = NULL;
131 DWORD BytesReturned = 0;
132 echo_server* echo = (echo_server*)arg;
133 UINT error = 0;
134 DWORD status = 0;
135
136 if ((error = echo_server_open_channel(echo)))
137 {
138 UINT error2 = 0;
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);
142
143 if (error2)
144 WLog_ERR(TAG, "echo server's OpenResult callback failed with error %" PRIu32 "",
145 error2);
146
147 goto out;
148 }
149
150 buffer = NULL;
151 BytesReturned = 0;
152 ChannelEvent = NULL;
153
154 if (WTSVirtualChannelQuery(echo->echo_channel, WTSVirtualEventHandle, &buffer,
155 &BytesReturned) == TRUE)
156 {
157 if (BytesReturned == sizeof(HANDLE))
158 ChannelEvent = *(HANDLE*)buffer;
159
160 WTSFreeMemory(buffer);
161 }
162
163 nCount = 0;
164 events[nCount++] = echo->stopEvent;
165 events[nCount++] = ChannelEvent;
166
167 /* Wait for the client to confirm that the Graphics Pipeline dynamic channel is ready */
168
169 while (1)
170 {
171 status = WaitForMultipleObjects(nCount, events, FALSE, 100);
172
173 if (status == WAIT_FAILED)
174 {
175 error = GetLastError();
176 WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error);
177 break;
178 }
179
180 if (status == WAIT_OBJECT_0)
181 {
182 IFCALLRET(echo->context.OpenResult, error, &echo->context,
183 ECHO_SERVER_OPEN_RESULT_CLOSED);
184
185 if (error)
186 WLog_ERR(TAG, "OpenResult failed with error %" PRIu32 "!", error);
187
188 break;
189 }
190
191 if (WTSVirtualChannelQuery(echo->echo_channel, WTSVirtualChannelReady, &buffer,
192 &BytesReturned) == FALSE)
193 {
194 IFCALLRET(echo->context.OpenResult, error, &echo->context,
195 ECHO_SERVER_OPEN_RESULT_ERROR);
196
197 if (error)
198 WLog_ERR(TAG, "OpenResult failed with error %" PRIu32 "!", error);
199
200 break;
201 }
202
203 ready = *((BOOL*)buffer);
204 WTSFreeMemory(buffer);
205
206 if (ready)
207 {
208 IFCALLRET(echo->context.OpenResult, error, &echo->context, ECHO_SERVER_OPEN_RESULT_OK);
209
210 if (error)
211 WLog_ERR(TAG, "OpenResult failed with error %" PRIu32 "!", error);
212
213 break;
214 }
215 }
216
217 s = Stream_New(NULL, 4096);
218
219 if (!s)
220 {
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;
225 }
226
227 while (ready)
228 {
229 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
230
231 if (status == WAIT_FAILED)
232 {
233 error = GetLastError();
234 WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error);
235 break;
236 }
237
238 if (status == WAIT_OBJECT_0)
239 break;
240
241 Stream_SetPosition(s, 0);
242 if (!WTSVirtualChannelRead(echo->echo_channel, 0, NULL, 0, &BytesReturned))
243 {
244 WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
245 error = ERROR_INTERNAL_ERROR;
246 break;
247 }
248
249 if (BytesReturned < 1)
250 continue;
251
252 if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
253 {
254 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
255 error = CHANNEL_RC_NO_MEMORY;
256 break;
257 }
258
259 if (WTSVirtualChannelRead(echo->echo_channel, 0, Stream_BufferAs(s, char),
260 (ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
261 {
262 WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
263 error = ERROR_INTERNAL_ERROR;
264 break;
265 }
266
267 IFCALLRET(echo->context.Response, error, &echo->context, Stream_Buffer(s), BytesReturned);
268
269 if (error)
270 {
271 WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
272 break;
273 }
274 }
275
276 Stream_Free(s, TRUE);
277 (void)WTSVirtualChannelClose(echo->echo_channel);
278 echo->echo_channel = NULL;
279out:
280
281 if (error && echo->context.rdpcontext)
282 setChannelError(echo->context.rdpcontext, error,
283 "echo_server_thread_func reported an error");
284
285 ExitThread(error);
286 return error;
287}
288
294static UINT echo_server_open(echo_server_context* context)
295{
296 echo_server* echo = (echo_server*)context;
297
298 if (echo->thread == NULL)
299 {
300 if (!(echo->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
301 {
302 WLog_ERR(TAG, "CreateEvent failed!");
303 return ERROR_INTERNAL_ERROR;
304 }
305
306 if (!(echo->thread = CreateThread(NULL, 0, echo_server_thread_func, (void*)echo, 0, NULL)))
307 {
308 WLog_ERR(TAG, "CreateEvent failed!");
309 (void)CloseHandle(echo->stopEvent);
310 echo->stopEvent = NULL;
311 return ERROR_INTERNAL_ERROR;
312 }
313 }
314
315 return CHANNEL_RC_OK;
316}
317
323static UINT echo_server_close(echo_server_context* context)
324{
325 UINT error = CHANNEL_RC_OK;
326 echo_server* echo = (echo_server*)context;
327
328 if (echo->thread)
329 {
330 (void)SetEvent(echo->stopEvent);
331
332 if (WaitForSingleObject(echo->thread, INFINITE) == WAIT_FAILED)
333 {
334 error = GetLastError();
335 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
336 return error;
337 }
338
339 (void)CloseHandle(echo->thread);
340 (void)CloseHandle(echo->stopEvent);
341 echo->thread = NULL;
342 echo->stopEvent = NULL;
343 }
344
345 return error;
346}
347
348static BOOL echo_server_request(echo_server_context* context, const BYTE* buffer, UINT32 length)
349{
350 union
351 {
352 const BYTE* cpv;
353 CHAR* pv;
354 } cnv;
355 cnv.cpv = buffer;
356 echo_server* echo = (echo_server*)context;
357 WINPR_ASSERT(echo);
358
359 return WTSVirtualChannelWrite(echo->echo_channel, cnv.pv, length, NULL);
360}
361
362echo_server_context* echo_server_context_new(HANDLE vcm)
363{
364 echo_server* echo = NULL;
365 echo = (echo_server*)calloc(1, sizeof(echo_server));
366
367 if (echo)
368 {
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;
373 }
374 else
375 WLog_ERR(TAG, "calloc failed!");
376
377 return (echo_server_context*)echo;
378}
379
380void echo_server_context_free(echo_server_context* context)
381{
382 echo_server* echo = (echo_server*)context;
383 echo_server_close(context);
384 free(echo);
385}