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 
191  switch (MessageId)
192  {
193  case 0x01:
194  error = telemetry_server_recv_rdp_telemetry_pdu(&telemetry->context, s);
195  break;
196  default:
197  WLog_ERR(TAG, "telemetry_process_message: unknown MessageId %" PRIu8 "", MessageId);
198  break;
199  }
200 
201 out:
202  if (error)
203  WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
204 
205  return error;
206 }
207 
208 static UINT telemetry_server_context_poll_int(TelemetryServerContext* context)
209 {
210  telemetry_server* telemetry = (telemetry_server*)context;
211  UINT error = ERROR_INTERNAL_ERROR;
212 
213  WINPR_ASSERT(telemetry);
214 
215  switch (telemetry->state)
216  {
217  case TELEMETRY_INITIAL:
218  error = telemetry_server_open_channel(telemetry);
219  if (error)
220  WLog_ERR(TAG, "telemetry_server_open_channel failed with error %" PRIu32 "!",
221  error);
222  else
223  telemetry->state = TELEMETRY_OPENED;
224  break;
225  case TELEMETRY_OPENED:
226  error = telemetry_process_message(telemetry);
227  break;
228  default:
229  break;
230  }
231 
232  return error;
233 }
234 
235 static HANDLE telemetry_server_get_channel_handle(telemetry_server* telemetry)
236 {
237  void* buffer = NULL;
238  DWORD BytesReturned = 0;
239  HANDLE ChannelEvent = NULL;
240 
241  WINPR_ASSERT(telemetry);
242 
243  if (WTSVirtualChannelQuery(telemetry->telemetry_channel, WTSVirtualEventHandle, &buffer,
244  &BytesReturned) == TRUE)
245  {
246  if (BytesReturned == sizeof(HANDLE))
247  CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE));
248 
249  WTSFreeMemory(buffer);
250  }
251 
252  return ChannelEvent;
253 }
254 
255 static DWORD WINAPI telemetry_server_thread_func(LPVOID arg)
256 {
257  DWORD nCount = 0;
258  HANDLE events[2] = { 0 };
259  telemetry_server* telemetry = (telemetry_server*)arg;
260  UINT error = CHANNEL_RC_OK;
261  DWORD status = 0;
262 
263  WINPR_ASSERT(telemetry);
264 
265  nCount = 0;
266  events[nCount++] = telemetry->stopEvent;
267 
268  while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
269  {
270  switch (telemetry->state)
271  {
272  case TELEMETRY_INITIAL:
273  error = telemetry_server_context_poll_int(&telemetry->context);
274  if (error == CHANNEL_RC_OK)
275  {
276  events[1] = telemetry_server_get_channel_handle(telemetry);
277  nCount = 2;
278  }
279  break;
280  case TELEMETRY_OPENED:
281  status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
282  switch (status)
283  {
284  case WAIT_OBJECT_0:
285  break;
286  case WAIT_OBJECT_0 + 1:
287  case WAIT_TIMEOUT:
288  error = telemetry_server_context_poll_int(&telemetry->context);
289  break;
290 
291  case WAIT_FAILED:
292  default:
293  error = ERROR_INTERNAL_ERROR;
294  break;
295  }
296  break;
297  default:
298  break;
299  }
300  }
301 
302  (void)WTSVirtualChannelClose(telemetry->telemetry_channel);
303  telemetry->telemetry_channel = NULL;
304 
305  if (error && telemetry->context.rdpcontext)
306  setChannelError(telemetry->context.rdpcontext, error,
307  "telemetry_server_thread_func reported an error");
308 
309  ExitThread(error);
310  return error;
311 }
312 
313 static UINT telemetry_server_open(TelemetryServerContext* context)
314 {
315  telemetry_server* telemetry = (telemetry_server*)context;
316 
317  WINPR_ASSERT(telemetry);
318 
319  if (!telemetry->externalThread && (telemetry->thread == NULL))
320  {
321  telemetry->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
322  if (!telemetry->stopEvent)
323  {
324  WLog_ERR(TAG, "CreateEvent failed!");
325  return ERROR_INTERNAL_ERROR;
326  }
327 
328  telemetry->thread = CreateThread(NULL, 0, telemetry_server_thread_func, telemetry, 0, NULL);
329  if (!telemetry->thread)
330  {
331  WLog_ERR(TAG, "CreateThread failed!");
332  (void)CloseHandle(telemetry->stopEvent);
333  telemetry->stopEvent = NULL;
334  return ERROR_INTERNAL_ERROR;
335  }
336  }
337  telemetry->isOpened = TRUE;
338 
339  return CHANNEL_RC_OK;
340 }
341 
342 static UINT telemetry_server_close(TelemetryServerContext* context)
343 {
344  UINT error = CHANNEL_RC_OK;
345  telemetry_server* telemetry = (telemetry_server*)context;
346 
347  WINPR_ASSERT(telemetry);
348 
349  if (!telemetry->externalThread && telemetry->thread)
350  {
351  (void)SetEvent(telemetry->stopEvent);
352 
353  if (WaitForSingleObject(telemetry->thread, INFINITE) == WAIT_FAILED)
354  {
355  error = GetLastError();
356  WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
357  return error;
358  }
359 
360  (void)CloseHandle(telemetry->thread);
361  (void)CloseHandle(telemetry->stopEvent);
362  telemetry->thread = NULL;
363  telemetry->stopEvent = NULL;
364  }
365  if (telemetry->externalThread)
366  {
367  if (telemetry->state != TELEMETRY_INITIAL)
368  {
369  (void)WTSVirtualChannelClose(telemetry->telemetry_channel);
370  telemetry->telemetry_channel = NULL;
371  telemetry->state = TELEMETRY_INITIAL;
372  }
373  }
374  telemetry->isOpened = FALSE;
375 
376  return error;
377 }
378 
379 static UINT telemetry_server_context_poll(TelemetryServerContext* context)
380 {
381  telemetry_server* telemetry = (telemetry_server*)context;
382 
383  WINPR_ASSERT(telemetry);
384 
385  if (!telemetry->externalThread)
386  return ERROR_INTERNAL_ERROR;
387 
388  return telemetry_server_context_poll_int(context);
389 }
390 
391 static BOOL telemetry_server_context_handle(TelemetryServerContext* context, HANDLE* handle)
392 {
393  telemetry_server* telemetry = (telemetry_server*)context;
394 
395  WINPR_ASSERT(telemetry);
396  WINPR_ASSERT(handle);
397 
398  if (!telemetry->externalThread)
399  return FALSE;
400  if (telemetry->state == TELEMETRY_INITIAL)
401  return FALSE;
402 
403  *handle = telemetry_server_get_channel_handle(telemetry);
404 
405  return TRUE;
406 }
407 
408 TelemetryServerContext* telemetry_server_context_new(HANDLE vcm)
409 {
410  telemetry_server* telemetry = (telemetry_server*)calloc(1, sizeof(telemetry_server));
411 
412  if (!telemetry)
413  return NULL;
414 
415  telemetry->context.vcm = vcm;
416  telemetry->context.Initialize = telemetry_server_initialize;
417  telemetry->context.Open = telemetry_server_open;
418  telemetry->context.Close = telemetry_server_close;
419  telemetry->context.Poll = telemetry_server_context_poll;
420  telemetry->context.ChannelHandle = telemetry_server_context_handle;
421 
422  telemetry->buffer = Stream_New(NULL, 4096);
423  if (!telemetry->buffer)
424  goto fail;
425 
426  return &telemetry->context;
427 fail:
428  WINPR_PRAGMA_DIAG_PUSH
429  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
430  telemetry_server_context_free(&telemetry->context);
431  WINPR_PRAGMA_DIAG_POP
432  return NULL;
433 }
434 
435 void telemetry_server_context_free(TelemetryServerContext* context)
436 {
437  telemetry_server* telemetry = (telemetry_server*)context;
438 
439  if (telemetry)
440  {
441  telemetry_server_close(context);
442  Stream_Free(telemetry->buffer, TRUE);
443  }
444 
445  free(telemetry);
446 }