FreeRDP
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 
42 typedef 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 
62 static 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 
123 static 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  CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE));
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;
279 out:
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 
294 static 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 
323 static 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 
348 static 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 
362 echo_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 
380 void 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 }