FreeRDP
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 
29 typedef enum
30 {
31  LOCATION_INITIAL,
32  LOCATION_OPENED,
33 } eLocationChannelState;
34 
35 typedef 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 
55 static 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 
74 static 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 
125 static UINT location_server_recv_client_ready(LocationServerContext* context, wStream* s,
126  const RDPLOCATION_HEADER* header)
127 {
128  RDPLOCATION_CLIENT_READY_PDU pdu = { 0 };
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 
152 static 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 
196 static 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 
231 static 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 
267 static 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 
333 out:
334  if (error)
335  WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error);
336 
337  return error;
338 }
339 
340 static 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 
366 static 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  CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE));
379 
380  WTSFreeMemory(buffer);
381  }
382 
383  return ChannelEvent;
384 }
385 
386 static 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 
444 static 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 
473 static 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 
510 static 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 
522 static 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 
539 static 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 
564 out:
565  Stream_Free(s, TRUE);
566  return error;
567 }
568 
569 static 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 
600 LocationServerContext* 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;
621 fail:
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 
629 void 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 }