FreeRDP
telemetry_main.c
1 
20 #include <freerdp/config.h>
21 
22 #include <freerdp/freerdp.h>
23 #include <freerdp/channels/log.h>
24 #include <freerdp/server/telemetry.h>
25 
26 #define TAG CHANNELS_TAG("telemetry.server")
27 
28 typedef enum
29 {
30  TELEMETRY_INITIAL,
31  TELEMETRY_OPENED,
32 } eTelemetryChannelState;
33 
34 typedef struct
35 {
36  TelemetryServerContext context;
37 
38  HANDLE stopEvent;
39 
40  HANDLE thread;
41  void* telemetry_channel;
42 
43  DWORD SessionId;
44 
45  BOOL isOpened;
46  BOOL externalThread;
47 
48  /* Channel state */
49  eTelemetryChannelState state;
50 
51  wStream* buffer;
52 } telemetry_server;
53 
54 static UINT telemetry_server_initialize(TelemetryServerContext* context, BOOL externalThread)
55 {
56  UINT error = CHANNEL_RC_OK;
57  telemetry_server* telemetry = (telemetry_server*)context;
58 
59  WINPR_ASSERT(telemetry);
60 
61  if (telemetry->isOpened)
62  {
63  WLog_WARN(TAG, "Application error: TELEMETRY channel already initialized, "
64  "calling in this state is not possible!");
65  return ERROR_INVALID_STATE;
66  }
67 
68  telemetry->externalThread = externalThread;
69 
70  return error;
71 }
72 
73 static UINT telemetry_server_open_channel(telemetry_server* telemetry)
74 {
75  TelemetryServerContext* context = &telemetry->context;
76  DWORD Error = ERROR_SUCCESS;
77  HANDLE hEvent = NULL;
78  DWORD BytesReturned = 0;
79  PULONG pSessionId = NULL;
80  UINT32 channelId = 0;
81  BOOL status = TRUE;
82 
83  WINPR_ASSERT(telemetry);
84 
85  if (WTSQuerySessionInformationA(telemetry->context.vcm, WTS_CURRENT_SESSION, WTSSessionId,
86  (LPSTR*)&pSessionId, &BytesReturned) == FALSE)
87  {
88  WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
89  return ERROR_INTERNAL_ERROR;
90  }
91 
92  telemetry->SessionId = (DWORD)*pSessionId;
93  WTSFreeMemory(pSessionId);
94  hEvent = WTSVirtualChannelManagerGetEventHandle(telemetry->context.vcm);
95 
96  if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
97  {
98  Error = GetLastError();
99  WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error);
100  return Error;
101  }
102 
103  telemetry->telemetry_channel = WTSVirtualChannelOpenEx(
104  telemetry->SessionId, TELEMETRY_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC);
105  if (!telemetry->telemetry_channel)
106  {
107  Error = GetLastError();
108  WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed with error %" PRIu32 "!", Error);
109  return Error;
110  }
111 
112  channelId = WTSChannelGetIdByHandle(telemetry->telemetry_channel);
113 
114  IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
115  if (!status)
116  {
117  WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
118  return ERROR_INTERNAL_ERROR;
119  }
120 
121  return Error;
122 }
123 
124 static UINT telemetry_server_recv_rdp_telemetry_pdu(TelemetryServerContext* context, wStream* s)
125 {
127  UINT error = CHANNEL_RC_OK;
128 
129  if (!Stream_CheckAndLogRequiredLength(TAG, s, 16))
130  return ERROR_NO_DATA;
131 
132  Stream_Read_UINT32(s, pdu.PromptForCredentialsMillis);
133  Stream_Read_UINT32(s, pdu.PromptForCredentialsDoneMillis);
134  Stream_Read_UINT32(s, pdu.GraphicsChannelOpenedMillis);
135  Stream_Read_UINT32(s, pdu.FirstGraphicsReceivedMillis);
136 
137  IFCALLRET(context->RdpTelemetry, error, context, &pdu);
138  if (error)
139  WLog_ERR(TAG, "context->RdpTelemetry failed with error %" PRIu32 "", error);
140 
141  return error;
142 }
143 
144 static UINT telemetry_process_message(telemetry_server* telemetry)
145 {
146  BOOL rc = 0;
147  UINT error = ERROR_INTERNAL_ERROR;
148  ULONG BytesReturned = 0;
149  BYTE MessageId = 0;
150  BYTE Length = 0;
151  wStream* s = NULL;
152 
153  WINPR_ASSERT(telemetry);
154  WINPR_ASSERT(telemetry->telemetry_channel);
155 
156  s = telemetry->buffer;
157  WINPR_ASSERT(s);
158 
159  Stream_SetPosition(s, 0);
160  rc = WTSVirtualChannelRead(telemetry->telemetry_channel, 0, NULL, 0, &BytesReturned);
161  if (!rc)
162  goto out;
163 
164  if (BytesReturned < 1)
165  {
166  error = CHANNEL_RC_OK;
167  goto out;
168  }
169 
170  if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
171  {
172  WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
173  error = CHANNEL_RC_NO_MEMORY;
174  goto out;
175  }
176 
177  if (WTSVirtualChannelRead(telemetry->telemetry_channel, 0, Stream_BufferAs(s, char),
178  (ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
179  {
180  WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
181  goto out;
182  }
183 
184  Stream_SetLength(s, BytesReturned);
185  if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
186  return ERROR_NO_DATA;
187 
188  Stream_Read_UINT8(s, MessageId);
189  Stream_Read_UINT8(s, Length);
190  if (!Stream_CheckAndLogRequiredLength(TAG, s, Length))
191  return ERROR_NO_DATA;
192 
193  switch (MessageId)
194  {
195  case 0x01:
196  error = telemetry_server_recv_rdp_telemetry_pdu(&telemetry->context, s);
197  break;
198  default:
199  WLog_ERR(TAG, "telemetry_process_message: unknown MessageId %" PRIu8 "", MessageId);
200  break;
201  }
202 
203 out:
204  if (error)
205  WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
206 
207  return error;
208 }
209 
210 static UINT telemetry_server_context_poll_int(TelemetryServerContext* context)
211 {
212  telemetry_server* telemetry = (telemetry_server*)context;
213  UINT error = ERROR_INTERNAL_ERROR;
214 
215  WINPR_ASSERT(telemetry);
216 
217  switch (telemetry->state)
218  {
219  case TELEMETRY_INITIAL:
220  error = telemetry_server_open_channel(telemetry);
221  if (error)
222  WLog_ERR(TAG, "telemetry_server_open_channel failed with error %" PRIu32 "!",
223  error);
224  else
225  telemetry->state = TELEMETRY_OPENED;
226  break;
227  case TELEMETRY_OPENED:
228  error = telemetry_process_message(telemetry);
229  break;
230  default:
231  break;
232  }
233 
234  return error;
235 }
236 
237 static HANDLE telemetry_server_get_channel_handle(telemetry_server* telemetry)
238 {
239  void* buffer = NULL;
240  DWORD BytesReturned = 0;
241  HANDLE ChannelEvent = NULL;
242 
243  WINPR_ASSERT(telemetry);
244 
245  if (WTSVirtualChannelQuery(telemetry->telemetry_channel, WTSVirtualEventHandle, &buffer,
246  &BytesReturned) == TRUE)
247  {
248  if (BytesReturned == sizeof(HANDLE))
249  ChannelEvent = *(HANDLE*)buffer;
250 
251  WTSFreeMemory(buffer);
252  }
253 
254  return ChannelEvent;
255 }
256 
257 static DWORD WINAPI telemetry_server_thread_func(LPVOID arg)
258 {
259  DWORD nCount = 0;
260  HANDLE events[2] = { 0 };
261  telemetry_server* telemetry = (telemetry_server*)arg;
262  UINT error = CHANNEL_RC_OK;
263  DWORD status = 0;
264 
265  WINPR_ASSERT(telemetry);
266 
267  nCount = 0;
268  events[nCount++] = telemetry->stopEvent;
269 
270  while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
271  {
272  switch (telemetry->state)
273  {
274  case TELEMETRY_INITIAL:
275  error = telemetry_server_context_poll_int(&telemetry->context);
276  if (error == CHANNEL_RC_OK)
277  {
278  events[1] = telemetry_server_get_channel_handle(telemetry);
279  nCount = 2;
280  }
281  break;
282  case TELEMETRY_OPENED:
283  status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
284  switch (status)
285  {
286  case WAIT_OBJECT_0:
287  break;
288  case WAIT_OBJECT_0 + 1:
289  case WAIT_TIMEOUT:
290  error = telemetry_server_context_poll_int(&telemetry->context);
291  break;
292 
293  case WAIT_FAILED:
294  default:
295  error = ERROR_INTERNAL_ERROR;
296  break;
297  }
298  break;
299  default:
300  break;
301  }
302  }
303 
304  (void)WTSVirtualChannelClose(telemetry->telemetry_channel);
305  telemetry->telemetry_channel = NULL;
306 
307  if (error && telemetry->context.rdpcontext)
308  setChannelError(telemetry->context.rdpcontext, error,
309  "telemetry_server_thread_func reported an error");
310 
311  ExitThread(error);
312  return error;
313 }
314 
315 static UINT telemetry_server_open(TelemetryServerContext* context)
316 {
317  telemetry_server* telemetry = (telemetry_server*)context;
318 
319  WINPR_ASSERT(telemetry);
320 
321  if (!telemetry->externalThread && (telemetry->thread == NULL))
322  {
323  telemetry->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
324  if (!telemetry->stopEvent)
325  {
326  WLog_ERR(TAG, "CreateEvent failed!");
327  return ERROR_INTERNAL_ERROR;
328  }
329 
330  telemetry->thread = CreateThread(NULL, 0, telemetry_server_thread_func, telemetry, 0, NULL);
331  if (!telemetry->thread)
332  {
333  WLog_ERR(TAG, "CreateThread failed!");
334  (void)CloseHandle(telemetry->stopEvent);
335  telemetry->stopEvent = NULL;
336  return ERROR_INTERNAL_ERROR;
337  }
338  }
339  telemetry->isOpened = TRUE;
340 
341  return CHANNEL_RC_OK;
342 }
343 
344 static UINT telemetry_server_close(TelemetryServerContext* context)
345 {
346  UINT error = CHANNEL_RC_OK;
347  telemetry_server* telemetry = (telemetry_server*)context;
348 
349  WINPR_ASSERT(telemetry);
350 
351  if (!telemetry->externalThread && telemetry->thread)
352  {
353  (void)SetEvent(telemetry->stopEvent);
354 
355  if (WaitForSingleObject(telemetry->thread, INFINITE) == WAIT_FAILED)
356  {
357  error = GetLastError();
358  WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
359  return error;
360  }
361 
362  (void)CloseHandle(telemetry->thread);
363  (void)CloseHandle(telemetry->stopEvent);
364  telemetry->thread = NULL;
365  telemetry->stopEvent = NULL;
366  }
367  if (telemetry->externalThread)
368  {
369  if (telemetry->state != TELEMETRY_INITIAL)
370  {
371  (void)WTSVirtualChannelClose(telemetry->telemetry_channel);
372  telemetry->telemetry_channel = NULL;
373  telemetry->state = TELEMETRY_INITIAL;
374  }
375  }
376  telemetry->isOpened = FALSE;
377 
378  return error;
379 }
380 
381 static UINT telemetry_server_context_poll(TelemetryServerContext* context)
382 {
383  telemetry_server* telemetry = (telemetry_server*)context;
384 
385  WINPR_ASSERT(telemetry);
386 
387  if (!telemetry->externalThread)
388  return ERROR_INTERNAL_ERROR;
389 
390  return telemetry_server_context_poll_int(context);
391 }
392 
393 static BOOL telemetry_server_context_handle(TelemetryServerContext* context, HANDLE* handle)
394 {
395  telemetry_server* telemetry = (telemetry_server*)context;
396 
397  WINPR_ASSERT(telemetry);
398  WINPR_ASSERT(handle);
399 
400  if (!telemetry->externalThread)
401  return FALSE;
402  if (telemetry->state == TELEMETRY_INITIAL)
403  return FALSE;
404 
405  *handle = telemetry_server_get_channel_handle(telemetry);
406 
407  return TRUE;
408 }
409 
410 TelemetryServerContext* telemetry_server_context_new(HANDLE vcm)
411 {
412  telemetry_server* telemetry = (telemetry_server*)calloc(1, sizeof(telemetry_server));
413 
414  if (!telemetry)
415  return NULL;
416 
417  telemetry->context.vcm = vcm;
418  telemetry->context.Initialize = telemetry_server_initialize;
419  telemetry->context.Open = telemetry_server_open;
420  telemetry->context.Close = telemetry_server_close;
421  telemetry->context.Poll = telemetry_server_context_poll;
422  telemetry->context.ChannelHandle = telemetry_server_context_handle;
423 
424  telemetry->buffer = Stream_New(NULL, 4096);
425  if (!telemetry->buffer)
426  goto fail;
427 
428  return &telemetry->context;
429 fail:
430  WINPR_PRAGMA_DIAG_PUSH
431  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
432  telemetry_server_context_free(&telemetry->context);
433  WINPR_PRAGMA_DIAG_POP
434  return NULL;
435 }
436 
437 void telemetry_server_context_free(TelemetryServerContext* context)
438 {
439  telemetry_server* telemetry = (telemetry_server*)context;
440 
441  if (telemetry)
442  {
443  telemetry_server_close(context);
444  Stream_Free(telemetry->buffer, TRUE);
445  }
446 
447  free(telemetry);
448 }