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 {
141 const UINT32 version = Stream_Get_UINT32(s);
142 switch (version)
143 {
144 case RDPLOCATION_PROTOCOL_VERSION_100:
145 pdu.protocolVersion = RDPLOCATION_PROTOCOL_VERSION_100;
146 break;
147 case RDPLOCATION_PROTOCOL_VERSION_200:
148 pdu.protocolVersion = RDPLOCATION_PROTOCOL_VERSION_200;
149 break;
150 default:
151 pdu.protocolVersion = RDPLOCATION_PROTOCOL_VERSION_200;
152 WLog_WARN(TAG,
153 "Received unsupported protocol version %" PRIu32
154 ", setting to highest supported %u",
155 version, pdu.protocolVersion);
156 break;
157 }
158 }
159
160 if (Stream_GetRemainingLength(s) >= 4)
161 Stream_Read_UINT32(s, pdu.flags);
162
163 IFCALLRET(context->ClientReady, error, context, &pdu);
164 if (error)
165 WLog_ERR(TAG, "context->ClientReady failed with error %" PRIu32 "", error);
166
167 return error;
168}
169
170static UINT location_server_recv_base_location3d(LocationServerContext* context, wStream* s,
171 const RDPLOCATION_HEADER* header)
172{
174 UINT error = CHANNEL_RC_OK;
175 double speed = 0.0;
176 double heading = 0.0;
177 double horizontalAccuracy = 0.0;
178 LOCATIONSOURCE source = LOCATIONSOURCE_IP;
179
180 WINPR_ASSERT(context);
181 WINPR_ASSERT(s);
182 WINPR_ASSERT(header);
183
184 pdu.header = *header;
185
186 if (!freerdp_read_four_byte_float(s, &pdu.latitude) ||
187 !freerdp_read_four_byte_float(s, &pdu.longitude) ||
188 !freerdp_read_four_byte_signed_integer(s, &pdu.altitude))
189 return FALSE;
190
191 if (Stream_GetRemainingLength(s) >= 1)
192 {
193 if (!freerdp_read_four_byte_float(s, &speed) ||
194 !freerdp_read_four_byte_float(s, &heading) ||
195 !freerdp_read_four_byte_float(s, &horizontalAccuracy) ||
196 !Stream_CheckAndLogRequiredLength(TAG, s, 1))
197 return FALSE;
198
199 {
200 const UINT8 src = Stream_Get_UINT8(s);
201 switch (src)
202 {
203 case LOCATIONSOURCE_IP:
204 case LOCATIONSOURCE_WIFI:
205 case LOCATIONSOURCE_CELL:
206 case LOCATIONSOURCE_GNSS:
207 break;
208 default:
209 WLog_ERR(TAG, "Invalid LOCATIONSOURCE value %" PRIu8 "", src);
210 return FALSE;
211 }
212 source = (LOCATIONSOURCE)src;
213 }
214
215 pdu.speed = &speed;
216 pdu.heading = &heading;
217 pdu.horizontalAccuracy = &horizontalAccuracy;
218 pdu.source = &source;
219 }
220
221 IFCALLRET(context->BaseLocation3D, error, context, &pdu);
222 if (error)
223 WLog_ERR(TAG, "context->BaseLocation3D failed with error %" PRIu32 "", error);
224
225 return error;
226}
227
228static UINT location_server_recv_location2d_delta(LocationServerContext* context, wStream* s,
229 const RDPLOCATION_HEADER* header)
230{
232 UINT error = CHANNEL_RC_OK;
233 double speedDelta = 0.0;
234 double headingDelta = 0.0;
235
236 WINPR_ASSERT(context);
237 WINPR_ASSERT(s);
238 WINPR_ASSERT(header);
239
240 pdu.header = *header;
241
242 if (!freerdp_read_four_byte_float(s, &pdu.latitudeDelta) ||
243 !freerdp_read_four_byte_float(s, &pdu.longitudeDelta))
244 return FALSE;
245
246 if (Stream_GetRemainingLength(s) >= 1)
247 {
248 if (!freerdp_read_four_byte_float(s, &speedDelta) ||
249 !freerdp_read_four_byte_float(s, &headingDelta))
250 return FALSE;
251
252 pdu.speedDelta = &speedDelta;
253 pdu.headingDelta = &headingDelta;
254 }
255
256 IFCALLRET(context->Location2DDelta, error, context, &pdu);
257 if (error)
258 WLog_ERR(TAG, "context->Location2DDelta failed with error %" PRIu32 "", error);
259
260 return error;
261}
262
263static UINT location_server_recv_location3d_delta(LocationServerContext* context, wStream* s,
264 const RDPLOCATION_HEADER* header)
265{
267 UINT error = CHANNEL_RC_OK;
268 double speedDelta = 0.0;
269 double headingDelta = 0.0;
270
271 WINPR_ASSERT(context);
272 WINPR_ASSERT(s);
273 WINPR_ASSERT(header);
274
275 pdu.header = *header;
276
277 if (!freerdp_read_four_byte_float(s, &pdu.latitudeDelta) ||
278 !freerdp_read_four_byte_float(s, &pdu.longitudeDelta) ||
279 !freerdp_read_four_byte_signed_integer(s, &pdu.altitudeDelta))
280 return FALSE;
281
282 if (Stream_GetRemainingLength(s) >= 1)
283 {
284 if (!freerdp_read_four_byte_float(s, &speedDelta) ||
285 !freerdp_read_four_byte_float(s, &headingDelta))
286 return FALSE;
287
288 pdu.speedDelta = &speedDelta;
289 pdu.headingDelta = &headingDelta;
290 }
291
292 IFCALLRET(context->Location3DDelta, error, context, &pdu);
293 if (error)
294 WLog_ERR(TAG, "context->Location3DDelta failed with error %" PRIu32 "", error);
295
296 return error;
297}
298
299static UINT location_process_message(location_server* location)
300{
301 BOOL rc = 0;
302 UINT error = ERROR_INTERNAL_ERROR;
303 ULONG BytesReturned = 0;
304 RDPLOCATION_HEADER header = { 0 };
305 wStream* s = NULL;
306
307 WINPR_ASSERT(location);
308 WINPR_ASSERT(location->location_channel);
309
310 s = location->buffer;
311 WINPR_ASSERT(s);
312
313 Stream_SetPosition(s, 0);
314 rc = WTSVirtualChannelRead(location->location_channel, 0, NULL, 0, &BytesReturned);
315 if (!rc)
316 goto out;
317
318 if (BytesReturned < 1)
319 {
320 error = CHANNEL_RC_OK;
321 goto out;
322 }
323
324 if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
325 {
326 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
327 error = CHANNEL_RC_NO_MEMORY;
328 goto out;
329 }
330
331 if (WTSVirtualChannelRead(location->location_channel, 0, Stream_BufferAs(s, char),
332 (ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
333 {
334 WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
335 goto out;
336 }
337
338 Stream_SetLength(s, BytesReturned);
339 if (!Stream_CheckAndLogRequiredLength(TAG, s, LOCATION_HEADER_SIZE))
340 return ERROR_NO_DATA;
341
342 {
343 const UINT16 pduType = Stream_Get_UINT16(s);
344 header.pduType = (LOCATION_PDUTYPE)pduType;
345 header.pduLength = Stream_Get_UINT32(s);
346
347 switch (pduType)
348 {
349 case PDUTYPE_CLIENT_READY:
350 error = location_server_recv_client_ready(&location->context, s, &header);
351 break;
352 case PDUTYPE_BASE_LOCATION3D:
353 error = location_server_recv_base_location3d(&location->context, s, &header);
354 break;
355 case PDUTYPE_LOCATION2D_DELTA:
356 error = location_server_recv_location2d_delta(&location->context, s, &header);
357 break;
358 case PDUTYPE_LOCATION3D_DELTA:
359 error = location_server_recv_location3d_delta(&location->context, s, &header);
360 break;
361 default:
362 WLog_ERR(TAG, "location_process_message: unknown or invalid pduType %" PRIu16 "",
363 pduType);
364 break;
365 }
366 }
367
368out:
369 if (error)
370 WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
371
372 return error;
373}
374
375static UINT location_server_context_poll_int(LocationServerContext* context)
376{
377 location_server* location = (location_server*)context;
378 UINT error = ERROR_INTERNAL_ERROR;
379
380 WINPR_ASSERT(location);
381
382 switch (location->state)
383 {
384 case LOCATION_INITIAL:
385 error = location_server_open_channel(location);
386 if (error)
387 WLog_ERR(TAG, "location_server_open_channel failed with error %" PRIu32 "!", error);
388 else
389 location->state = LOCATION_OPENED;
390 break;
391 case LOCATION_OPENED:
392 error = location_process_message(location);
393 break;
394 default:
395 break;
396 }
397
398 return error;
399}
400
401static HANDLE location_server_get_channel_handle(location_server* location)
402{
403 void* buffer = NULL;
404 DWORD BytesReturned = 0;
405 HANDLE ChannelEvent = NULL;
406
407 WINPR_ASSERT(location);
408
409 if (WTSVirtualChannelQuery(location->location_channel, WTSVirtualEventHandle, &buffer,
410 &BytesReturned) == TRUE)
411 {
412 if (BytesReturned == sizeof(HANDLE))
413 ChannelEvent = *(HANDLE*)buffer;
414
415 WTSFreeMemory(buffer);
416 }
417
418 return ChannelEvent;
419}
420
421static DWORD WINAPI location_server_thread_func(LPVOID arg)
422{
423 DWORD nCount = 0;
424 HANDLE events[2] = { 0 };
425 location_server* location = (location_server*)arg;
426 UINT error = CHANNEL_RC_OK;
427 DWORD status = 0;
428
429 WINPR_ASSERT(location);
430
431 nCount = 0;
432 events[nCount++] = location->stopEvent;
433
434 while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0))
435 {
436 switch (location->state)
437 {
438 case LOCATION_INITIAL:
439 error = location_server_context_poll_int(&location->context);
440 if (error == CHANNEL_RC_OK)
441 {
442 events[1] = location_server_get_channel_handle(location);
443 nCount = 2;
444 }
445 break;
446 case LOCATION_OPENED:
447 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
448 switch (status)
449 {
450 case WAIT_OBJECT_0:
451 break;
452 case WAIT_OBJECT_0 + 1:
453 case WAIT_TIMEOUT:
454 error = location_server_context_poll_int(&location->context);
455 break;
456
457 case WAIT_FAILED:
458 default:
459 error = ERROR_INTERNAL_ERROR;
460 break;
461 }
462 break;
463 default:
464 break;
465 }
466 }
467
468 (void)WTSVirtualChannelClose(location->location_channel);
469 location->location_channel = NULL;
470
471 if (error && location->context.rdpcontext)
472 setChannelError(location->context.rdpcontext, error,
473 "location_server_thread_func reported an error");
474
475 ExitThread(error);
476 return error;
477}
478
479static UINT location_server_open(LocationServerContext* context)
480{
481 location_server* location = (location_server*)context;
482
483 WINPR_ASSERT(location);
484
485 if (!location->externalThread && (location->thread == NULL))
486 {
487 location->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
488 if (!location->stopEvent)
489 {
490 WLog_ERR(TAG, "CreateEvent failed!");
491 return ERROR_INTERNAL_ERROR;
492 }
493
494 location->thread = CreateThread(NULL, 0, location_server_thread_func, location, 0, NULL);
495 if (!location->thread)
496 {
497 WLog_ERR(TAG, "CreateThread failed!");
498 (void)CloseHandle(location->stopEvent);
499 location->stopEvent = NULL;
500 return ERROR_INTERNAL_ERROR;
501 }
502 }
503 location->isOpened = TRUE;
504
505 return CHANNEL_RC_OK;
506}
507
508static UINT location_server_close(LocationServerContext* context)
509{
510 UINT error = CHANNEL_RC_OK;
511 location_server* location = (location_server*)context;
512
513 WINPR_ASSERT(location);
514
515 if (!location->externalThread && location->thread)
516 {
517 (void)SetEvent(location->stopEvent);
518
519 if (WaitForSingleObject(location->thread, INFINITE) == WAIT_FAILED)
520 {
521 error = GetLastError();
522 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
523 return error;
524 }
525
526 (void)CloseHandle(location->thread);
527 (void)CloseHandle(location->stopEvent);
528 location->thread = NULL;
529 location->stopEvent = NULL;
530 }
531 if (location->externalThread)
532 {
533 if (location->state != LOCATION_INITIAL)
534 {
535 (void)WTSVirtualChannelClose(location->location_channel);
536 location->location_channel = NULL;
537 location->state = LOCATION_INITIAL;
538 }
539 }
540 location->isOpened = FALSE;
541
542 return error;
543}
544
545static UINT location_server_context_poll(LocationServerContext* context)
546{
547 location_server* location = (location_server*)context;
548
549 WINPR_ASSERT(location);
550
551 if (!location->externalThread)
552 return ERROR_INTERNAL_ERROR;
553
554 return location_server_context_poll_int(context);
555}
556
557static BOOL location_server_context_handle(LocationServerContext* context, HANDLE* handle)
558{
559 location_server* location = (location_server*)context;
560
561 WINPR_ASSERT(location);
562 WINPR_ASSERT(handle);
563
564 if (!location->externalThread)
565 return FALSE;
566 if (location->state == LOCATION_INITIAL)
567 return FALSE;
568
569 *handle = location_server_get_channel_handle(location);
570
571 return TRUE;
572}
573
574static UINT location_server_packet_send(LocationServerContext* context, wStream* s)
575{
576 location_server* location = (location_server*)context;
577 UINT error = CHANNEL_RC_OK;
578 ULONG written = 0;
579
580 WINPR_ASSERT(location);
581 WINPR_ASSERT(s);
582
583 const size_t pos = Stream_GetPosition(s);
584 WINPR_ASSERT(pos <= UINT32_MAX);
585 if (!WTSVirtualChannelWrite(location->location_channel, Stream_BufferAs(s, char), (ULONG)pos,
586 &written))
587 {
588 WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
589 error = ERROR_INTERNAL_ERROR;
590 goto out;
591 }
592
593 if (written < Stream_GetPosition(s))
594 {
595 WLog_WARN(TAG, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "", written,
596 Stream_GetPosition(s));
597 }
598
599out:
600 Stream_Free(s, TRUE);
601 return error;
602}
603
604static UINT location_server_send_server_ready(LocationServerContext* context,
605 const RDPLOCATION_SERVER_READY_PDU* serverReady)
606{
607 wStream* s = NULL;
608 UINT32 pduLength = 0;
609 UINT32 protocolVersion = 0;
610
611 WINPR_ASSERT(context);
612 WINPR_ASSERT(serverReady);
613
614 protocolVersion = serverReady->protocolVersion;
615
616 pduLength = LOCATION_HEADER_SIZE + 4 + 4;
617
618 s = Stream_New(NULL, pduLength);
619 if (!s)
620 {
621 WLog_ERR(TAG, "Stream_New failed!");
622 return ERROR_NOT_ENOUGH_MEMORY;
623 }
624
625 /* RDPLOCATION_HEADER */
626 Stream_Write_UINT16(s, PDUTYPE_SERVER_READY);
627 Stream_Write_UINT32(s, pduLength);
628
629 Stream_Write_UINT32(s, protocolVersion);
630 Stream_Write_UINT32(s, serverReady->flags);
631
632 return location_server_packet_send(context, s);
633}
634
635LocationServerContext* location_server_context_new(HANDLE vcm)
636{
637 location_server* location = (location_server*)calloc(1, sizeof(location_server));
638
639 if (!location)
640 return NULL;
641
642 location->context.vcm = vcm;
643 location->context.Initialize = location_server_initialize;
644 location->context.Open = location_server_open;
645 location->context.Close = location_server_close;
646 location->context.Poll = location_server_context_poll;
647 location->context.ChannelHandle = location_server_context_handle;
648
649 location->context.ServerReady = location_server_send_server_ready;
650
651 location->buffer = Stream_New(NULL, 4096);
652 if (!location->buffer)
653 goto fail;
654
655 return &location->context;
656fail:
657 WINPR_PRAGMA_DIAG_PUSH
658 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
659 location_server_context_free(&location->context);
660 WINPR_PRAGMA_DIAG_POP
661 return NULL;
662}
663
664void location_server_context_free(LocationServerContext* context)
665{
666 location_server* location = (location_server*)context;
667
668 if (location)
669 {
670 location_server_close(context);
671 Stream_Free(location->buffer, TRUE);
672 }
673
674 free(location);
675}