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 = nullptr;
66 DWORD StartTick = 0;
67 DWORD BytesReturned = 0;
68 PULONG pSessionId = nullptr;
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 == nullptr)
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 = nullptr;
126 void* buffer = nullptr;
127 DWORD nCount = 0;
128 HANDLE events[8];
129 BOOL ready = FALSE;
130 HANDLE ChannelEvent = nullptr;
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 = nullptr;
151 BytesReturned = 0;
152 ChannelEvent = nullptr;
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(nullptr, 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_ResetPosition(s);
242 if (!WTSVirtualChannelRead(echo->echo_channel, 0, nullptr, 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 = nullptr;
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 == nullptr)
299 {
300 if (!(echo->stopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
301 {
302 WLog_ERR(TAG, "CreateEvent failed!");
303 return ERROR_INTERNAL_ERROR;
304 }
305
306 if (!(echo->thread =
307 CreateThread(nullptr, 0, echo_server_thread_func, (void*)echo, 0, nullptr)))
308 {
309 WLog_ERR(TAG, "CreateEvent failed!");
310 (void)CloseHandle(echo->stopEvent);
311 echo->stopEvent = nullptr;
312 return ERROR_INTERNAL_ERROR;
313 }
314 }
315
316 return CHANNEL_RC_OK;
317}
318
324static UINT echo_server_close(echo_server_context* context)
325{
326 UINT error = CHANNEL_RC_OK;
327 echo_server* echo = (echo_server*)context;
328
329 if (echo->thread)
330 {
331 (void)SetEvent(echo->stopEvent);
332
333 if (WaitForSingleObject(echo->thread, INFINITE) == WAIT_FAILED)
334 {
335 error = GetLastError();
336 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
337 return error;
338 }
339
340 (void)CloseHandle(echo->thread);
341 (void)CloseHandle(echo->stopEvent);
342 echo->thread = nullptr;
343 echo->stopEvent = nullptr;
344 }
345
346 return error;
347}
348
349static BOOL echo_server_request(echo_server_context* context, const BYTE* buffer, UINT32 length)
350{
351 union
352 {
353 const BYTE* cpv;
354 CHAR* pv;
355 } cnv;
356 cnv.cpv = buffer;
357 echo_server* echo = (echo_server*)context;
358 WINPR_ASSERT(echo);
359
360 return WTSVirtualChannelWrite(echo->echo_channel, cnv.pv, length, nullptr);
361}
362
363echo_server_context* echo_server_context_new(HANDLE vcm)
364{
365 echo_server* echo = nullptr;
366 echo = (echo_server*)calloc(1, sizeof(echo_server));
367
368 if (echo)
369 {
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;
374 }
375 else
376 WLog_ERR(TAG, "calloc failed!");
377
378 return (echo_server_context*)echo;
379}
380
381void echo_server_context_free(echo_server_context* context)
382{
383 echo_server* echo = (echo_server*)context;
384 echo_server_close(context);
385 free(echo);
386}