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 = NULL;
81 DWORD BytesReturned = 0;
82 PULONG pSessionId = NULL;
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 = NULL,
191 .heading = NULL,
192 .horizontalAccuracy = NULL,
193 .source = NULL };
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 = NULL,
252 .headingDelta = NULL };
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 = NULL,
290 .headingDelta = NULL };
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_SetPosition(s, 0);
326 const BOOL rc = WTSVirtualChannelRead(location->location_channel, 0, NULL, 0, &BytesReturned);
327 if (!rc)
328 goto out;
329
330 if (BytesReturned < 1)
331 {
332 error = CHANNEL_RC_OK;
333 goto out;
334 }
335
336 if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
337 {
338 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
339 error = CHANNEL_RC_NO_MEMORY;
340 goto out;
341 }
342
343 if (WTSVirtualChannelRead(location->location_channel, 0, Stream_BufferAs(s, char),
344 (ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
345 {
346 WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
347 goto out;
348 }
349
350 Stream_SetLength(s, BytesReturned);
351 if (!Stream_CheckAndLogRequiredLength(TAG, s, LOCATION_HEADER_SIZE))
352 return ERROR_NO_DATA;
353
354 {
355 const UINT16 pduType = Stream_Get_UINT16(s);
356 const RDPLOCATION_HEADER header = { .pduType = (LOCATION_PDUTYPE)pduType,
357 .pduLength = Stream_Get_UINT32(s) };
358
359 switch (pduType)
360 {
361 case PDUTYPE_CLIENT_READY:
362 error = location_server_recv_client_ready(&location->context, s, &header);
363 break;
364 case PDUTYPE_BASE_LOCATION3D:
365 error = location_server_recv_base_location3d(&location->context, s, &header);
366 break;
367 case PDUTYPE_LOCATION2D_DELTA:
368 error = location_server_recv_location2d_delta(&location->context, s, &header);
369 break;
370 case PDUTYPE_LOCATION3D_DELTA:
371 error = location_server_recv_location3d_delta(&location->context, s, &header);
372 break;
373 default:
374 WLog_ERR(TAG, "location_process_message: unknown or invalid pduType %" PRIu16 "",
375 pduType);
376 break;
377 }
378 }
379
380out:
381 if (error)
382 WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
383
384 return error;
385}
386
387static UINT location_server_context_poll_int(LocationServerContext* context)
388{
389 location_server* location = (location_server*)context;
390 UINT error = ERROR_INTERNAL_ERROR;
391
392 WINPR_ASSERT(location);
393
394 switch (location->state)
395 {
396 case LOCATION_INITIAL:
397 error = location_server_open_channel(location);
398 if (error)
399 WLog_ERR(TAG, "location_server_open_channel failed with error %" PRIu32 "!", error);
400 else
401 location->state = LOCATION_OPENED;
402 break;
403 case LOCATION_OPENED:
404 error = location_process_message(location);
405 break;
406 default:
407 break;
408 }
409
410 return error;
411}
412
413static HANDLE location_server_get_channel_handle(location_server* location)
414{
415 void* buffer = NULL;
416 DWORD BytesReturned = 0;
417 HANDLE ChannelEvent = NULL;
418
419 WINPR_ASSERT(location);
420
421 if (WTSVirtualChannelQuery(location->location_channel, WTSVirtualEventHandle, &buffer,
422 &BytesReturned) == TRUE)
423 {
424 if (BytesReturned == sizeof(HANDLE))
425 ChannelEvent = *(HANDLE*)buffer;
426
427 WTSFreeMemory(buffer);
428 }
429
430 return ChannelEvent;
431}
432
433static DWORD WINAPI location_server_thread_func(LPVOID arg)
434{
435 DWORD nCount = 0;
436 HANDLE events[2] = { 0 };
437 location_server* location = (location_server*)arg;
438 UINT error = CHANNEL_RC_OK;
439 DWORD status = 0;
440
441 WINPR_ASSERT(location);
442
443 nCount = 0;
444 events[nCount++] = location->stopEvent;
445
446 while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
447 {
448 switch (location->state)
449 {
450 case LOCATION_INITIAL:
451 error = location_server_context_poll_int(&location->context);
452 if (error == CHANNEL_RC_OK)
453 {
454 events[1] = location_server_get_channel_handle(location);
455 nCount = 2;
456 }
457 break;
458 case LOCATION_OPENED:
459 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
460 switch (status)
461 {
462 case WAIT_OBJECT_0:
463 break;
464 case WAIT_OBJECT_0 + 1:
465 case WAIT_TIMEOUT:
466 error = location_server_context_poll_int(&location->context);
467 break;
468
469 case WAIT_FAILED:
470 default:
471 error = ERROR_INTERNAL_ERROR;
472 break;
473 }
474 break;
475 default:
476 break;
477 }
478 }
479
480 (void)WTSVirtualChannelClose(location->location_channel);
481 location->location_channel = NULL;
482
483 if (error && location->context.rdpcontext)
484 setChannelError(location->context.rdpcontext, error,
485 "location_server_thread_func reported an error");
486
487 ExitThread(error);
488 return error;
489}
490
491static UINT location_server_open(LocationServerContext* context)
492{
493 location_server* location = (location_server*)context;
494
495 WINPR_ASSERT(location);
496
497 if (!location->externalThread && (location->thread == NULL))
498 {
499 location->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
500 if (!location->stopEvent)
501 {
502 WLog_ERR(TAG, "CreateEvent failed!");
503 return ERROR_INTERNAL_ERROR;
504 }
505
506 location->thread = CreateThread(NULL, 0, location_server_thread_func, location, 0, NULL);
507 if (!location->thread)
508 {
509 WLog_ERR(TAG, "CreateThread failed!");
510 (void)CloseHandle(location->stopEvent);
511 location->stopEvent = NULL;
512 return ERROR_INTERNAL_ERROR;
513 }
514 }
515 location->isOpened = TRUE;
516
517 return CHANNEL_RC_OK;
518}
519
520static UINT location_server_close(LocationServerContext* context)
521{
522 UINT error = CHANNEL_RC_OK;
523 location_server* location = (location_server*)context;
524
525 WINPR_ASSERT(location);
526
527 if (!location->externalThread && location->thread)
528 {
529 (void)SetEvent(location->stopEvent);
530
531 if (WaitForSingleObject(location->thread, INFINITE) == WAIT_FAILED)
532 {
533 error = GetLastError();
534 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
535 return error;
536 }
537
538 (void)CloseHandle(location->thread);
539 (void)CloseHandle(location->stopEvent);
540 location->thread = NULL;
541 location->stopEvent = NULL;
542 }
543 if (location->externalThread)
544 {
545 if (location->state != LOCATION_INITIAL)
546 {
547 (void)WTSVirtualChannelClose(location->location_channel);
548 location->location_channel = NULL;
549 location->state = LOCATION_INITIAL;
550 }
551 }
552 location->isOpened = FALSE;
553
554 return error;
555}
556
557static UINT location_server_context_poll(LocationServerContext* context)
558{
559 location_server* location = (location_server*)context;
560
561 WINPR_ASSERT(location);
562
563 if (!location->externalThread)
564 return ERROR_INTERNAL_ERROR;
565
566 return location_server_context_poll_int(context);
567}
568
569static BOOL location_server_context_handle(LocationServerContext* context, HANDLE* handle)
570{
571 location_server* location = (location_server*)context;
572
573 WINPR_ASSERT(location);
574 WINPR_ASSERT(handle);
575
576 if (!location->externalThread)
577 return FALSE;
578 if (location->state == LOCATION_INITIAL)
579 return FALSE;
580
581 *handle = location_server_get_channel_handle(location);
582
583 return TRUE;
584}
585
586static UINT location_server_packet_send(LocationServerContext* context, wStream* s)
587{
588 location_server* location = (location_server*)context;
589 UINT error = CHANNEL_RC_OK;
590 ULONG written = 0;
591
592 WINPR_ASSERT(location);
593 WINPR_ASSERT(s);
594
595 const size_t pos = Stream_GetPosition(s);
596 WINPR_ASSERT(pos <= UINT32_MAX);
597 if (!WTSVirtualChannelWrite(location->location_channel, Stream_BufferAs(s, char), (ULONG)pos,
598 &written))
599 {
600 WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
601 error = ERROR_INTERNAL_ERROR;
602 goto out;
603 }
604
605 if (written < Stream_GetPosition(s))
606 {
607 WLog_WARN(TAG, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "", written,
608 Stream_GetPosition(s));
609 }
610
611out:
612 Stream_Free(s, TRUE);
613 return error;
614}
615
616static UINT location_server_send_server_ready(LocationServerContext* context,
617 const RDPLOCATION_SERVER_READY_PDU* serverReady)
618{
619 wStream* s = NULL;
620 UINT32 pduLength = 0;
621 UINT32 protocolVersion = 0;
622
623 WINPR_ASSERT(context);
624 WINPR_ASSERT(serverReady);
625
626 protocolVersion = serverReady->protocolVersion;
627
628 pduLength = LOCATION_HEADER_SIZE + 4 + 4;
629
630 s = Stream_New(NULL, pduLength);
631 if (!s)
632 {
633 WLog_ERR(TAG, "Stream_New failed!");
634 return ERROR_NOT_ENOUGH_MEMORY;
635 }
636
637 /* RDPLOCATION_HEADER */
638 Stream_Write_UINT16(s, PDUTYPE_SERVER_READY);
639 Stream_Write_UINT32(s, pduLength);
640
641 Stream_Write_UINT32(s, protocolVersion);
642 Stream_Write_UINT32(s, serverReady->flags);
643
644 return location_server_packet_send(context, s);
645}
646
647LocationServerContext* location_server_context_new(HANDLE vcm)
648{
649 location_server* location = (location_server*)calloc(1, sizeof(location_server));
650
651 if (!location)
652 return NULL;
653
654 location->context.vcm = vcm;
655 location->context.Initialize = location_server_initialize;
656 location->context.Open = location_server_open;
657 location->context.Close = location_server_close;
658 location->context.Poll = location_server_context_poll;
659 location->context.ChannelHandle = location_server_context_handle;
660
661 location->context.ServerReady = location_server_send_server_ready;
662
663 location->buffer = Stream_New(NULL, 4096);
664 if (!location->buffer)
665 goto fail;
666
667 return &location->context;
668fail:
669 WINPR_PRAGMA_DIAG_PUSH
670 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
671 location_server_context_free(&location->context);
672 WINPR_PRAGMA_DIAG_POP
673 return NULL;
674}
675
676void location_server_context_free(LocationServerContext* context)
677{
678 location_server* location = (location_server*)context;
679
680 if (location)
681 {
682 location_server_close(context);
683 Stream_Free(location->buffer, TRUE);
684 }
685
686 free(location);
687}