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