FreeRDP
Loading...
Searching...
No Matches
server/location_main.c
1
20#include <freerdp/config.h>
21
22#include <freerdp/freerdp.h>
23#include <freerdp/channels/log.h>
24#include <freerdp/server/location.h>
25#include <freerdp/utils/encoded_types.h>
26
27#define TAG CHANNELS_TAG("location.server")
28
29typedef enum
30{
31 LOCATION_INITIAL,
32 LOCATION_OPENED,
33} eLocationChannelState;
34
35typedef struct
36{
37 LocationServerContext context;
38
39 HANDLE stopEvent;
40
41 HANDLE thread;
42 void* location_channel;
43
44 DWORD SessionId;
45
46 BOOL isOpened;
47 BOOL externalThread;
48
49 /* Channel state */
50 eLocationChannelState state;
51
52 wStream* buffer;
53} location_server;
54
55static UINT location_server_initialize(LocationServerContext* context, BOOL externalThread)
56{
57 UINT error = CHANNEL_RC_OK;
58 location_server* location = (location_server*)context;
59
60 WINPR_ASSERT(location);
61
62 if (location->isOpened)
63 {
64 WLog_WARN(TAG, "Application error: Location channel already initialized, "
65 "calling in this state is not possible!");
66 return ERROR_INVALID_STATE;
67 }
68
69 location->externalThread = externalThread;
70
71 return error;
72}
73
74static UINT location_server_open_channel(location_server* location)
75{
76 LocationServerContext* context = &location->context;
77 DWORD Error = ERROR_SUCCESS;
78 HANDLE hEvent = NULL;
79 DWORD BytesReturned = 0;
80 PULONG pSessionId = NULL;
81 UINT32 channelId = 0;
82 BOOL status = TRUE;
83
84 WINPR_ASSERT(location);
85
86 if (WTSQuerySessionInformationA(location->context.vcm, WTS_CURRENT_SESSION, WTSSessionId,
87 (LPSTR*)&pSessionId, &BytesReturned) == FALSE)
88 {
89 WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
90 return ERROR_INTERNAL_ERROR;
91 }
92
93 location->SessionId = (DWORD)*pSessionId;
94 WTSFreeMemory(pSessionId);
95 hEvent = WTSVirtualChannelManagerGetEventHandle(location->context.vcm);
96
97 if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED)
98 {
99 Error = GetLastError();
100 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error);
101 return Error;
102 }
103
104 location->location_channel = WTSVirtualChannelOpenEx(
105 location->SessionId, LOCATION_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC);
106 if (!location->location_channel)
107 {
108 Error = GetLastError();
109 WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed with error %" PRIu32 "!", Error);
110 return Error;
111 }
112
113 channelId = WTSChannelGetIdByHandle(location->location_channel);
114
115 IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
116 if (!status)
117 {
118 WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
119 return ERROR_INTERNAL_ERROR;
120 }
121
122 return Error;
123}
124
125static UINT location_server_recv_client_ready(LocationServerContext* context, wStream* s,
126 const RDPLOCATION_HEADER* header)
127{
129 UINT error = CHANNEL_RC_OK;
130
131 WINPR_ASSERT(context);
132 WINPR_ASSERT(s);
133 WINPR_ASSERT(header);
134
135 pdu.header = *header;
136
137 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
138 return ERROR_NO_DATA;
139
140 Stream_Read_UINT32(s, pdu.protocolVersion);
141
142 if (Stream_GetRemainingLength(s) >= 4)
143 Stream_Read_UINT32(s, pdu.flags);
144
145 IFCALLRET(context->ClientReady, error, context, &pdu);
146 if (error)
147 WLog_ERR(TAG, "context->ClientReady failed with error %" PRIu32 "", error);
148
149 return error;
150}
151
152static UINT location_server_recv_base_location3d(LocationServerContext* context, wStream* s,
153 const RDPLOCATION_HEADER* header)
154{
156 UINT error = CHANNEL_RC_OK;
157 double speed = 0.0;
158 double heading = 0.0;
159 double horizontalAccuracy = 0.0;
160 LOCATIONSOURCE source = 0;
161
162 WINPR_ASSERT(context);
163 WINPR_ASSERT(s);
164 WINPR_ASSERT(header);
165
166 pdu.header = *header;
167
168 if (!freerdp_read_four_byte_float(s, &pdu.latitude) ||
169 !freerdp_read_four_byte_float(s, &pdu.longitude) ||
170 !freerdp_read_four_byte_signed_integer(s, &pdu.altitude))
171 return FALSE;
172
173 if (Stream_GetRemainingLength(s) >= 1)
174 {
175 if (!freerdp_read_four_byte_float(s, &speed) ||
176 !freerdp_read_four_byte_float(s, &heading) ||
177 !freerdp_read_four_byte_float(s, &horizontalAccuracy) ||
178 !Stream_CheckAndLogRequiredLength(TAG, s, 1))
179 return FALSE;
180
181 Stream_Read_UINT8(s, source);
182
183 pdu.speed = &speed;
184 pdu.heading = &heading;
185 pdu.horizontalAccuracy = &horizontalAccuracy;
186 pdu.source = &source;
187 }
188
189 IFCALLRET(context->BaseLocation3D, error, context, &pdu);
190 if (error)
191 WLog_ERR(TAG, "context->BaseLocation3D failed with error %" PRIu32 "", error);
192
193 return error;
194}
195
196static UINT location_server_recv_location2d_delta(LocationServerContext* context, wStream* s,
197 const RDPLOCATION_HEADER* header)
198{
200 UINT error = CHANNEL_RC_OK;
201 double speedDelta = 0.0;
202 double headingDelta = 0.0;
203
204 WINPR_ASSERT(context);
205 WINPR_ASSERT(s);
206 WINPR_ASSERT(header);
207
208 pdu.header = *header;
209
210 if (!freerdp_read_four_byte_float(s, &pdu.latitudeDelta) ||
211 !freerdp_read_four_byte_float(s, &pdu.longitudeDelta))
212 return FALSE;
213
214 if (Stream_GetRemainingLength(s) >= 1)
215 {
216 if (!freerdp_read_four_byte_float(s, &speedDelta) ||
217 !freerdp_read_four_byte_float(s, &headingDelta))
218 return FALSE;
219
220 pdu.speedDelta = &speedDelta;
221 pdu.headingDelta = &headingDelta;
222 }
223
224 IFCALLRET(context->Location2DDelta, error, context, &pdu);
225 if (error)
226 WLog_ERR(TAG, "context->Location2DDelta failed with error %" PRIu32 "", error);
227
228 return error;
229}
230
231static UINT location_server_recv_location3d_delta(LocationServerContext* context, wStream* s,
232 const RDPLOCATION_HEADER* header)
233{
235 UINT error = CHANNEL_RC_OK;
236 double speedDelta = 0.0;
237 double headingDelta = 0.0;
238
239 WINPR_ASSERT(context);
240 WINPR_ASSERT(s);
241 WINPR_ASSERT(header);
242
243 pdu.header = *header;
244
245 if (!freerdp_read_four_byte_float(s, &pdu.latitudeDelta) ||
246 !freerdp_read_four_byte_float(s, &pdu.longitudeDelta) ||
247 !freerdp_read_four_byte_signed_integer(s, &pdu.altitudeDelta))
248 return FALSE;
249
250 if (Stream_GetRemainingLength(s) >= 1)
251 {
252 if (!freerdp_read_four_byte_float(s, &speedDelta) ||
253 !freerdp_read_four_byte_float(s, &headingDelta))
254 return FALSE;
255
256 pdu.speedDelta = &speedDelta;
257 pdu.headingDelta = &headingDelta;
258 }
259
260 IFCALLRET(context->Location3DDelta, error, context, &pdu);
261 if (error)
262 WLog_ERR(TAG, "context->Location3DDelta failed with error %" PRIu32 "", error);
263
264 return error;
265}
266
267static UINT location_process_message(location_server* location)
268{
269 BOOL rc = 0;
270 UINT error = ERROR_INTERNAL_ERROR;
271 ULONG BytesReturned = 0;
272 RDPLOCATION_HEADER header = { 0 };
273 wStream* s = NULL;
274
275 WINPR_ASSERT(location);
276 WINPR_ASSERT(location->location_channel);
277
278 s = location->buffer;
279 WINPR_ASSERT(s);
280
281 Stream_SetPosition(s, 0);
282 rc = WTSVirtualChannelRead(location->location_channel, 0, NULL, 0, &BytesReturned);
283 if (!rc)
284 goto out;
285
286 if (BytesReturned < 1)
287 {
288 error = CHANNEL_RC_OK;
289 goto out;
290 }
291
292 if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
293 {
294 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
295 error = CHANNEL_RC_NO_MEMORY;
296 goto out;
297 }
298
299 if (WTSVirtualChannelRead(location->location_channel, 0, Stream_BufferAs(s, char),
300 (ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
301 {
302 WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
303 goto out;
304 }
305
306 Stream_SetLength(s, BytesReturned);
307 if (!Stream_CheckAndLogRequiredLength(TAG, s, LOCATION_HEADER_SIZE))
308 return ERROR_NO_DATA;
309
310 Stream_Read_UINT16(s, header.pduType);
311 Stream_Read_UINT32(s, header.pduLength);
312
313 switch (header.pduType)
314 {
315 case PDUTYPE_CLIENT_READY:
316 error = location_server_recv_client_ready(&location->context, s, &header);
317 break;
318 case PDUTYPE_BASE_LOCATION3D:
319 error = location_server_recv_base_location3d(&location->context, s, &header);
320 break;
321 case PDUTYPE_LOCATION2D_DELTA:
322 error = location_server_recv_location2d_delta(&location->context, s, &header);
323 break;
324 case PDUTYPE_LOCATION3D_DELTA:
325 error = location_server_recv_location3d_delta(&location->context, s, &header);
326 break;
327 default:
328 WLog_ERR(TAG, "location_process_message: unknown or invalid pduType %" PRIu8 "",
329 header.pduType);
330 break;
331 }
332
333out:
334 if (error)
335 WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
336
337 return error;
338}
339
340static UINT location_server_context_poll_int(LocationServerContext* context)
341{
342 location_server* location = (location_server*)context;
343 UINT error = ERROR_INTERNAL_ERROR;
344
345 WINPR_ASSERT(location);
346
347 switch (location->state)
348 {
349 case LOCATION_INITIAL:
350 error = location_server_open_channel(location);
351 if (error)
352 WLog_ERR(TAG, "location_server_open_channel failed with error %" PRIu32 "!", error);
353 else
354 location->state = LOCATION_OPENED;
355 break;
356 case LOCATION_OPENED:
357 error = location_process_message(location);
358 break;
359 default:
360 break;
361 }
362
363 return error;
364}
365
366static HANDLE location_server_get_channel_handle(location_server* location)
367{
368 void* buffer = NULL;
369 DWORD BytesReturned = 0;
370 HANDLE ChannelEvent = NULL;
371
372 WINPR_ASSERT(location);
373
374 if (WTSVirtualChannelQuery(location->location_channel, WTSVirtualEventHandle, &buffer,
375 &BytesReturned) == TRUE)
376 {
377 if (BytesReturned == sizeof(HANDLE))
378 ChannelEvent = *(HANDLE*)buffer;
379
380 WTSFreeMemory(buffer);
381 }
382
383 return ChannelEvent;
384}
385
386static DWORD WINAPI location_server_thread_func(LPVOID arg)
387{
388 DWORD nCount = 0;
389 HANDLE events[2] = { 0 };
390 location_server* location = (location_server*)arg;
391 UINT error = CHANNEL_RC_OK;
392 DWORD status = 0;
393
394 WINPR_ASSERT(location);
395
396 nCount = 0;
397 events[nCount++] = location->stopEvent;
398
399 while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
400 {
401 switch (location->state)
402 {
403 case LOCATION_INITIAL:
404 error = location_server_context_poll_int(&location->context);
405 if (error == CHANNEL_RC_OK)
406 {
407 events[1] = location_server_get_channel_handle(location);
408 nCount = 2;
409 }
410 break;
411 case LOCATION_OPENED:
412 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
413 switch (status)
414 {
415 case WAIT_OBJECT_0:
416 break;
417 case WAIT_OBJECT_0 + 1:
418 case WAIT_TIMEOUT:
419 error = location_server_context_poll_int(&location->context);
420 break;
421
422 case WAIT_FAILED:
423 default:
424 error = ERROR_INTERNAL_ERROR;
425 break;
426 }
427 break;
428 default:
429 break;
430 }
431 }
432
433 (void)WTSVirtualChannelClose(location->location_channel);
434 location->location_channel = NULL;
435
436 if (error && location->context.rdpcontext)
437 setChannelError(location->context.rdpcontext, error,
438 "location_server_thread_func reported an error");
439
440 ExitThread(error);
441 return error;
442}
443
444static UINT location_server_open(LocationServerContext* context)
445{
446 location_server* location = (location_server*)context;
447
448 WINPR_ASSERT(location);
449
450 if (!location->externalThread && (location->thread == NULL))
451 {
452 location->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
453 if (!location->stopEvent)
454 {
455 WLog_ERR(TAG, "CreateEvent failed!");
456 return ERROR_INTERNAL_ERROR;
457 }
458
459 location->thread = CreateThread(NULL, 0, location_server_thread_func, location, 0, NULL);
460 if (!location->thread)
461 {
462 WLog_ERR(TAG, "CreateThread failed!");
463 (void)CloseHandle(location->stopEvent);
464 location->stopEvent = NULL;
465 return ERROR_INTERNAL_ERROR;
466 }
467 }
468 location->isOpened = TRUE;
469
470 return CHANNEL_RC_OK;
471}
472
473static UINT location_server_close(LocationServerContext* context)
474{
475 UINT error = CHANNEL_RC_OK;
476 location_server* location = (location_server*)context;
477
478 WINPR_ASSERT(location);
479
480 if (!location->externalThread && location->thread)
481 {
482 (void)SetEvent(location->stopEvent);
483
484 if (WaitForSingleObject(location->thread, INFINITE) == WAIT_FAILED)
485 {
486 error = GetLastError();
487 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
488 return error;
489 }
490
491 (void)CloseHandle(location->thread);
492 (void)CloseHandle(location->stopEvent);
493 location->thread = NULL;
494 location->stopEvent = NULL;
495 }
496 if (location->externalThread)
497 {
498 if (location->state != LOCATION_INITIAL)
499 {
500 (void)WTSVirtualChannelClose(location->location_channel);
501 location->location_channel = NULL;
502 location->state = LOCATION_INITIAL;
503 }
504 }
505 location->isOpened = FALSE;
506
507 return error;
508}
509
510static UINT location_server_context_poll(LocationServerContext* context)
511{
512 location_server* location = (location_server*)context;
513
514 WINPR_ASSERT(location);
515
516 if (!location->externalThread)
517 return ERROR_INTERNAL_ERROR;
518
519 return location_server_context_poll_int(context);
520}
521
522static BOOL location_server_context_handle(LocationServerContext* context, HANDLE* handle)
523{
524 location_server* location = (location_server*)context;
525
526 WINPR_ASSERT(location);
527 WINPR_ASSERT(handle);
528
529 if (!location->externalThread)
530 return FALSE;
531 if (location->state == LOCATION_INITIAL)
532 return FALSE;
533
534 *handle = location_server_get_channel_handle(location);
535
536 return TRUE;
537}
538
539static UINT location_server_packet_send(LocationServerContext* context, wStream* s)
540{
541 location_server* location = (location_server*)context;
542 UINT error = CHANNEL_RC_OK;
543 ULONG written = 0;
544
545 WINPR_ASSERT(location);
546 WINPR_ASSERT(s);
547
548 const size_t pos = Stream_GetPosition(s);
549 WINPR_ASSERT(pos <= UINT32_MAX);
550 if (!WTSVirtualChannelWrite(location->location_channel, Stream_BufferAs(s, char), (ULONG)pos,
551 &written))
552 {
553 WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
554 error = ERROR_INTERNAL_ERROR;
555 goto out;
556 }
557
558 if (written < Stream_GetPosition(s))
559 {
560 WLog_WARN(TAG, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "", written,
561 Stream_GetPosition(s));
562 }
563
564out:
565 Stream_Free(s, TRUE);
566 return error;
567}
568
569static UINT location_server_send_server_ready(LocationServerContext* context,
570 const RDPLOCATION_SERVER_READY_PDU* serverReady)
571{
572 wStream* s = NULL;
573 UINT32 pduLength = 0;
574 UINT32 protocolVersion = 0;
575
576 WINPR_ASSERT(context);
577 WINPR_ASSERT(serverReady);
578
579 protocolVersion = serverReady->protocolVersion;
580
581 pduLength = LOCATION_HEADER_SIZE + 4 + 4;
582
583 s = Stream_New(NULL, pduLength);
584 if (!s)
585 {
586 WLog_ERR(TAG, "Stream_New failed!");
587 return ERROR_NOT_ENOUGH_MEMORY;
588 }
589
590 /* RDPLOCATION_HEADER */
591 Stream_Write_UINT16(s, PDUTYPE_SERVER_READY);
592 Stream_Write_UINT32(s, pduLength);
593
594 Stream_Write_UINT32(s, protocolVersion);
595 Stream_Write_UINT32(s, serverReady->flags);
596
597 return location_server_packet_send(context, s);
598}
599
600LocationServerContext* location_server_context_new(HANDLE vcm)
601{
602 location_server* location = (location_server*)calloc(1, sizeof(location_server));
603
604 if (!location)
605 return NULL;
606
607 location->context.vcm = vcm;
608 location->context.Initialize = location_server_initialize;
609 location->context.Open = location_server_open;
610 location->context.Close = location_server_close;
611 location->context.Poll = location_server_context_poll;
612 location->context.ChannelHandle = location_server_context_handle;
613
614 location->context.ServerReady = location_server_send_server_ready;
615
616 location->buffer = Stream_New(NULL, 4096);
617 if (!location->buffer)
618 goto fail;
619
620 return &location->context;
621fail:
622 WINPR_PRAGMA_DIAG_PUSH
623 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
624 location_server_context_free(&location->context);
625 WINPR_PRAGMA_DIAG_POP
626 return NULL;
627}
628
629void location_server_context_free(LocationServerContext* context)
630{
631 location_server* location = (location_server*)context;
632
633 if (location)
634 {
635 location_server_close(context);
636 Stream_Free(location->buffer, TRUE);
637 }
638
639 free(location);
640}