FreeRDP
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Modules Pages
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
28typedef enum
29{
30 TELEMETRY_INITIAL,
31 TELEMETRY_OPENED,
32} eTelemetryChannelState;
33
34typedef 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
54static 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
73static 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
124static 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
144static 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
203out:
204 if (error)
205 WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
206
207 return error;
208}
209
210static 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
237static 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
257static 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
315static 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
344static 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
381static 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
393static 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
410TelemetryServerContext* 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;
429fail:
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
437void 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}